Siber Yıldız 2020 Write-Up EKS Elektrikler Gitti

2020, Dec 27    

Selamlar, bu yazımda Siber Yıldız 2020 yarışmasında sorulan Elektrikler Gitti isimli sorunun çözümünü anlatacağım.

Soru dosyası : Siber_Yildiz_ICS.pcap

Soruda Siber_Yildiz_ICS isminde bir pcap dosyası verilmiş. Ağ analizini yaparak flag değerini elde etmemiz istenmiş.

image-1

Pcap dosyasını wireshark aracıyla incelediğimizde içinde birçok protokole ait paket olduğunu farkediyoruz. Fakat soru adının Elektrikler Gitti olması ve kategorisinin EKS olması bize ipucu veriyor. Aradığımız paket türü endüstriyel sahada kullanılan bir protokole ait olabilir diye düşünerek incelemeye devam ediyoruz. İstatistikler sekmesinden protokol hiyerarşisini incelediğimizde pcap içindeki tüm protokolleri görüyoruz. Protokol hiyerarşisi içerisinde EKS spesifik ModbusTCP protokolünü ve S7Comm protokolünü görüyoruz.

image-1


image-1

Pcap içerisindeki paket oranlarına baktığımızda ModbusTCP protokolünün S7Comm‘a göre daha fazla pakete sahip olduğunu görüyoruz. Bu bir ipucu mu yoksa bir yanıltmaca mı diye öğrenmek için pcap’i incelemeye devam ediyoruz. Pcap içinde FTP trafiğinin olduğunu görüyoruz. FTP trafiğini incelediğimizde Aciklama.txt isminde bir text dosyasının transfer edildiğini görüyoruz. İlgili pakete sağ tıklayıp Follow -> TCP Stream seçeneği ile akışı görebiliriz.

image-1


image-1


image-1

Transfer edilen dosyayı bulabilmek için ftp-data filtresini kullanıyoruz. Filtre sonucunda karşımıza çıkan iki paketten birinde Aciklama.txt dosyasının yer aldığını görüyoruz. Sağ alttaki kısımda text dosyasının içinde bir python scriptinin olduğunu farkediyoruz.

image-1

Python scriptini daha net görebilmek ve scripti export edebilmek için ilgili pakate sağ tıklayıp Follow -> TCP Stream seçeneği ile açıyoruz. Açtıktan sonra python scriptini eks.py ismiyle kaydedelim.

image-1


image-1


image-1

Python scriptini incelediğimizde flag değerini verilen inputa göre ürettiğini görüyoruz. Peki bu input nedir?


#!/usr/bin/python
#coding:utf-8

coils_bytes = '**********************************'.decode('hex')
print len(coils_bytes)
flag = ''
for data in coils_bytes:
        #print int('{:08b}'.format(ord(data)))
        #print int('{:08b}'.format(ord(data)), 2)
        #print int('{:08b}'.format(ord(data))[::-1])
        #print int('{:08b}'.format(ord(data))[::-1], 2)
    #print int('{:08b}'.format(ord(data)),2),int('{:08b}'.format(ord(data))[::-1], 2)
    flag += chr(int('{:08b}'.format(ord(data))[::-1], 2))
print flag

Python scripti içerisindeki input değerini tutan değişkenin ismi ipucu niteliğinde : coils_bytes. Coil değişkeni ModbusTCP protokolünde kullanılan bir değişken tipidir.

Öncelikle ModbusTCP iletişimini anlamamız için protokol hakkında bilgi sahibi olmamız gerekir. ModbusTCP protokolü seri haberleşme ile değerleri cleartext olarak gönderir. ModbusTCP iletişiminde 2 taraf vardır: Master Cihaz ve Slave Cihaz. Master cihazı IT altyapılarında bulunan istemci(client) cihazlara benzetebiliriz. Slave cihazı ise yine IT altyapılarında bulunan sunucu(server) cihazlarına benzetebiliriz. Master ve slave cihazlar arasında istemci-sunucu mimarisine benzer şekilde request-response ilişkisi bulunur. Master cihaz slave cihaza istek paketi gönderir ve slave cihazdan gelen yanıtı bekler. Fakat buradaki bir diğer önemli husus ModbusTCP protokolünde bulunan fonksiyon kodlarıdır. Fonksiyon kodları protokol içinde gönderilen paketin içinde bulunan bir değerdir. Bu değer ile slave cihaz master cihazın isteğinin ne olduğunu anlar.

S7comm yerine ModbusTCP paketlerine bakmamız gerektiğini anlamış olduk. Modbus filtresiyle birlikte ModbusTCP paketlerini inceleyelim.

image-1

Yukarıdaki görselde görüldüğü üzere fonksiyon kodu 15‘tir. Bunun anlamı Write Multiple Coils şeklinde bir görev tanımıdır. Bu görev tanımıyla master cihaz slave cihaza der ki: göndermiş olduğum datayı benim sana belirtmiş olduğum birime kaydet. Yani bir başka deyişle slave cihaz üzerindeki coil değişkenlerinin değer alanına datayı yaz. Write görevi olan fonksiyon kodlarında data alanı, query paketleri içindedir. Bir query paketinin içindeki dataya bakalım. 28 byte uzunluğunda bir data gördük. Python scriptimize verebileceğimiz türden bir data.

image-1

Peki vereceğimiz data gerçekten bu mu? Birden çok query paketi var. Öncelikle response paketlerini eleyelim. modbus.data filtresiyle sadece query paketlerini görelim. 403 adet query paketinin olduğunu farkediyoruz. Manuel olarak tek tek data alanlarına bakmak pek mantıklı değil. Bu işi otomatize hale getirmeliyiz.

image-1

Query paketleri içindeki data alanlarının hepsini tek seferde alabilmek için tshark aracını kullanabiliriz. Öncelikle tshark aracına verebilmek için sadece query paketlerinden oluşan bir pcap dosyası olarak paketleri export edelim.

image-1

Dosya adını queries.pcap olacak şekilde yazalım.

image-1

Tshark aracının outputunda tekrar edecek sonuçları da eleyerek tüm modbus data alanlarını görelim.

kali@kali:~/siberyildiz2020$ tshark -r ./queries.pcap -T fields -e modbus.data | sort | uniq
9a86d63686ce2e9676fa82b686fa9a86763696cefa0a86d6a62e0000

f46c6c9c2ccc8666c61c0c66661c4ca6462cec0cec46c626462caccc2c9c86c6ac740e160efc9626bcac6c2c666c2ccc2c1ca666268626a69c0ccc86cc461ccc869c1cacecac9c260c

Karşımıza iki farklı hex değeri geliyor. Daha önceden kaydetmiş olduğumuz eks.py isimli python scriptimizdeki coils_bytes değişkeni içerisine bu iki hex değerini sırasıyla yazarak scripti çalıştıralım.


#!/usr/bin/python
#coding:utf-8

coils_bytes = '9a86d63686ce2e9676fa82b686fa9a86763696cefa0a86d6a62e0000'.decode('hex')
print len(coils_bytes)
flag = ''
for data in coils_bytes:
        #print int('{:08b}'.format(ord(data)))
        #print int('{:08b}'.format(ord(data)), 2)
        #print int('{:08b}'.format(ord(data))[::-1])
        #print int('{:08b}'.format(ord(data))[::-1], 2)
    #print int('{:08b}'.format(ord(data)),2),int('{:08b}'.format(ord(data))[::-1], 2)
    flag += chr(int('{:08b}'.format(ord(data))[::-1], 2))
print flag


kali@kali:~/siberyildiz2020$ sudo python eks.py 
28
Yaklastin_Ama_Yanlis_Paket

Yanlış paketteki değer ile flag değerini oluşturmaya çalıştığımızı söyleyen bir yanıt ile karşılaştık. Diğer hex değerini deneyelim.


#!/usr/bin/python
#coding:utf-8

coils_bytes = 'f46c6c9c2ccc8666c61c0c66661c4ca6462cec0cec46c626462caccc2c9c86c6ac740e160efc9626bcac6c2c666c2ccc2c1ca666268626a69c0ccc86cc461ccc869c1cacecac9c260c'.decode('hex')
print len(coils_bytes)
flag = ''
for data in coils_bytes:
        #print int('{:08b}'.format(ord(data)))
        #print int('{:08b}'.format(ord(data)), 2)
        #print int('{:08b}'.format(ord(data))[::-1])
        #print int('{:08b}'.format(ord(data))[::-1], 2)
    #print int('{:08b}'.format(ord(data)),2),int('{:08b}'.format(ord(data))[::-1], 2)
    flag += chr(int('{:08b}'.format(ord(data))[::-1], 2))
print flag


kali@kali:~/siberyildiz2020$ sudo python eks.py 
73
/66943afc80ff82eb4707bcdb45349ac5.php?id=564f64348efdade903a3b83a985759d0

Bingo! Flag değerini alacağımız adrese ait path değerini elde etmiş olduk. Soruya ait IP adresine path olarak ekleyip flag değerimizi alalım.

image-1


image-1

Yarışma sorusunu hazırlayan ve yarışmayı düzenleyen ekibe teşekkürler.

Yazımı okuduğunuz için teşekkürler, bir başka yazıda görüşmek dileğiyle.. :smile: