| 关键词: 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 |
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|