September 26, 2026
EKS/ICS Pentest Labı — Bölüm 5: FUXA SCADA ve OpenPLC Entegrasyonu
Bölüm 4'te OpenPLC Runtime’ı Docker üzerinde çalıştırarak PLC katmanını oluşturduk. Structured Text kullanarak motor, pompa, vana, tank…

By Sefa Yıldırım
7 min read
Bölüm 4'te OpenPLC Runtime'ı Docker üzerinde çalıştırarak PLC katmanını oluşturduk. Structured Text kullanarak motor, pompa, vana, tank seviyesi, sıcaklık ve alarm değişkenlerini içeren basit bir proses simülasyonu geliştirdik.
Ayrıca OpenPLC'nin 502/TCP üzerinden Modbus TCP haberleşmesini doğruladık ve Kali Linux üzerinden mbpoll kullanarak PLC değişkenlerini okuyup değiştirebildik.
Bu bölümde ise laboratuvarın SCADA katmanını PLC ile birleştireceğiz.
Amaç, Bölüm 4'te terminal üzerinden gerçekleştirdiğimiz manuel kontrolleri bu kez FUXA SCADA arayüzü üzerinden gerçekleştirmek ve PLC ile SCADA arasındaki haberleşmeyi uçtan uca doğrulamaktır.
Hedeflediğimiz yapı:
ICS / OT LAB
|
Host-Only Network
192.168.56.0/24
|
+------------+------------+
| |
Kali Linux OT Server
192.168.56.102 192.168.56.20
|
Docker Environment
|
+----------+----------+
| |
FUXA SCADA OpenPLC Runtime
| |
+---- Modbus TCP -----+
TCP/502 ICS / OT LAB
|
Host-Only Network
192.168.56.0/24
|
+------------+------------+
| |
Kali Linux OT Server
192.168.56.102 192.168.56.20
|
Docker Environment
|
+----------+----------+
| |
FUXA SCADA OpenPLC Runtime
| |
+---- Modbus TCP -----+
TCP/502Bu bölümün sonunda FUXA üzerinden OpenPLC'ye Modbus TCP bağlantısı kurmuş olacak ve SCADA ekranından PLC üzerindeki proses değerlerini izleyip kontrol edebileceğiz.
Örneğin hedeflediğimiz akış:
FUXA SCADA
|
| Modbus TCP
▼
OpenPLC Runtime
|
▼
PLC Control Logic
|
├── Motor
├── Pump
├── Valve
├── Tank Level
├── Temperature
└── AlarmFUXA SCADA
|
| Modbus TCP
▼
OpenPLC Runtime
|
▼
PLC Control Logic
|
├── Motor
├── Pump
├── Valve
├── Tank Level
├── Temperature
└── Alarmöylece laboratuvarımız, yalnızca doğrudan PLC'ye erişebildiğimiz bir yapıdan, SCADA → PLC → Proses ilişkisinin gözlemlenebildiği daha gerçekçi bir OT ortamına dönüşecek.
FUXA SCADA Web Arayüzünün Doğrulanması
Bölüm 3'te Docker üzerinde kurduğumuz FUXA container'ını yeniden çalıştırdıktan sonra, SCADA Web arayüzüne Kali Linux üzerinden erişimi doğruladık.
Tarayıcı üzerinden:
http://192.168.56.20:1881http://192.168.56.20:1881adresine erişildiğinde FUXA Web arayüzünün başarıyla açıldığı görüldü.
Bu aşamada SCADA katmanının çalıştığını ve Kali Linux üzerinden OT Server üzerindeki FUXA servisine erişilebildiğini doğrulamış olduk.
FUXA SCADA Projesinin Hazırlanması
Şimdi FUXA içerisinde OpenPLC ile haberleşecek SCADA projesini oluşturacağız.
Amacımız henüz grafik ekran tasarlamak değil. Öncelikle FUXA'nın OpenPLC'yi bir Modbus TCP cihazı olarak tanımasını sağlayacağız.
Hedef bağlantımız:
FUXA SCADA
|
| Modbus TCP
| 192.168.56.20:502
▼
OpenPLC RuntimeFUXA SCADA
|
| Modbus TCP
| 192.168.56.20:502
▼
OpenPLC RuntimeBurada önemli nokta şu:
FUXA ve OpenPLC aynı OT Server üzerinde Docker container olarak çalışıyor olsa da, bizim SCADA tarafındaki haberleşme hedefimiz OpenPLC'nin OT Server üzerinden yayınladığı 502/TCP Modbus servisi olacak.
FUXA Modbus Haberleşme Desteğinin Hazırlanması
FUXA Editor içerisinde Connections bölümüne geçtiğimizde, cihaz bağlantıları için gerekli protokol sürücülerinin Plugins bölümü üzerinden kurulması gerektiğini gördük.
Bu nedenle öncelikle FUXA'nın Plugins bölümünü açtık.
Burada farklı endüstriyel haberleşme protokollerine ait çeşitli driver'ların bulunduğu görüldü. Modbus TCP haberleşmesi için ise:
MODBUS
└── modbus-serialMODBUS
└── modbus-serialeklenti olarak listeleniyordu.
Eklentinin mevcut durumu:
Plugin : modbus-serial
Version : 8.0.19
Installed : -Plugin : modbus-serial
Version : 8.0.19
Installed : -Bu nedenle modbus-serial eklentisini FUXA içerisine kurduk.
Bu aşamayla birlikte FUXA tarafında Modbus cihaz bağlantısını oluşturmak için gerekli driver'ın kurulumu tamamlanmış oldu.
OpenPLC'nin FUXA'ya Modbus TCP Cihazı Olarak Eklenmesi
Modbus driver kurulumunun ardından FUXA'nın Connections bölümüne geri döndük.
Bu bölüm üzerinden FUXA ile haberleşecek cihazların bağlantı tanımlarını oluşturabiliyoruz. Laboratuvarımızdaki PLC katmanı OpenPLC olduğu için yeni bir bağlantı oluşturarak OpenPLC Runtime'ı FUXA'ya bir Modbus TCP cihazı olarak tanımladık.
Yeni bağlantı oluşturmak için + butonunu kullandık ve bağlantıya:
OpenPLCOpenPLCadını verdik.
Bağlantı tipi olarak ise:
ModbusTCPModbusTCPseçeneğini kullandık.
OpenPLC Runtime'ın OT Server üzerinde 502/TCP portu üzerinden Modbus TCP servisi sunduğunu Bölüm 4'te doğrulamıştık. Bu nedenle FUXA tarafındaki bağlantı hedefini:
192.168.56.20:502192.168.56.20:502olarak belirledik.
OpenPLC'nin Modbus haberleşmesinde kullandığımız Slave ID ise:
11olarak tanımlandı.
Bağlantının temel yapılandırması şu şekilde oldu:
ParametreDeğerNameOpenPLCTypeModbusTCPPolling500 msConnection OptionsTcpPortSlave IP and Port192.168.56.20:502Slave ID1EnableAktif
Diğer gelişmiş bağlantı seçeneklerinde FUXA'nın varsayılan değerlerini koruduk.
Yapılandırmayı kaydettikten sonra Connections ekranında OpenPLC isimli bağlantının oluşturulduğunu gördük.
Bağlantı kartında:
OpenPLC
192.168.56.20:502
Modbus-TCP Slave ID: 1OpenPLC
192.168.56.20:502
Modbus-TCP Slave ID: 1bilgileri görüntülendi.
Ayrıca bağlantının yanında bulunan yeşil durum göstergesi, FUXA'nın OpenPLC Runtime ile Modbus TCP bağlantısını aktif olarak kurabildiğini gösterdi.
Böylece FUXA SCADA ile OpenPLC arasındaki temel haberleşme kanalını oluşturmuş olduk.
Bu noktada laboratuvarımızdaki iletişim akışı artık şu hale geldi:
Kali Linux
|
| HTTP
▼
FUXA SCADA
|
| Modbus TCP
| 192.168.56.20:502
▼
OpenPLC Runtime
|
▼
PLC Control LogicKali Linux
|
| HTTP
▼
FUXA SCADA
|
| Modbus TCP
| 192.168.56.20:502
▼
OpenPLC Runtime
|
▼
PLC Control LogicArtık FUXA yalnızca çalışan bir SCADA Web arayüzü olmaktan çıkıp, OpenPLC üzerinde çalışan proses değişkenleriyle haberleşebilen bir SCADA katmanı haline gelmiş durumda.
İlk olarak OpenPLC içerisinde daha önce oluşturduğumuz Tank_Level değişkenini FUXA tarafında Tag olarak tanımladık. Tag yapılandırmasının ardından FUXA'nın OpenPLC'den veri okuyabildiğini doğrulamak amacıyla Tank_Level değerini kontrol ettik.
FUXA ekranında Tank_Level değişkeninin değerinin 500 olarak görüntülenmesiyle birlikte, SCADA → Modbus TCP → OpenPLC haberleşmesinin başarılı şekilde çalıştığını doğrulamış olduk.
OpenPLC Değişkenlerinin FUXA Tag'leri Olarak Tanımlanması
Tank_Level değişkeninin başarıyla okunmasının ardından diğer proses değişkenlerini de FUXA tarafında Tag olarak tanımlamaya geçiyoruz.
Bölüm 4'te oluşturduğumuz PLC programında motor, pompa, vana, tank seviyesi, sıcaklık ve alarm durumlarını temsil eden değişkenler bulunuyordu.
Modbus tarafında bu değişkenler iki temel veri alanında yer alıyor:
Coils
├── Motor_Command
├── Pump_Command
├── Valve_Command
├── Motor
├── Pump
├── Valve
└── Alarm
Holding Registers
├── Tank_Level
└── TemperatureCoils
├── Motor_Command
├── Pump_Command
├── Valve_Command
├── Motor
├── Pump
├── Valve
└── Alarm
Holding Registers
├── Tank_Level
└── TemperatureBu nedenle FUXA içerisinde Tag oluştururken değişkenin veri tipine göre ilgili Modbus alanını seçmemiz gerekiyor.
Tank_Level için kullandığımız yapı:
ParametreDeğerDeviceOpenPLCTagnameTank_LevelRegisterHolding RegistersTypeInt16Address Offset1
FUXA'da oluşturulan Tag şu şekilde görüntülendi:
Name Address Connection Type Value
----------------------------------------------------
Tank_Level 400001 OpenPLC Int16 500Name Address Connection Type Value
----------------------------------------------------
Tank_Level 400001 OpenPLC Int16 500Buradaki 500 değeri, Bölüm 4'te Kali Linux üzerinden mbpoll kullanarak OpenPLC'nin ilgili register'ından okuduğumuz değerle aynıdır.
Böylece FUXA'nın yalnızca OpenPLC'ye bağlantı kurabildiğini değil, PLC içerisindeki gerçek bir proses değişkenini okuyabildiğini de doğrulamış olduk.
FUXA Üzerinden PLC Değişkeninin Kontrol Edilmesi
FUXA üzerinden yalnızca PLC değişkenlerini okumakla kalmayıp, SCADA arayüzü üzerinden PLC'ye komut gönderebilmek için bir kontrol elemanı oluşturduk.
İlk testimizde PLC içerisindeki Motor_Command değişkenini kontrol edeceğiz. Bu değişken OpenPLC tarafında bir BOOL olarak tanımlanmış ve Modbus TCP üzerinden Coil alanında bulunmaktadır.
Motor_Command için Switch Oluşturulması
FUXA Editor içerisinde MainView ekranına geçerek Controls bölümünde bulunan Switch kontrolünü çalışma alanına ekledik.
Eklediğimiz Switch'in özelliklerine girerek Tag alanını yapılandırdık. Tag seçim ekranında daha önce oluşturduğumuz:
OpenPLC → Motor_CommandOpenPLC → Motor_CommandTag'ini seçtik.
Böylece FUXA üzerindeki Switch ile OpenPLC içerisindeki Motor_Command değişkeni arasında doğrudan bir ilişki oluşturmuş olduk.
Switch yapılandırmasında:
ParametreDeğerNameswitch_1TagMotor_CommandOff Value0On Value1
olarak belirlendi.
Bu yapılandırmayla Switch kapalı durumdayken FUXA Motor_Command değişkenine 0, açık duruma getirildiğinde ise 1 değerini yazacaktır.
[Switch ayarları ekran görüntüsü]
Switch'i kaydettikten sonra FUXA'nın çalışma ekranında kontrol elemanının görüntülendiğini gördük.
FUXA → OpenPLC Yazma İşleminin Doğrulanması
FUXA çalışma ekranında oluşturduğumuz Switch'i aktif hale getirdik.
Bunun sonucunda:
Motor_Command = 1Motor_Command = 1değerinin OpenPLC'ye Modbus TCP üzerinden gönderilmesini bekledik.
Yazma işleminin gerçekten PLC'ye ulaştığını doğrulamak için Kali Linux üzerinden mbpoll kullanarak ilgili Coil değerlerini tekrar okuduk:
mbpoll -m tcp -a 1 -t 0 -r 1 -c 4 192.168.56.20mbpoll -m tcp -a 1 -t 0 -r 1 -c 4 192.168.56.20Elde edilen sonuç:
[1]: 1
[2]: 0
[3]: 0
[4]: 1[1]: 1
[2]: 0
[3]: 0
[4]: 1şeklinde oldu.
Burada:
[1] → Motor_Command = 1
[4] → Motor = 1[1] → Motor_Command = 1
[4] → Motor = 1olarak görülmektedir.
Motor_Command değerinin 1 olması FUXA üzerinden gerçekleştirilen yazma işleminin OpenPLC'ye ulaştığını gösterirken, Motor değerinin de 1 olması PLC programındaki kontrol mantığının komutu işlediğini göstermektedir.
Böylece FUXA ile OpenPLC arasındaki haberleşmeyi yalnızca okuma seviyesinde değil, yazma ve PLC kontrol mantığının çalıştırılması seviyesinde de doğrulamış olduk.
FUXA SCADA
|
| Motor_Command = 1
▼
Modbus TCP
|
▼
OpenPLC
|
| PLC Logic
▼
Motor = 1FUXA SCADA
|
| Motor_Command = 1
▼
Modbus TCP
|
▼
OpenPLC
|
| PLC Logic
▼
Motor = 1Bu test ile birlikte laboratuvarımızdaki temel SCADA → PLC → Proses iletişim akışı uçtan uca doğrulanmış oldu.
SCADA Kontrol Yapısının Tamamlanması
Motor kontrolünün FUXA üzerinden başarıyla doğrulanmasının ardından, aynı yapı kullanılarak diğer proses kontrol değişkenleri için de HMI kontrolleri oluşturuldu.
FUXA MainView üzerinde oluşturulan kontrol yapısı:
| HMI Kontrolü | PLC Tag | Modbus Alanı | Görevi |
|---|---|---|---|
| Motor Switch | `Motor_Command` | Coil `1` | Motor komutu |
| Pump Switch | `Pump_Command` | Coil `2` | Pompa komutu |
| Valve Switch | `Valve_Command` | Coil `3` | Vana komutu || HMI Kontrolü | PLC Tag | Modbus Alanı | Görevi |
|---|---|---|---|
| Motor Switch | `Motor_Command` | Coil `1` | Motor komutu |
| Pump Switch | `Pump_Command` | Coil `2` | Pompa komutu |
| Valve Switch | `Valve_Command` | Coil `3` | Vana komutu |Bu noktada SCADA ekranı üzerinden PLC'ye gönderilecek temel proses komutları tamamlanmış oldu.
PLC tarafından üretilen durum ve proses değerleri ise FUXA tarafında daha önce oluşturduğumuz Tag'ler üzerinden izlenmektedir:
| PLC Değişkeni | Modbus Alanı | FUXA Tipi |
|---|---|---|
| `Motor` | Coil `4` | `Bool` |
| `Pump` | Coil `5` | `Bool` |
| `Valve` | Coil `6` | `Bool` |
| `Alarm` | Coil `7` | `Bool` |
| `Tank_Level` | Holding Register `400001` | `Int16` |
| `Temperature` | Holding Register `400002` | `Int16` || PLC Değişkeni | Modbus Alanı | FUXA Tipi |
|---|---|---|
| `Motor` | Coil `4` | `Bool` |
| `Pump` | Coil `5` | `Bool` |
| `Valve` | Coil `6` | `Bool` |
| `Alarm` | Coil `7` | `Bool` |
| `Tank_Level` | Holding Register `400001` | `Int16` |
| `Temperature` | Holding Register `400002` | `Int16` |Böylece FUXA tarafında komut değişkenleri ile PLC tarafından üretilen durum/proses değişkenlerini birbirinden ayıran temel bir SCADA yapısı oluşturuldu.
Uçtan Uca Proses Kontrollerinin Doğrulanması
SCADA kontrol yapısının tamamlanmasının ardından, FUXA üzerinden gönderilen komutların OpenPLC tarafından doğru şekilde işlenip işlenmediğini ve PLC kontrol mantığının proses değişkenleri üzerindeki etkisini test ettik.
Testler sırasında FUXA üzerinden motor, pompa ve vana komutları kontrol edilirken, oluşan durum değişiklikleri FUXA üzerindeki Tag değerlerinden ve Kali Linux üzerinden gerçekleştirilen Modbus TCP okumalarından doğrulandı.
İlk olarak motor komutu test edildi. Motor_Command değeri 1 yapıldığında PLC tarafından üretilen Motor değerinin de 1 olduğu görüldü. Böylece FUXA üzerinden gönderilen komutun PLC kontrol mantığı tarafından işlendiği doğrulandı.
Pompa testi sırasında motor aktif durumda tutuldu ve Pump_Command değeri 1 yapıldı. Bunun sonucunda Pump değeri aktif hale gelirken Tank_Level değerinin artarak 1000 seviyesine ulaştığı ve Temperature değerinin 80 seviyesine yükseldiği gözlemlendi.
Vana testinde ise motor ve pompa kapalı tutuldu ve yalnızca Valve_Command aktif hale getirildi. Bu durumda Valve değeri 1 olurken Tank_Level değerinin kademeli olarak azalarak 0 seviyesine ulaştığı doğrulandı.
Son olarak alarm mekanizması test edildi. PLC programındaki kontrol mantığına göre Tank_Level değeri 800 veya üzerinde olduğunda Alarm değeri 1 olmakta, değer 800 seviyesinin altına düştüğünde ise Alarm tekrar 0 olmaktadır. Yapılan testlerde her iki durum da doğrulandı.
Test sonuçlarını özetlersek:
| Test | Beklenen Davranış | Sonuç |
|---|---|---|
| Motor kontrolü | `Motor_Command = 1` → `Motor = 1` | Başarılı |
| Pompa kontrolü | `Pump_Command = 1` → `Pump = 1` ve `Tank_Level` artışı | Başarılı |
| Vana kontrolü | `Valve_Command = 1` → `Valve = 1` ve `Tank_Level` azalışı | Başarılı |
| Temperature | Motor aktifken sıcaklığın yükselmesi | Başarılı |
| Alarm aktivasyonu | `Tank_Level >= 800` → `Alarm = 1` | Başarılı |
| Alarm reset | `Tank_Level < 800` → `Alarm = 0` | Başarılı || Test | Beklenen Davranış | Sonuç |
|---|---|---|
| Motor kontrolü | `Motor_Command = 1` → `Motor = 1` | Başarılı |
| Pompa kontrolü | `Pump_Command = 1` → `Pump = 1` ve `Tank_Level` artışı | Başarılı |
| Vana kontrolü | `Valve_Command = 1` → `Valve = 1` ve `Tank_Level` azalışı | Başarılı |
| Temperature | Motor aktifken sıcaklığın yükselmesi | Başarılı |
| Alarm aktivasyonu | `Tank_Level >= 800` → `Alarm = 1` | Başarılı |
| Alarm reset | `Tank_Level < 800` → `Alarm = 0` | Başarılı |Bu testlerle birlikte FUXA üzerinden oluşturduğumuz SCADA kontrol yapısının OpenPLC ile haberleştiği ve PLC'nin aldığı komutları kendi kontrol mantığı üzerinden işleyerek proses değişkenlerini değiştirdiği doğrulanmış oldu.
Aynı zamanda FUXA'da görüntülenen değerlerin Kali Linux üzerinden mbpoll ile gerçekleştirilen Modbus TCP okumalarıyla karşılaştırılması, SCADA katmanı ile PLC arasındaki veri akışının tutarlı olduğunu gösterdi.
Sonuç olarak laboratuvarımızdaki temel iletişim ve kontrol zinciri:
FUXA SCADA
|
| Modbus TCP
▼
OpenPLC Runtime
|
▼
PLC Control Logic
|
▼
Process Variables
|
├── Motor
├── Pump
├── Valve
├── Tank_Level
├── Temperature
└── AlarmFUXA SCADA
|
| Modbus TCP
▼
OpenPLC Runtime
|
▼
PLC Control Logic
|
▼
Process Variables
|
├── Motor
├── Pump
├── Valve
├── Tank_Level
├── Temperature
└── Alarmşeklinde uçtan uca doğrulanmış oldu.
Sonuç
Böylece laboratuvarımızdaki SCADA → PLC → Proses iletişim ve kontrol zinciri uçtan uca doğrulanmış oldu.
Bir sonraki bölümde bu yapıyı saldırgan perspektifiyle ele alarak OT/ICS pentest aşamasına geçeceğiz.