Before all
今天心血來潮研究了一下工業控制和Modbusㄉ東西,最後在github上寫了個小專案,歡迎來玩玩 ><
https://github.com/William957-web/modbus_weather_station_lab
References:
https://www.csimn.com/CSI_pages/Modbus101.html
https://pymodbus.readthedocs.io/en/latest/source/client.html
P.S. TryHackMe上面的 Attacking ICS Plant系列推薦去打打看
Note
工控名詞
所謂的工控,就是工業控制(?)好像很明確了
看幾個常見名詞
- Operational Technology, OT
- Industrial Control System, ICS
- Programmable Logic Controller, PLC
- Supervisor Control And Data Acqusition, SCADA
- Distributed Control System, DCS
- Human Machine Interaction, HMI
簡單來說,OT就是操作技術的大圈圈,而ICS就是今天的主角工業控制
工業控制裡面大致上又可以分為 PLC (邏輯控制的程式)、SCADA(可監控和控制的系統)、DCS (分散式的控制系統)以及 HMI(人機互動的部分)
SCADA v.s. DCS
相較之下,通常DCS更注重於控制,而SCADA則注重監測
而SCADA也較為靈活,但並沒有絕對的優缺比較,而兩者本質上是出於相同目的的。
最後,DCS, SCADA會用到PLC,本身也會被HMI去做實體化,而PLC就是接收SENSOR跟呼叫ACTUATORS,所以它們之間的關係圖大概長這樣:
P.S. 在正式部署前的測試環境叫做testbed
modbus protocol
工業控制有很多種 protocol,modbus就是常用的其中一種!
這些protocol的目的是在工控環境下傳遞正確的資訊,讓工廠有正確的操作。
再來是 modbus 的簡介:
本體是基於RTU over RS-485(實體層),而遠端呼叫時modbus是透過tcp傳送的,也是後續lab採用的方法(容易被打的地方)。
modbus協議定義了主從(master/slave)的關係,必須有個master以及一個以上的slave去接收/回顯訊息。
每個 modbus 的封包都必須包含一個 function code 以及要呼叫的變量(?)以及值
Function Code Table
Function Code | Register Type |
---|---|
1 | Read Coil |
2 | Read Discrete Input |
3 | Read Holding Registers |
4 | Read Input Registers |
5 | Write Single Coil |
6 | Write Single Holding Register |
15 | Write Multiple Coils |
16 | Write Multiple Holding Registers |
變量分為這三種:
- Discrete Input : 1 bit, read only
- Coil : 1 bit, read/write
- Input Registers : 16 bits, read only
- Holding Registers : 16 bits, read/write
傳遞時封包列起來會像這樣:
(Read Holding Registers)
(Write Single Holding Register)
會看到有個 Func 的選項,就是丟出去的 function code,modbus段就是丟出去的參數們
Python Implementation
以我做的 lab 為範例
安裝 python modbus 庫,就包含server端跟client端ㄌ
1 | pip3 install pymodbus==2.5.2 |
p.s. 以前版本限制沒那麼多,我是用這個
Client
attack/recon.py
1 | #!/usr/bin/env python3 |
利用ModbusClient
建立連線,以read_holding_registers
去閱讀Holding Register
,就是func code 3的呼叫
attack/set_register.py
1 | #!/usr/bin/env python3 |
跟剛剛很像,以write_register
去寫入register
Server
station.py
1 | from pymodbus.server.sync import StartTcpServer |
這段程式碼建立了一百個初始值為0的data block
1 | store = ModbusSlaveContext( |
接著以context = ModbusServerContext(slaves=store, single=True)
建立一個 modbus server(slave資料就是剛剛的datablock)
函數update_registers
裡面的setValues
則是去改 register 值。
大致上就是這些啦~
After all
工控其實挺有趣的,之前在HTB也有打過抽換惡意PLC彈rev shell的題目,之後可以多碰碰
但要先處理資格考…🐳