题目:level0
老规矩先进PE
查看是64位程序,可以考虑直接IDA莽,也可以进checksec查一下详细参数
比较脆,无PIE且没了栈保护(Stack-canary),容易GOT改写( RELRO)
简单运行一遍,发现是hello word
进64位IDA
鼠标悬停在main函数上按f5反编译
查看源码:
过于简单,没啥可用的信息,双击它return的脆弱函数进一步查看
int __cdecl main(int argc, const char **argv, const char **envp)
{
write(1, "Hello, World\n", 0xDuLL);
return vulnerable_function();
}
查函数源码
函数return了一个 read(0, &buf, 0x200uLL);
也就是回了一个read操作,大小为0x200uLL也就是200个空间,那是不是也就意味着只要我们把这200个空间打满,然后追加上对应的内容,溢出的部分就能被程序read到。
但目前我们并不清楚着200个空间具体是多大,但既然是在read一个&buf,因此双击进入变量查看
于是为了便于审计我们摘出这三个地址:
0000000000000080是buf地址
0000000000000000是s地址
0000000000000008是read地址
从s到buf是多少个地址,0000000000000080 - 0000000000000000个地址(注意是16进制),也就是0x80个地址,这是buf到s的。
从read到s多少个地址,0000000000000008 - 0000000000000000个地址,也就是0x08个地址,这是从s到r的
两者相加,一共0x88个地址,把这些个地址用字符堵死,后续就能伪造执行的read
问题回到read,既然知道现在我们能通过溢出让系统read,但它到底要read个啥?自问自答一波肯定是flag,但程序中好像也没有直接能在system中cat flag的函数,那就尝试找一个类似的函数。
看到左侧有一个callsystem函数
双击进去后发现函数能拿到bin/sh的权限,针不戳,于是去Exports窗口找该函数的地址
完整地址是:0000000000400596
,这里其实只取后八位就可以:00400596
,也就是0x00400596
用脚本把前面0x88个空间打死然后把这个函数地址用p64(0x00400596)拼接上,就可以让read拿到我们的bin/sh权限了。
构造脚本:
from pwn import *
r = remote("220.249.52.133", 51852)
payload = 'A'*0x88 + p64(0x00400596) #堵死0x88个空间然后拼接上callsystem函数地址
r.sendline(payload)
r.interactive()
出flag