""" 路径遍历漏洞知识 """ from ..base import KnowledgeDocument, KnowledgeCategory PATH_TRAVERSAL = KnowledgeDocument( id="vuln_path_traversal", title="Path Traversal", category=KnowledgeCategory.VULNERABILITY, tags=["path", "traversal", "file", "directory", "lfi", "rfi"], severity="high", cwe_ids=["CWE-22", "CWE-23"], owasp_ids=["A01:2021"], content=""" 路径遍历允许攻击者访问应用程序根目录之外的文件,可能导致敏感信息泄露或代码执行。 ## 危险模式 ### Python ```python # 危险 - 直接拼接路径 file_path = "/uploads/" + user_filename open(base_dir + request.args['file']) os.path.join(base_dir, user_input) # 仍然危险! # 危险 - 文件下载 @app.route('/download') def download(): filename = request.args.get('file') return send_file(f'/files/{filename}') # 危险 - 模板包含 render_template(user_template) ``` ### Node.js ```javascript // 危险 const filePath = path.join(__dirname, req.query.file); fs.readFile('./uploads/' + filename); res.sendFile(req.params.path); ``` ### PHP ```php // 危险 include($_GET['page']); require($user_input); file_get_contents($filename); ``` ## 攻击载荷 ``` ../../../etc/passwd ..\\..\\..\\windows\\system32\\config\\sam ....//....//....//etc/passwd ..%2f..%2f..%2fetc/passwd %2e%2e%2f%2e%2e%2f%2e%2e%2fetc/passwd ..%252f..%252f..%252fetc/passwd (双重编码) ``` ## 敏感文件目标 ``` # Linux /etc/passwd /etc/shadow /etc/hosts /proc/self/environ /var/log/apache2/access.log # Windows C:\\Windows\\System32\\config\\SAM C:\\Windows\\win.ini C:\\inetpub\\logs\\LogFiles # 应用配置 .env config.php settings.py application.yml ``` ## 检测要点 1. 所有文件操作函数 2. 用户输入是否用于构建路径 3. 是否有路径规范化 4. 是否验证最终路径在允许范围内 ## 安全实践 1. 验证和规范化路径 2. 使用白名单 3. 检查路径是否在允许目录内 4. 使用安全的文件ID映射 ## 修复示例 ```python import os def safe_join(base_dir, user_path): # 规范化路径 base_dir = os.path.abspath(base_dir) full_path = os.path.abspath(os.path.join(base_dir, user_path)) # 验证路径在基础目录内 if not full_path.startswith(base_dir + os.sep): raise ValueError("Path traversal detected") return full_path # 使用 try: safe_path = safe_join('/uploads', user_filename) with open(safe_path) as f: content = f.read() except ValueError: abort(403) # 更安全 - 使用文件ID映射 @app.route('/download/') def download(file_id): file_record = File.query.get(file_id) if file_record and file_record.user_id == current_user.id: return send_file(file_record.path) abort(404) ``` """, )