Pages

Tuesday, December 13, 2016

Modbus Stager: Using PLCs as a payload/shellcode distribution system

This weekend I have been playing around with Modbus and I have developed a stager in assembly to retrieve a payload from the holding registers of a PLC. Since there are tons of PLCs exposed to the Internet, I thought whether it would be possible to take advantage of the processing and memory provided by them to store certain payload so that it can be recovered later (from the stager).

So, the scenario is as follows:
  1. An attacker locates a PLC exposed to the Internet with enough space to store certain payload. It’s easy to find Modbus devices with tens of KB available.
  2. The attacker uploads the payload to the PLC's memory.
  3. The attacker infects one host with a dropper that uses the stager to “speak” Modbus and to retrieve the stage from the PLC and to execute it.
     The main advantages of this method are:
    • The use of third party PLCs provides anonymity and makes traceability difficult. No need to upload the payload to a server.
    • The payload is stored on the PLC’s memory, making forensic analysis difficult. In addition, once the payload is retrieved, its contents could be overwritten easily (even from the stager itself).

    I think the Modbus stager could be useful too in certain ICS environments where protocols other than Modbus may raise the red flag and where WinHTTP/WinInet stagers may not be the most appropriate. So, in this kind of scenarios you just need a Modbus handler or just use an emulator from which to serve the stage when the stager connects to it. I have also seen networks that expose Modbus devices to be remotely managed, so this would also be a good place to use the stager.

    Important Note: Please do not execute these actions against any third party PLC. Any writing on the PLCs registers may disrupt the process control strategy for which it was programmed.

    To get an idea of the number of PLCs with Modbus exposed to Internet I wrote a tiny script by using the Censys API. With a good network card you can scan Internet on your own with tools like masscan or Zmap looking for devices running Modbus on port 502.

    As you can see from the following output at least 5500 PLC are available out there.


    Many of this IPs are just honeypots easily to detect;  for instance, Conpot as well as others hosted in Cloud services. For our intention even the honeypots could be useful as long as they have enough memory.

    Well, to upload the payload to the PLC I have created a python script called plcInjectPayload.py. Depending on the control strategy loaded, the PLC will have more or less memory accessible so the script will first check if there is enough room for the payload. To check the size, a Modbus request with an operation ID 03 (Read Holding Register) will be sent, trying to read a particular record from a certain address (each record is 16 bits). If an exception 0x83 is obtained, the PLC won’t be useful for us.

    To upload the payload use the option -upload as follow. This option admits the parameter -addr to indicate the starting address, that is to say, the holding register number from which to load the payload (address 0 if not specified).



    If the payload has an odd number of bytes it will be padded with an additional “0x90”  to avoid some problems when retrieving it. In the example before, the size is 1536 bytes; to check that it was loaded successfully we can download the same number of bytes from address 0 with the option -download


    Apart from using the script to upload a certain payload it is obvious that it can also be used to upload any type of file. I think it's an interesting way to exfiltrate and share information. Who would suspect that the holding registers of a certain public PLC store a .docx or a .zip file?

    It is important to note that the holding records where the payload is loaded can be modified by the PLC. Since we do not know the PLC I/O and its process control strategy is probable that we need to look for a stable range of memory that is not susceptible to changes. The idea would be to upload the payload from a certain direction and then check, during some time, that the payload has not undergone any modification. With plcInjectPayload.py and a couple of bash instruction you can do that.

    Once the payload is uploaded to the PLC it is necessary to retrieve it from the victim's computer. To get this I have created a stager that speaks Modbus; it takes less than 500 bytes (I will try to optimize much more its size). The reverse_tcp and block_api code of it has been taken from Metasploit. The following image corresponds with the asm code of block_recv_modbus.asm, the part in charge of retrieving the payload via Modbus. So this block communicates with the PLC via Modbus to retrieve the payload. The code gets the first 4 bytes to know the stage size and to reserve the necessary memory via VirtualAlloc. Then, it get the payload by making  successive "read holding" requests (function code 03).  Due to protocol specifications, for each read request, the PLC can return a maximum of 250 bytes (125 holding registers) so the stager will recover the payload in chunks of this size.


    Let see a practical example. Recently I found in www.exploit-db.com a nice keylogger shellcode for Windows in about 600 bytes; the size is small but sufficient to make a poc in which are involved several Modbus requests (remember that the maximum number of bytes per request is 250 bytes). The shellcode, once executed, write to "log.bin"  in the user %TEMP% directory the keystrokes. 

    So, first we put the payload in a binary file and prefix it with its length in little endian (taking 4 bytes). 


    Now, let’s upload it to the PLC from address 0:



    Once the stager is run the payload will be recovered in 3 requests: 250 + 250 + 102 = 602 bytes. The following diagram depicts in detail the Modbus traffic generated.


    As seen in the next picture, the Wireshark output follows the above scheme. The process Monitor window confirms that the stage is running successfully (look at the log.bin file saving the keystrokes)


    I have checked it with a Modbus emulator and with real PLC and it works fine although as I said before I think the shellcode can be optimized much more. To make the first tests I did a Modbus handler in python (plcModbusHandler.py) to send the payload to the stager; emulating this way the PLC.


    I’ll try to port the handler to Metasploit. Here a video with the whole process.

    1 comment: