【CVE-2022-28282】Mozilla Firefox 资源管理错误漏洞
作者:Sec-Labs | 发布时间:
漏洞简介
POC
https://github.com/Pwnrin/CVE-2022-28282
简单QL的老把戏
这是个简单的老把戏。
如果我们在Object的原型中定义了 "then "的Getter,当Promise->resolve获得Object作为参数时,就会触发用户的JS回调。
https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-promise.resolve
我写了几条简单而 "暴力 "的规则来搜索可能由这一技巧引起的UAF问题。
比如说。
- 两个原始指针之间的回调
- 在回调中释放了一个成员变量
- ......
from Function magic_func,Function g_resolve,FunctionCall g_setva_fc,VariableAccess va,VariableAccess setnull_va,Expr e_tmp1,Expr e,FunctionCall fc,FunctionCall fc_tmp1,FunctionCall fc_tmp2,FunctionCall fc_tmp3,FunctionCall fc_tmp4,FunctionCall fc_tmp5
where g_resolve.getName().regexpMatch("ResolvePromiseInternal")
and g_resolve.getParentScope().toString().matches("js")
// mMember
and va.getControlFlowScope()=magic_func
and va.getEnclosingFunction() = magic_func
and va.getTarget().getName().regexpMatch("m([A-Z])((.)*)")
and not va.getTarget().getName().matches("mImpl")
// mMember->trigger()->js::ResolvePromiseInternal()
and e.getEnclosingFunction() = magic_func
and fc.getEnclosingFunction() = magic_func
and fc.getTarget().getName().matches("operator->")
and va.getParent() = fc
and fc.getParent() = fc_tmp1
and fc_tmp1.getTarget() = fc_tmp2.getEnclosingFunction()
and fc_tmp2.getTarget() = fc_tmp3.getEnclosingFunction()
and fc_tmp3.getTarget() = fc_tmp4.getEnclosingFunction()
and fc_tmp4.getTarget() = fc_tmp5.getEnclosingFunction()
and fc_tmp5.getTarget() = g_resolve
// exists: mMember = NULL
and setnull_va.getTarget()=va.getTarget()
and g_setva_fc.getTarget().getName().matches("operator=")
and setnull_va.getParent() = g_setva_fc
and e_tmp1.toString().matches("0")
and e_tmp1.getParent() = g_setva_fc
select magic_func,magic_func.getParentScope(),va,g_setva_fc,fc_tmp1
正如你所看到的:这个规则非常简单,有很多地方可以改进。
- 搜索的暴力方式
- 成员变量不必设置为空
......
(但这的确是我的一个QL的初始版本,通过改进这些规则,我发现了一些其他有趣的东西)。
通过调整FunctionCall递归的层数,我得到了一个结果的集合。
不幸的是,在初始结果集中只有一个元素。幸运的是,这个结果里有一些有趣的东西。
这个结果
1 OnParsingCompleted Document mDocumentL10n ......
2 LocalizationLinkAdded Document mDocumentL10n ......
分析
void Document::LocalizationLinkAdded(Element* aLinkElement) {
if (!AllowsL10n()) {
return;
}
......
......
if (!mDocumentL10n) {
Element* elem = GetDocumentElement();
MOZ_DIAGNOSTIC_ASSERT(elem);
bool isSync = elem->HasAttr(nsGkAtoms::datal10nsync);
mDocumentL10n = DocumentL10n::Create(this, isSync);
......
}
mDocumentL10n->AddResourceId(NS_ConvertUTF16toUTF8(href));
if (mReadyState >= READYSTATE_INTERACTIVE) {
mDocumentL10n->TriggerInitialTranslation(); // **** 1 ****
} else {
......
......
}
}
void DocumentL10n::TriggerInitialTranslation() {
······
······
nsTArray<RefPtr<Promise>> promises;
ErrorResult rv;
promises.AppendElement(TranslateDocument(rv));
if (NS_WARN_IF(rv.Failed())) {
InitialTranslationCompleted(false);
mReady->MaybeRejectWithUndefined();
return;
}
promises.AppendElement(TranslateRoots(rv)); // **** 2 ****
Element* documentElement = mDocument->GetDocumentElement(); // // **** 3 ****
......
......
}
当一个HTMLLinkElement(with rel="localization")被添加到文档中时,该文档将创建一个DocumentL10n并触发TriggerInitialTranslation。在Document::LocalizationLinkAdded的(1)到DocumentL10n::TriggerInitialTranslation的(2)的路径中,只有文档在mDocumentL10n中持有DocumentL10n的引用。
在TranslateRoots(2)中,该函数将使用一个Promise来解决ErrorResult rv。如果我们之前在Object的原型中定义了 "then "的Getter,Promise->resolve将在TranslateRoots(2)中触发用户的JS回调。而且我们可以把HTMLLinkElement的rel设置为null来触发Document::LocalizationLinkRemoved。它将把mDocumentL10n设置为nullptr并删除DocumentL10n的引用,然后我们可以在gc中销毁该对象。当程序返回到TriggerInitialTranslation时,它将导致(3)中的Use-After-Free(访问成员变量:mDocument)。
漏洞
由于AllowsL10n中有一个页面权限检查,该漏洞需要通过Web扩展来触发。