From 0d8f10bb11123896d9544332c2ceae4a7e20ea8d Mon Sep 17 00:00:00 2001 From: adzkyyy Date: Sat, 25 Oct 2025 12:29:18 +0700 Subject: [PATCH] add sla --- receiver/challenges/carbeat.py | 157 ++++++++++++++++++++++++++++++ services/carbeat/chall.py | 116 ++++++++++++++++++++++ services/carbeat/chall/mybini | Bin 40848 -> 40872 bytes services/carbeat/chall/mybini.cpp | 4 + services/carbeat/dist/carbeat.zip | Bin 12524 -> 12619 bytes services/carbeat/sla.py | 157 ++++++++++++++++++++++++++++++ 6 files changed, 434 insertions(+) create mode 100644 receiver/challenges/carbeat.py create mode 100644 services/carbeat/chall.py create mode 100644 services/carbeat/sla.py diff --git a/receiver/challenges/carbeat.py b/receiver/challenges/carbeat.py new file mode 100644 index 0000000..54023f6 --- /dev/null +++ b/receiver/challenges/carbeat.py @@ -0,0 +1,157 @@ +from .Challenge import Challenge + +import subprocess +import time +import re +import os + +class Carbeat(Challenge): + flag_location = 'flags/carbeat.txt' + history_location = 'history/carbeat.txt' + + _CONTAINER = "carbeat_container" + _SERVICE_CMD = ["docker", "exec", "-i", _CONTAINER, "/home/ctf/chall/mybini"] + _HEX_RE = re.compile(r'^[0-9a-fA-F]+$') + + def _read_container_flag(self) -> str: + out = subprocess.run(["docker", "exec", self._CONTAINER, "cat", "/flag.txt"], + capture_output=True, text=True) + if out.returncode != 0 or not out.stdout.strip(): + raise FileNotFoundError("Flag not found in container (/flag.txt)") + return out.stdout.strip() + + def _spawn(self): + return subprocess.Popen( + self._SERVICE_CMD, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=0, + ) + + def _read_until(self, proc, token, timeout=5.0, max_bytes=1_000_000): + start = time.time() + buf = [] + r = proc.stdout.read + while True: + if time.time() - start > timeout: + tail = ''.join(buf)[-500:] + raise TimeoutError(f"Timeout waiting for '{token}'. Got so far:\n{tail}") + ch = r(1) + if ch == "" and proc.poll() is not None: + raise RuntimeError(f"Process ended while waiting for '{token}'. Output:\n{''.join(buf)}") + buf.append(ch) + if len(buf) > max_bytes: + raise RuntimeError("Exceeded max read size") + if token in "".join(buf): + return "".join(buf) + + def _send_line(self, proc, s: str): + proc.stdin.write(s + "\n") + proc.stdin.flush() + + def _expect_hex_field(self, text: str, label: str) -> str: + m = re.search(rf"{re.escape(label)}\s*:\s*([0-9a-fA-F]+)", text) + assert m, f"Missing '{label}' in output. Tail:\n{text[-400:]}" + hx = m.group(1) + assert self._HEX_RE.match(hx), f"{label} is not hex" + return hx + + def distribute(self, flag): + try: + os.makedirs(os.path.dirname(self.flag_location), exist_ok=True) + with open(self.flag_location, 'w') as f: + f.write(flag) + + os.makedirs(os.path.dirname(self.history_location), exist_ok=True) + with open(self.history_location, 'a') as f: + f.write(flag + '\n') + + self.logger.info(f'Flag {flag} written to {self.flag_location}') + return True + except Exception as e: + self.logger.error(f'Could not write flag to {self.flag_location}: {e}') + return False + + + def register(self, proc, name, level, timeout=1.): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, "1") + self._read_until(proc, ": ", timeout=timeout) + self._send_line(proc, name) + self._read_until(proc, ": ", timeout=timeout) + self._send_line(proc, str(level)) + + def login(self, proc, name, timeout=1.): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, "2") + self._read_until(proc, ": ", timeout=timeout) + self._send_line(proc, name) + + def list_user(self, proc, timeout=1.): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, "3") + return self._read_until(proc, "\n1. register") + + def bini(self, proc, id, data, timeout=1.0): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, str(id)) + self._read_until(proc, ": ", timeout=timeout) + self._send_line(proc, data) + + def logout(self, proc, timeout=1.): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, "4") + + def exit(self, proc, timeout=1.): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, "3") + + def check(self): + try: + with open(self.flag_location, 'r') as f: + host_flag = f.read().strip() + container_flag = self._read_container_flag() + assert host_flag == container_flag, 'Flag mismatch between host and container' + self.logger.info('[ok] flag parity (carbeat)') + + proc = self._spawn() + + self.register(proc, "karbit", 1337) + + name = self.list_user(proc) + assert "karbit" in name + + self.login(proc, "karbit") + self.bini(proc, 1, "waguri") + self.bini(proc, 2, "0") + waguri = self._read_until(proc, "0. edit", timeout=1.) + assert "waguri" in waguri + + self.bini(proc, 3, "0") + self.logout(proc) + self.exit(proc) + + try: + proc.wait(timeout=2.0) + except subprocess.TimeoutExpired: + proc.kill() + raise AssertionError("Program did not exit after option 3") + self.logger.info("[ok] service exit on 3") + + proc_alarm = self._spawn() + self._read_until(proc_alarm, "zzz", timeout=190.0) # 180s + slack + try: + proc_alarm.wait(timeout=5.0) + except subprocess.TimeoutExpired: + proc_alarm.kill() + raise AssertionError("Alarm fired but process did not exit") + self.logger.info("[ok] alarm fired ('zzz') and process self-terminated") + + self.logger.info('Check passed for carbeat') + return True + + except Exception as e: + self.logger.error(f'Could not check carbeat: {e}') + return False \ No newline at end of file diff --git a/services/carbeat/chall.py b/services/carbeat/chall.py new file mode 100644 index 0000000..3592d1b --- /dev/null +++ b/services/carbeat/chall.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 + +import os +import binascii +import hashlib +import threading +import time +import sys +from Crypto.Cipher import AES +from Crypto.Util.Padding import pad, unpad + +seed_bits = 23 +seed_max = 1 << seed_bits +seed_len = (seed_bits + 7) // 8 +key = os.urandom(16) + +def hash_seed(seed_int: int) -> bytes: + sb = seed_int.to_bytes(seed_len, "big") + return hashlib.sha256(sb).digest()[:16] + +seed = int.from_bytes(os.urandom(4), "big") % seed_max +seed2 = int.from_bytes(os.urandom(4), "big") % seed_max +K1 = hash_seed(seed) +K2 = hash_seed(seed2) + + +flag = "FLAGOAKSDASKDOASDKAODSKDOK" + +def read_hex(prompt: str): + s = input(prompt).strip() + try: + return binascii.unhexlify(s) + except Exception: + print("hmm") + return None + +def enc_cfb(pt: bytes) -> bytes: + iv = os.urandom(16) + aes = AES.new(key, AES.MODE_CFB, iv=iv, segment_size=128) + ct = aes.encrypt(pt) + return iv + ct + +def enc_cbc(data: bytes, iv1: bytes, iv2: bytes, padd: bool) -> bytes: + x = pad(data, 16) if padd else data + c1 = AES.new(K1, AES.MODE_CBC, iv=iv1).encrypt(x) + c2 = AES.new(K2, AES.MODE_CBC, iv=iv2).encrypt(c1) + return c2 + +def menu(): + print(""" +1. encrypt +2. profit +3. get third +4. exit + """) + +third = 0 +iv11 = None +iv22 = None + +def alarm(): + time.sleep(180) + print("zzz") + sys.exit(0) + +threading.Thread(target=alarm, daemon=True).start() + +while True: + menu() + op = input("> ").strip() + + if op == "1": + data = read_hex("pt: ") + if data is None: + print() + continue + out = enc_cfb(data) + print("ct: ", out.hex()) + print() + + elif op == "2": + if iv11 is not None and iv22 is not None: + iv1, iv2 = iv11, iv22 + iv11 = iv22 = None + else: + iv1 = os.urandom(16) + iv2 = os.urandom(16) + ct = enc_cbc(flag, iv1, iv2, padd=True) + print("iv1: ", iv1.hex()) + print("iv2: ", iv2.hex()) + print("ct: ", ct.hex()) + print() + + elif op == "3": + if third: + print("sheesh") + continue + block = read_hex("pt: ") + if block is None: + print() + continue + if len(block) != 16: + print("hmmm\n") + continue + iv1 = os.urandom(16) + iv2 = os.urandom(16) + ct = enc_cbc(block, iv1, iv2, padd=False) + iv11, iv22 = iv1, iv2 + print("ct: ", ct.hex()) + third = 1 + print() + + elif op == "4": + break + else: + print("mabokkkk?") \ No newline at end of file diff --git a/services/carbeat/chall/mybini b/services/carbeat/chall/mybini index 9de5401cea9ab273ad058aefc15e91d4ae0162fa..ba1f823b400be8bfbaae2083bf56d37178598225 100644 GIT binary patch delta 7143 zcmZu$3se+Wwyi3<5gh)aO@q+<|DnO4D2f76XicJ$5k-^Q>vbrq-(3b@$ol+;i_e zx2h@X4ogjkr8+P7)*wMSl2s7$H$}YVm^_G+n=Y6guLr1&>*Ow)Q@(drTN(;(YFh-@ zZ9 zd3xB{d(AszZVwNg{>#lBd9=|k+&2s{wd4eGzqCg5lo%I@GrHvqyL3r>K>Kxu7B72; z0ir}EXERN8?x@hZaZqy-8iUoFT%Nkd--=NA{C<2c*O&F<8+p63AMX~c#;H^J7X+Js^yH-3&%H>Dp0gV~wWg-3)P2nhDstSZy$0 zKEn#pQ>5|(03xkMOi3lwE6;~n?dTmh1GT$3IlI}gdb$MG$=%7>--dM{XLkdQ0UK+> z4x}Wo3&AGauufD6c01UqHmn=%0DA^(t_|x!{{j0DY_SdNNsqurdpJ2)+OR|DDGvk9 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 OA!E;I{8-trC-T3|Gx0zG diff --git a/services/carbeat/chall/mybini.cpp b/services/carbeat/chall/mybini.cpp index 94ca282..6a1a614 100644 --- a/services/carbeat/chall/mybini.cpp +++ b/services/carbeat/chall/mybini.cpp @@ -158,6 +158,7 @@ int main(){ std::cout << "1. register" << std::endl; std::cout << "2. login" << std::endl; std::cout << "3. list karbit" << std::endl; + std::cout << "4. exit" << std::endl; std::cout << "> "; std::cin >> c; @@ -185,6 +186,9 @@ int main(){ case 3: for (auto& user : users) user.info(); break; + case 4: + std::cout << "bye!" << std::endl; + exit(0); default: std::cout << "invalid option" << std::endl; } diff --git a/services/carbeat/dist/carbeat.zip b/services/carbeat/dist/carbeat.zip index f9ddc34a0249893673bbfa4e921235a13667c767..f6271c9f0792a6f0375f8c39e64a7f15dddd8f8d 100644 GIT binary patch delta 12395 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
    mT)h$QHJdIgxY~1{7Cv|{!$GtbdBRWh`#eT` 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 str: + out = subprocess.run(["docker", "exec", self._CONTAINER, "cat", "/flag.txt"], + capture_output=True, text=True) + if out.returncode != 0 or not out.stdout.strip(): + raise FileNotFoundError("Flag not found in container (/flag.txt)") + return out.stdout.strip() + + def _spawn(self): + return subprocess.Popen( + self._SERVICE_CMD, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + bufsize=0, + ) + + def _read_until(self, proc, token, timeout=5.0, max_bytes=1_000_000): + start = time.time() + buf = [] + r = proc.stdout.read + while True: + if time.time() - start > timeout: + tail = ''.join(buf)[-500:] + raise TimeoutError(f"Timeout waiting for '{token}'. Got so far:\n{tail}") + ch = r(1) + if ch == "" and proc.poll() is not None: + raise RuntimeError(f"Process ended while waiting for '{token}'. Output:\n{''.join(buf)}") + buf.append(ch) + if len(buf) > max_bytes: + raise RuntimeError("Exceeded max read size") + if token in "".join(buf): + return "".join(buf) + + def _send_line(self, proc, s: str): + proc.stdin.write(s + "\n") + proc.stdin.flush() + + def _expect_hex_field(self, text: str, label: str) -> str: + m = re.search(rf"{re.escape(label)}\s*:\s*([0-9a-fA-F]+)", text) + assert m, f"Missing '{label}' in output. Tail:\n{text[-400:]}" + hx = m.group(1) + assert self._HEX_RE.match(hx), f"{label} is not hex" + return hx + + def distribute(self, flag): + try: + os.makedirs(os.path.dirname(self.flag_location), exist_ok=True) + with open(self.flag_location, 'w') as f: + f.write(flag) + + os.makedirs(os.path.dirname(self.history_location), exist_ok=True) + with open(self.history_location, 'a') as f: + f.write(flag + '\n') + + self.logger.info(f'Flag {flag} written to {self.flag_location}') + return True + except Exception as e: + self.logger.error(f'Could not write flag to {self.flag_location}: {e}') + return False + + + def register(self, proc, name, level, timeout=1.): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, "1") + self._read_until(proc, ": ", timeout=timeout) + self._send_line(proc, name) + self._read_until(proc, ": ", timeout=timeout) + self._send_line(proc, str(level)) + + def login(self, proc, name, timeout=1.): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, "2") + self._read_until(proc, ": ", timeout=timeout) + self._send_line(proc, name) + + def list_user(self, proc, timeout=1.): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, "3") + return self._read_until(proc, "\n1. register") + + def bini(self, proc, id, data, timeout=1.0): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, str(id)) + self._read_until(proc, ": ", timeout=timeout) + self._send_line(proc, data) + + def logout(self, proc, timeout=1.): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, "4") + + def exit(self, proc, timeout=1.): + self._read_until(proc, "> ", timeout=timeout) + self._send_line(proc, "3") + + def check(self): + try: + with open(self.flag_location, 'r') as f: + host_flag = f.read().strip() + container_flag = self._read_container_flag() + assert host_flag == container_flag, 'Flag mismatch between host and container' + self.logger.info('[ok] flag parity (carbeat)') + + proc = self._spawn() + + self.register(proc, "karbit", 1337) + + name = self.list_user(proc) + assert "karbit" in name + + self.login(proc, "karbit") + self.bini(proc, 1, "waguri") + self.bini(proc, 2, "0") + waguri = self._read_until(proc, "0. edit", timeout=1.) + assert "waguri" in waguri + + self.bini(proc, 3, "0") + self.logout(proc) + self.exit(proc) + + try: + proc.wait(timeout=2.0) + except subprocess.TimeoutExpired: + proc.kill() + raise AssertionError("Program did not exit after option 3") + self.logger.info("[ok] service exit on 3") + + proc_alarm = self._spawn() + self._read_until(proc_alarm, "zzz", timeout=190.0) # 180s + slack + try: + proc_alarm.wait(timeout=5.0) + except subprocess.TimeoutExpired: + proc_alarm.kill() + raise AssertionError("Alarm fired but process did not exit") + self.logger.info("[ok] alarm fired ('zzz') and process self-terminated") + + self.logger.info('Check passed for carbeat') + return True + + except Exception as e: + self.logger.error(f'Could not check carbeat: {e}') + return False \ No newline at end of file