
Di PHDays 9, kami mengadakan kompetisi untuk meretas pabrik pompa gas - kontes
Ninja Industri . Ada tiga stan di lokasi dengan berbagai parameter keselamatan (Tidak Ada Keamanan, Keamanan Rendah, Keamanan Tinggi) meniru proses industri yang sama: udara dipompa ke balon (dan kemudian turun) di bawah tekanan.
Terlepas dari berbagai parameter keamanan, perangkat keras dudukannya tetap sama: Siemens Simatic S7-300 PLC; tombol blow-off darurat dan alat pengukur tekanan (terhubung ke input digital PLC (DI)); katup untuk pemompaan dan pendarahan (terhubung ke output digital PLC (DO)) - lihat gambar di bawah ini.

PLC, tergantung pada pembacaan tekanan dan sesuai dengan programnya, membuat keputusan tentang meniup atau meniup bola (membuka dan menutup katup yang sesuai). Namun, mode kontrol manual disediakan di semua dudukan, yang memungkinkan untuk mengontrol kondisi katup tanpa batasan apa pun.
Stand dibedakan oleh kompleksitas memungkinkan mode ini: pada stand yang tidak terlindungi itu paling mudah dilakukan, tetapi pada stand High Security, karenanya, itu lebih sulit.
Dalam dua hari, lima dari enam masalah diselesaikan; pemenang tempat pertama mendapat 233 poin (dia menghabiskan satu minggu mempersiapkan kompetisi). Tiga pemenang: I place - a1exdandy, II - Rubikoid, III - Ze.
Namun, selama PHDays, tidak ada peserta yang mampu mengatasi ketiga tribun, jadi kami memutuskan untuk melakukan kontes online dan menerbitkan tugas yang paling sulit pada awal Juni. Peserta harus menyelesaikan tugas dalam sebulan, menemukan bendera, menjelaskan solusinya secara rinci dan menarik.
Di bawah potongan, kami menerbitkan analisis solusi terbaik untuk penugasan yang dikirim selama sebulan, ditemukan oleh Alexey Kovrizhnykh (a1exdandy) dari perusahaan Keamanan Digital, yang menempati posisi pertama dalam kompetisi selama PHDays. Di bawah ini kami berikan teksnya dengan komentar kami.
Analisis awal
Jadi, dalam tugas itu ada arsip dengan file:
- block_upload_traffic.pcapng
- DB100.bin
- hints.txt
File hints.txt berisi informasi dan tips yang diperlukan untuk menyelesaikan tugas. Berikut isinya:
- Petrovich memberi tahu saya kemarin bahwa dari PlcSim Anda dapat mengunduh blok di Step7.
- Di stan, Siemens Simatic S7-300 series PLC digunakan.
- PlcSim adalah emulator PLC yang memungkinkan Anda menjalankan dan men-debug program untuk Siemens S7 PLCs.
File DB100.bin, tampaknya, berisi blok data DB100 PLC:
00000000: 0100 0102 6e02 0401 0206 0100 0101 0102 .... n ...........
00000010: 1002 0501 0202 2002 0501 0206 0100 0102 ...... .........
00000020: 0102 7702 0401 0206 0100 0103 0102 0a02 ..w .............
00000030: 0501 0202 1602 0501 0206 0100 0104 0102 ................
00000040: 7502 0401 0206 0100 0105 0102 0a02 0501 u ...............
00000050: 0202 1602 0501 0206 0100 0106 0102 3402 .............. 4.
00000060: 0401 0206 0100 0107 0102 2602 0501 0202 .......... & .....
00000070: 4c02 0501 0206 0100 0108 0102 3302 0401 L ........... 3 ...
00000080: 0206 0100 0109 0102 0a02 0501 0202 1602 ................
00000090: 0501 0206 0100 010a 0102 3702 0401 0206 .......... 7 .....
000000a0: 0100 010b 0102 2202 0501 0202 4602 0501 ...... "..... F ...
000000b0: 0206 0100 010c 0102 3302 0401 0206 0100 ........ 3 .......
000000c0: 010d 0102 0a02 0501 0202 1602 0501 0206 ................
000000d0: 0100 010e 0102 6d02 0401 0206 0100 010f ...... m .........
000000e0: 0102 1102 0501 0202 2302 0501 0206 0100 ........ # .......
000000f0: 0110 0102 3502 0401 0206 0100 0111 0102 .... 5 ...........
00000100: 1202 0501 0202 2502 0501 0206 0100 0112 ......% .........
00000110: 0102 3302 0401 0206 0100 0113 0102 2602 ..3 ........... &.
00000120: 0501 0202 4c02 0501 0206 0100 .... L .......
Dilihat dari namanya, file block_upload_traffic.pcapng berisi dump lalu lintas pemuatan blok ke PLC.
Perlu dicatat bahwa tumpukan lalu lintas di situs kompetisi selama konferensi ini sedikit lebih sulit didapat. Untuk melakukan ini, perlu dipahami skrip dari file proyek untuk TeslaSCADA2. Dari situ dimungkinkan untuk memahami di mana dump yang dienkripsi dengan RC4 berada dan kunci mana yang harus digunakan untuk mendekripsi. Dump blok data di situs dapat diperoleh menggunakan klien protokol S7. Saya menggunakan klien demo dari paket Snap7 untuk ini.
Ekstraksi unit pemrosesan sinyal dari tempat pembuangan lalu lintas
Melihat isi dump, Anda dapat memahami bahwa blok pemrosesan sinyal OB1, FC1, FC2 dan FC3 ditransmisikan di dalamnya:

Diperlukan untuk mengekstrak blok ini. Ini dapat dilakukan, misalnya, dengan skrip berikut, setelah mengonversi lalu lintas dari format pcapng ke pcap:
Setelah mempelajari blok yang diterima, Anda dapat melihat bahwa mereka selalu dimulai dengan byte 70 70 (hal). Sekarang Anda perlu belajar cara menganalisisnya. Sebuah petunjuk untuk tugas tersebut menunjukkan bahwa Anda perlu menggunakan PlcSim untuk ini.
Mendapatkan instruksi yang bisa dibaca manusia dari blok
Pertama, mari kita coba program S7-PlcSim dengan memuat beberapa blok dengan instruksi berulang (= Q 0,0) ke dalamnya menggunakan perangkat lunak Simatic Manager, dan simpan hasilnya dalam emulator PLC dalam file example.plc. Dengan melihat isi file, Anda dapat dengan mudah menentukan awal blok yang dimuat dengan tanda tangan 70 70, yang kami temukan sebelumnya. Sebelum blok, tampaknya, ukuran blok ditulis dalam bentuk nilai bit-endian 4-byte.

Setelah kami menerima informasi tentang struktur file plc, rencana tindakan berikut muncul untuk membaca program PLC S7:
- Menggunakan Simatic Manager, kami membuat struktur blok di S7-PlcSim mirip dengan yang kami dapatkan dari dump. Ukuran blok harus sesuai (dicapai dengan mengisi blok dengan jumlah instruksi yang tepat) dan pengidentifikasi mereka (OB1, FC1, FC2, FC3).
- Simpan PLC ke file.
- Kami mengganti konten blok dalam file yang diterima dengan blok dari dump lalu lintas. Awal blok ditentukan oleh tanda tangan.
- File yang dihasilkan diunggah ke S7-PlcSim dan kami melihat isi blok di Simatic Manager.
Blok dapat diganti, misalnya, dengan kode berikut:
with open('original.plc', 'rb') as f: plc = f.read() blocks = [] for fname in ['OB1.bin', 'FC1.bin', 'FC2.bin', 'FC3.bin']: with open(fname, 'rb') as f: blocks.append(f.read()) i = plc.find(b'pp') for block in blocks: plc = plc[:i] + block + plc[i+len(block):] i = plc.find(b'pp', i + 1) with open('target.plc', 'wb') as f: f.write(plc)
Aleksey melanjutkan jalan yang mungkin lebih rumit, tetapi tetap dengan cara yang benar. Kami berasumsi bahwa peserta akan menggunakan program NetToPlcSim sehingga PlcSim dapat berkomunikasi melalui jaringan, memuat blok ke PlcSim melalui Snap7, dan kemudian mengunduh blok ini sebagai proyek dari PlcSim menggunakan lingkungan pengembangan.
Dengan membuka file yang dihasilkan di S7-PlcSim, Anda dapat membaca blok yang ditimpa menggunakan Simatic Manager. Fungsi manajemen perangkat utama dicatat di blok FC1. Variabel # TEMP0 menarik perhatian khusus, ketika dihidupkan, tampaknya kontrol PLC beralih ke mode manual berdasarkan nilai-nilai memori bit M2.2 dan M2.3. # TEMP0 diatur oleh FC3.

Untuk mengatasi masalah tersebut, perlu untuk menganalisis fungsi FC3 dan memahami apa yang perlu dilakukan sehingga mengembalikan unit logis.
Blok pemrosesan sinyal PLC di stand Keamanan Rendah di lokasi kompetisi diatur dengan cara yang sama, tetapi untuk menetapkan nilai variabel # TEMP0, cukup untuk menulis baris cara ninja saya ke blok DB1. Memeriksa nilai dalam blok diatur dengan jelas dan tidak memerlukan pengetahuan mendalam tentang bahasa pemrograman blok. Jelas, pada tingkat Keamanan Tinggi akan jauh lebih sulit untuk mencapai kontrol manual dan perlu untuk memahami seluk-beluk bahasa STL (salah satu cara pemrograman S7 PLC).
Membalikkan Blok FC3
Isi blok FC3 di representasi STL: Kode ini cukup banyak dan bagi seseorang yang tidak terbiasa dengan STL, mungkin terlihat rumit. Tidak masuk akal untuk membongkar setiap instruksi dalam kerangka artikel ini, untuk instruksi terperinci dan fitur bahasa STL, lihat manual yang sesuai:
Daftar Pernyataan (STL) untuk Pemrograman S7-300 dan S7-400 . Di sini saya akan memberikan kode yang sama setelah pemrosesan - mengganti nama label dan variabel dan menambahkan komentar yang menggambarkan algoritma kerja dan beberapa konstruksi bahasa STL. Saya langsung mencatat bahwa dalam blok yang dipertimbangkan mesin virtual diimplementasikan yang mengeksekusi beberapa bytecode yang terletak di blok DB100, yang isinya kita ketahui. Instruksi mesin virtual adalah 1 byte kode operasi dan byte argumen, satu byte untuk setiap argumen. Semua instruksi yang ditinjau memiliki dua argumen, saya menetapkan nilainya dalam komentar sebagai X dan Y.
Setelah mendapat gagasan tentang instruksi mesin virtual, kami akan menulis disassembler kecil untuk mem-parsing bytecode di blok DB100:
import string alph = string.ascii_letters + string.digits with open('DB100.bin', 'rb') as f: m = f.read() pc = 0 while pc < len(m): op = m[pc] if op == 1: print('R{} = DB101[{}]'.format(m[pc + 2], m[pc + 1])) pc += 3 elif op == 2: c = chr(m[pc + 1]) c = c if c in alph else '?' print('R{} = {:02x} ({})'.format(m[pc + 2], m[pc + 1], c)) pc += 3 elif op == 4: print('R0 = 0; R{} = (R{} == R{})'.format( m[pc + 1], m[pc + 1], m[pc + 2])) pc += 3 elif op == 5: print('R0 = 0; R{} = R{} - R{}'.format( m[pc + 1], m[pc + 1], m[pc + 2])) pc += 3 elif op == 6: print('CHECK (R{} == R{})\n'.format( m[pc + 1], m[pc + 2])) pc += 3 else: print('unk opcode {}'.format(op)) break
Hasilnya, kami mendapatkan kode mesin virtual berikut:
Kode mesin virtual R1 = DB101[0] R2 = 6e (n) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[1] R2 = 10 (?) R0 = 0; R1 = R1 - R2 R2 = 20 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[2] R2 = 77 (w) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[3] R2 = 0a (?) R0 = 0; R1 = R1 - R2 R2 = 16 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[4] R2 = 75 (u) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[5] R2 = 0a (?) R0 = 0; R1 = R1 - R2 R2 = 16 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[6] R2 = 34 (4) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[7] R2 = 26 (?) R0 = 0; R1 = R1 - R2 R2 = 4c (L) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[8] R2 = 33 (3) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[9] R2 = 0a (?) R0 = 0; R1 = R1 - R2 R2 = 16 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[10] R2 = 37 (7) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[11] R2 = 22 (?) R0 = 0; R1 = R1 - R2 R2 = 46 (F) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[12] R2 = 33 (3) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[13] R2 = 0a (?) R0 = 0; R1 = R1 - R2 R2 = 16 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[14] R2 = 6d (m) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[15] R2 = 11 (?) R0 = 0; R1 = R1 - R2 R2 = 23 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[16] R2 = 35 (5) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[17] R2 = 12 (?) R0 = 0; R1 = R1 - R2 R2 = 25 (?) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0) R1 = DB101[18] R2 = 33 (3) R0 = 0; R1 = (R1 == R2) CHECK (R1 == R0) R1 = DB101[19] R2 = 26 (?) R0 = 0; R1 = R1 - R2 R2 = 4c (L) R0 = 0; R1 = R1 - R2 CHECK (R1 == R0)
Seperti yang Anda lihat, program ini hanya memeriksa setiap simbol dari DB101 untuk kesetaraan ke nilai tertentu. Baris terakhir untuk melewati semua pemeriksaan: n0w u 4r3 7h3 m4573r. Jika garis ini ditempatkan di blok DB101, kontrol manual dari PLC diaktifkan dan akan mungkin untuk meledakkan atau mengempiskan balon.
Itu saja! Alexey mendemonstrasikan pengetahuan tingkat tinggi yang layak untuk seorang ninja industri :) Kami mengirimkan hadiah yang tak terlupakan kepada pemenang. Terima kasih banyak untuk semua peserta!