level0
一道缓冲区溢出题。
位于 xctf 的 pwn 题 level0。
首先检查附件信息:
64 位 ELF 程序。然后丢到 ida 进行反编译。
main 函数如下,很简单,标准输出 Hello World\n 就进入 vulnerable_function 函数
|
|
vulnerable_function 函数反编译如下:
|
|
buf 只有 0x80(128) 字节空间,却读了 200 字节的空间。显然溢出了。
查看函数栈空间,发现:
|
|
在数组结束后 0x000 后马上就到返回地址。这个返回值可以跳到任何我们想去的地方。
然后再看函数表发现了 callsystem 函数:
|
|
直接就给了 shell。
挺直接的,有种给你喂饭的感觉。
事到如此,我们只需要溢出到这个返回地址,让他调到 callsystem 就拿到 shell 了。
通过 gdb 获得 callsystem 的地址:
首先对 main 函数打断点并运行:
直接获得 callsystem 函数的地址。
接下来就是用 pwntools 进行处理:
|
|
remote() 函数连接到服务器。
|
|
这边构造攻击载荷,首先 0x80 个 A 字符塞满字符串,然后用 0x8 个字符覆盖与返回地址间的 8 个字节空间。用 p64() 函数将 callsystem 地址转换为小短序的字节串,这个操作能够覆盖原本函数栈的返回地址,让我们跳到目标函数地址内。
注意这里字符前面加 b 确定类型,不然会报错。
recvuntil() 函数等待到前面 Hello, World 读了之后再填入载荷。
使用 sendline 发送载荷
通过 interactive() 获得 shell
运行后进入 shell 就能获得 flag 啦!

hello_pwn
同样的,也是缓冲区溢出。
main 函数反汇编后显而易见:
先看看 sub_400686 函数有什么:
|
|
没有想到直接就给了……
那么还说什么呢,只要能够把 dword_60106C 变成对应的数据就可以了。关键就在于覆盖数据。
分析 main 发现,read 函数输入了 16 (0x10)字节数据,但是 unk_601068 和 dword_60106C 之间只差了 4 个字节!!!
那么就好办了,直接上 pwntools:
|
|
用 4 个 a 覆盖掉这些数据,然后接着把 dword_60106C 变成 015635260541 十六进制是 0x6E756161 。
注意到是这个数据字符串是 nuaa ,但是如果要以字符串覆盖,需要注意小端序,也就是要写成 'aaun。
这样就如愿以偿拿到 flag 咯!

level2
栈溢出题。
这里需要重新构造函数的调用栈。
使用 file 查看程序信息,发现是 32 位 elf。
反编译 main 函数:
|
|
函数执行了 vulnerable_function 函数后使用 system 输出 Hello World。
vulnerable_function 函数内有溢出漏洞:
|
|
这里 buf 只有 0x88 ,而可以输入 0x100 数据。
查看函数栈可以发现 buf 数组后面就是返回:
那么我就可以通过这个返回跳到 system 函数中去,然后伪造一个函数调用栈调用 shell。
在 ida pro 中按 shift + F12 查看字符串,发现在 .data 段居然有 shell 路径!
那不就好办了吗,首先需要知道怎么伪造函数调用栈。
这里数组后面就是整个调用栈的空间,首先是栈帧基址 eb/p 占用 4 个字节,之后是函数返回地址。
而后还有 system 返回地址然后才是 system 的参数。
system 的地址在这里查看
最后上 pwntools:
|
|
这里 0x8048320 是 system 函数的地址,x804A024 是 /bin/sh 字符串的地址。
不过这道题还是需要有函数调用栈的汇编基础,这样理解还是不够透彻的。