标题: 如何确定扫描方案 创建: 2006-04-06 15:16 更新: 链接: https://scz.617.cn/network/200604061516.txt 以前有人问过如何写扫描器。这个问题涉及面较多,大体上分成引擎与插件两部分。 引擎负责插件的调度,插件完成具体的单个扫描方案。 我就不提纲挈领地介绍这些东西了,这次举个抽像的例子介绍如何确定单个扫描方案。 文中均为抽象举例,请勿对号入座。 ☆ 远程扫描识别Grin服务 Grin服务缺省侦听1111/TCP口,TCP连接建立之后服务端向客户端发送16字节数据: > nc -w 1 -n 1.1.1.1 1111 | xxd -g 1 0000000: aa bb cc dd a8 fa 6b 01 00 00 00 00 00 00 43 41 ......k.......CA 据此去某地区找了一些测试机: 166.40.8.56 AA BB CC DD D4 00 00 00 9A 99 99 99 99 99 43 41 | 166.40.13.108 32 32 30 20 53 65 72 76 2D 55 20 4D 6F 44 20 76 |220 Serv-U MoD v 166.40.84.203 AA BB CC DD 7B 27 00 00 E7 1D A7 E8 48 AE 43 41 | 166.40.84.243 AA BB CC DD 7B 27 00 00 E7 1D A7 E8 48 AE 43 41 | 166.40.86.54 AA BB CC DD 7B 27 00 00 E7 1D A7 E8 48 AE 43 41 | 166.40.86.55 AA BB CC DD E0 BC 95 00 E7 1D A7 E8 48 AE 43 41 | 166.40.86.59 AA BB CC DD E0 6E 95 00 E7 1D A7 E8 48 AE 43 41 | 166.40.86.60 AA BB CC DD 7B 27 00 00 E7 1D A7 E8 48 AE 43 41 | 166.40.201.188 AA BB CC DD D4 00 00 00 9A 99 99 99 99 99 43 41 | 166.40.208.13 AA BB CC DD 94 C1 E8 77 CD CC CC CC CC CC 10 40 | 166.40.221.1 AA BB CC DD D4 00 00 00 9A 99 99 99 99 99 43 41 | 166.40.223.126 4D 69 63 72 6F 73 6F 66 74 20 57 69 6E 64 6F 77 |Microsoft Window 166.41.54.218 AA BB CC DD D4 00 00 00 9A 99 99 99 99 99 43 41 | 166.41.78.170 AA BB CC DD 3A 67 E1 77 66 66 66 66 66 66 43 41 | 166.41.131.163 AA BB CC DD D4 00 00 00 CD CC CC CC CC CC 10 40 | 166.41.179.132 AA BB CC DD A8 FA 4C 01 9A 99 99 99 99 99 43 41 | 166.41.190.34 AA BB CC DD D0 50 14 00 66 66 66 66 66 66 10 40 | 166.42.89.107 AA BB CC DD D4 00 00 00 33 33 33 33 33 33 43 41 | 166.42.89.108 AA BB CC DD 00 00 00 00 D7 A3 70 3D 0A D7 43 41 | 166.43.38.138 AA BB CC DD D4 00 00 00 33 33 33 33 33 33 43 41 | 166.43.39.42 AA BB CC DD 00 00 00 00 EC 51 B8 1E 85 EB 43 41 | 166.43.171.36 AA BB CC DD 00 00 00 00 48 E1 7A 14 AE 47 43 41 | 166.43.224.28 AA BB CC DD 00 00 00 00 AE 47 E1 7A 14 AE 0B 40 | 166.43.224.205 AA BB CC DD 00 00 00 00 48 E1 7A 14 AE 47 43 41 | 这个就是写个程序多线程建立1111/TCP连接,超时读取16字节并对之进行16进制转储。 有一个是改了端口的Serv-U,另一个是被人入侵过的。 > nc -w 2 -n 166.41.190.34 1111 | xxd -g 1 0000000: aa bb cc dd d0 50 14 00 66 66 66 66 66 66 10 40 .....P..ffffff.@ 没有在这16字节中找到明显的长度域,猜测服务端固定发送16字节。最前面四字节始 终固定为"AA BB CC DD",可以做为识别特征。最后两字节绝大多数测试目标返回的 是"43 41",但仍有其它变化,决定不采用最后两字节。 先建立到1111/TCP的连接,固定读取16字节响应数据,如果超时发生前没有读够4字 节,失败。如果前4字节不等于"AA BB CC DD",失败。 客户端向服务端固定发送如下数据: > hexout "\xAA\xBB\xCC\xDD\x00\x00\x00\x00\xC3\xF5\x28\x5C\x8F\xC2\x0D\x40" | xxd -g 1 0000000: aa bb cc dd 00 00 00 00 c3 f5 28 5c 8f c2 0d 40 ..........(\...@ 据我观察服务端会固定响应1024字节,没看出有什么明显的长度域存在,因此也只能 假设前述观察是必然结论。我们不需要那么多响应数据,只需读取前16字节即可: 0000000: ff ff 00 00 ee ee 00 00 03 00 00 00 02 00 00 00 ................ 假设读够了16字节,并保存在buf[]中。检查最开始8字节是否等于"FF FF 00 00 EE EE 00 00"。据我观察前8字节都是固定的: 166.40.86.55 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.40.84.243 FF FF 00 00 EE EE 00 00 03 00 00 00 02 00 00 00 166.40.84.203 FF FF 00 00 EE EE 00 00 03 00 00 00 02 00 00 00 166.40.86.54 FF FF 00 00 EE EE 00 00 03 00 00 00 02 00 00 00 166.40.86.60 FF FF 00 00 EE EE 00 00 03 00 00 00 02 00 00 00 166.40.208.13 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.41.190.34 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.43.224.205 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.42.89.107 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.41.54.218 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.40.221.1 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.40.8.56 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.41.78.170 FF FF 00 00 EE EE 00 00 03 00 00 00 02 00 00 00 166.43.39.42 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.43.38.138 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.42.89.108 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.43.171.36 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.40.86.59 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.40.201.188 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 166.41.179.132 FF FF 00 00 EE EE 00 00 03 00 00 00 02 00 00 00 166.41.131.163 FF FF 00 00 EE EE 00 00 03 00 00 00 00 00 00 00 这个就是写个程序多线程建立1111/TCP连接,发送16字节固定数据,超时读取32字节 并对偏移+0x010处的16字节做16进制转储。 这次没有大范围扫描,而是将前次扫描出来的开了Grin服务的IP存入一个文件,再根 据这个文件进行新的扫描。 回头看这组数据: 0000000: ff ff 00 00 ee ee 00 00 03 00 00 00 02 00 00 00 ................ 偏移+0x008 03 00 00 00 Grin 3.x(little-endian序) 偏移+0x00C 02 00 00 00 Grin 3.2 再比如这组数据: 0000000: ff ff 00 00 ee ee 00 00 04 00 00 00 00 00 00 00 ................ 偏移+0x008 04 00 00 00 Grin 4.x(little-endian序) 偏移+0x00C 00 00 00 00 Grin 4.0 可以获取目标Grin服务的版本信息。 综合考虑后,扫描方案建议如下: -------------------------------------------------------------------------- 1) 建立到1111/TCP的连接,直接发送16字固定数据: 0000000: aa bb cc dd 00 00 00 00 c3 f5 28 5c 8f c2 0d 40 ..........(\...@ 这样做的好处在于简化编程。 2) 读取32字节响应数据,如果不够32字节,失败。服务端一开始就会发送16字节到客户 端,然后对步骤1中的请求报文响应以1024字节。由于是TCP字节流,可以一次性读这 1040字节,我们只需要前32字节即可。 这样做的好处在于简化编程。 3) 如果前4字节不等于"AA BB CC DD",失败。 4) 如果偏移+0x010处的8字节不等于"FF FF 00 00 EE EE 00 00",失败。否则准备报告 "检测到远端Grin服务正在运行中"。后面的步骤意在析取更多信息,属于得之我幸失 之我命的事,要注意避免漏报。 5) 分别从偏移+0x018、0x01C处析取Grin服务版本信息。这次的响应数据包含了最开始 的来自服务端的16字节,因此偏移不再是前面演示的那样,需要加0x010。 6) 第4步通过就可以报告了,但报告的详细程度取决于第5步。 -------------------------------------------------------------------------- 下面是一些测试数据: > hexout "\xAA\xBB\xCC\xDD\x00\x00\x00\x00\xC3\xF5\x28\x5C\x8F\xC2\x0D\x40" | nc -w 2 -n 166.41.190.34 1111 | xxd -g 1 0000000: aa bb cc dd d0 50 14 00 66 66 66 66 66 66 10 40 .....P..ffffff.@ 0000010: ff ff 00 00 ee ee 00 00 03 00 00 00 00 00 00 00 ................ > hexout "\xAA\xBB\xCC\xDD\x00\x00\x00\x00\xC3\xF5\x28\x5C\x8F\xC2\x0D\x40" | nc -w 2 -n 192.168.7.150 1111 | xxd -g 1 0000000: aa bb cc dd a8 d3 13 00 0a d7 a3 70 3d 0a 0c 40 0..........p=..@ 0000010: ff ff 00 00 ee ee 00 00 04 00 00 00 00 00 00 00 ................ 看到这里,应能大致理解我们是如何确定扫描方案的,如何在误报、漏报之间搞平衡 的。基本方法还是大胆假设、小心求证。基于统计分析最终确定一种扫描方案。 更复杂的,得配合逆向工程分析私有协议。但务必要明白,不是所有的任务都需要逆 向工程。 更简单的,直接获取BANNER信息判断版本报漏洞。 显然,插件漏报、误报都是有可能的。 个人认为,80%的扫描方案是一件体力活。当然还有20%的扫描方案是非常让人抓狂的 技术活。曾经有个小同学咨询于我,我就这么告诉他的。说直白点,80%的时候你都 在统计分析中,就像这个Grin服务的扫描方案,没啥难度。耐不住寂寞的话,肯定会 无聊得疯掉的,但是,会有20%的尖峰毛刺出现,极具挑战性,那你就等着抓狂吧。 好了,不多说这事了,希望对你写漏洞扫描插件有点点启发。