
En PHDays 9, realizamos un concurso para piratear una planta de bombeo de gas: el concurso
Industrial Ninja . Había tres stands en el sitio con varios parámetros de seguridad (Sin seguridad, Baja seguridad, Alta seguridad) que emulaban el mismo proceso industrial: se bombeaba aire al globo (y luego descendía) bajo presión.
A pesar de varios parámetros de seguridad, el hardware de los stands era el mismo: Siemens Simatic S7-300 PLC; botón de purga de emergencia y dispositivo de medición de presión (conectado a entradas digitales de PLC (DI)); válvulas para bombeo y purga (conectadas a las salidas digitales del PLC (DO)) - vea la figura a continuación.

El PLC, según las lecturas de presión y de acuerdo con su programa, tomó la decisión de soplar o soplar la bola (abrió y cerró las válvulas correspondientes). Sin embargo, se proporcionó un modo de control manual en todos los stands, lo que permitió controlar los estados de las válvulas sin ninguna restricción.
Los stands se distinguieron por la complejidad de habilitar este modo: en un stand sin protección fue lo más fácil de hacer, pero en el stand de Alta Seguridad, en consecuencia, fue más difícil.
En dos días, cinco de los seis problemas fueron resueltos; El ganador del primer lugar obtuvo 233 puntos (pasó una semana preparándose para la competencia). Tres ganadores: coloco - a1exdandy, II - Rubikoid, III - Ze.
Sin embargo, durante los PHDays, ninguno de los participantes pudo superar las tres posiciones, por lo que decidimos hacer una competencia en línea y publicamos la tarea más difícil a principios de junio. Los participantes tuvieron que completar la tarea en un mes, encontrar la bandera, describir la solución en detalle e interesante.
Bajo el corte, publicamos un análisis de la mejor solución para la tarea enviada durante el mes, fue encontrada por Alexey Kovrizhnykh (a1exdandy) de la compañía de Seguridad Digital, quien ocupó el primer lugar en la competencia durante PHDays. A continuación proporcionamos su texto con nuestros comentarios.
Análisis inicial
Entonces, en la tarea había un archivo con archivos:
- block_upload_traffic.pcapng
- DB100.bin
- hints.txt
El archivo hints.txt contiene la información necesaria y consejos para resolver la tarea. Aquí están sus contenidos:
- Petrovich me dijo ayer que desde PlcSim puedes descargar bloques en Step7.
- En el stand, se utilizaron PLC de la serie Siemens Simatic S7-300.
- PlcSim es un emulador de PLC que le permite ejecutar y depurar programas para PLC Siemens S7.
El archivo DB100.bin, aparentemente, contiene un bloque de datos del PLC DB100: 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 .......
A juzgar por el nombre, el archivo block_upload_traffic.pcapng contiene un volcado de tráfico de carga de bloques al PLC.
Vale la pena señalar que este volcado de tráfico en el sitio de la competencia durante la conferencia fue un poco más difícil de conseguir. Para hacer esto, era necesario comprender el script del archivo del proyecto para TeslaSCADA2. A partir de él, fue posible comprender dónde se encuentra el volcado cifrado con RC4 y qué clave se debe utilizar para descifrarlo. Se pueden obtener volcados de bloques de datos en el sitio utilizando el cliente de protocolo S7. Utilicé el cliente de demostración del paquete Snap7 para esto.
Extraer unidades de procesamiento de señales de un volcado de tráfico
Al observar el contenido del volcado, puede comprender que los bloques de procesamiento de señal OB1, FC1, FC2 y FC3 se transmiten en él:

Es necesario extraer estos bloques. Esto se puede hacer, por ejemplo, con el siguiente script, después de convertir el tráfico del formato pcapng a pcap:
Después de estudiar los bloques recibidos, puede observar que siempre comienzan con los bytes 70 70 (pp). Ahora necesita aprender a analizarlos. Una sugerencia para la tarea sugiere que necesita usar PlcSim para esto.
Obteniendo instrucciones legibles por humanos de bloques
Primero, intentemos programar S7-PlcSim cargando varios bloques con instrucciones repetitivas (= Q 0.0) usando el software Simatic Manager, y guarde el resultado en el emulador PLC en el archivo example.plc. Al observar el contenido del archivo, puede determinar fácilmente el comienzo de los bloques cargados mediante la firma 70 70, que descubrimos anteriormente. Antes de los bloques, aparentemente, el tamaño del bloque se escribe en forma de un valor little-endian de 4 bytes.

Después de recibir información sobre la estructura de los archivos plc, apareció el siguiente plan de acción para leer programas PLC S7:
- Usando el Administrador Simatic, creamos una estructura de bloques en S7-PlcSim similar a la que obtuvimos del volcado. Los tamaños de bloque deben coincidir (logrado al llenar los bloques con el número correcto de instrucciones) y sus identificadores (OB1, FC1, FC2, FC3).
- Guarde el PLC en un archivo.
- Reemplazamos el contenido de los bloques en el archivo recibido con los bloques del volcado de tráfico. El comienzo de los bloques está determinado por la firma.
- El archivo resultante se carga en S7-PlcSim y observamos el contenido de los bloques en Simatic Manager.
Los bloques se pueden reemplazar, por ejemplo, con el siguiente código:
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 siguió un camino posiblemente más complicado, pero aún así. Asumimos que los participantes usarían el programa NetToPlcSim para que PlcSim pudiera comunicarse a través de la red, cargar bloques en PlcSim a través de Snap7 y luego descargar estos bloques como un proyecto desde PlcSim usando el entorno de desarrollo.
Al abrir el archivo resultante en S7-PlcSim, puede leer bloques sobrescritos utilizando Simatic Manager. Las principales funciones de gestión de dispositivos se registran en el bloque FC1. La variable # TEMP0 atrae especial atención, cuando se enciende, parece que el control del PLC se cambia al modo manual en función de los valores de la memoria de bits M2.2 y M2.3. # TEMP0 es configurado por FC3.

Para resolver el problema, es necesario analizar la función FC3 y comprender lo que hay que hacer para que devuelva una unidad lógica.
Los bloques de procesamiento de señal PLC en el stand de baja seguridad en el sitio de la competencia se organizaron de la misma manera, pero para establecer el valor de la variable # TEMP0, fue suficiente para escribir la línea de mi camino ninja al bloque DB1. La comprobación del valor en el bloque se organizó claramente y no requirió un conocimiento profundo del lenguaje de programación del bloque. Obviamente, en el nivel de Alta Seguridad, será mucho más difícil lograr el control manual y es necesario comprender las complejidades del lenguaje STL (una de las formas de programar el PLC S7).
Reverse Block FC3
El contenido del bloque FC3 en la representación STL: El código es bastante voluminoso y para una persona que no está familiarizada con STL, puede parecer complicado. No tiene sentido desarmar cada instrucción en el marco de este artículo, para obtener instrucciones detalladas y las características del lenguaje STL, consulte el manual correspondiente:
Lista de instrucciones
(STL) para la programación S7-300 y S7-400 . Aquí daré el mismo código después del procesamiento: renombrar etiquetas y variables y agregar comentarios que describan el algoritmo de trabajo y algunas construcciones del lenguaje STL. Noto de inmediato que en el bloque en consideración se implementa una máquina virtual que ejecuta algún código de bytes ubicado en el bloque DB100, cuyo contenido conocemos. Las instrucciones de la máquina virtual son 1 byte de código operativo y bytes de argumentos, un byte para cada argumento. Todas las instrucciones revisadas tienen dos argumentos, designé sus valores en los comentarios como X e Y.
Código de procesamiento posterior Después de tener una idea de las instrucciones de la máquina virtual, escribiremos un pequeño desensamblador para analizar el código de bytes en el bloque 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
Como resultado, obtenemos el siguiente código de máquina virtual:
Código de máquina 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)
Como puede ver, este programa simplemente verifica la igualdad de cada símbolo de DB101 a un cierto valor. La línea final para pasar todos los controles: n0w u 4r3 7h3 m4573r. Si esta línea se coloca en el bloque DB101, se activa el control manual del PLC y será posible inflar o desinflar el globo.
Eso es todo! Alexey demostró un alto nivel de conocimiento digno de un ninja industrial :) Enviamos premios memorables al ganador. Muchas gracias a todos los participantes!