- A+
数据包注入是对已经建立的网络连接通过构建任意协议(TCP…UDP…)然后用原始套接字发送的方式进行妨碍的过程,这种方法被广泛使用在网络渗透测试中,比如DDOS,端口扫描等。
一个数据包由IP头部信息、TCP/UDP头部信息和数据构成:
Packet = IP Header + TCP/UDP Header + Data
大多数操作系统的socket API都支持包注入(尤其是基于Berkeley Sockets的),微软在windows xp之后为了避免包嗅探限制了原始套接字的能力。这篇文章只适用于UNIX/类UNIX系统。
TCP协议被广泛运用于互联网上的数据传输,它是一种面向连接(连接导向)的、可靠的、基于IP的传输层协议。
TCP的首部格式:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | -+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-
—Source Port是源端口,16位。
—Destination Port是目的端口,16位。
—Sequence Number是发送数据包中的第一个字节的序列号,32位。
—Acknowledgment Number是确认序列号,32位。
—Data Offset是数据偏移,4位,该字段的值是TCP首部(包括选项)长度乘以4。
—标志位: 6位,URG表示Urgent Pointer字段有意义:
ACK表示Acknowledgment Number字段有意义
PSH表示Push功能,RST表示复位TCP连接
SYN表示SYN报文(在建立TCP连接的时候使用)
FIN表示没有数据需要发送了(在关闭TCP连接的时候使用)
Window表示接收缓冲区的空闲空间,16位,用来告诉TCP连接对端自己能够接收的最大数据长度。
—Checksum是校验和,16位。
—Urgent Pointers是紧急指针,16位,只有URG标志位被设置时该字段才有意义,表示紧急数据相对序列号(Sequence Number字段的值)的偏移。
更多TCP协议的详细信息可以在网上轻易找到,在这里不再赘述。
为了建立一个可以自己构造数据的包,我们使用"SOCK_RAW"这种socket格式,使用"IPPROTO_RAW"协议,它会告诉系统我们将提供网络层和传输层。
s = socket.socket(socket.AF_INET,socket.SOCK_RAW,)
通过这个简单的类,我们可以进行IP头部信息构造
class ip(object): def __init__(self, source, destination): self.version = 4 self.ihl = 5 # Internet Header Length self.tos = 0 # Type of Service self.tl = 0 # total length will be filled by kernel self.id = 54321 self.flags = 0 # More fragments self.offset = 0 self.ttl = 255 self.protocol = socket.IPPROTO_TCP self.checksum = 0 # will be filled by kernel self.source = socket.inet_aton(source) self.destination = socket.inet_aton(destination) def pack(self): ver_ihl = (self.version << 4) + self.ihl flags_offset = (self.flags << 13) + self.offset ip_header = struct.pack("!BBHHHBBH4s4s", ver_ihl, self.tos, self.tl, self.id, flags_offset, self.ttl, self.protocol, self.checksum, self.source, self.destination)
"pack"方法会对IP头部元素进行打包并返回它
ipobj = ip("127.0.0.1", "127.0.0.2") # Creating an ip object instancei pobj.source = "localhost" # Changing IP element value
构造TCP头部信息
TCP类允许我们轻易地操作TCP头部元素并打包它们
class tcp(object): def __init__(self, srcp, dstp): self.srcp = srcp self.dstp = dstp self.seqn = 0 self.ackn = 0 self.offset = 5 # Data offset: 5x4 = 20 bytes self.reserved = 0 self.urg = 0 self.ack = 0 self.psh = 1 self.rst = 0 self.syn = 0 self.fin = 0 self.window = socket.htons(5840) self.checksum = 0 self.urgp = 0 self.payload = "" def pack(self, source, destination): data_offset = (self.offset << 4) + 0 flags = self.fin + (self.syn << 1) + (self.rst << 2) + (self.psh << 3) + (self.ack << 4) + (self.urg << 5) tcp_header = struct.pack('!HHLLBBHHH', self.srcp, self.dstp, self.seqn, self.ackn, data_offset, flags, self.window, self.checksum, self.urgp) #pseudo header fields source_ip = source destination_ip = destination reserved = 0 protocol = socket.IPPROTO_TCP total_length = len(tcp_header) + len(self.payload) # Pseudo header psh = struct.pack("!4s4sBBH", source_ip, destination_ip, reserved, protocol, total_length) psh = psh + tcp_header + self.payload tcp_checksum = checksum(psh) tcp_header = struct.pack("!HHLLBBH", self.srcp, self.dstp, self.seqn, self.ackn, data_offset, flags, self.window) tcp_header+= struct.pack('H', tcp_checksum) + struct.pack('!H', self.urgp)
我们知道,TCP协议是一种面向连接(连接导向)的、可靠的、基于IP的传输层协议,提供一种面向连接的、可靠的字节流服务,面向连接意味着两个使用TCP的应用(通常是一个客户和一个服务器)在彼此交换数据包之前必须先建立一个TCP连接。
伪造的头部信息有五个不同的区域且包含了source ip和destination ip
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source IP address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Destination IP address | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Reserved | Protocol | Total Length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
•Source IP address (32 bits): 发送者的IP地址 •Destination IP address (32 bits): 接受者的IP地址 •Reserved (8 bits): 清零 •Protocol (8 bits): 传输协议(6为TCP, 17为UDP)
在TCP头部校验计算中,校验和字段必须清零。一旦值被计算出,在发送包之前必须插入字段中。
构造头部字段
source_ip = source destination_ip = destination reserved = 0 protocol = socket.IPPROTO_TCP
打包伪造的头部并且将它插入TCP头部和数据中:
# 伪造头部 psh = struct.pack("!4s4sBBH", source_ip, destination_ip, reserved, protocol, total_length) psh = psh + tcp_header + self.payload
校验函数:
def checksum(data): s = 0 n = len(data) % 2 for i in range(0, len(data)-n, 2): s+= ord(data[i]) + (ord(data[i+1]) << 8) if n: s+= ord(data[i+1]) while (s >> 16): print("s >> 16: ", s >> 16) s = (s & 0xFFFF) + (s >> 16) print("sum:", s) s = ~s & 0xffff
一个小列子:
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_RAW) src_host = "10.0.2.15" dest_host = socket.gethostbyname("www.reddit.com") data = "TEST!!" # IP Header ipobj = ip(src_host, dest_host) iph = ip_object.pack() # TCP Header tcpobj = tcp(1234, 80) tcpobj.data_length = len(data) # Used in pseudo header tcph = tcpobj.pack(ipobj.source, ipobj.destination) # Injection packet = iph + tcph + data
Pinject.py Running the script: python pinject.py --src=10.0.2.15 --dst=www.reddit.com [+] Local Machine: 10.0.2.15 [+] Remote Machine: 198.41.209.142 [+] Raw scoket created [+] Data to inject: TEST!! [+] Constructing IP Header [+] Constructing TCP Header
wireshark的截图:
- 我的微信
- 这是我的微信扫一扫
- 我的微信公众号
- 我的微信公众号扫一扫