socket( PF_INET SOCK_RAW IPPROTO_TCP ); 在RedHat 下这两种socket都可以正常建立内核支持了的但是对于Solaris 如果以root身份truss跟蹤这两个函数发现第二个socket建立的时候 内核不支持这种情况下指定IPPROTO_TCP库函数本身做了处理 so_socket( ) Err# EPROTOTYPE stat(/dev/rawip xEFFFFAC) = so_socket( /dev/rawip ) = setsockopt( xEFFFFBB ) = 从执行效果看这样的处理和Linux下的意义不同了 如果考虑广泛兼容性应该扔弃第二种socket全部以IPPROTO_RAW方式出现这样的话理论上可以考虑不用TCP/UDP协议但是涉及client/server模式显然应该继 续使用TCP/UDP从突破防火墙角度看还是以鬼子的ACK方式为好UDP通信被很多防火墙屏蔽TCP也好不到哪里去而且按照目前的设想等于仅仅使用TCP的头部概 念并没有使用TCP协议的超时重传等机制更没有有限状态机介入为什么不使用UDP呢?还是应该从防火墙角度考虑这个设计选择具体问题具体分析吧现在的难点是完全使用IPPROTO_RAW写没多大问题读有了麻烦又需要重翻UNP此外 丢包是毫无疑问的因此必须尽量设计成无状态方式(NFS Server就是一个例子)这 个也仅仅是说说技术问题尚未可知 关于内核传递IP报文到一个raw_socket有几点需要注意我们分别探讨之 ) TCP/UDP报文(IP报文负载为TCP/UDP)永远不会传递给raw_socketStevens介绍 的时候以BSD家族为例 对于Linux显然已经不适用这个结论socket( PF_INET SOCK_RAW IPPROTO_TCP ) 就可以接收到TCP报文Linux内核是给了这个机会的此时正常的TCP协议层也会收到TCP报文(后面我们会写测试代码验证它)于是造成潜在的安全隐患在无需 数据链路层和网卡混杂模式介入的情况下利用raw_socket监视发往本机的TCP报文尽管只有root才可以创建raw_socket但获得创建raw_socket的机会和获得完整root权限相比要大得多对于Solaris系统内核应该是没有支持 socket( PF_INET SOCK_RAW IPPROTO_TCP )方式尽管以root身份执行库函数并没有报错(此时库函数自己做了其他处理) 对于Windows K从backend拖回来的程序执行效果以及袁哥分析代码的结论看K可能支持socket( PF_INET SOCK_RAW IPPROTO_TCP )这种方式抓包分析 backdoor的client/server通信发现除了预料中的ACK还夹带有RST只能说明K内核传递IP报文到raw_socket的同时传递给了正常的TCP协议层RST是由正常 TCP协议层发出的NT/x估计没戏 考虑我们要达到的目的如果内核不给这个机会(传递TCP报文到raw_socket)意味着ACK方式破产UDP自然也不用想了虽然Linux可以但我们希望得到一个更广泛兼容的backdoor可以从数据链路层考虑这个问题牵扯的问题更多没有太大必要 ) 对于伯克利实现而言内核一般处理了几种常见ICMP报文(种回应请求时间戳请求地址掩码请求)其余未处理ICMP报文交给raw_socket注意内核并没有 处理上面三种请求报文的应答报文想想pingc的实现如果内核处理icmp echo reply即使指定IPPROTO_ICMP处于应用层的ping也没有机会得到应答报文这里所说内核处理都是指处理入IP报文对于发送IP报文基本上任 由应用程序处理的所以ping可以发送自己的icmp echo request Linux/Solaris的实现有差别提供给应用层更多机会内核处理了icmp echo request同时会交给socket( PF_INET SOCK_RAW IPPROTO_ICMP )不同于BSD 实现内核未处理的icmp报文依旧交给raw_socket这给我们一个机会编写自己的icmp daemon利用被内核传递到raw_socket的icmp报文进行交互式通信从突破防火墙角度考虑比较现实一般管理员会允许icmp echo request进入管理员要是在防火墙上过滤了icmp echo request估计我们也没有机会在这种敌人内部安装icmp daemon走先 ) 所有的IGMP报文交给raw_socket 同上可以利用现在的操作系统好象已经开始在内核里处理igmp那样的话机会不大而且防火墙对IGMP报文比较敏感 socket( AF_INET SOCK_RAW IPPROTO_IGMP )Linux上可以接收到IGMP报文 Solaris上不行 ) 如果内核无法理解IP报文头中高层协议类型传递该报文给raw_socket 内核无法理解的对于防火墙也是无法理解的除非不考虑突破防火墙的网络拓扑否则暂时别想此外从前面的测试中看到Linux/Solaris下必须精确指定第 三个参数可以接收匹配IP报文如果要利用内核无法理解之协议类型必须确保该类型可以指定在第三个参数中 ) IP分片一定是在内核中重组完成了才会传递给raw_socket 换句话说raw_socket无法分析IP分片数据链路层可以这里隐含着一个意思IP分片重组永远在内核完成一旦这部分的处理代码出了问题就是内核的麻烦所以死得快 ) 如果内核决定传递一个IP报文到raw_socket则系统中所有进程创建的所有raw_socket都会收到这个IP报文这是一个潜在的安全问题 我们在测试程序中创建socket( PF_INET SOCK_RAW IPPROTO_ICMP )启动了两 个实例然后从其他主机ping本机两个实例都收到了icmp echo request ) 创建socket( PF_INET SOCK_RAW )并且不调用bindconnect这样的 raw_socket接收所有内核传递上来的IP报文第三个参数是指定匹配的如果非 零不匹配的IP报文不会被传递给该raw_socket对于这种系统企图监视本机 所有入IP报文不需要数据链路层介入也不要求网卡混杂模式简单创建一个 raw_socket指定第三个参数为即可 遗憾的是我们在Linux下测试根本就不支持第三个参数指定为指定成 (IPPROTO_RAW)也无法达到Stevens描述的效果主要用于发送Stevens介 绍的可能仅仅是BSD实现吧 关于这个觉得看看Linux关于raw_socket的实现部分比较好瞎猜也不是办法 ) 有些代码使用了raw_socket并未指定IP_HDRINCL选项年为了解决 traceroute问题引入了一个patch创建SOCK_RAW时指定第三个参数为 IPPROTO_RAW(值)效果和指定IP_HDRINCL选项一样还更方便些 /* * For Solaris * gcc O o raw rawc lsocket lnsl * * For Linux * gcc O o raw rawc */ #include #include #include #include #include #include #include #include #include #include #include #define SUCCESS #define FAILURE int recvSocket; u_char packet[ ]; void Close ( int fd ) { if ( close( fd ) == ) { perror( close ); exit( FAILURE ); } return; } /* end of Close */ void outputBinary ( const unsigned char * byteArray const size_t byteArrayLen ) { u_long offset; int i j k; fprintf( stderr byteArray [ %lu bytes ] > \n byteArrayLen ); if ( byteArrayLen <= ) { return; } i = ; offset = ; for ( k = byteArrayLen / ; k > ; k offset += ) { fprintf( stderr %X offset ); for ( j = ; j < ; j++ i++ ) { if ( j == ) { fprintf( stderr %X byteArray[i] ); } else { fprintf( stderr %X byteArray[i] ); } } fprintf( stderr ); i = ; for ( j = ; j < 16; j++, i++ ) { /* if ( isprint( (int)byteArray[i] ) ) */ if ( ( byteArray[i] >= ) && ( byteArray[i] <= 255 ) ) { fprintf( stderr, "%c", byteArray[i] ); } else { fprintf( stderr, "." ); } } fprintf( stderr, "\n" ); } /* end of for */ k = byteArrayLen - i; if ( k <= 0 ) { return; } fprintf( stderr, "%08X ", offset ); for ( j = 0 ; j < k; j++, i++ ) { if ( j == 8 ) { fprintf( stderr, "-%02X", byteArray[i] ); } else { fprintf( stderr, " %02X", byteArray[i] ); } } i -= k; for ( j = 16 - k; j > 0; j-- ) { fprintf( stderr, " " ); } fprintf( stderr, " " ); for ( j = 0; j < k; j++, i++ ) { if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] <= 255 |