Hoe werkt ModBus
Het ModBus communicatie protocol is net zoals het Profinet protocol een oerdegelijk maar verouderd protocol. Deze protocollen zijn ontzettend goed in hun basistaak maar nooit gebouwd met security in-mind. Omdat we ModBus erg vaak tegenkomen binnen de PA omgeving wil ik in deze post meer inzicht geven in de werking van het ModBus protocol. Hopelijk geeft dit meer inzicht waardoor je ModBus apparaten makkelijker kunt herkennen en indien nodig exploiteren of juist beschermen.
Tegenwoordig kent ook de industriële omgeving zeer degelijke protocollen die wel op basis van goede security best-practises gebouwd zijn. Denk b.v. aan WirelessHART. Zoals al gezegd valt ModBus niet onder die categorie. En dat is ook helemaal niet zo gek als je weet dat ModBus al stamt uit 1979. Dit is dus as-we-write een 41 jaar oud protocol! ModBus is dan ook van origine een serieel protocol gemaakt door Schneider Electric (toen: Modicon). ModBus is ontwikkeld voor het verbinden van en communiceren tussen industriële apparaten zoals PLC’s. Omdat de data input van de huidige PLC’s veel groter is dan vroeger kan ModBus beperkend werken. ModBus ondersteund geen grote binaire objecten. Daarnaast zijn er nog vele andere nadelen aan het ModBus protocol in samenwerking met moderne apparatuur. Het grappige van dit verhaal is dat er zelfs na 40 jaar geen echt goede vervanger is voor ModBus. Misschien dat OPC-UA een waardige vervanger kan zijn maar ook deze standaard is nog niet vrijgegeven en ondertussen ook al een verouderde techniek. Misschien moeten we ook niet zo zeuren. Misschien is ModBus wel echt zo goed. Het is goedkoop, het is low-speed en dus veel robuuster omdat het weinig last heeft van interferentie, het praat met andere standaarden, het heeft geen computer nodig om te functioneren en het is bovendien erg gemakkelijk te implementeren. In industriële omgevingen gaat stabiliteit boven alles en met dat in het oog is ModBus een perfect protocol. Helaas hebben we dus de limitatie van snelheid en de limitatie van veiligheid. Om de veiligheid van een onveilig protocol te waarborgen is het zaak om te zorgen voor network segregation, dus het scheiden van nodes en netwerken waar mogelijk. Probeer ModBus nodes niet of zeer beperkt beschikbaar te maken. Bouw b.v. airgaps in. Wat niet bedraad of draadloos verbonden is kan ook niet misbruikt worden. Daarnaast (en ook dat zou je network segregation kunnen noemen) is het zaak om ModBus apparaten te beveiligen middels een ander apparaat. Dit apparaat (b.v. een firewall) kun je voor het onveilige apparaat plaatsen om het verkeer te loggen en te filteren.
Network Segregation helpt je echter niet met de andere flaws van ModBus. Het protocol wordt er b.v. niet sneller op. Wel zijn er de afgelopen jaren diverse ontwikkelingen geweest. De bekendste ModBus varianten zijn:
- ModBus RTU – Dit is het originele seriële ModBus protocol (om te communiceren met een Remote Transfer Unit). Data wordt binair verstuurd en ModBus RTU kent een error-checking (CRC) mechanisme.
- ModBus ASCII – Zelfde als ModBus RTU maar dan wordt de data verstuurd in ASCII opmaak.
- ModBus over TCP – Deze variant wordt ook weleens ModBus over TCT/IP of ModBus RTU/IP genoemd en maakt gebruik van de Transport Layer om verkeer over een IP based netwerk te versturen. Deze versie kent een CRC control mechanisme in tegenstelling met het ModBus TCP (zonder het woordje “over”) variant waarbij data alleen op de onderliggende (data link) laag gecontroleerd wordt en niet op de transport laag.
- ModBus Plus – ModBus Plus maakt het mogelijk voor meerdere ModBus masters om met elkaar te communiceren (peer-to-peer). Een master moet hiervoor voorzien zijn van dedicated co-processoren. Ook kan zonder speciale insteekkaart geen connectie worden gemaakt met een computer.
Als je bovenstaande goed gelezen hebt dan las je hier “master”. Dat klopt. ModBus maakt gebruik van zogenaamde “master” en “slave” devices. Een netwerk kan bestaan uit meerdere masters en meerdere slave devices. Een slave device ontvangt alleen data. Een master device verzendt de data. Standaard (behalve bij ModBus Plus) kunnen master devices niet met elkaar praten en ook slave devices kunnen niet met elkaar praten. Elke slave in het netwerk heeft een ID. Het eerste byte in een data pakketje wordt gebruikt om het slave ID te vermelden. Omdat er maar 1 byte beschikbaar is kunnen er in theorie maar 256 slaves op het netwerk aanwezig zijn. Er is echter een mogelijkheid om dit uit te breiden naar 2-byte addressing waardoor de scope vergroot wordt naar 65535 mogelijk slave apparaten. 2-byte addressing werkt alleen ale iedere node op het ModBus netwerk dit ondersteund.
Een ModBus RTU pakketje ziet er als volgt uit:
Zoals hierboven is aangegeven bevat een ModBus RTU pakketje een adresseringsveld van 1 byte groot gevolgd door de PDU (Protocol Data Unit) welke bestaat uit een functiecode en de werkelijke data om tenslotte te eindigen met een controleveld, de CRC check.
Een ModBus slave device verwerkt de data in zijn register. Een ModBus slave device kent 1 groot register bestaande uit 4 logisch gescheiden delen, namelijk:
- Coil – Read/Write
- Coil – Rea-only
- Register – Read-only
- Register – Read/Write
Zoals je ziet zijn er 2 zogenaamde “coil“ registers en 2 “register“ registers aanwezig. Beide hebben een “read-only“ en een “read/write“ register. Het “coil“ register bestaat uit zogenaamde “coils” welke een uit 1 bit per coil bestaan. Een coil is in principe een output waarde van b.v. een sensor of een relay. Wanneer een coil waarde geschreven (input) is dan noemen we dat een “discrete input“ ofwel een ”contact”. Een ”register” kan een waarde van 16 bits (2bytes) bevatten. Elk register is groot genoeg om 9999 waardes te bevatten. De reden dat het registernummer niet voorkomt in een ModBus pakketje is omdat het feitelijk 1 register is welke werkt met een offset. De logische nummering (offset) van de registers is als volgt:
- 1-9999 = Coil – Read/Write<. De oficiele ”tabelnaam” is: ”Discrete Output Coils”.
- 10001 – 19999 – Coil – Read-only. De oficiele ”tabelnaam” is: ”Discrete Input Contacts”.
- 20001 – 39999 – Register – Read-only. De oficiele ”tabelnaam” is: ”Analog Input Registers” (input data).
- 40001 – 49999 = Register – Read/Write. De oficiele ”tabelnaam” is: ”Analog Output Holding Registers” (output date).
Een modbus register is dus 49999 bits (grofweg 6250 bytes en 0,006 MB groot). Binnen het data adres in een ModBus pakketje wordt de offset aangegeven. Wanneer de offset 40001 is zal de data bedoeld zijn voor de eerste positieplaats binnen het read/write register.
Wat volgt is vervolgens de functiecode. De functiecode geeft aan welke actie moet gebeuren in welk gedeelte van het register:
- 01 (01 in hex) = ”Read“ in register 1 (Discrete Output Coils)
- 05 (05 in hex) = ”Write Single“ in register 1 (Discrete Output Coils)
- 15 (0F in hex) = ”Write Multiple“ in register 1 (Discrete Output Coils)
- 02 (02 in hex) = ”Read in register 2 (Discrete Input Contact)
- 04 (04 in hex) = ”Read“ in register 3 (Discrete Input registers)
- 03 (03 in hex) = ”Read“ in register 4 (Analog Output Holding Registers)
- 06 (06 in hex) = ”Write Single“ in register 4 (Analog Output Holding Registers)
- 16 (10 in hex) = ”Write Multiple“ in register 4 (Analog Output Holding Registers)
Zoals je ziet kan er alleen een ” read” actie uitgevoerd worden op een ”read-only” register en kunnen er 3 acties uitgevoerd worden op een ”read/write” register. Namelijk het lezen van een register, het schrijven van 1 registerplaats (1 bit) en het schrijven van meerdere registerplaatsen. Er bestaan nog diverse andere functiecodes (maximaal 1 byte dus 1-255 codes) die wat minder vaak gebruikt worden (bron: Wikipedia):
Nadat de functiecode is doorgegeven wordt het datapakket gelezen. In het datapakket bevat eveneens een aantal waardes. Te weten:
- Het slave map register adres
- Het aantal registers welke beschreven of gelezen moeten worden
>>Starting address Hi = start adres van het register dat bewerkt of gelezen moet worden
>>Starting address Lo = eind adres van het register dat bewerkt of gelezen moet worden
>>Count of register Hi = start register van de waarde (vaak bij een respons / read)
>>Count of register Lo = eind register van de waarde - Eventuele data die geschreven moet worden
Dus wanneer we de waarde van een coil willen uitlezen (register 1 / discrete output coils) gebruiken we functiecode 1. We willen dan de waarde in de 1e 100 registervelden lezen en geven dus een “Count of register Hi” van 0 mee en een “Count of register Lo” van 99 (hex = 64) en dus (in hex) 0064.
Wanneer een slave de input ontvangt van de master en deze kan interpreteren en uitvoeren dan zal de slave dezelfde functiecode retourneren. Zo weet de master dat de actie succesvol is uitgevoerd. Wanneer er echter een functie een fout retourneert zal de functiecode geretourneerd worden waarbij de hoogste byte op 1 wordt geplaatst. Zo weet de master dat er een foutieve uitvoering is geweest. Meer details over de fout zullen worden toegevoegd in het data onderdeel van de PDU.
Functiecode = Binaire functiecode – Binaire functiecode indien fout:
- 01 (01 in hex) = 00000001 – 10000001
- 05 (05 in hex) = 00000101 – 10000101
- 15 (0F in hex) = 00001111 – 10001111
- 02 (02 in hex) = 00000010 – 10000010
- 04 (04 in hex) = 00000100 – 10000100
- 03 (03 in hex) = 00000011 – 10000011
- 06 (06 in hex) = 00000110 – 10000110
- 16 (10 in hex) = 00010000 – 10010000
Conclusie
Omdat er geen beveiliging op het ModBus protocol zit betekent dat feitelijk dat iedereen die ModBus verkeer kan “detecteren” ook ModBus verkeer kan verzenden en dus ModBus based apparaten kan uitlezen, de configuratie kan modificeren en de apparaten kunnen aansturen. Dit maakt ModBus en andere oudere protocollen erg gevaarlijk en niet meer geschikt voor deze tijd. Aan de andere kant is ModBus een zeer efficient en degelijk protocol welke met weinig overhead en data redelijk complete taken kan uitvoeren zoals bediening, monitoring en foutcontrole etc. ModBus zal voorlopig niet verdwijnen maar het kan niet missen dat dit protocol op de nominatie staat en dat er binnenkort een ander protocol langzaam de rol van ModBus over zal nemen.
Vond je deze ModBus post leuk? Tof! Nog toffer zou het zijn om dan mijn post weder te delen op je eigen social media kanalen of op je eigen website. Een “like” is uiteraard ook erg leuk! Dankjewel!