loader
Foto

Sniffer TCP, UDP, ICMP (és ARP) protokollokra

Egy új sniffer alkalmazást készítünk el Python nyelven, mert a TCP helyett UDP és ICMP, illetve ARP protokollt figyelnénk.

A célunk most az, hogy egy olyan sniffert hozzunk létre, amely nem csak a TCP protokollt képes kezelni, hanem akár az UDP-t, illetve az ICMP-t is. A korábbi cikkünkben a socket elkapására a következő sort alkalmaztuk:

s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_TCP)

 

A "socket.IPPROTO_TCP" paraméterrel állítottuk be azt, hogy a TCP protokollt figyeljük, és majd annak a tartalmát olvassuk ki. Igen ám, de mit csináljunk akkor, ha nem csak a TCP, hanem az UDP vagy az ICMP csomagok olvasását is el kívánjuk végezni? Ebben az esetben ez a paraméter nem alkalmazható. Nézzük meg Linux alatt egy szövegszerkesztővel (pl.: leafpad, nano, stb) az "it_ether.h" file tartalmát:

nano /usr/include/linux/it_ether.h

 

Ebben a file-ban találjuk meg a hálózati definíciókat. Mi most az Internet Protocol packet-et akarjuk alkalmazni, ezért választhatnánk paraméternek akár a 0x0800-at, de mi a 0x0003-at adtuk meg a kérés/válasz feldolgozása miatt.

kep
1. ábra   Az "it_ether.h" file tartalma (részlet)
 

Az Ethernet packet felépítése látható a következő ábrán. A cél és a forrás MAC címek után következik az Ether Type, amely azt árulja el, hogy milyen típusú a packet. Például a 0x0806 az ARP-ot takarja, a 0x0800 azt, ahol megtalálhatók a TCP, UDP, ICMP protokollok. A frame után találjuk az ábrán a "Data"-t, majd a CRC-t.

kep
2. ábra   Az Ethernet packet felépítése
 

A "Data"-ban lehet IP (amelyben UDP, TCP, ICMP is megtalálható), ARP, stb.. A következő ábra az UDP felépítését mutatja. Két-két byte tartalmazza a forrás és a cél portot, majd a méretét, illetve a checksum-ot találjuk a header-ben.

kep
3. ábra   UDP fejléc
 

A 4. ábrán az ICMP protokoll header-jének a felépítése látható. A számunkra talán a két legfontosabb a Type és a Code mezők, ahol konkréten megadásra kerülnek az ICMP részletei (pl.: ping).

kep
4. ábra   ICMP protokoll fejléce
 

Rövid elméleti bevezető után már tudjuk értelmezni a Python nyelven írt sniffer alkalmazásunkat. Nézzük meg a következő programunk működését.
A socket elkapásánál a "0x0003" került megadásra, azaz, mindent elkapunk. Amikor nem vagyunk kiváncsiak a TCP és az UDP adataira, akkor a "show" értékét 'n'-re kell állítani. Programunk legelején adtuk meg az Ethernet keret méretét is.
Az Ehernet keret felépítésénél látható volt, hogy ott a két MAC cím található meg, illetve a típus. Ezért alkalmaztuk a "6s6s2s" patternt az "unpack()" metódusnál.

Az Ethernet keret tartalmának kiolvasása után következik az IP keret feldolgozása. Ebben különböző protokollok találhatók, ezért nagyon fontos az, hogy az IP-ből kinyerjük a protokoll kódját (pl.: TCP: 6).

import socket, struct, binascii

try:
    s = socket.socket( socket.PF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))
except socket.error , msg:
    print 'Sajnos gond van. Hiba: ' + str(msg[0]) + ' - ' + msg[1]
    sys.exit()

show = 'y'
ethLength = 14

while True:
    packet = s.recvfrom(65565)
     
    ethHeader = packet[0][0:14]
    ethUnp = struct.unpack("!6s6s2s", ethHeader)
    print 'Ethernet Frame:'
    print '   Source MAC: ' + binascii.hexlify(ethUnp[1]) + ', Dest MAC: ' + binascii.hexlify(ethUnp[0])
    print '   ' + binascii.hexlify(ethUnp[2])

    #------ IP Header ----------
    ipHeader = packet[0][14:34]
    ipHeaderUnp = struct.unpack("!BBHHHBBH4s4s", ipHeader)

    version = ipHeaderUnp[0] >> 4
    ihl = ipHeaderUnp[0] & 0xF
    iphLength = ihl * 4

    totalLength = ipHeaderUnp[2]
    identification = ipHeaderUnp[3]
    ttl = ipHeaderUnp[5]
    protocol = ipHeaderUnp[6]
    headerChecksum = ipHeaderUnp[7]
    sIP = socket.inet_ntoa(ipHeaderUnp[8])
    dIP = socket.inet_ntoa(ipHeaderUnp[9])

    print 'IP Header:'
    print '   Source IP: ' + sIP + ', Dest. IP: ' + dIP
    print '   Version: ' + str(version) + ', IP Header length: ' + str(iphLength)
    print '   TTL: ' + str(ttl) + ', Protocol: ' + str(protocol)
    print '   Id: ' + str(identification) + ', TotalLength: ' + str(totalLength)
    print '   Header Checksum: ' + str(headerChecksum)


    if protocol == 6 :
      tcpHeader = packet[0][34:54]
      tcpHeaderUnp = struct.unpack("!HHLLBBHHH", tcpHeader)

      sPort = tcpHeaderUnp[0]
      dPort = tcpHeaderUnp[1]
      seqNumber = tcpHeaderUnp[2]
      ackNumber = tcpHeaderUnp[3]
      offsetReserved = tcpHeaderUnp[4]
      tcpLength = offsetReserved >> 4

      tcpFlags = tcpHeaderUnp[5]
      window = tcpHeaderUnp[6]
      tcpChecksum = tcpHeaderUnp[7]
      urgPointer = tcpHeaderUnp[8]

      print 'TCP Header:'
      print '   Source Port: ' + str(sPort) + ', Dest. Port: ' + str(dPort)
      print '   Sequence number: ' + str(seqNumber) + ', Ack Number: ' + str(ackNumber)
      print '   TCP Length: ' + str(tcpLength)
      print '   Window: ' + str(window) + ', TCP_Checksum: ' + str(tcpChecksum)
      print '   Urgent Pointer: ' + str(urgPointer)

      if show == 'y' :
         tcpHeaderLength = ethLength + iphLength + tcpLength*4
         tcpData = packet[0][tcpHeaderLength:]
         print '   Data: ' + str(tcpData)
    
    elif protocol == 1 :
      temp = ethLength + iphLength
      icmpHeader = packet[0][temp:temp+4]
      icmphUnp = struct.unpack('!BBH' , icmpHeader)
      type = icmphUnp[0]
      code = icmphUnp[1]
      checksum = icmphUnp[2]

      print 'ICMP header:'
      print '  type: ' + str(type) + ', code: ' + str(code)
      print '  checksum: ' + str(checksum)

    elif protocol == 17 :
      temp = ethLength + iphLength
      udpHeader = packet[0][temp:temp+8]
      udpHeaderUnp = struct.unpack('!HHHH' , udpHeader)
      sPort = udpHeaderUnp[0]
      dPort = udpHeaderUnp[1]
      udpLength = udpHeaderUnp[2]
      udpChecksum = udpHeaderUnp[3]

      print 'UDP Header:'
      print '   Source Port: ' + str(sPort) + ', Dest. Port: ' + str(dPort)
      print '   Length: ' + str(udpLength) + ', Checksum: ' + str(udpChecksum)

      if show == 'y' :
         udpHeaderLength = ethLength + iphLength + udpLength
         udpData = packet[0][udpHeaderLength:]
         print '   Data: ' + str(udpData)

    print '---------------------------'
    print

 

Indítsuk el a programunkat, és a hallgatózás a hálózaton elkezdődik. Egy eredményrészlet látható az 5. ábrán, ahol az ICMP protokoll adatai látthatók. 

kep
5. ábra   Elkapott sniffer adatok
 

Ha tovább figyeljük a sniffer által mutatott adatokat, akkor olyan protokoll "adatai" is megjelennek, amelyet nem kezeltünk le az alkalmazásunkban. A "Protocol" értéke 55, amely az ARP-ot takarja. Megjegyezzük, hogy ha nem nézzük meg a "Protocol" értékét, akkor is rá tudunk jönni az ARP-ra, mert a MAC cím értéke FFFFFFFFFFFF (6. ábra).

kep
6. ábra   ICMP és ARP protokoll
 

Módosítsuk most úgy a sniffer alkalmazásunkat, hogy az ARP protokollt is kezelni tudjuk. Ehhez ismerni kell az ARP header felépítését, amelyet a 7. ábrán láthatunk.

kep
7. ábra   ARP fejléc felépítése
 

Módosítsuk tehát az alkalmazásunkat a következők szerint:

import socket, struct, binascii

try:
    s = socket.socket( socket.PF_PACKET , socket.SOCK_RAW , socket.ntohs(0x0003))
except socket.error , msg:
    print 'Sajnos gond van. Hiba: ' + str(msg[0]) + ' - ' + msg[1]
    sys.exit()

show = 'y'
ethLength = 14

while True:
    packet = s.recvfrom(65565)
     
    ethHeader = packet[0][0:14]
    ethUnp = struct.unpack("!6s6s2s", ethHeader)

    # 800:IP,  806:ARP
    ethType = binascii.hexlify(ethUnp[2])

    print 'Ethernet Frame:'
    print '   Source MAC: ' + binascii.hexlify(ethUnp[1]) + ', Dest MAC: ' + binascii.hexlify(ethUnp[0])

# ARP
    if ethType == '0806':
      arpHeader = packet[0][14:42]
      arpHeaderUnp = struct.unpack('!2s2s1s1s2s6s4s6s4s', arpHeader)
      print 'ARP Header:'
      print '   Hardware type: ' + binascii.hexlify(arpHeaderUnp[0]) + ', Hardware size: ' + binascii.hexlify(arpHeaderUnp[2])
      print '   Protocol type: ' + binascii.hexlify(arpHeaderUnp[1]) + ', Protocol size: ' + binascii.hexlify(arpHeaderUnp[3])
      print '   Source IP: ' + binascii.hexlify(arpHeaderUnp[6]) + ', Source MAC: ' + binascii.hexlify(arpHeaderUnp[5])
      print '   Dest IP: ' + binascii.hexlify(arpHeaderUnp[8]) + ', Dest MAC: ' + binascii.hexlify(arpHeaderUnp[7])
      print '   Opcode: ' + binascii.hexlify(arpHeaderUnp[4])

# IP
    elif ethType == '0800':
      ipHeader = packet[0][14:34]
      ipHeaderUnp = struct.unpack("!BBHHHBBH4s4s", ipHeader)

      version = ipHeaderUnp[0] >> 4
      ihl = ipHeaderUnp[0] & 0xF
      iphLength = ihl * 4

      totalLength = ipHeaderUnp[2]
      identification = ipHeaderUnp[3]
      ttl = ipHeaderUnp[5]
      protocol = ipHeaderUnp[6]
      headerChecksum = ipHeaderUnp[7]
      sIP = socket.inet_ntoa(ipHeaderUnp[8])
      dIP = socket.inet_ntoa(ipHeaderUnp[9])

      print 'IP Header:'
      print '   Source IP: ' + sIP + ', Dest. IP: ' + dIP
      print '   Version: ' + str(version) + ', IP Header length: ' + str(iphLength)
      print '   TTL: ' + str(ttl) + ', Protocol: ' + str(protocol)
      print '   Id: ' + str(identification) + ', TotalLength: ' + str(totalLength)
      print '   Header Checksum: ' + str(headerChecksum)


      if protocol == 6 :
        tcpHeader = packet[0][34:54]
        tcpHeaderUnp = struct.unpack("!HHLLBBHHH", tcpHeader)

        sPort = tcpHeaderUnp[0]
        dPort = tcpHeaderUnp[1]
        seqNumber = tcpHeaderUnp[2]
        ackNumber = tcpHeaderUnp[3]
        offsetReserved = tcpHeaderUnp[4]
        tcpLength = offsetReserved >> 4

        tcpFlags = tcpHeaderUnp[5]
        window = tcpHeaderUnp[6]
        tcpChecksum = tcpHeaderUnp[7]
        urgPointer = tcpHeaderUnp[8]

        print 'TCP Header:'
        print '   Source Port: ' + str(sPort) + ', Dest. Port: ' + str(dPort)
        print '   Sequence number: ' + str(seqNumber) + ', Ack Number: ' + str(ackNumber)
        print '   TCP Length: ' + str(tcpLength)
        print '   Window: ' + str(window) + ', TCP_Checksum: ' + str(tcpChecksum)
        print '   Urgent Pointer: ' + str(urgPointer)

        if show == 'y' :
           tcpHeaderLength = ethLength + iphLength + tcpLength*4
           tcpData = packet[0][tcpHeaderLength:]
           print '   Data: ' + str(tcpData)
    
      elif protocol == 1 :
        temp = ethLength + iphLength
        icmpHeader = packet[0][temp:temp+4]
        icmphUnp = struct.unpack('!BBH' , icmpHeader)
        type = icmphUnp[0]
        code = icmphUnp[1]
        checksum = icmphUnp[2]

        print 'ICMP header:'
        print '  type: ' + str(type) + ', code: ' + str(code)
        print '  checksum: ' + str(checksum)

      elif protocol == 17 :
        temp = ethLength + iphLength
        udpHeader = packet[0][temp:temp+8]
        udpHeaderUnp = struct.unpack('!HHHH' , udpHeader)
        sPort = udpHeaderUnp[0]
        dPort = udpHeaderUnp[1]
        udpLength = udpHeaderUnp[2]
        udpChecksum = udpHeaderUnp[3]

        print 'UDP Header:'
        print '   Source Port: ' + str(sPort) + ', Dest. Port: ' + str(dPort)
        print '   Length: ' + str(udpLength) + ', Checksum: ' + str(udpChecksum)

        if show == 'y' :
           udpHeaderLength = ethLength + iphLength + udpLength
           udpData = packet[0][udpHeaderLength:]
           print '   Data: ' + str(udpData)

    print '---------------------------'
    print

 

Futtassuk most a módosított sniffer alkalmazásunkat, ahol már az ARP csomagok is kiértékelésre kerülnek (8. ábra).

kep
8.ábra   Sniffer alkalmazásunk futás közben
 

 



Egyéb cikkek

További cikkeink ebben a témakörben

Régebbi cikkeink

Elkezdünk egy sorozatot, amelyhez a Kali Linux használata elengedhetetlen lesz, de természetesen folytatjuk a C# nyelv aszinkron használatának a bemutatását is. Az IT biztonság (amely elég széles területet foglal magába) egyik elengedhetetlen eszköze. . . .

Az nmap (grafikus megjelenítésnél a ZenMap) használata az IT biztonság, illetve az üzemeltetés területén dolgozó szakembereknél szinte elkerülhetetlen. Az ingyenes szoftver segítségével tesztelhetők a számítógépeink, a számítógéphálózatunk, vizsgálha. . . .

A saját informatikai rendszerünk tesztelésénél előfordulhat, hogy meg kell változtatni a MAC címünket. Hogyan tehetjük ezt meg a Kali Linux segítségével? Megnézzük most ezt ebben a cikkben.. . . .