数据包注入是对已经建立的网络连接通过构建任意协议(TCP…UDP…)然后用原始套接字发送的方式进行妨碍的过程,这种方法被广泛使用在网络渗透测试中,比如DDOS,端口扫描等。
一个数据包由IP头部信息、TCP/UDP头部信息和数据构成:
<span class="setting">Packet = <span class="value">IP Header + TCP/UDP Header + Data</span></span>
大多数操作系统的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
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
| Source Port | Destination Port |
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
| Sequence Number |
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
| Acknowledgment Number |
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
| Data | |U|A|P|R|S|F| |
| Offset| Reserved |R|C|S|S|Y|I| Window |
| | |G|K|H|T|N|N| |
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
| Checksum | Urgent Pointer |
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
| Options | Padding |
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
| data |
<span class="deletion">-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-</span>
—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"协议,它会告诉系统我们将提供网络层和传输层。
<span class="keyword" style="font-weight: bold;">s</span> = <span class="keyword" style="font-weight: bold;">socket</span>.<span class="keyword" style="font-weight: bold;">socket</span>(<span class="keyword" style="font-weight: bold;">socket</span>.AF_INET,<span class="keyword" style="font-weight: bold;">socket</span>.SOCK_RAW,)
通过这个简单的类,我们可以进行IP头部信息构造
<span class="class" style="font-weight: bold; color: #445588;"><span class="keyword">class</span> <span class="title">ip</span><span class="params">(object)</span>:</span>
<span class="function"><span class="keyword" style="font-weight: bold;">def</span> <span class="title" style="font-weight: bold; color: #990000;">__init__</span><span class="params">(self, source, destination)</span>:</span>
self.version = <span class="number" style="color: #009999;">4</span>
self.ihl = <span class="number" style="color: #009999;">5</span> <span class="comment" style="font-style: italic; color: #999988;"># Internet Header Length</span>
self.tos = <span class="number" style="color: #009999;">0</span> <span class="comment" style="font-style: italic; color: #999988;"># Type of Service</span>
self.tl = <span class="number" style="color: #009999;">0</span> <span class="comment" style="font-style: italic; color: #999988;"># total length will be filled by kernel</span>
self.id = <span class="number" style="color: #009999;">54321</span>
self.flags = <span class="number" style="color: #009999;">0</span> <span class="comment" style="font-style: italic; color: #999988;"># More fragments</span>
self.offset = <span class="number" style="color: #009999;">0</span>
self.ttl = <span class="number" style="color: #009999;">255</span>
self.protocol = socket.IPPROTO_TCP
self.checksum = <span class="number" style="color: #009999;">0</span> <span class="comment" style="font-style: italic; color: #999988;"># will be filled by kernel</span>
self.source = socket.inet_aton(source)
self.destination = socket.inet_aton(destination)
<span class="function"><span class="keyword" style="font-weight: bold;">def</span> <span class="title" style="font-weight: bold; color: #990000;">pack</span><span class="params">(self)</span>:</span>
ver_ihl = (self.version << <span class="number" style="color: #009999;">4</span>) + self.ihl
flags_offset = (self.flags << <span class="number" style="color: #009999;">13</span>) + self.offset
ip_header = struct.pack(<span class="string" style="color: #dd1144;">"!BBHHHBBH4s4s"</span>,
ver_ihl,
self.tos,
self.tl,
self.id,
flags_offset,
self.ttl,
self.protocol,
self.checksum,
self.source,
self.destination)
"pack"方法会对IP头部元素进行打包并返回它
<span class="class" style="font-weight: bold; color: #445588;"><span class="keyword">class</span> <span class="title">tcp</span>(<span class="title">object</span>):</span>
<span class="function"><span class="keyword" style="font-weight: bold;">def</span> <span class="title" style="font-weight: bold; color: #990000;">__init__</span><span class="params">(<span class="keyword" style="font-weight: bold;">self</span>, srcp, dstp)</span>:</span>
<span class="keyword" style="font-weight: bold;">self</span>.srcp = srcp
<span class="keyword" style="font-weight: bold;">self</span>.dstp = dstp
<span class="keyword" style="font-weight: bold;">self</span>.seqn = <span class="number" style="color: #009999;">0</span>
<span class="keyword" style="font-weight: bold;">self</span>.ackn = <span class="number" style="color: #009999;">0</span>
<span class="keyword" style="font-weight: bold;">self</span>.offset = <span class="number" style="color: #009999;">5</span> <span class="comment" style="font-style: italic; color: #999988;"># Data offset: 5x4 = 20 bytes</span>
<span class="keyword" style="font-weight: bold;">self</span>.reserved = <span class="number" style="color: #009999;">0</span>
<span class="keyword" style="font-weight: bold;">self</span>.urg = <span class="number" style="color: #009999;">0</span>
<span class="keyword" style="font-weight: bold;">self</span>.ack = <span class="number" style="color: #009999;">0</span>
<span class="keyword" style="font-weight: bold;">self</span>.psh = <span class="number" style="color: #009999;">1</span>
<span class="keyword" style="font-weight: bold;">self</span>.rst = <span class="number" style="color: #009999;">0</span>
<span class="keyword" style="font-weight: bold;">self</span>.syn = <span class="number" style="color: #009999;">0</span>
<span class="keyword" style="font-weight: bold;">self</span>.fin = <span class="number" style="color: #009999;">0</span>
<span class="keyword" style="font-weight: bold;">self</span>.window = socket.htons(<span class="number" style="color: #009999;">5840</span>)
<span class="keyword" style="font-weight: bold;">self</span>.checksum = <span class="number" style="color: #009999;">0</span>
<span class="keyword" style="font-weight: bold;">self</span>.urgp = <span class="number" style="color: #009999;">0</span>
<span class="keyword" style="font-weight: bold;">self</span>.payload = <span class="string" style="color: #dd1144;">""</span>
<span class="function"><span class="keyword" style="font-weight: bold;">def</span> <span class="title" style="font-weight: bold; color: #990000;">pack</span><span class="params">(<span class="keyword" style="font-weight: bold;">self</span>, source, destination)</span>:</span>
data_offset = (<span class="keyword" style="font-weight: bold;">self</span>.offset << <span class="number" style="color: #009999;">4</span>) + <span class="number" style="color: #009999;">0</span>
flags = <span class="keyword" style="font-weight: bold;">self</span>.fin + (<span class="keyword" style="font-weight: bold;">self</span>.syn << <span class="number" style="color: #009999;">1</span>) + (<span class="keyword" style="font-weight: bold;">self</span>.rst << <span class="number" style="color: #009999;">2</span>) + (<span class="keyword" style="font-weight: bold;">self</span>.psh << <span class="number" style="color: #009999;">3</span>) + (<span class="keyword" style="font-weight: bold;">self</span>.ack << <span class="number" style="color: #009999;">4</span>) + (<span class="keyword" style="font-weight: bold;">self</span>.urg << <span class="number" style="color: #009999;">5</span>)
tcp_header = struct.pack(<span class="string" style="color: #dd1144;">'!HHLLBBHHH'</span>,
<span class="keyword" style="font-weight: bold;">self</span>.srcp,
<span class="keyword" style="font-weight: bold;">self</span>.dstp,
<span class="keyword" style="font-weight: bold;">self</span>.seqn,
<span class="keyword" style="font-weight: bold;">self</span>.ackn,
data_offset,
flags,
<span class="keyword" style="font-weight: bold;">self</span>.window,
<span class="keyword" style="font-weight: bold;">self</span>.checksum,
<span class="keyword" style="font-weight: bold;">self</span>.urgp)
<span class="comment" style="font-style: italic; color: #999988;">#pseudo header fields</span>
source_ip = source
destination_ip = destination
reserved = <span class="number" style="color: #009999;">0</span>
protocol = socket.<span class="constant">IPPROTO_TCP</span>
total_length = len(tcp_header) + len(<span class="keyword" style="font-weight: bold;">self</span>.payload)
<span class="comment" style="font-style: italic; color: #999988;"># Pseudo header</span>
psh = struct.pack(<span class="string" style="color: #dd1144;">"!4s4sBBH"</span>,
source_ip,
destination_ip,
reserved,
protocol,
total_length)
psh = psh + tcp_header + <span class="keyword" style="font-weight: bold;">self</span>.payload
tcp_checksum = checksum(psh)
tcp_header = struct.pack(<span class="string" style="color: #dd1144;">"!HHLLBBH"</span>,
<span class="keyword" style="font-weight: bold;">self</span>.srcp,
<span class="keyword" style="font-weight: bold;">self</span>.dstp,
<span class="keyword" style="font-weight: bold;">self</span>.seqn,
<span class="keyword" style="font-weight: bold;">self</span>.ackn,
data_offset,
flags,
<span class="keyword" style="font-weight: bold;">self</span>.window)
tcp_header+= struct.pack(<span class="string" style="color: #dd1144;">'H'</span>, tcp_checksum) + struct.pack(<span class="string" style="color: #dd1144;">'!H'</span>, <span class="keyword" style="font-weight: bold;">self</span>.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
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
| Source IP address |
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
| Destination IP address |
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
| Reserved | Protocol | Total Length |
<span class="addition">+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+</span>
•Source IP address (32 bits): 发送者的IP地址
•Destination IP address (32 bits): 接受者的IP地址
•Reserved (8 bits): 清零
•Protocol (8 bits): 传输协议(6为TCP, 17为UDP)
在TCP头部校验计算中,校验和字段必须清零。一旦值被计算出,在发送包之前必须插入字段中。
构造头部字段
<span class="setting">source_ip = <span class="value">source</span></span>
<span class="setting">destination_ip = <span class="value">destination</span></span>
<span class="setting">reserved = <span class="value"><span class="number" style="color: #009999;">0</span></span></span>
<span class="setting">protocol = <span class="value">socket.IPPROTO_TCP</span></span>
打包伪造的头部并且将它插入TCP头部和数据中:
<span class="preprocessor" style="font-weight: bold; color: #999999;"># 伪造头部</span>
psh = <span class="keyword" style="font-weight: bold;">struct</span>.pack(<span class="string" style="color: #dd1144;">"!4s4sBBH"</span>,
source_ip,
destination_ip,
reserved,
protocol,
total_length)
psh = psh + tcp_header + self.payload
校验函数:
<span class="function"><span class="keyword" style="font-weight: bold;">def</span> <span class="title" style="font-weight: bold; color: #990000;">checksum</span><span class="params">(data)</span>:</span>
s = <span class="number" style="color: #009999;">0</span>
n = len(data) % <span class="number" style="color: #009999;">2</span>
<span class="keyword" style="font-weight: bold;">for</span> i <span class="keyword" style="font-weight: bold;">in</span> range(<span class="number" style="color: #009999;">0</span>, len(data)-n, <span class="number" style="color: #009999;">2</span>):
s+= ord(data[i]) + (ord(data[i+<span class="number" style="color: #009999;">1</span>]) << <span class="number" style="color: #009999;">8</span>)
<span class="keyword" style="font-weight: bold;">if</span> n:
s+= ord(data[i+<span class="number" style="color: #009999;">1</span>])
<span class="keyword" style="font-weight: bold;">while</span> (s >> <span class="number" style="color: #009999;">16</span>):
print(<span class="string" style="color: #dd1144;">"s >> 16: "</span>, s >> <span class="number" style="color: #009999;">16</span>)
s = (s & <span class="number" style="color: #009999;">0xFFFF</span>) + (s >> <span class="number" style="color: #009999;">16</span>)
print(<span class="string" style="color: #dd1144;">"sum:"</span>, s)
s = ~s & <span class="number" style="color: #009999;">0xffff</span>
一个小列子:
<span class="keyword" style="font-weight: bold;">s</span> = <span class="keyword" style="font-weight: bold;">socket</span>.<span class="keyword" style="font-weight: bold;">socket</span>(<span class="keyword" style="font-weight: bold;">socket</span>.AF_INET,
<span class="keyword" style="font-weight: bold;">socket</span>.SOCK_RAW,
<span class="keyword" style="font-weight: bold;">socket</span>.IPPROTO_RAW)
src_host = <span class="string" style="color: #dd1144;">"10.0.2.15"</span>
dest_host = <span class="keyword" style="font-weight: bold;">socket</span>.<span class="keyword" style="font-weight: bold;">gethostbyname</span>(<span class="string" style="color: #dd1144;">"www.reddit.com"</span>)
data = <span class="string" style="color: #dd1144;">"TEST!!"</span>
<span class="comment" style="font-style: italic; color: #999988;"># IP Header</span>
ipobj = ip(src_host, dest_host)
iph = ip_object.<span class="keyword" style="font-weight: bold;">pack</span>()
<span class="comment" style="font-style: italic; color: #999988;"># TCP Header</span>
tcpobj = tcp(<span class="number" style="color: #009999;">1234</span>, <span class="number" style="color: #009999;">80</span>)
tcpobj.data_length = len(data) <span class="comment" style="font-style: italic; color: #999988;"># Used in pseudo header</span>
tcph = tcpobj.<span class="keyword" style="font-weight: bold;">pack</span>(ipobj.source,
ipobj.destination)
<span class="comment" style="font-style: italic; color: #999988;"># Injection</span>
packet = iph + tcph + data
Pinject.py
Running the script:
python pinject.py <span class="comment" style="font-style: italic; color: #999988;">--src=10.0.2.15 --dst=www.reddit.com</span>
[+] 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的截图: