使用 Python 的 Socket 模块构建一个 UDP 扫描工具
译文:oschina
英文:bt3gl
当涉及到对一些目标网络的侦察时,出发点无疑是首先发现宿主主机。这个任务还可能包含嗅探和解析网络中数据包的能力。
几周前,我曾经谈到了如何使用Wireshark来进行数据包嗅探,但如果你没有wireshark,你如何去监控网络流量呢?
这一次,Python提供了几种解决方案,今天我将一步步演示如何建立一个UDP主机发现工具。首先,我们要看我们如何处理原始套接字来编写一个简单的嗅探器,它能够查看和解析网络数据包。然后,我们将在子网内多线程运行该进程,结果将在我们的扫描仪上。
原始套接字酷之所在是它能够访问底层网络的信息。比如,我们可以用它来检查IP和ICMP报头,这是在OSI模型的第三层(网络层)。
使用UDP数据报最酷的事情是:当发送信息穿越子网时,不同于TCP,它不太多的开销(还记得TCP握手吧)。我们需要做的就是等待ICMP回应,对方主机是否可用或关闭(不可访问)。记住,ICMP协议本质上是一个特殊的控制协议,它指示错误报告和控制机器数据传输的的行为。
编写网络包嗅探器
我们从一个小功能开始:用 Python 的 socket 库来编写一个简单的网络包嗅探器。
在这个嗅探器中,我们创建一个原始 socket 并将它绑定到一个外部网卡。这个网卡要启用混淆模式(promiscuous mode),也就是说获经过这个网卡的所有数据包都会被捕获,包括那些目标地址不是它的数据包。
使用 Windows 时要注意一点:我们需要发送一个 IOCTL 包才能将网卡设置为混淆模式。另外,虽然 linux 需要使用 ICMP,Windows 却可以以一种独立于协议的方式来嗅探收到的数据包。
- import socket
- import os
- # host to listen
- HOST = '192.168.1.114'
- def sniffing(host, win, socket_prot):
- while 1:
- sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_prot)
- sniffer.bind((host, 0))
- # include the IP headers in the captured packets
- sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
- if win == 1:
- sniffer.ioctl(socket.SIO_RCVALL, socket_RCVALL_ON)
- # read in a single packet
- print sniffer.recvfrom(65565)
- def main(host):
- if os.name == 'nt':
- sniffing(host, 1, socket.IPPROTO_IP)
- else:
- sniffing(host, 0, socket.IPPROTO_ICMP)
- if __name__ == '__main__':
- main(HOST)
在终端中运行如下命令进行测试:
- $ sudo python sniffer.py
在另一个终端中 ping 或 traceroute 某些地址,如 www.google.com 会得到如下结果:
- $ sudo python raw_socket.py
- ('E\x00\x00T\xb3\xec\x00\x005\x01\xe4\x13J}\xe1\x11\xc0\xa8\x01r\x00\x00v\xdfx\xa2\x00\x01sr\x98T\x00\x00\x00\x008\xe3\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567', ('74.125.225.17', 0))
- ('E\x00\x00T\xb4\x1b\x00\x005\x01\xe3\xe4J}\xe1\x11\xc0\xa8\x01r\x00\x00~\xd7x\xa2\x00\x02tr\x98T\x00\x00\x00\x00/\xea\r\x00\x00\x00\x00\x00\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01234567', ('74.125.225.17', 0))
很明显需要对这些头信息进行解码。
IP ICMP层解码
IP 头
典型的 IP 头有如下结构,每个字段都对应一个变量 (这个头最初是用 C 编写的):
ICMP头
同样,ICMP 由于内容的不同其消息类型也不同,但每个消息都包括三个一致的元素:type,code (告知接收主机这个 ICMP 消息的解码类型)和 checksum。
对于我们的扫描器,如果得到的 type 和 code 值是3,这意味着 Destination Unreachable(目标不可达)和 Port Unreachable (端口不可达) ICMP 消息错误
为描述 ICMP 消息头,我们用 python 的 ctypes 库来创建一个类
- import ctypes
- class ICMP(ctypes.Structure):
- _fields_ = [
- ('type', ctypes.c_ubyte),
- ('code', ctypes.c_ubyte),
- ('checksum', ctypes.c_ushort),
- ('unused', ctypes.c_ushort),
- ('next_hop_mtu',ctypes.c_ushort)
- ]
- def __new__(self, socket_buffer):
- return self.from_buffer_copy(socket_buffer)
- def __init__(self, socket_buffer):
- pass
编写消息头解码器
现在可以着手编写 IP/ICMP 消息头解码器了。下面的脚本创建了一个 sniffer socket(正如前面做的那样),然后在一个循环中持续读取数据包并进行解码。
注意代码中将 IP 头的前20个字节读取到了缓存,然后再打印消息头的变量。ICMP 头数据如下:
- import socket
- import os
- import struct
- import ctypes
- from ICMPHeader import ICMP
- # host to listen on
- HOST = '192.168.1.114'
- def main():
- socket_protocol = socket.IPPROTO_ICMP
- sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
- sniffer.bind(( HOST, 0 ))
- sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
- while 1:
- raw_buffer = sniffer.recvfrom(65565)[0]
- ip_header = raw_buffer[0:20]
- iph = struct.unpack('!BBHHHBBH4s4s' , ip_header)
- # Create our IP structure
- version_ihl = iph[0]
- version = version_ihl >> 4
- ihl = version_ihl & 0xF
- iph_length = ihl * 4
- ttl = iph[5]
- protocol = iph[6]
- s_addr = socket.inet_ntoa(iph[8]);
- d_addr = socket.inet_ntoa(iph[9]);
- print 'IP -> Version:' + str(version) + ', Header Length:' + str(ihl) + \
- ', TTL:' + str(ttl) + ', Protocol:' + str(protocol) + ', Source:'\
- + str(s_addr) + ', Destination:' + str(d_addr)
- # Create our ICMP structure
- buf = raw_buffer[iph_length:iph_length + ctypes.sizeof(ICMP)]
- icmp_header = ICMP(buf)
- print "ICMP -> Type:%d, Code:%d" %(icmp_header.type, icmp_header.code) + '\n'
- if __name__ == '__main__':
- main()
测试解码器
在一个终端中运行该脚本,然后在另一个终端运行一个 ping 命令会得到如下结果(注意 ICMP type 值为 0):
- $ ping www.google.com
- PING www.google.com (74.125.226.16) 56(84) bytes of data.
- 64 bytes from lga15s42-in-f16.1e100.net (74.125.226.16): icmp_seq=1 ttl=56 time=15.7 ms
- 64 bytes from lga15s42-in-f16.1e100.net (74.125.226.16): icmp_seq=2 ttl=56 time=15.0 ms(...)
- $ sudo python ip_header_decode.py
- IP -> Version:4, Header Length:5, TTL:56, Protocol:1, Source:74.125.226.16, Destination:192.168.1.114
- ICMP -> Type:0, Code:0
- IP -> Version:4, Header Length:5, TTL:56, Protocol:1, Source:74.125.226.16, Destination:192.168.1.114
- ICMP -> Type:0, Code:0(...)
另外,如果我们运行 traceroute:
- $ traceroute www.google.com
- traceroute to www.google.com (74.125.226.50), 30 hops max, 60 byte packets
- 1 * * *
- 2 * * *
- 3 67.59.255.137 (67.59.255.137) 17.183 ms 67.59.255.129 (67.59.255.129) 70.563 ms 67.59.255.137 (67.59.255.137) 21.480 ms
- 4 451be075.cst.lightpath.net (65.19.99.117) 14.639 ms rtr102.wan.hcvlny.cv.net (65.19.99.205) 24.086 ms 451be075.cst.lightpath.net (65.19.107.117) 24.025 ms
- 5 64.15.3.246 (64.15.3.246) 24.005 ms 64.15.0.218 (64.15.0.218) 23.961 ms 451be0c2.cst.lightpath.net (65.19.120.194) 23.935 ms
- 6 72.14.215.203 (72.14.215.203) 23.872 ms 46.943 ms *
- 7 216.239.50.141 (216.239.50.141) 48.906 ms 46.138 ms 46.122 ms
- 8 209.85.245.179 (209.85.245.179) 46.108 ms 46.095 ms 46.074 ms
- 9 lga15s43-in-f18.1e100.net (74.125.226.50) 45.997 ms 19.507 ms 16.607 ms
会得到这种输出 (注意 ICMP 的响应类型):
- sudo python ip_header_decode.py
- IP -> Version:4, Header Length:5, TTL:252, Protocol:1, Source:65.19.99.117, Destination:192.168.1.114
- ICMP -> Type:11, Code:0(...)IP -> Version:4, Header Length:5, TTL:250, Protocol:1, Source:72.14.215.203, Destination:192.168.1.114
- ICMP -> Type:11, Code:0
- IP -> Version:4, Header Length:5, TTL:56, Protocol:1, Source:74.125.226.50, Destination:192.168.1.114
- ICMP -> Type:3, Code:3
- IP -> Version:4, Header Length:5, TTL:249, Protocol:1, Source:216.239.50.141, Destination:192.168.1.114
- ICMP -> Type:11, Code:0(...)IP -> Version:4, Header Length:5, TTL:56, Protocol:1, Source:74.125.226.50, Destination:192.168.1.114
- ICMP -> Type:3, Code:3
编写扫描器
安装 netaddr
在编写完整的扫描器前首先要安装 netaddr,它是一个用于表示和处理网络地址的 python 库。
Netaddr 提供了操作 IPv4,IPv6 和子网 Mac 等地址的能力。它非常有用,因为我们会用到子网掩码,如192.168.1.0/24
- $ sudo pip install netaddr
我们可以使用如下的代码段来测试这个库 (成功会打印“OK”):
- import netaddr
- ip = '192.168.1.114'
- if ip in netaddr.IPNetwork('192.168.1.0/24'):
- print('OK!')
深入扫描器
我们会将上面所提到的组织在一起来完成我们的扫描器,然后添加一个循环来向目标子网内的所有地址发送 UDP 数据报。
- import threading
- import time
- import socket
- import os
- import struct
- from netaddr import IPNetwork, IPAddress
- from ICMPHeader import ICMP
- import ctypes
- # host to listen on
- HOST = '192.168.1.114'
- # subnet to target (iterates through all IP address in this subnet)
- SUBNET = '192.168.1.0/24'
- # string signature
- MESSAGE = 'hellooooo'
- # sprays out the udp datagram
- def udp_sender(SUBNET, MESSAGE):
- time.sleep(5)
- sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
- for ip in IPNetwork(SUBNET):
- try:
- sender.sendto(MESSAGE, ("%s" % ip, 65212))
- except:
- pass
- def main():
- t = threading.Thread(target=udp_sender, args=(SUBNET, MESSAGE))
- t.start()
- socket_protocol = socket.IPPROTO_ICMP
- sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
- sniffer.bind(( HOST, 0 ))
- sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
- # continually read in packets and parse their information
- while 1:
- raw_buffer = sniffer.recvfrom(65565)[0]
- ip_header = raw_buffer[0:20]
- iph = struct.unpack('!BBHHHBBH4s4s' , ip_header)
- # Create our IP structure
- version_ihl = iph[0]
- ihl = version_ihl & 0xF
- iph_length = ihl * 4
- src_addr = socket.inet_ntoa(iph[8]);
- # Create our ICMP structure
- buf = raw_buffer[iph_length:iph_length + ctypes.sizeof(ICMP)]
- icmp_header = ICMP(buf)
- # check for the type 3 and code and within our target subnet
- if icmp_header.code == 3 and icmp_header.type == 3:
- if IPAddress(src_addr) in IPNetwork(SUBNET):
- if raw_buffer[len(raw_buffer) - len(MESSAGE):] == MESSAGE:
- print("Host up: %s" % src_addr)
- if __name__ == '__main__':
- main()
运行后得到的结果如下:
- $ sudo python scanner.py
- Host up: 192.168.1.114(...)
非常棒!
另外,可以将扫描得到的结果与路由器 DHCP 表中的 IP 地址作对比,它们应该是相同的。
参考:
Tutorial to learn netaddr.
Black Hat Python.
My Gray hat repo.
使用 Python 的 Socket 模块构建一个 UDP 扫描工具的更多相关文章
- 老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具
老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具 poptest是业内唯一的测试开发工程师培训机构,测试开发工程师主要是为测试服务开发测试工具,在工作中要求你做网络级别的安全 ...
- Python基于socket模块实现UDP通信功能示例
Python基于socket模块实现UDP通信功能示例 本文实例讲述了Python基于socket模块实现UDP通信功能.分享给大家供大家参考,具体如下: 一 代码 1.接收端 import ...
- python中socket模块详解
socket模块简介 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.socket通常被叫做"套接字",用于描述IP地址和端口,是一个通信 ...
- 【Python】socket模块应用
[Socket] 本文记录了一些socket模块的简单应用,对于具体原理还没来得及深究. ■ 利用socket模块进行端口连接验证和扫描 在linux中常用nc命令来进行远端端口是否开放的验证.但是这 ...
- python基础===socket模块的讲解(转)
一.网络知识的一些介绍 socket 是网络连接端点.例如当你的Web浏览器请求www.jb51.net上的主页时,你的Web浏览器创建一个socket并命令它去连接 www.jb51.net的Web ...
- socket模块实现基于UDP聊天模拟程序;socketserver模块实现服务端 socket客户端代码示例
socket模块 serSocket.setblocking(False) 设置为非阻塞: #coding=utf-8 from socket import * import time # 用来存储所 ...
- python之socket模块详解--小白博客
主要是创建一个服务端,在创建服务端的时候,主要步骤如下:创建socket对象socket——>绑定IP地址和端口bind——>监听listen——>得到请求accept——>接 ...
- Python的socket模块与交互式指令
socket简介 在编程的过程中,我们需要使用网络编程,这时我们不得不和网络通信的底层基础打交道了.我们必须让自己传输的数据符合网络通信的基本协议,即TCP/IP协议,但是网络通信协议本身很复杂.我们 ...
- 利用socket.io构建一个聊天室
利用socket.io来构建一个聊天室,输入自己的id和消息,所有的访问用户都可以看到,类似于群聊. socket.io 这里只用来做一个简单的聊天室,官网也有例子,很容易就做出来了.其实主要用的东西 ...
随机推荐
- C++编译器模板机制剖析
思考:为什么函数模板可以和函数重载放在一块.C++编译器是如何提供函数模板机制的? 一.编译器编译原理 什么是gcc gcc(GNU C Compiler)编译器的作者是Richard Stallma ...
- Swift Optional
拆包和解包的原因: 其实所谓的 nil 就是 Optional.None, 非 nil 就是Optional.Some, 然后会通过Some(T)包装(wrap)原始值,这也是为什么在使用 Optio ...
- 无法在web服务器下启动调试。该Web服务器未及时响应
下午在运行项目的时候,突然出现了以下错误: 无法在web服务器上启动调试.该Web服务器未及时响应.可能是因为另一个调试器已连接到该Web服务器. 搜索了很久才找到这个解决方案: 1:Web.conf ...
- linux常用命令:ps 命令
Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信 ...
- 面试问题整理之python测试
1.下列哪个语句在Python中是非法的? A.x = y = z =1 B.x = (y = z + 1) C.x, y = y, x D.x += y 答案:B 2.关于Python内存管理,下列 ...
- java反射field和method的顺序问题
最近在有思考到序列化性能优化的问题,关于java反射field和method的顺序问题,这里有详细的讨论http://stackoverflow.com/questions/5001172/java- ...
- 05:ModelForm 数据验证 & 生成html & 数据库操作
目录:Django其他篇 01:Django基础篇 02:Django进阶篇 03:Django数据库操作--->Model 04: Form 验证用户数据 & 生成html 05:Mo ...
- 使用dll,将image库开放出去
这是很经典的想法了,但是如何来做,不经过摸索不知道细节. 最简单: dll处 #include "stdafx.h" #ifdef _DEBUG #define new ...
- 《网络对抗》——逆向及Bof基础实践
<网络对抗>--逆向及Bof基础实践 原理 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数. 手工修改可执行文件,改变程序执行流程,直接跳转到g ...
- linux内核分析 第5章读书笔记
第五章 系统调用 一.与内核通信 系统调用在用户控件进程和硬件设备之间添加了一个中间层,作用有: 为用户空间提供了一种硬件的抽象接口 系统调用保证了系统的稳定和安全 每个进程都运行在虚拟系统中,而在用 ...