Werken met Siemens S7 Protocol
In de vorige post hebben we het gehad over het werken met ICS/SCADA protocollen en hebben we een kleine deep-dive gedaan in het Profinet protocol. In navolging daarop leek het me interessant om eens te kijken naar een ander protocol welke vaak gebruikt wordt in de “Siemens wereld”, namelijk hun eigen S7 protocol. Het nadeel van dit protocol is dat deze niet algemeen beschreven is. Echter wil dat niet zeggen dat er helemaal geen informatie over te vinden is. Ik ga in deze blog-post een inkijkje geven in het protocol en het gebruik maar het is onmogelijk om een volledige deep-dive te doen. Deze post is bedoeld om aan de slag te gaan met het protocol en het maken van custom Siemens S7 pakketten.
Om alvast de kaders te schetsen, S7 protocollen zijn over het algemeen simpeler dan Profinet maar slechter gedocumenteerd. We hebben het in deze post over het S7Comm protocol en niet over het nieuwere S7 Comm Plus protocol. Een “normaal” S7 Comm-pakket wordt geïdentificeerd door de magic byte 0x32 maar een S7 Comm Plus pakket gebruikt 0x72 als magic byte. Het einde van een S7 Comm Plus pakket wordt aangegeven door een frame-eindreeks van 6 bytes: 00 00 72 01 00 00. Echter is de algemene structuur van het S7 Comm Plus protocol totaal anders en nog slechter gedocumenteerd. Over het algemeen is het S7 Comm Plus protocol veel veiliger door de aanwezigheid van encryptiemogelijkheden en de beveiliging tegen replay-attacks. Een interessant document m.b.t. security en het S7 Comm Plus protocol is hier te lezen: https://www.blackhat.com/docs/eu-17/materials/eu-17-Lei-The-Spear-To-Break%20-The-Security-Wall-Of-S7CommPlus-wp.pdf.
Maar wij gaan het dus hebben over het “normale” S7 Comm protocol welke we nog tegenkomen binnen 90% van alle Siemens omgevingen.
Siemens S7 Comm nader toegelicht
S7 is een afkorting voor “Step7” en mocht je willen communiceren met S7 dan zijn er 2 interessante open-source projecten om dit te doen, namelijk Snap7 (https://snap7.sourceforge.net/) en PLC4x (https://plc4x.apache.org/). Maar what’s the fun in that…. 😉 Wij gaan lekker handmatig aan de slag met Python en Scapy zoals we dat ook deden in de vorige post over Profinet.
Net als de meeste ICS/SCADA protocolen volgt ook Siemens het traditionele master/slave (client/server) model. Hierbij verstuurd een Master device zoals een PC S7-verzoeken naar een veldapparaat, de zogenoemde Slave. Deze verzoeken worden gebruikt om gegevens op te vragen of te verzenden naar het apparaat of om bepaalde opdrachten uit te voeren. Er zijn een paar uitzonderingen wanneer een PLC de communicatiemaster kan zijn maar dit is afhankelijk van het type en de implementatie van het protocol. Naast een master en een slave kan er ook een “partner” aanwezig zijn in een Siemens communicatienetwerk. Deze noemen we ook wel de “peer”. Al deze devices samen in een Siemens netwerk noemen we ook wel het “Siemens Theatre”.
- Een client / slave kan alleen queries versturen
- Een server / master kan alleen op queries antwoorden
- Een partner kan met zowel de master als de slave communiceren
In onderstaande voorbeeld staan er 3 slaves/clients aan de linkerkant en fungeert de PLC als master/server.
De Partners kunnen ongevraagd gegevens uitwisselen met andere partners (nadat er een succesvolle connectie tot stand is gebracht). Door Siemens wordt dit vaak Client-Client communicatie genoemd. De peer die de verbinding aanvraagt heet de “Active Partner” en de peer die de verbinding accepteert heet de “Passive partner”. Maar in de meeste gevallen komen we master/slave communicatie tegen.
Siemens S7 Protocol
Als we uiteindelijk op de data-laag gaan kijken dan kunnen in een Siemens netwerk verschillende protocollen voor verschillende doeleinden gebruikt worden zoals Profinet en Profibus. Maar S7 vormt vrijwel altijd de backbone van Siemens communicatie en is altijd in verschillende mate aanwezig. Met S7 Communicatie refereren we in dit geval naar de Ethernet implementatie welke afhankelijk is van ISO TCP. ISO is by-design een “block-oriented” model dus is S7 dat ook. Er worden dus meerdere pakketten over-en-weer verzonden welke allemaal een “block” aan data bevatten. Dit blok noemen we de PDU (Protocol Data Unit) en het formaat van dit blok alsmede het aantal paralelle transmissies wordt overeengekomen tijdens het opzetten van de verbinding tussen de master en de slave. Maar daarover later meer. Het S7 protocol is een function/command oriented protocol. Dit betekend dat elke transmissie een commando of een antwoord op een gevraagd commando bevat.
S7 commando’s zijn onderverdeeld in diverse categorieen zoals b.v.
- Data Read/Write
- Cyclic Data Read/Write
- Directory info
- System Info
- Blocks move
- PLC Control
- Date and Time
- Security
- Programming
Het uiteindelijke data pakketje bevat de informatie welke belangrijk is voor de categorie die gebruikt wordt. Met andere woorden, het uiteindelijke pakketje ziet er per categorie anders uit. Zo heeft een S7 data pakketje altijd een header en een set aan parameters. Maar soms ook parameter data of een data-blok. Maar dit is dus afhankelijk van de categorie.
De implementatie van het S7-protocol gebruikt TCP/IP maar is hiervoor afhankelijk van de blokgeoriënteerde ISO-transportservice. Het S7-protocol is verpakt in de TPKT- en ISO-COTP-protocollen, waardoor de PDU via TCP kan worden overgedragen. ISO over TCP-communicatie is gedefinieerd in RFC1006, de ISO-COTP is gedefinieerd in RFC2126 welke weer is gebaseerd op het ISO 8073-protocol (RFC905). In essentie ziet dit er als volgt uit:
Op de protocol laag
Omdat het S7 protocol als encapsulated payload van een TCP stream wordt overgedragen moeten we ons bij de communicatie ook houden aan de regels welke gelden bij TCP. In de vorige post bespraken we het Profinet protocol welke vele malen simpeler was en primair over het Ethernet gedragen werdt. S7 verkeer is daardoor, ondanks dat het plain-text is wel wat lastiger te volgen. Laten we eerst eens kijken naar de basis. Om een TCP verbinding tot stand te brengen moeten we een valide 3-way handshake uitvoeren zodat er een TCP kanaal geopend wordt. Pas als we een open communicatiekanaal hebben kunnen we data over-en-weer sturen. Voor de 3-way handshake betekend dit dat degene die het verkeer initieerd een SYN pakketje verstuurd. Als de server (ontvanger) open staat voor communicatie stuurt deze een SYN/ACK. De ACK is een akkoord op de eerder verzonden SYN en de server verstuurd vervolgens ook zijn eigen SYN. Hierop verwacht de server ook een ACK retour en dus verstuurd de client na het ontvangen van de SYN/ACK nog een ACK pakket. Gedurende deze 3-way handshake worden bepaalde afspraken gemaakt over de verbinding en na de laatste ACK wordt het communicatiekanaal geopend welke voldoet aan de gemaakte afspraken. Dit kanaal wordt gesloten bij een FIN pakket (welke met een ACK geaccordeerd moet worden) of bij een RST pakket welke gebruikt kan worden om eenzijdig het kanaal te sluiten.
Een typische 3-way handshake in WIreshark ziet er als volgt uit:
Belangrijk bij een TCP verbinding is dat alle pakketten in een stream (alle data tussen de eerste SYN en de laatste FIN/RST) de juiste opvolgnummers hebben. Belangrijk hierbij zijn de Sequence en Acknowledge counters. Bijde counters zijn aanwezig en kunnen veranderen op basis van het type verkeer en de payloads van een TCP bestand. Omdat TCP communicatie altijd in onderlinge overeenstemming gaat geeft de ontvangende kant van een pakketje altijd een akkoord (ACK) zodat de verzendende kant weet dat het pakketje succesvol is ontvangen. Daarnaast wordt deze informatie gebruikt om datapakketten te reconstrueren welke zijn opgedeeld in meerdere TCP pakketten.
Laten we zeggen dat het eerste pakket dat verstuurd wordt begint met Sequence (hierna afgekort als seq) nummer 1. Dit kan overigens ieder willekeurig nummer zijn en is niet altijd 1. Daarnaast is de eerste ACK ook nummer 1. Het eerste pakketje bevat geen data en dus geeft de server een ACK retour. Deze counters zijn als volgt:
ACK = Sequence nummer van het ontvangen pakket + Segment lengte / payload van ontvangen pakket. Aangezien er geen payload is, is de ACK dus 1
SEQ = Ontvangen ACK, en dus 1.
De client ontvangt deze ACK en zal een vervolg data pakket versturen met het sequence nummer welke gelijk stond aan de ontvangen ACK. In dit geval bevat het pakket 40 bytes aan data.
ACK = Sequence nummer van het ontvangen pakket + Segment lengte / payload van ontvangen pakket. Aangezien er geen payload is, is de ACK dus 1
SEQ = Ontvangen ACK, en dus 1.
De server zend een akkoord terug op de ontvangen data.
dit geval bevat het pakket 40 bytes aan data.
ACK = Sequence nummer van het ontvangen pakket (1) + Segment lengte / payload van ontvangen pakket (40). De ACK is dus 41
SEQ = Ontvangen ACK, en dus 1.
Het volgende pakket dat verstuurd wordt naar de server met 10 bytes aan data ziet er dan als volgt uit:
ACK = Sequence nummer van het ontvangen pakket (1) + Segment lengte / payload van ontvangen pakket (0). De ACK is dus 1
SEQ = Ontvangen ACK, en dus 41.
En de server zend nu weer een ack retour:
ACK = Sequence nummer van het ontvangen pakket (41) + Segment lengte / payload van ontvangen pakket (10). De ACK is dus 51
SEQ = Ontvangen ACK, en dus 1.
Wanneer we dus zelf gaan werken met Step7 pakketten is dit een belangrijke vereiste om in de gaten te houden.
TPKT
Het TPKT protocol is een protocol welke werkt bovenop TCP en beschreven wordt als een ISO Transport Service. TCP en ISO zijn feitelijk 2 rivaliserende netwerk protocollen welke normaliter nooit in 1 stream samen kunnen worden verzonden. TPKT verzorgt de vertaling tussen deze 2 protocollen.
De International Standards Organization, of ISO, sponsorde de creatie van een aantal netwerk protocollen. Hieruit is het OSI protocol ontstaan welke werkt volgens het alom bekende OSI (Open Systems Interconnection) model. TCP/IP (Transmission Control Protocol/Internet Protocol) is een rivaliserend en tegenwoordig meer dominant protocol. Beide protocollen werken op basis van lagen (stapels). OSI kent meer lagen van TCP/IP. Zo bevind TCP zich binnen de TCP/IP laagmethodiek op de “Transportlaag” en het internetprotocol (IP) bevindt zich op de “Internetlaag” terwijl IP zich binnen het OSI model op de “Netwerklaag” bevindt.
OSI.TPKT biedt een methode om OSI-gegevens over TCP/IP-netwerken te vervoeren. TPKT werkt zoals veel protocollen in “reverse” modus. Zodra een TPKT pakket arriveert wordt de TPKT van het pakket afgehaald en op de protocol stack geplaatst. TPKT is een “encapsulation” protocol en emuleert het OSI TSAP (Transport Service Access Point). Het verwerkende OSI protocol weet dus niet dat TCP/IP gebruikt is om deze OSI data te versturen.
Zoals je ondertussen begrepen hebt maakt het Siemens S7 protocol primair gebruik van OSI en niet van TCP/IP.
ISO 8073 / COTP
COTP wordt net als TCP gebruikt om data veilig en compleet over het netwerk te versturen. COTP bevindt zich dan ook net als TCP op de transportlaag van het OSI model. COTP is het transportprotocol van de ISO protocol familie en ook bekend onder ISO 8073.
COTP is een “packet-based” protocol welke dus anders werkt dan een “strea”-based” protocol zoals TCP. COTP transporteert datapakketten van de ene gebruiker naar de andere. De ontvangende gebruiker heeft hierbij dezelfde data-grenzen als de verzendende gebruiker. TCP transporteert een continue datastroom naar de ontvanger, dus het protocol bovenop TCP moet vaak zelf dergelijke grenzen toevoegen (zoals het zojuist besproken TPKT protocol).
Binnen TCP zijn we bekend met poorten. Maar binnen OSI hebben we het over TSAP (Transport Service Access Point). Om meerdere conversaties tussen twee hosts te multiplexen gebruikt COTP zogenaamde TSAP’s. Een TSAP is een binaire blob van variabele lengte. Waneer meer data verzonden moet worden dan 1 TSAP kan bevatten worden meerdere TSAP’s gebruikt en gematcht bij het tot stand brengen van de verbinding. TSAP’s zijn singletons. Dit betekend dat wanneer een TSAP wordt gebruikt voor een verbinding, deze niet door een andere vervinding kan worden gebruikt. Een COTP pakket bestaat uit verschillende segmenten / octetten. Het aantal octetten wordt aangegeven door een binaire waarde in “lengte-indicator” veld. Dit veld heeft een maximale waarde van 254 (1111 1110). Het volgende veld verteld iets over de PDU en is onderverdeeld in twee delen. Het eerste deel is de PDU-typespecificatie welke de functie van de PDU beschrijft zoals bijvoorbeeld gegevensoverdracht (1111). Deze code’s bestaan altijd uit 4 bits. Het tweede deel is het kredietdeel welke wordt gebruikt om een betrouwbare transportdienst aan te duiden. Deze waarde staat echter altijd op 0000 omdat TP geen betrouwbaar transport biedt. Het derde veld bevat het TPDU-nummer en een indicatie-flag voor het einde van de overdracht. Op basis van het type COTP pakket worden er meer of minder segmenten gebruikt en toegevoegd. Dit protocol is dus net als het TCP en S7 protocol minder statisch in gebruik als the TPKT protocol.
Siemens Step7
TKTP is het vertalingsprotocol, COTP het transport protocol en tenslotte hebben we het eigen Siemens Step7 protocol. Dit protocol wordt door Siemens Simatic S7 equipment begrepen en verwerkt en is niet open-source, wat betekend dat niet alle informatie publiekelijk gepubliceerd is. Gelukkig weten we ondertussen heel veel van het S7 protocol en heeft WireShark zijn eigen dissector zodat we ook de segmenten in de S7 pakketten kunnen bekijken en analyseren.
Het S7 protocol werkt op Profibus netwerken alsmede MPI (Siemens Multi-Point Interface) netwerken. We komen dit protocol ook vaak tegen binnen TCP/IP netwerken. Dit laatste scenario is de focus van deze post en vereist dus een TPKT en COTP payload om de data succesvol te versturen.
Het S7 protocol wordt gebruikt voor vele ICS/SCADA doeleinden zoals PLC-programmering, gegevensuitwisseling tussen PLC’s, toegang tot PLC-gegevens van SCADA-systemen en diagnostische doeleinden. De eerste byte van een S7 pakket is altijd 0x32 en fungeert als protocolidentificatie.
Het pakket bestaat vervolgens uit een aantal default segmenten en vele variabele segmenten welke op basis van een specifieke functie gebruikt worden.
Na het protocol ID segment bevindt zich het ROSCTR segment ofwel de “message type”. Dit veld geeft de functie van het pakket aan zoals een job request, een ack of userdata.
Vervolgens bevindt zich een reserved veld welke altijd 0x0000 is gevolgd door het PDU Reference segment. De waarde van dit segment wordt door de master gegenereerd en wordt elke nieuwe transmissie opgehoogd. Dit veld is om responses te mappen aan de uitgevraagde requests.
Daarna vinden we de parameter lengte welke, niet heel verrassend de lengte van het parameter-veld aangeeft. Het daarop volgende veld is de “data lengte” en geeft het formaat van de data payload aan (als deze aanwezig is). Het veld daarna is de S7 functie en beschrijft de actie op het doelapparaat zoals het verzoek tot communicatie, het wegschrijven van data of het stopzetten van de PLC.
Net als de 3-way handshake bij TCP dient ook voor gegevensoverdracht van het S7 protocol een communicatie-overeenkomst plaatst te vinden. Pas nadat er een communicatie-akkoord (ACK_DATA) is gegeven kan de communicatie beginnen. We hebben dus bij een succesvolle communicatie te maken TCP/IP pakketten welke de juiste stream moeten volgen, TPKT pakketten voor de vertaling tussen OSI en TCP, COTP voor het OSI communicatiekanaal en Siemens Step7 voor Siemens-specifieke communicatie. Zoals je ziet is dit alles wat complexer dan gewoon Profinet verkeer. Echter is het verkeer niet encrypted en dus kunnen we het afluisteren, modificeren en versturen.
Siemens S7 met Scapy
We kunnen dus ons eigen S7 verkeer genereren. En net als in de vorige post is de tool bij uitstek hiervoor “Scapy”. Maar zoals je al in de vorige alinea kon lezen is dit wel wat ingewikkelder en vereist het meerdere stappen om S7 communicatie op te zetten. Maar omdat het verkeer niet versleuteld is kunnen we elk segment zelf beinvloeden en zodoende dus custom-S7 pakketten naar de devices toezenden. Vanuit “pentest” gedachte is dit uiteraard ontzettend interessant. Kunnen we het ontvangende device beinvloeden, besturen of zelfs plat leggen?
Laten we eens een custom S7-COMM pakketje maken. We gebruiken hiervoor weer mijn custom “ics-payload.py” script. https://github.com/mrbaselier/ICS-Payload. Dit script stelt me in staat om een valide S7 pakketje samen te stellen en geeft meteen per veld een inzicht van de waardes. Wat betekend het segment en welke waarde wordt verwacht. Echter zijn er meer stappen nodig dan alleen het maken van een valide TPKT, COTP en S7 pakket / payload. We hebben ook te maken met een TCP 3-way handshake. Voor de 3-way handshake kunnen we de volgende Scapy pakketten versturen(https://raw.githubusercontent.com/mrbaselier/ICS-Payload/main/Scapy-example-3-way-handshake.py):
#! /usr/bin/env python3 # WHAT DOES THIS SCRIPT DO? # 1. Setup a TCP connection with a valid 3-way handshake # 2. Gracefully close the connection ######## IMPORTS ######## from scapy.all import * import struct import binascii import threading import time import codecs ######## SETTINGS / VARIABLES ######## source_ip = '192.168.178.57' destination_ip = '192.168.178.30' source_port = random.randint(20000,50000) destination_port = 102 tcp_start_seq_nr = random.randint(20000,50000) send_interface = "Ethernet 2" ######## FUNCTIONS ######## def string_to_bytes(value): value = str.encode(value) #print(value) return value def int_to_bytes_1(value): value = value.to_bytes(1, byteorder='big') #print(value) return value def int_to_bytes_2(value): value = value.to_bytes(2, byteorder='big') #print(value) return value def int_to_bytes_4(value): value = value.to_bytes(4, byteorder='big') #print(value) return value ######## PART1 - Start with 3-way handshake - Send SYN and await response ######## l3 = IP(#version=4, flags=2, proto=6, src=source_ip, dst=destination_ip ) #TCP tcp_options = [('MSS', 1460), ('SAckOK', b''), ('Timestamp', (3840354, 0)), ('NOP', None), ('WScale', 0)] l4 = TCP(sport=source_port, dport=destination_port, flags='S', options=tcp_options, seq=tcp_start_seq_nr ) SYNpacket = l3 / l4 #Show packet #SYNpacket.show2() #Send packet and await response RETURNPACKET = sr1(SYNpacket) ######## PART2 - After receiving SYN/ACK, send an ACK ######## l4 = TCP(sport=source_port, dport=destination_port, flags='A', seq=RETURNPACKET.ack, ack=RETURNPACKET.seq + 1 ) ACKpacket = l3 / l4 #Show packet #ACKpacket.show2() #Send packet send(ACKpacket) ######## PART3 - Close the connection, send a FIN ######## l4 = TCP(sport=source_port, dport=destination_port, flags='F', seq=RETURNPACKET.ack, ack=RETURNPACKET.seq + 1 ) ACKpacket = l3 / l4 #Show packet #ACKpacket.show2() #Send packet RETURNPACKET = sr1(ACKpacket) ######## PART4 - Close the connection. After receiving a FIN/ACK, reply with an ACK ######## l4 = TCP(sport=source_port, dport=destination_port, flags='A', seq=RETURNPACKET.ack, ack=RETURNPACKET.seq + 1 ) ACKpacket = l3 / l4 #Show packet #ACKpacket.show2() #Send packet send(ACKpacket) |
Zoals je ziet beginnen we met het versturen van een SYN pakket. Dit doen we met het “sr1” commando waarbij Scapy dus wacht op het eerste retour pakket. Dit zal bij een succesvolle connectie een SYN/ACK zijn. Vervolgens verzenden we een ACK op de toegestuurde SYN. En dat zorgt ervoor dat er een valide communicatiekanaal geopend wordt. Zoals je ziet zetten we bij elk TCP/IP pakket de volgende TCP waardes:
seq=RETURNPACKET.ack,
ack=RETURNPACKET.seq + 1
We stellen de sequence dus gelijk aan de ontvangen ACK van het retourpakket en de ACK is gelijk aan de ontvangen sequence + 1. We zullen in sommige gevallen ook “RETURNPACKET.len + RETURNPACKET.seq” gebruiken. Dit is om de sequences en acknowledge counters te voorzien van de juiste waardes van een valide TCP/IP stream.
In dit voorbeeld sluiten we de TCP connectie door een FIN te initieren. We wachten dan weer op de FIN/ACK en geven deze laatste FIN ook akkoord met een ACK om de verbinding netjes te sluiten. Maar voordat we de verbinding sluiten kunnen we de vervolgstap starten, namelijk het opzetten van de S7 communicatie. Maar voordat we de S7 communicatie kunnen starten moeten we een succesvolle ISO verbinding opzetten voor het ISO protocol. We doen dit uiteraard samen met een TPKT pakket welke voor ons de ISO gegevens vanuit de TCP laag over kan geven aan de ISO laag. In essentie ziet dit er als volgt uit:
#TPKT tpkt_version = int_to_bytes_1(3) tpkt_reserved = int_to_bytes_1(0) tpkt_length = int_to_bytes_2(22) #Compile Raw part of the TPKT package TPKT = tpkt_version + tpkt_reserved + tpkt_length #=================================== #ISO_8073 iso_length = int_to_bytes_1(17) iso_pdu_type = int_to_bytes_1(224) iso_dest_ref = int_to_bytes_2(0) iso_src_ref = int_to_bytes_2(10) iso_class = int_to_bytes_1(0) iso_parameter_code = int_to_bytes_1(193) iso_parameter_length = int_to_bytes_1(2) iso_source_tsap1 = int_to_bytes_1(2) iso_source_tsap2 = int_to_bytes_1(0) iso_parameter_code2 = int_to_bytes_1(194) iso_parameter_length2 = int_to_bytes_1(2) iso_dest_tsap1 = int_to_bytes_1(2) iso_dest_tsap2 = int_to_bytes_1(0) iso_parameter_code3 = int_to_bytes_1(192) iso_parameter_length3 = int_to_bytes_1(1) iso_tpdu_size = int_to_bytes_1(10) #Compile Raw part of ISO_8073 package ISO_8073 = iso_length + iso_pdu_type + iso_dest_ref + iso_src_ref + iso_class + iso_parameter_code + iso_parameter_length + iso_source_tsap1 + iso_source_tsap2 + iso_parameter_code2 + iso_parameter_length2 + iso_dest_tsap1 + iso_dest_tsap2 + iso_parameter_code3 + iso_parameter_length3 + iso_tpdu_size #=================================== payload = TPKT + ISO_8073 l4 = TCP(sport=source_port, dport=destination_port, flags='PA', seq=ACKpacket.seq, ack=ACKpacket.ack ) CONNpacket = l3 / l4 / payload #Show packet #CONNpacket.show2() #Send packet and await response RETURNPACKET = sr1(CONNpacket) RETURNPACKET.show2() |
We moeten nu wachten op het ISO (COTP) return pakket en deze accorderen. En pas hierna zijn we in staat om een succesvolle S7 communicatie op te zetten. Dit ziet er als volgt uit. Dit setup pakket is gemaakt voor een Siemens Logo en kan in lichte mate afwijken van andere Siemens producten. Dit geld overigens voor alle S7 pakketten. Idealier “luister” je dus eerst S7 communicatie af van je target device om met die informatie het juiste pakket te kunnen maken.
Siemens Logo Setup Communication:
#TPKT tpkt_version = int_to_bytes_1(3) tpkt_reserved = int_to_bytes_1(0) tpkt_spacer = int_to_bytes_1(0) tpkt_length = int_to_bytes_1(25) #Compile Raw part of the TPKT package TPKT = tpkt_version + tpkt_reserved + tpkt_spacer + tpkt_length #=================================== #ISO_8073 iso_length = int_to_bytes_1(2) iso_pdu_type = int_to_bytes_1(240) iso_tpdu_number = int_to_bytes_1(128) #Compile Raw part of ISO_8073 package ISO_8073 = iso_length + iso_pdu_type + iso_tpdu_number #=================================== #STEP7 s7_protocol_id = int_to_bytes_1(50) s7_rosctr = int_to_bytes_1(1) s7_red_id = int_to_bytes_2(0) s7_protocol_dataunit_ref = int_to_bytes_2(8449) s7_param_length_pt1 = int_to_bytes_1(0) s7_param_length_pt2 = int_to_bytes_1(8) s7_data_length_pt1 = int_to_bytes_1(0) s7_data_length_pt2 = int_to_bytes_1(0) s7_function = int_to_bytes_1(240) s7_reserved = int_to_bytes_1(0) s7_max_amq_calling = int_to_bytes_2(1) s7_max_amq_called = int_to_bytes_2(2) s7_pdu_length = int_to_bytes_2(240) #Compile Raw part of the Siemens S7 package SiemensS7 = s7_protocol_id + s7_rosctr + s7_red_id + s7_protocol_dataunit_ref + s7_param_length_pt1 + s7_param_length_pt2 + s7_data_length_pt1 + s7_data_length_pt2 + s7_function + s7_reserved + s7_max_amq_calling + s7_max_amq_called + s7_pdu_length#+ s7_item_count + s7_variable_specification + s7_length_next + s7_syntaxid + s7_transportsize + s7_length_next_address_spec + s7_db_nr + s7_area_data_blocks + s7_address_pt1 + s7_address_pt2 + s7_address_pt3 + s7_return_code + s7_transport_size + s7_item_length + s7_item_data #=================================== payload = TPKT + ISO_8073 + SiemensS7 l4 = TCP(sport=source_port, dport=destination_port, flags='PA', seq=RETURNPACKET.ack, ack=RETURNPACKET.len + RETURNPACKET.seq-40 ) COMMpacket = l3 / l4 / payload #Send packet and await response RETURNPACKET = sr1(COMMpacket) |
Wat opvalt in deze is dat we binnen het TCP/IP verkeer de ACK zetten op “RETURNPACKET.len + RETURNPACKET.seq-40”. Deze -40 is belangrijk om de counters gelijk te houden na de COTP communicatie waar we (en dat zie je niet in dit voorbeeld maar wel in het complete voorbeeld). Verder zien we dat functie 240 (F0 in hex) gebruikt en dat is de “Setup Communication” functie. De overige parameters die gezet zijn en waar tijdens deze communicatie-setup overeenkoms over berijkt moet worden zijn s7_max_amq_calling, s7_max_amq_called en s7_pdu_length ofwel het formaat van de PDU’s welke over en weer gestuurd worden.
Nadat er een communicatie ACK is ontvangen weten we dat we een succesvol Siemens S7 kanaal geopend hebben en kunnen we interacteren met het ontvangende device over het S7 protocol. Zo kunnen we data schrijven, lezen en commando’s uitvoeren zoals het stoppen van de PLC. Als aanvaller kunnen we nu ook relatief gemakkelijk pakketten loopen en middels threading in bulk versturen. Zie voor een kleine demonstratie hiervan de video op mijn YouTube kanaal (https://www.youtube.com/c/jarnobaselier).
Wil je een voorbeeld zien van het volledige script waarbij we S7 communicatie opzetten met een Siemens Logo en hier variabelen naartoe schrijven en uitlezen bekijke dan dit voorbeeld script: https://raw.githubusercontent.com/mrbaselier/ICS-Payload/main/Scapy-example-Siemens-Step7-SetupCommunication-Alter-and-read-variables.py. Ook dit script is gemaakt op basis van de “ics-payload.py” file en als je uitleg wilt bij alle segmenten dan raad ik je aan om die file te bekijken.
❤ Ik hoop dat deze informatie nuttig was en dat ik een duidelijk beeld heb kunnen schetsen over het communiceren over het S7 protocol, de stappen welke nodig zijn om deze communicatie op te zetten en dat je nu meer inzicht hebt in de werking en inhoud van het protocol alsmede de vertaling welke nodig is om vanuit een TCP stream data over te zetten naar een ISO stream. Was dit interessant? Vergeet dan s.v.p. niet om deze post te liken en/of te delen en neem zeker ook eens een kijkje op mijn YouTube kanaal. Alvast ontzettend bedankt! ❤