• Aucun résultat trouvé

Parsing TTL Fields with Scapy

Dans le document Violent Python (Page 156-159)

Let’s begin writing our script by printing out the source IP address and TTL of incoming packets. At this point we will return to using Scapy for the rest of the chapter. It would be just as easy to write this code using dpkt. We will set up a function to sniff and pass each individual packet to the function testTTL(), which examines the packet for the IP layer, extracting the IP source address and TTL fields and prints these fields to the screen.

from scapy.all import * def testTTL(pkt):

try:

if pkt.haslayer(IP):

ipsrc = pkt.getlayer(IP).src ttl = str(pkt.ttl)

print '[+] Pkt Received From: '+ipsrc+' with TTL: ' \ + ttl

except:

pass def main():

sniff(prn=testTTL, store=0) if __name__ == '__main__':

main()

Running our code, we see that we have received quite a few packets from diff-erent source addresses, with varying TTLs. These results also include the decoy scans from 8.8.8.8 with a TTL of 13. As we know that the TTL should be 64 minus 11 = 53 hops away, we can argue that somebody spoofed these. It’s important to note at this point that while Linux/Unix systems usually start with an initial TTL of 64, Windows-based systems start with a TTL of 128. For the purposes of our script here, we’ll assume we are only dissecting IP packets from Linux workstations scanning our target, so let’s add a function to check the received TTL against the actual TTL.

analyst# python printTTL.py

[+] Pkt Received From: 192.168.1.7 with TTL: 64 [+] Pkt Received From: 173.255.226.98 with TTL: 52 [+] Pkt Received From: 8.8.8.8 with TTL: 13 [+] Pkt Received From: 8.8.8.8 with TTL: 13 [+] Pkt Received From: 192.168.1.7 with TTL: 64 [+] Pkt Received From: 173.255.226.98 with TTL: 52 [+] Pkt Received From: 8.8.8.8 with TTL: 13

Our function checkTTL() takes an IP source address with its respective received TTL as input and prints out a message for invalid TTLs. First, let’s use a quick conditional statement to eliminate packets from private IP addresses (10.0.0.0–10.255.255.255, 172.16.0.0–172.31.255.255, and 192.168.0.0–

192.168.255.255). To do this, we import the IPy library. To avoid the class IP conflicting with the Scapy class IP, we reclassify it as IPTEST. If the IPTEST(ipsrc).

iptype() returns ‘PRIVATE’, we return from our checkTTL function, ignoring the packet for examination.

We receive quite a few unique packets from the same source address. We only want to check the source address once. If we have not seen the source address previously, let’s build an IP packet with a destination address equal to the source.

Additionally, we make the packet an ICMP echo request so that the destination will respond. Once the destination address responds, we place the TTL value in a dictionary, indexed by the IP source address. We then check to see if that differ-ence between the actual received TTL and the TTL on the original packet exceeds a threshold value. Packets may take different routes on the way to a destination and therefore have different TTLs; however, if thehops distance differs byfive hops, we can assume it may be a spoofed TTL and print a warning message to the screen.

from scapy.all import * from IPy import IP as IPTEST ttlValues = {}

THRESH = 5

def checkTTL(ipsrc, ttl):

if IPTEST(ipsrc).iptype() == 'PRIVATE':

return

if not ttlValues.has_key(ipsrc):

pkt = sr1(IP(dst=ipsrc) / ICMP(), \ retry=0, timeout=1, verbose=0) ttlValues[ipsrc] = pkt.ttl

if abs(int(ttl) - int(ttlValues[ipsrc])) > THRESH:

print '\n[!] Detected Possible Spoofed Packet From: '\

+ ipsrc

print '[!] TTL: ' + ttl + ', Actual TTL: ' \ + str(ttlValues[ipsrc])

We add some option parsing for the specific address to listen in on, followed by an option to set the threshold to produce the final code. Less than fifty lines of code and we have H.D. Moore’s solution to the Pentagon dilemma from over a decade ago.

CHAPTER 4: Network Traffic Analysis with Python 148

import time import optparse

from scapy.all import * from IPy import IP as IPTEST ttlValues = {}

THRESH = 5

def checkTTL(ipsrc, ttl):

if IPTEST(ipsrc).iptype() == 'PRIVATE':

return

if not ttlValues.has_key(ipsrc):

pkt = sr1(IP(dst=ipsrc) / ICMP(), \ retry=0, timeout=1, verbose=0) ttlValues[ipsrc] = pkt.ttl

if abs(int(ttl) - int(ttlValues[ipsrc])) > THRESH:

print '\n[!] Detected Possible Spoofed Packet From: '\

+ ipsrc

print '[!] TTL: ' + ttl + ', Actual TTL: ' \ + str(ttlValues[ipsrc])

def testTTL(pkt):

try:

if pkt.haslayer(IP):

ipsrc = pkt.getlayer(IP).src ttl = str(pkt.ttl)

checkTTL(ipsrc, ttl) except:

pass def main():

parser = optparse.OptionParser("usage%prog "+\

"-i<interface> -t <thresh>")

parser.add_option('-i', dest='iface', type='string',\

help='specify network interface')

parser.add_option('-t', dest='thresh', type='int', help='specify threshold count ')

(options, args) = parser.parse_args() if options.iface == None:

conf.iface = 'eth0' else:

conf.iface = options.iface

if options.thresh != None:

THRESH = options.thresh else:

THRESH = 5

sniff(prn=testTTL, store=0) if __name__ == '__main__':

main()

Running our code, we can see that it correctly identifies the decoy Nmap scan from 8.8.8.8 because of the TTL of 13 compared to the actual TTL of 53 (for our packet). It is important to note that our value is generated off an initial default TTL for Linux of 64. Although RFC 1700 recommends the default TTL as 64, Microsoft Windows has used an initial TTL of 128 since MS Windows NT 4.0. Additionally, some other Unix variants have different TTLs such as Solaris 2.x with a default TTL of 255. For now, we will leave the script as and assume spoofed packets are originating from a Linux based machine.

analyst# python spoofDetect.py –i eth0 –t 5 [!] Detected Possible Spoofed Packet From: 8.8.8.8 [!] TTL: 13, Actual TTL: 53

[!] Detected Possible Spoofed Packet From: 8.8.8.8 [!] TTL: 13, Actual TTL: 53

[!] Detected Possible Spoofed Packet From: 8.8.8.8 [!] TTL: 13, Actual TTL: 53

[!] Detected Possible Spoofed Packet From: 8.8.8.8 [!] TTL: 13, Actual TTL: 53

<..SNIPPED..>

STORM’S FAST-FLUX AND CONFICKER’S

Dans le document Violent Python (Page 156-159)