译文:oschina

英文:bt3gl

当涉及到对一些目标网络的侦察时,出发点无疑是首先发现宿主主机。这个任务还可能包含嗅探和解析网络中数据包的能力。

几周前,我曾经谈到了如何使用Wireshark来进行数据包嗅探,但如果你没有wireshark,你如何去监控网络流量呢?

这一次,Python提供了几种解决方案,今天我将一步步演示如何建立一个UDP主机发现工具。首先,我们要看我们如何处理原始套接字来编写一个简单的嗅探器,它能够查看和解析网络数据包。然后,我们将在子网内多线程运行该进程,结果将在我们的扫描仪上。

原始套接字酷之所在是它能够访问底层网络的信息。比如,我们可以用它来检查IP和ICMP报头,这是在OSI模型的第三层(网络层)。

使用UDP数据报最酷的事情是:当发送信息穿越子网时,不同于TCP,它不太多的开销(还记得TCP握手吧)。我们需要做的就是等待ICMP回应,对方主机是否可用或关闭(不可访问)。记住,ICMP协议本质上是一个特殊的控制协议,它指示错误报告和控制机器数据传输的的行为。

 

编写网络包嗅探器

我们从一个小功能开始:用 Python 的 socket 库来编写一个简单的网络包嗅探器。

在这个嗅探器中,我们创建一个原始 socket 并将它绑定到一个外部网卡。这个网卡要启用混淆模式(promiscuous mode),也就是说获经过这个网卡的所有数据包都会被捕获,包括那些目标地址不是它的数据包。

使用 Windows 时要注意一点:我们需要发送一个 IOCTL 包才能将网卡设置为混淆模式。另外,虽然 linux 需要使用 ICMP,Windows 却可以以一种独立于协议的方式来嗅探收到的数据包。

  1. import socket
  2.  
  3. import os
  4.  
  5. # host to listen
  6.  
  7. HOST = '192.168.1.114'
  8.  
  9. def sniffing(host, win, socket_prot):
  10.  
  11. while 1:
  12.  
  13. sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_prot)
  14.  
  15. sniffer.bind((host, 0))
  16.  
  17. # include the IP headers in the captured packets
  18.  
  19. sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
  20.  
  21. if win == 1:
  22.  
  23. sniffer.ioctl(socket.SIO_RCVALL, socket_RCVALL_ON)
  24.  
  25. # read in a single packet
  26.  
  27. print sniffer.recvfrom(65565)
  28.  
  29. def main(host):
  30.  
  31. if os.name == 'nt':
  32.  
  33. sniffing(host, 1, socket.IPPROTO_IP)
  34.  
  35. else:
  36.  
  37. sniffing(host, 0, socket.IPPROTO_ICMP)
  38.  
  39. if __name__ == '__main__':
  40.  
  41. main(HOST)

在终端中运行如下命令进行测试:

  1. $ sudo python sniffer.py

 

在另一个终端中 ping 或 traceroute 某些地址,如 www.google.com 会得到如下结果:

  1. $ sudo python raw_socket.py
  2.  
  3. ('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))
  4.  
  5. ('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 库来创建一个类

  1. import ctypes
  2.  
  3. class ICMP(ctypes.Structure):
  4.  
  5. _fields_ = [
  6.  
  7. ('type', ctypes.c_ubyte),
  8.  
  9. ('code', ctypes.c_ubyte),
  10.  
  11. ('checksum', ctypes.c_ushort),
  12.  
  13. ('unused', ctypes.c_ushort),
  14.  
  15. ('next_hop_mtu',ctypes.c_ushort)
  16.  
  17. ]
  18.  
  19. def __new__(self, socket_buffer):
  20.  
  21. return self.from_buffer_copy(socket_buffer)
  22.  
  23. def __init__(self, socket_buffer):
  24.  
  25. pass

 

 

编写消息头解码器

现在可以着手编写 IP/ICMP 消息头解码器了。下面的脚本创建了一个 sniffer socket(正如前面做的那样),然后在一个循环中持续读取数据包并进行解码。

注意代码中将 IP 头的前20个字节读取到了缓存,然后再打印消息头的变量。ICMP 头数据如下:

  1. import socket
  2.  
  3. import os
  4.  
  5. import struct
  6.  
  7. import ctypes
  8.  
  9. from ICMPHeader import ICMP
  10.  
  11. # host to listen on
  12.  
  13. HOST = '192.168.1.114'
  14.  
  15. def main():
  16.  
  17. socket_protocol = socket.IPPROTO_ICMP
  18.  
  19. sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
  20.  
  21. sniffer.bind(( HOST, 0 ))
  22.  
  23. sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
  24.  
  25. while 1:
  26.  
  27. raw_buffer = sniffer.recvfrom(65565)[0]
  28.  
  29. ip_header = raw_buffer[0:20]
  30.  
  31. iph = struct.unpack('!BBHHHBBH4s4s' , ip_header)
  32.  
  33. # Create our IP structure
  34.  
  35. version_ihl = iph[0]
  36.  
  37. version = version_ihl >> 4
  38.  
  39. ihl = version_ihl & 0xF
  40.  
  41. iph_length = ihl * 4
  42.  
  43. ttl = iph[5]
  44.  
  45. protocol = iph[6]
  46.  
  47. s_addr = socket.inet_ntoa(iph[8]);
  48.  
  49. d_addr = socket.inet_ntoa(iph[9]);
  50.  
  51. print 'IP -> Version:' + str(version) + ', Header Length:' + str(ihl) + \
  52.  
  53. ', TTL:' + str(ttl) + ', Protocol:' + str(protocol) + ', Source:'\
  54.  
  55. + str(s_addr) + ', Destination:' + str(d_addr)
  56.  
  57. # Create our ICMP structure
  58.  
  59. buf = raw_buffer[iph_length:iph_length + ctypes.sizeof(ICMP)]
  60.  
  61. icmp_header = ICMP(buf)
  62.  
  63. print "ICMP -> Type:%d, Code:%d" %(icmp_header.type, icmp_header.code) + '\n'
  64.  
  65. if __name__ == '__main__':
  66.  
  67. main()

 

测试解码器

在一个终端中运行该脚本,然后在另一个终端运行一个 ping 命令会得到如下结果(注意 ICMP type 值为 0):

  1. $ ping www.google.com
  2.  
  3. PING www.google.com (74.125.226.16) 56(84) bytes of data.
  4.  
  5. 64 bytes from lga15s42-in-f16.1e100.net (74.125.226.16): icmp_seq=1 ttl=56 time=15.7 ms
  6.  
  7. 64 bytes from lga15s42-in-f16.1e100.net (74.125.226.16): icmp_seq=2 ttl=56 time=15.0 ms(...)
  8.  
  9. $ sudo python ip_header_decode.py
  10.  
  11. IP -> Version:4, Header Length:5, TTL:56, Protocol:1, Source:74.125.226.16, Destination:192.168.1.114
  12.  
  13. ICMP -> Type:0, Code:0
  14.  
  15. IP -> Version:4, Header Length:5, TTL:56, Protocol:1, Source:74.125.226.16, Destination:192.168.1.114
  16.  
  17. ICMP -> Type:0, Code:0(...)

 

另外,如果我们运行 traceroute:

  1. $ traceroute www.google.com
  2.  
  3. traceroute to www.google.com (74.125.226.50), 30 hops max, 60 byte packets
  4.  
  5. 1 * * *
  6.  
  7. 2 * * *
  8.  
  9. 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
  10.  
  11. 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
  12.  
  13. 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
  14.  
  15. 6 72.14.215.203 (72.14.215.203) 23.872 ms 46.943 ms *
  16.  
  17. 7 216.239.50.141 (216.239.50.141) 48.906 ms 46.138 ms 46.122 ms
  18.  
  19. 8 209.85.245.179 (209.85.245.179) 46.108 ms 46.095 ms 46.074 ms
  20.  
  21. 9 lga15s43-in-f18.1e100.net (74.125.226.50) 45.997 ms 19.507 ms 16.607 ms

 

会得到这种输出 (注意 ICMP 的响应类型):

  1. sudo python ip_header_decode.py
  2.  
  3. IP -> Version:4, Header Length:5, TTL:252, Protocol:1, Source:65.19.99.117, Destination:192.168.1.114
  4.  
  5. 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
  6.  
  7. ICMP -> Type:11, Code:0
  8.  
  9. IP -> Version:4, Header Length:5, TTL:56, Protocol:1, Source:74.125.226.50, Destination:192.168.1.114
  10.  
  11. ICMP -> Type:3, Code:3
  12.  
  13. IP -> Version:4, Header Length:5, TTL:249, Protocol:1, Source:216.239.50.141, Destination:192.168.1.114
  14.  
  15. 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
  16.  
  17. ICMP -> Type:3, Code:3

 

编写扫描器

安装 netaddr

在编写完整的扫描器前首先要安装 netaddr,它是一个用于表示和处理网络地址的 python 库。

Netaddr 提供了操作 IPv4,IPv6 和子网 Mac 等地址的能力。它非常有用,因为我们会用到子网掩码,如192.168.1.0/24

  1. $ sudo pip install netaddr

我们可以使用如下的代码段来测试这个库 (成功会打印“OK”):

  1. import netaddr
  2.  
  3. ip = '192.168.1.114'
  4.  
  5. if ip in netaddr.IPNetwork('192.168.1.0/24'):
  6.  
  7. print('OK!')

 

深入扫描器

我们会将上面所提到的组织在一起来完成我们的扫描器,然后添加一个循环来向目标子网内的所有地址发送 UDP 数据报。

  1. import threading
  2.  
  3. import time
  4.  
  5. import socket
  6.  
  7. import os
  8.  
  9. import struct
  10.  
  11. from netaddr import IPNetwork, IPAddress
  12.  
  13. from ICMPHeader import ICMP
  14.  
  15. import ctypes
  16.  
  17. # host to listen on
  18.  
  19. HOST = '192.168.1.114'
  20.  
  21. # subnet to target (iterates through all IP address in this subnet)
  22.  
  23. SUBNET = '192.168.1.0/24'
  24.  
  25. # string signature
  26.  
  27. MESSAGE = 'hellooooo'
  28.  
  29. # sprays out the udp datagram
  30.  
  31. def udp_sender(SUBNET, MESSAGE):
  32.  
  33. time.sleep(5)
  34.  
  35. sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
  36.  
  37. for ip in IPNetwork(SUBNET):
  38.  
  39. try:
  40.  
  41. sender.sendto(MESSAGE, ("%s" % ip, 65212))
  42.  
  43. except:
  44.  
  45. pass
  46.  
  47. def main():
  48.  
  49. t = threading.Thread(target=udp_sender, args=(SUBNET, MESSAGE))
  50.  
  51. t.start()
  52.  
  53. socket_protocol = socket.IPPROTO_ICMP
  54.  
  55. sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)
  56.  
  57. sniffer.bind(( HOST, 0 ))
  58.  
  59. sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)
  60.  
  61. # continually read in packets and parse their information
  62.  
  63. while 1:
  64.  
  65. raw_buffer = sniffer.recvfrom(65565)[0]
  66.  
  67. ip_header = raw_buffer[0:20]
  68.  
  69. iph = struct.unpack('!BBHHHBBH4s4s' , ip_header)
  70.  
  71. # Create our IP structure
  72.  
  73. version_ihl = iph[0]
  74.  
  75. ihl = version_ihl & 0xF
  76.  
  77. iph_length = ihl * 4
  78.  
  79. src_addr = socket.inet_ntoa(iph[8]);
  80.  
  81. # Create our ICMP structure
  82.  
  83. buf = raw_buffer[iph_length:iph_length + ctypes.sizeof(ICMP)]
  84.  
  85. icmp_header = ICMP(buf)
  86.  
  87. # check for the type 3 and code and within our target subnet
  88.  
  89. if icmp_header.code == 3 and icmp_header.type == 3:
  90.  
  91. if IPAddress(src_addr) in IPNetwork(SUBNET):
  92.  
  93. if raw_buffer[len(raw_buffer) - len(MESSAGE):] == MESSAGE:
  94.  
  95. print("Host up: %s" % src_addr)
  96.  
  97. if __name__ == '__main__':
  98.  
  99. main()

 

运行后得到的结果如下:

  1. $ sudo python scanner.py
  2.  
  3. Host up: 192.168.1.114(...)

非常棒!

另外,可以将扫描得到的结果与路由器 DHCP 表中的 IP 地址作对比,它们应该是相同的。

 

参考:

  • Tutorial to learn netaddr.

  • Black Hat Python.

  • My Gray hat repo.

使用 Python 的 Socket 模块构建一个 UDP 扫描工具的更多相关文章

  1. 老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具

    老李分享:使用 Python 的 Socket 模块开发 UDP 扫描工具 poptest是业内唯一的测试开发工程师培训机构,测试开发工程师主要是为测试服务开发测试工具,在工作中要求你做网络级别的安全 ...

  2. Python基于socket模块实现UDP通信功能示例

    Python基于socket模块实现UDP通信功能示例 本文实例讲述了Python基于socket模块实现UDP通信功能.分享给大家供大家参考,具体如下: 一 代码 1.接收端     import ...

  3. python中socket模块详解

    socket模块简介 网络上的两个程序通过一个双向的通信连接实现数据的交换,这个连接的一端称为一个socket.socket通常被叫做"套接字",用于描述IP地址和端口,是一个通信 ...

  4. 【Python】socket模块应用

    [Socket] 本文记录了一些socket模块的简单应用,对于具体原理还没来得及深究. ■ 利用socket模块进行端口连接验证和扫描 在linux中常用nc命令来进行远端端口是否开放的验证.但是这 ...

  5. python基础===socket模块的讲解(转)

    一.网络知识的一些介绍 socket 是网络连接端点.例如当你的Web浏览器请求www.jb51.net上的主页时,你的Web浏览器创建一个socket并命令它去连接 www.jb51.net的Web ...

  6. socket模块实现基于UDP聊天模拟程序;socketserver模块实现服务端 socket客户端代码示例

    socket模块 serSocket.setblocking(False) 设置为非阻塞: #coding=utf-8 from socket import * import time # 用来存储所 ...

  7. python之socket模块详解--小白博客

    主要是创建一个服务端,在创建服务端的时候,主要步骤如下:创建socket对象socket——>绑定IP地址和端口bind——>监听listen——>得到请求accept——>接 ...

  8. Python的socket模块与交互式指令

    socket简介 在编程的过程中,我们需要使用网络编程,这时我们不得不和网络通信的底层基础打交道了.我们必须让自己传输的数据符合网络通信的基本协议,即TCP/IP协议,但是网络通信协议本身很复杂.我们 ...

  9. 利用socket.io构建一个聊天室

    利用socket.io来构建一个聊天室,输入自己的id和消息,所有的访问用户都可以看到,类似于群聊. socket.io 这里只用来做一个简单的聊天室,官网也有例子,很容易就做出来了.其实主要用的东西 ...

随机推荐

  1. C++编译器模板机制剖析

    思考:为什么函数模板可以和函数重载放在一块.C++编译器是如何提供函数模板机制的? 一.编译器编译原理 什么是gcc gcc(GNU C Compiler)编译器的作者是Richard Stallma ...

  2. Swift Optional

    拆包和解包的原因: 其实所谓的 nil 就是 Optional.None, 非 nil 就是Optional.Some, 然后会通过Some(T)包装(wrap)原始值,这也是为什么在使用 Optio ...

  3. 无法在web服务器下启动调试。该Web服务器未及时响应

    下午在运行项目的时候,突然出现了以下错误: 无法在web服务器上启动调试.该Web服务器未及时响应.可能是因为另一个调试器已连接到该Web服务器. 搜索了很久才找到这个解决方案: 1:Web.conf ...

  4. linux常用命令:ps 命令

    Linux中的ps命令是Process Status的缩写.ps命令用来列出系统中当前运行的那些进程.ps命令列出的是当前那些进程的快照,就是执行ps命令的那个时刻的那些进程,如果想要动态的显示进程信 ...

  5. 面试问题整理之python测试

    1.下列哪个语句在Python中是非法的? A.x = y = z =1 B.x = (y = z + 1) C.x, y = y, x D.x += y 答案:B 2.关于Python内存管理,下列 ...

  6. java反射field和method的顺序问题

    最近在有思考到序列化性能优化的问题,关于java反射field和method的顺序问题,这里有详细的讨论http://stackoverflow.com/questions/5001172/java- ...

  7. 05:ModelForm 数据验证 & 生成html & 数据库操作

    目录:Django其他篇 01:Django基础篇 02:Django进阶篇 03:Django数据库操作--->Model 04: Form 验证用户数据 & 生成html 05:Mo ...

  8. 使用dll,将image库开放出去

    这是很经典的想法了,但是如何来做,不经过摸索不知道细节.   最简单: dll处   #include "stdafx.h"   #ifdef _DEBUG #define new ...

  9. 《网络对抗》——逆向及Bof基础实践

    <网络对抗>--逆向及Bof基础实践 原理 利用foo函数的Bof漏洞,构造一个攻击输入字符串,覆盖返回地址,触发getShell函数. 手工修改可执行文件,改变程序执行流程,直接跳转到g ...

  10. linux内核分析 第5章读书笔记

    第五章 系统调用 一.与内核通信 系统调用在用户控件进程和硬件设备之间添加了一个中间层,作用有: 为用户空间提供了一种硬件的抽象接口 系统调用保证了系统的稳定和安全 每个进程都运行在虚拟系统中,而在用 ...