Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions go-getter/Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
Empty file added go-getter/challenge.yml
Empty file.
1 change: 1 addition & 0 deletions go-getter/flag.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bcactf{g0_r3v3rs3_3ng1n33r1ng_m4st3r}
82 changes: 82 additions & 0 deletions go-getter/genconst.py
Original file line number Diff line number Diff line change
@@ -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 <flag>")
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()
Binary file added go-getter/go-getter
Binary file not shown.
140 changes: 140 additions & 0 deletions go-getter/main.go
Original file line number Diff line number Diff line change
@@ -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.")
}
}
31 changes: 31 additions & 0 deletions go-getter/solve.py
Original file line number Diff line number Diff line change
@@ -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())
Empty file added go-getter/solve.txt
Empty file.
28 changes: 28 additions & 0 deletions locked-out-again/Dockerfile
Original file line number Diff line number Diff line change
@@ -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"]
21 changes: 21 additions & 0 deletions locked-out-again/challenge.yml
Original file line number Diff line number Diff line change
@@ -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
1 change: 1 addition & 0 deletions locked-out-again/flag.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
bcactf{5u0_d1n6_10ck_1n_m4n_4nd_h0p_0ff_7h3_8r41nr07}
Loading