I'm asking here just because I've searched a lot and until now I wasn't able to find a clue about how to accomplish this task. The task at hand is to build a python script that is able to:
- Sniff tcp packets;
- Decode de payload that in my case is encrypted with JWT - The JWT in turn is a json in base64;
- Repack with the decrypted data;
- Reinject the packet on the same interface if possible, but can be a virtual interface too;
- It has to happen on the fly.
All traffic that arrives to the server come into an interface that hasn't any IP associated, so the sniffing has to happen in layer 2 and reinjected in layer 2, but case is possible could be captured in layer 2 and reinjected right into layer 3, but in this case I'm not sure how to do it. By layer 2 I mean got to be bound to an interface, not with hostname and port.
I've based my script on this http://www.offensivepython.com/2014/08/tcp-packet-injection-with-python.html for the injection and works fine in the lab (Kali, Centos 6.8) passing the tuple host, port to socket.bind() and for sniffing I wrote the following:
#!/usr/bin/python
import socket
import ctypes
import fcntl
from struct import *
from time import sleep
class ifreq(ctypes.Structure):
_fields_ = [("ifr_ifrn", ctypes.c_char * 16),("ifr_flags", ctypes.c_short)]
IFACE="eth0"
ETH_P_IP=0x0800
IFF_PROMISC = 0x100
SIOCGIFFLAGS = 0x8913
SIOCSIFFLAGS = 0x8914
skt = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_IP))
ifr = ifreq()
ifr.ifr_ifrn = IFACE
fcntl.ioctl(skt.fileno(), SIOCGIFFLAGS, ifr)
ifr.ifr_flags |= IFF_PROMISC
fcntl.ioctl(skt.fileno(), SIOCSIFFLAGS, ifr)
print "[+] Sniffing packets"
while True:
pct = skt.recvfrom(65535)
pct = pct[0]
ipheader = pct[:20]
iph = unpack('!BBHHHBBH4s4s', ipheader)
version_ih1 = iph[0]
version = version_ih1 >> 4
ih1 = version_ih1 & 0xF
iph_length = ih1 * 4
ttl = iph[5]
protocol = iph[6]
src_addr = socket.inet_ntoa(iph[8])
dst_addr = socket.inet_ntoa(iph[9])
print 'Version: %s \nIP Header Length: %s\nTTL: %s\nProtocol: %s\nSource Addr: %s\nDest Addr: %s'%(version, iph_length, ttl, protocol, src_addr, dst_addr)
tcp_header = pct[iph_length:iph_length+20]
tcph = unpack('!HHLLBBHHH', tcp_header)
src_port = tcph[0]
dst_port = tcph[1]
sequence = tcph[2]
ack = tcph[3]
doff_reserved = tcph[4]
tcph_length = doff_reserved >> 4
if src_port == 0 | dst_port == 0:
continue
print 'SRC Port: %s \nDST_Port: %s\nSEQ: %s\nACK: %s\nDoff Reserved: %s\nTCP Header Length: %s'%(src_port, dst_port, sequence, ack, doff_reserved, tcph_length)
h_size = iph_length + tcph_length * 4
data_size = len(pct) - h_size
data = pct[h_size:]
print 'Data: %s\n' %data
sleep(2)
I've tested this solution and works fine for injection in my labs (Kali, CentOS 6.8), I can run a tcpdump and see the packets coming to the specified interface but in production (RHEL 6.8) does now show anything in tcpdump.
I've been searching this in the socket linux docs, python socket docs in some books like Core Python, Foundation of Network Programming, The Linux Programming Interface and has from none to little information on how to properly use AF_PACKET. My questions are:
- Is it possible decode the traffic at layer 2 level;
- In case yes and following the reasoning of the scripts aforementioned what am I doing wrong?
- Is there some differences between the sockets implementation across the different Linux distro (RHEL, Centos, Debian);
- How to proper use the socket interface using the arguments (socket.AF_PACKET, socket.SOCK_RAW,[OPTIONAL]);
- the OPTIONAL argument in the item above is the protocol, in some examples I've seen socket.htons(ETH_P_IP), ETH_P_IP, socket.ntohs(ETH_P_IP), and the constant sometimes is ETH_P_ALL, what are the differences?
- Could someone leave an example here?
This question is because I'm working with a Threat Detection tool that processes all data and generate alerts, incidents and reports, but with the data encoded with JWT is not possible to see what is actually happening in the requests, therefore is not possible to see the actual data and properly use the tool.
Thanks in advance.