黑基网 首页 服务器 Linux 查看内容

raw_socket server设计文档(1)

2005-5-24 23:02| 投稿: Linux|

摘要: socket( PF_INET, SOCK_RAW, IPPROTO_RAW ); socket( PF_INET,...
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 EPROTOTYPE stat("/dev/rawip", 0xEFFFFAC4) = 0 so_socket(2, 4, 6, "/dev/rawip", 1) = 4 setsockopt(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 SUCCESS 0 #define FAILURE -1 int recvSocket; 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 ] ----> ", byteArrayLen ); if ( byteArrayLen <= 0 ) { return; } i = 0; offset = 0; for ( k = byteArrayLen / 16; k > 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 < 16; j++, i++ ) { /* if ( isprint( (int)byteArray[i] ) ) */ if ( ( byteArray[i] >= ' ' ) && ( byteArray[i] <= 255 ) ) { fprintf( stderr, "%c", byteArray[i] ); } else { fprintf( stderr, "." ); } } fprintf( stderr, " " ); } /* 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 ) ) { fprintf( stderr, "%c", byteArray[i] ); } else { fprintf( stderr, "." ); } } fprintf( stderr, " " ); return; } /* end of outputBinary */ int Socket ( int family, int type, int protocol ) { int n; if ( ( n = socket( family, type, protocol ) ) < 0 ) { perror( "socket" ); exit( FAILURE ); } return( n ); } /* end of Socket */ int main ( int argc, char * argv[] ) { recvSocket = Socket( AF_INET, SOCK_RAW, IPPROTO_ICMP ); while ( 1 ) { memset( packet, 0, 1500 ); outputBinary( packet, recvfrom( recvSocket, packet, 1500, 0, NULL, NULL ) ); } Close( recvSocket ); exit( SUCCESS ); } /* end of main */ -------------------------------------------------------------------------- 可以参考阅读Phrack49-6、Phrack51-6两篇文章。 上面的raw.c可以用于测试,现在简单讨论一下要实现的功能。一个rs和rc,rs接收 icmp echo request报文,从icmp数据区提取有效数据。icmp数据区可能需要自定义 格式(暂时不考虑加密传输的问题)。比如rs最后得到的指示是要做ls -la操作,那么 rs应该fork/exec执行/bin/sh -c "ls -la"这个命令。可能涉及到无名管道的技术。 rs将最终shell命令执行输出利用icmp echo reply负载发送到rc。rc负责接收用户输 入并利用icmp echo request负载发送到rs,还接收来自rs的icmp echo reply并提取 有效数据输出到标准输出。简单地、概括地讲,icmp pad backdoor就是这样的。具 体实现中可能有变化,核心部分不变,那就是利用icmp pad交换数据。 另外有个想法,就是利用icmp pad建立一个通道,server host上运行一个daemon, client host上也运行一个daemon,假设server host位于firewall一侧,client host位于firewall另一侧。client创建一个tcp socket,监听7704端口, telnet client 7704,一切输入都被放入icmp pad,client向server发送icmp报文。 server创建一个tcp socket,连接到本机23端口。server接收icmp报文,提取icmp pad数据,发送到本机23端口。一切来自本机telnetd的数据都被放入icmp pad, server向client发送icmp报文。client接收icmp报文,提取icmp pad,从本机7704端 口上建立的连接发送出去,实际就是返回给最终用户。 | user telnet 7704 | telnetd(23) | | | | | | client host ---------- firewall ---------- server host listen 7704 允许icmp进出 connect 23 | | | | | | ------------------- icmp tunnel ---------------- | | 防火墙设置了很多过滤规则,比如只允许http、icmp通过,此时上述icmp tunnel很 好用,可以根据不同需要修改server host上connect的目标端口。可以根据不同需要 安置client/server。client host总是代表最终用户可以直接访问的那一侧。 如果既要突破防火墙过滤规则,又要保证server host上的隐蔽性,至少不能被 netstat看出异常,上述icmp tunn

鲜花

握手

雷人

路过

鸡蛋

相关阅读

最新评论

相关分类

返回顶部