早些时候的著名后门byshell就考虑到了这个问题,于是使用了一种非常挫的方式去解决。客户端将数据发送到80端口,服务端将IIS的进程打开,循环遍历IIS进程的整个内存去寻找数据标记。显然这种方法从效率和稳定性上来讲,都是不可取的。这里姑且不把这种方法当做方法。
过去有人提出过一种端口复用的方法去建立的后门,这个方法使用了setsockopt()这个API,这个API在MSDN里说是用来设置套接字的选项的。原型如下。
int setsockopt( SOCKET s, int level, int optname, const char FAR* optval, int optlen );
我们这里只关心它的第三个参数,这个参数用来设置套接字状态。这个参数有一个取值为SO_REUSEADDR,MSDN对这个参数的解释如下。
The state of the SO_REUSEADDR socket option determines whether the local transport address to which a socket will be bound is always shared with other sockets. This socket option applies only to listening sockets, datagram sockets, and connection-oriented sockets.
也就是说,当第三个参数的取值设置为SO_REUSEADDR时,套接字的端口是可以共享复用的。具体共享细节为后来居上,后建立该参数链接的套接字先拿到数据。此方法目前对Apache和IIS5.0及以下版本有效。那为什么IIS6.0及以上就不行了呢?之后会做解释。
0×02
通过逆向和查阅开源代码可以得知,Apache和IIS5.0及以下版本使用了应用层的IOCP模型进行通信,尽管框架比较复杂,但是依然在应用层创建了套接字。到这里你是否有新的想法呢?没错,我想到了可以使用远程线程注入一个DLL进行Api Hook例如WSARecv()、WSASend()这样的API获取套接字和异步IO的缓冲区指针,再使用getpeername()函数对比客户端信息,紧接着用套接字进行IO。
或者也可以使用更简单粗暴的方法,直接使用SPI安装一个LSP,也可以抓到数据,但是就比较难再做通信了。我们将这种在应用层建立套接字通信转接通信过程的方法总结为下图,红线表示可以利用的地方。