0x00 背景


[email protected]JavaScript BackDoor中提到了利用rundll32.exe执行一段JavaScript代码即可反弹一个Http Shell,这里将之前看到的对其原理进行分析的文章翻译和大家分享。

links: http://thisissecurity.net/2014/08/20/poweliks-command-line-confusion/

最近,hFireF0X在逆向工程论坛kernelmode.info上发表了对Win32/Poweeliks恶意软件详细调查的文章。这个恶意软件的特别之处就是它存在于window注册表中并且使用rundll32.exe来执行JavaScript代码。

我发现很有趣的是我显然不是唯一一个发现可以通过rundll32来执行一些JavaScript代码的人。

p1

当我们第一次发现命令行执行JavaScript,我们很好奇它是如何工作的。

在接下来的文章中,我们会分析JavaScript代码时如何执以及为什么会在调用这样简单的命令行代码时执行:

#!bash
rundll32.exe javascript:"\..\mshtml,RunHTMLApplication ";alert(‘foo’);

0x01 Rundll32介绍


关于rundll32.dll的使用在MSDN上有专门的文档;它往往被用来调用一个DLL文件的输出函数,可以通过以下的命令来实现:

#!bash
RUNDLL32.EXE <dllname>,<entrypoint> <optional arguments>

entrypoint就是输出函数;它的函数原型应该是如下所示:

#!cpp
void CALLBACK EntryPoint(HWND hwnd, HINSTANCE hinst, LPSTR lpszCmdLine, int nCmdShow);

参数lpszCmdLine是由rundll32.exe命令语句中的<optional arguments>值确定的。

我们会尝试指出rundll32.exe是如何能够调用被mshtml.dll输出的函数RunHTMLApplication,以及“Javascript”前缀是如何被用来执行实际的JavaScript代码的。

0x02 Rundll32.exe分析


a.参数

rundll32.exe做的第一件事就是使用内置的ParseCommand命令来对命令进行解析。这个函数会查找一个逗号(‘,’,0x2c)来定位dll的名称和一个空格(‘ ’,0x20)来定位入口点名称

p2

当使用我们的样本命令时,ParseCommand会返回javascript:"\..\mshtml作为DLL名称,返回RunHTMLApplication作为入口点。

p3

0x03 Dll loader


rundll32.exe会尝试几种方法来从最初的“JavaScript:"\..\mshtml”加载实际的DLL。

第一次测试使用了函数GetFileAttributes("javascript:"\..\mshtml")。这个函数最终会接近C:\Windows\system32\mshtml。如果这个文件没有被发现,那么这个函数就会返回-1。

p4

接下来SearchPath会被调用来确定DLL的名称。这个函数会读取注册表键值HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SafeProcessSearchMode。微软对于这个键值的定义是:

当这个REG_DWORD类型的注册表键值被设为1,SearchPath会首先搜索系统路径中指定的文件夹,然后会搜索当前工作的文件夹。当这个注册表键值被设为0时,电脑首先会搜索当前工作的文件夹,然后再去搜索系统路径中指定的文件夹。系统对于这个注册表键的默认值是0。

在默认情况下,这个注册表键值是不存在的(在win xp、7、8下),所以SearchPath会优先尝试在rundll32.dll的当前目录中加载文件mshtml,其次才会尝试在系统路径中进行定位。

p5

如果所有的尝试都失败了,那么rundll32.exe就会进行下一步。GetFileAttribute将再次被调用来给模块查找manifest文件:javascript:"\..\mshtml.manifest

若所有的之前的步骤都失败了,rundll32.exe最终会调用Loadlibrary("javascript:"\..\mshtml")

LoadLibrary只是位于ntdll.dll中的一个对LdrLoadDLL的简单封装。在内部,LdrLoadDll会添加默认的扩展dll并且把结果字串(javascript:”\..\mshtml.dll)作为路径来解析。“..”代表再向上一层文件夹:它就解析成了mshtml.dll

mshtml.dll已经被确定了,LdrLoadDll就可以加载系统目录中的lib库了。

p6

rundll32.exe接下来会使用之前提取的入口点名称RunHTMLApplication调用GetProcAddress

这个时候我们可以发现,javascript:前缀看起来毫无用处:LoadLibrary("foobar:\"\..\mshtml")工作的很好。所以,为什么要加这么一个前缀呢?

0x04 协议处理


一旦入口地址已经被解析出来,rundll32.dll会调用函数mshtml.dll!RunHTMLApplication

即使没有被记录,实际的RunHTMLApplication也能从c:\windows\system32\mshta.exe的调用中推断出来(这个应用专门来启动一个.hta文件)。

#!cpp
HRESULT RunHTMLApplication(
HINSTANCE hinst,
HINSTANCE hPrevInst,
LPSTR szCmdLine,
int nCmdShow
);

这和期望的rundll32.exe的入口点地址很相似:

#!cpp
void CALLBACK EntryPoint(
HWND hwnd,
HINSTANCE hinst,
LPSTR lpszCmdLine,
int nCmdShow
);

RunHTMLApplication接受一个窗口的句柄而不是一个模块的句柄作为第一个参数。这个参数会在mshml登记一个窗口类或者为一个类创建窗口时被使用。传递一个同实际的实例并不相一致的值并不会给user32带来很大影响。

第二个参数完全没有被使用,所以这个不匹配并不重要。

最后一个参数,nCmdShow,被RunHTMLApplication函数用来展示代管这个HTML应用的主机窗口。rundll32经常使用值SW_SHOWDEFAULT来调用入口点函数来指示任何可能打开的窗口使用窗口默认配置。

在我们的例子中,可能更让人感兴趣的参数是 lpszCmdLine (";alert('foo'))

p7

这显然会导致问题,因为这并不是一个有效的JavaScript语句(注意在语句末尾丢掉的双引号)。但是它仍然有效,因为RunHTMLApplication忽略给定的参数,并且更倾向于从windows API GetCommandLine中重新请求原始的命令(封装在GetCmdKLine函数的一个调用中)。

p8

完整的命令包含了可执行文件和参数的名称:GetCmdLine通过整理可执行规范来提取参数。

p9

在这之后,RunHTMLApplication会调用CreateURLMonitor:

p10

这也是需要字符串“javascript”的地方。

CreateURLMonitor解析命令行来提取char”:”(0x3A)之前的字符串:“javascript”。

p11

CreateUrlMoniker会爬取注册表键值HKCR\SOFTWARE\Classes\PROTOCOLS\Handler\。这些键值和一个协议集以及它们的CLSID有关。

CreateUrlMoniker会发现一个合适的协议处理器来对Javascript的协议进行处理(HKCR\SOFTWARE\Classes\PROTOCOLS\Handler\javascript):

p12

CLSID{3050F3B2-98B5-11CF-BB82-00AA00BDCE0B}符合微软“Microsoft HTML Javascript Pluggable Protocol”规范。

p13

这也是为什么字符串“javascript”必须在参数的开始部分的原因。

相同的机制在人们在IE的导航栏输入javascript:alert(‘alert’)时也会起作用。

p14

位于“:”分隔符之后的字符串会被JavaScript URL moniker解释成JavaScript指令:

#!js
"\..\mshtml,RunHTMLApplication ";alert(‘foo’);

这是一个带有字符串"\..\mshtml,RunHTMLApplication "和函数(alert)的无效JavaScript语句(因为双引号会跳过所有之前的步骤!)。

最终RunHTMLApplication会调用CHTMLApp::Run,这条JavaScript语句也会被执行:

p15

0x05 安全影响


从安全的角度来看,通过rundll32来执行JavaScript就像执行一个HTML应用。

换句话说,我们可以使用IE的全部权利。当区域安全被关闭,允许跨域脚本访问,我们就可以拥有读写客户端机器文件和注册表的权力。

通过这个技巧,JavaScript可以在IE外部被执行,并且脚本没有任何安全概念的约束,比如保护模式\沙盒。

0x06 结论


按照我们的理解,这个技术语序绕过一些信任内置的rundll32的行为的安全产品。

0x07 本文相关


  1. https://twitter.com/hFireF0X
  2. http://www.kernelmode.info/forum/viewtopic.php?f=16&t=3377
  3. http://support.microsoft.com/kb/164787/en
  4. http://msdn.microsoft.com/enus/library/windows/desktop/aa365527%28v=vs.85%29.aspx
  5. https://thisiscybersec.files.wordpress.com/2014/08/capture-d_c3a9cran-2014-08-20-c3a0-16-16-36.png
  6. https://thisiscybersec.files.wordpress.com/2014/08/capture-d_c3a9cran-2014-08-20-c3a0-16-16-36.png