raw_socket server设计文档(1)

  socket( PF_INET, SOCK_RAW, IPPROTO_RAW );socket( PF_INET, SOCK_RAW, IPPROTO_TCP );在RedHat 6.1下这两种socket都可以正常建立,内核支持了的。但是对于Solaris 2.6,如果以root身份truss跟踪这两个函数,发现第二个socket建立的时候内核不支持这种情况下指定IPPROTO_TCP,库函数本身做了处理:so_socket(2, 4, 6, “”, 1)Err#98 EPROTOTYPEstat(“/dev/rawip”, 0xEFFFFAC4)= 0so_socket(2, 4, 6, “/dev/rawip”, 1)= 4setsockopt(4, 65535, 4105, 0xEFFFFBB4, 4)= 0从执行效果看,这样的处理和Linux下的意义不同了。如果考虑广泛兼容性,应该扔弃第二种socket,全部以IPPROTO_RAW方式出现。这样的话,理论上可以考虑不用TCP/UDP协议,但是涉及client/server模式,显然应该继续使用TCP/UDP。从突破防火墙角度看,还是以鬼子的ACK方式为好。UDP通信被很多防火墙屏蔽,TCP也好不到哪里去。而且按照目前的设想,等于仅仅使用TCP的头部概念,并没有使用TCP协议的超时、重传等机制,更没有有限状态机介入,为什么不使用UDP呢?还是应该从防火墙角度考虑这个设计选择,具体问题具体分析吧。现在的难点是完全使用IPPROTO_RAW,写没多大问题,读有了麻烦,又需要重翻UNP;此外,丢包是毫无疑问的,因此必须尽量设计成无状态方式(NFS Server就是一个例子),这个也仅仅是说说,技术问题尚未可知。关于内核传递IP报文到一个raw_socket,有几点需要注意,我们分别探讨之:1) TCP/UDP报文(IP报文负载为TCP/UDP)”永远”不会传递给raw_socket。Stevens介绍 的时候以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 2K,从backend拖回来的程序执行效果以及袁哥分析代码的结论看, 2K可能支持socket( PF_INET, SOCK_RAW, IPPROTO_TCP )这种方式。抓包分析 backdoor的client/server通信,发现除了预料中的ACK,还夹带有RST,只能说明 2K内核传递IP报文到raw_socket的同时传递给了正常的TCP协议层,RST是由正常 TCP协议层发出的。NT/9x估计没戏。 考虑我们要达到的目的,如果内核不给这个机会(传递TCP报文到raw_socket),意 味着ACK方式破产。UDP自然也不用想了。虽然Linux可以,但我们希望得到一个更 广泛兼容的backdoor。可以从数据链路层考虑这个问题,牵扯的问题更多,没有 太大必要。2) 对于伯克利实现而言,内核一般处理了几种常见ICMP报文(3种,回应请求、时间 戳请求、地址掩码请求),,其余未处理ICMP报文交给raw_socket。注意内核并没有 处理上面三种请求报文的应答报文,想想ping.c的实现,如果内核处理 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,走先。3) 所有的IGMP报文交给raw_socket。 同上,可以利用。现在的操作系统好象已经开始在内核里处理igmp,那样的话, 机会不大。而且防火墙对IGMP报文比较敏感。 socket( AF_INET, SOCK_RAW, IPPROTO_IGMP ),Linux上可以接收到IGMP报文, Solaris上不行。4) 如果内核无法理解IP报文头中高层协议类型,传递该报文给raw_socket。 内核无法理解的,对于防火墙也是无法理解的,除非不考虑突破防火墙的网络拓 扑,否则暂时别想。此外从前面的测试中看到,Linux/Solaris下必须精确指定第 三个参数可以接收匹配IP报文,如果要利用内核无法理解之协议类型,必须确保 该类型可以指定在第三个参数中。5) IP分片一定是在内核中重组完成了才会传递给raw_socket。 换句话说,raw_socket无法分析IP分片,数据链路层可以。这里隐含着一个意思, IP分片重组永远在内核完成,一旦这部分的处理代码出了问题,就是内核的麻烦, 所以死得快。6) 如果内核决定传递一个IP报文到raw_socket,则系统中所有进程创建的所有 raw_socket都会收到这个IP报文,这是一个潜在的安全问题。 我们在测试程序中创建socket( PF_INET, SOCK_RAW, IPPROTO_ICMP ),启动了两 个实例,然后从其他主机ping本机,两个实例都收到了icmp echo request。7) 创建socket( PF_INET, SOCK_RAW, 0 ),并且不调用bind、connect,这样的 raw_socket接收所有内核传递上来的IP报文。第三个参数是指定匹配的,如果非 零,不匹配的IP报文不会被传递给该raw_socket。对于这种系统,企图监视本机 所有入IP报文,不需要数据链路层介入,也不要求网卡混杂模式,简单创建一个 raw_socket,指定第三个参数为0即可。 遗憾的是,我们在Linux下测试,根本就不支持第三个参数指定为0,指定成 255(IPPROTO_RAW)也无法达到Stevens描述的效果,255主要用于发送,Stevens介 绍的可能仅仅是BSD实现吧。 关于这个,觉得看看Linux关于raw_socket的实现部分比较好,瞎猜也不是办法。8) 有些代码使用了raw_socket,并未指定IP_HDRINCL选项。1988年为了解决 traceroute问题引入了一个patch,创建SOCK_RAW时,指定第三个参数为 IPPROTO_RAW(值255),效果和指定IP_HDRINCL选项一样,还更方便些。————————————————————————–/* * For Solaris * gcc -O3 -o raw raw.c -lsocket -lnsl* * For Linux* gcc -O3 -o raw raw.c*/#include #include #include #include #include #include #include #include #include #include #include #define SUCCESS0#define FAILURE-1intrecvSocket;u_char packet[ 1500 ];void Close ( int fd ){ if ( close( fd ) == -1 ) {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 0; k–, offset += 16 ) {fprintf( stderr, “%08X “, offset );for ( j = 0; j < 16; j++, i++ ){if ( j == 8 ){fprintf( stderr, "-%02X", byteArray[i] );}else{fprintf( stderr, " %02X", byteArray[i] );}}fprintf( stderr, " " );i -= 16;for ( j = 0; j = ‘ ‘ ) && ( byteArray[i] = ‘ ‘ ) && ( byteArray[i] server的时候使用icmp echo reply效果比较好。有些Linux可能做了设置,不处理icmp echo request,但是icmp echo reply可以在没有出现过icmp echorequest的情况下被发送被接收,而且也更容易突破防火墙。也可以不使用icmp echo,但效果可能都不如icmp echo reply。server –> client的时候使用icmp echo request效果比较好,还是从突破防火墙角度考虑。暂时先这样考虑,以后实在不行就提供参数设置。在SPARC/Solaris 2.6下man p2open看看,Linux下没有这个函数。编译的时候需要指定-lgen开关。如果要在Linux实现同样功能,可能需要自己pipe、fork、dup2然后exec?根据p2open的man手册,应该没有重定向标准错误输出,既然如此,还是采用pipe方式好些,可能将来移植方便吧。socket( AF_INET, SOCK_RAW, IPPROTO_ICMP )这种套接字,接收的时候对应整个IP报文,可以读取也必须读取整个IP报文,包括IP头;发送的时候如果没有额外设置IP_HDRINCL选项,就不能干涉IP头部数据,sendto()参数中的指针指向ICMP数据区,而不是IP头,内核会构造IP头。ICMP报文头部的校验和包括ICMP数据区,也就是说对应整个ICMP报文。因为使用到无名管道,应该处理SIGPIPE信号。很多问题靠man手册和例子代码是无法理解的。一定要从实际编程、调试中才能找到问题、解决问题。这次的raw_socket_server.c里处理SIGPIPE信号的技术比较典型,虽然不是最好的解决办法,但对于我们企图达到的效果足够了。首先设置忽略该信号,然后在write的返回值处进行errno判断,如果EPIPE,就认为远程shell已经终止,此时我们关闭以前建立的所有管道,重新创建新的管道,重新fork/exec出远程shell,这个远程shell的标准输入/输出/错误输出都重定向过了。目前要尽量保持client/server均处在”无状态”中,避免操作的前后依赖性,否则很多地方处理复杂化。7.12注:上述这个设计文档的实战例子已经可用,所有提到的猜测和技术问题都得到实践验证,所以,如果你要写自己的icmp tunnel,可以开工了。上面没有提到的就是需要加密传输、口令验证。 其实生命无论长短,只要我们能努力绽放出生命的光彩,便会拥有精彩的人生。

raw_socket server设计文档(1)

相关文章:

你感兴趣的文章:

标签云: