目录遍历漏洞详解

目录遍历(Directory Traversal)也称为路径遍历(Path Traversal),是一种常见的Web安全漏洞。我将从原理、利用和防御三个方面详细讲解。

一、漏洞原理

1.1 基本概念

目录遍历漏洞允许攻击者访问Web应用程序根目录之外的文件和目录。当应用程序使用用户提供的输入来构造文件路径,但未进行充分验证时,就会产生这个漏洞。

1.2 核心原理

  • 特殊字符序列../(Unix/Linux)或..\(Windows)用于返回上级目录
  • 不当的输入处理:应用程序直接将用户输入拼接到文件路径中
  • 缺乏访问控制:没有限制可访问的目录范围

1.3 典型场景

易受攻击的代码示例

1
2
3
4
5
6
<?php
// PHP示例
$file = $_GET['filename'];
$content = file_get_contents("/var/www/html/files/" . $file);
echo $content;
?>
1
2
3
4
5
6
7
# Python示例
from flask import request, send_file

@app.route('/download')
def download():
filename = request.args.get('filename')
return send_file(f'/app/files/{filename}')

二、漏洞利用

2.1 基本利用方法

简单遍历

1
http://example.com/download?file=../../../etc/passwd

Windows系统

1
http://example.com/download?file=..\..\..\windows\system32\drivers\etc\hosts

2.2 绕过技术

URL编码

1
http://example.com/file?name=..%2F..%2F..%2Fetc%2Fpasswd

双重编码

1
http://example.com/file?name=..%252F..%252F..%252Fetc%252Fpasswd

16位Unicode编码

1
..%c0%af..%c0%af..%c0%afetc/passwd

添加空字节(旧版本语言):

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

绝对路径

1
http://example.com/file?name=/etc/passwd

过滤器绕过

  • ....//....//....//etc/passwd (过滤单个../
  • ..;/..;/..;/etc/passwd
  • .././.././.././etc/passwd

2.3 常见攻击目标

Linux系统

  • /etc/passwd - 用户账户信息
  • /etc/shadow - 密码哈希(需要权限)
  • /etc/hosts - 主机名映射
  • /proc/self/environ - 环境变量
  • /var/log/apache2/access.log - 日志文件
  • ~/.ssh/id_rsa - SSH私钥

Windows系统

  • C:\Windows\System32\drivers\etc\hosts
  • C:\Windows\win.ini
  • C:\boot.ini
  • C:\inetpub\wwwroot\web.config

应用程序配置文件

  • .env - 环境配置
  • config.php - 数据库凭证
  • web.xml - Java配置
  • settings.py - Python配置

三、防御措施

3.1 输入验证

白名单验证

1
2
3
4
5
6
7
8
9
10
11
import os
from pathlib import Path

ALLOWED_FILES = {'report1.pdf', 'report2.pdf', 'image.jpg'}

def safe_download(filename):
if filename not in ALLOWED_FILES:
raise ValueError("文件不允许访问")

filepath = os.path.join('/app/files', filename)
return filepath

文件名清理

1
2
3
4
5
6
7
8
9
import os
import re

def sanitize_filename(filename):
# 只允许字母、数字、下划线、连字符和点
clean_name = re.sub(r'[^a-zA-Z0-9_\-\.]', '', filename)
# 移除路径分隔符
clean_name = clean_name.replace('/', '').replace('\\', '')
return clean_name

3.2 路径规范化

使用标准库函数

1
2
3
4
5
6
7
8
9
10
11
12
13
import os
from pathlib import Path

def safe_path_join(base_dir, user_input):
# 规范化路径
base_path = Path(base_dir).resolve()
requested_path = (base_path / user_input).resolve()

# 确保最终路径在基础目录内
if not str(requested_path).startswith(str(base_path)):
raise ValueError("路径遍历尝试被拒绝")

return requested_path
1
2
3
4
5
6
7
8
9
10
11
12
13
// Java示例
import java.nio.file.*;

public String validatePath(String baseDir, String userInput) {
Path basePath = Paths.get(baseDir).toAbsolutePath().normalize();
Path requestedPath = basePath.resolve(userInput).normalize();

if (!requestedPath.startsWith(basePath)) {
throw new SecurityException("路径遍历攻击");
}

return requestedPath.toString();
}

3.3 访问控制

文件系统权限

  • 使用最小权限原则运行Web应用
  • 限制Web用户只能访问必要的目录
  • 使用chroot或容器隔离

应用层限制

1
2
3
4
5
6
7
8
def check_file_access(filepath, allowed_dir):
real_path = os.path.realpath(filepath)
allowed_path = os.path.realpath(allowed_dir)

common_prefix = os.path.commonprefix([real_path, allowed_path])

if common_prefix != allowed_path:
raise PermissionError("访问被拒绝")

3.4 使用安全API

避免直接文件操作

1
2
3
4
5
6
7
8
9
10
11
12
13
# 不安全
file_content = open(user_input).read()

# 安全方式 - 使用映射
FILE_MAP = {
'report1': '/safe/path/report1.pdf',
'report2': '/safe/path/report2.pdf'
}

def get_file(file_id):
if file_id not in FILE_MAP:
raise ValueError("无效的文件ID")
return open(FILE_MAP[file_id], 'rb').read()

3.5 其他防御措施

Web应用防火墙(WAF)

  • 配置规则检测../模式
  • 阻止编码变体

日志和监控

1
2
3
4
5
6
import logging

def access_file(filename):
if '..' in filename or '/' in filename:
logging.warning(f"可疑的文件访问尝试: {filename}")
raise ValueError("非法路径")

安全配置

  • 禁用目录列表
  • 配置适当的错误页面(避免信息泄露)
  • 使用安全的文件上传目录(与应用代码分离)

3.6 框架特定的防御

Express.js

1
2
3
4
5
6
7
8
9
10
11
12
13
const path = require('path');
const express = require('express');

app.get('/download', (req, res) => {
const safePath = path.normalize(req.query.file).replace(/^(\.\.(\/|\\|$))+/, '');
const fullPath = path.join(__dirname, 'files', safePath);

if (!fullPath.startsWith(path.join(__dirname, 'files'))) {
return res.status(403).send('禁止访问');
}

res.sendFile(fullPath);
});

四、检测方法

4.1 手动测试

  1. 在文件参数中输入../../../etc/passwd
  2. 尝试各种编码变体
  3. 测试绝对路径访问

4.2 自动化工具

  • Burp Suite - 使用Intruder模块
  • OWASP ZAP - 自动扫描
  • DotDotPwn - 专门的目录遍历工具

五、总结

目录遍历漏洞的防御核心是:

  1. 永远不要信任用户输入
  2. 使用白名单验证
  3. 规范化并验证所有路径
  4. 实施最小权限原则
  5. 使用安全的API和框架功能

通过综合使用这些防御措施,可以有效防止目录遍历攻击,保护应用程序和服务器安全。