team-logo
Published on

BroncoCTF 2025 - Crypto challenges

Authors

Introduction

We solved 4 out of 5 tasks. More info about this CTF is here

tasks

Table of contents

Across the Tracks

across-the-track Writeup author: Lazarus

It's Rail Fence (Zig-Zag) Cipher. Full text:

Some·ciphers·are·easier·to·solve.·Some·ciphers·are·harder·to·solve.·You·definitely·could·brute·force·this·one·if·you·did·it·by·hand.·I·had·to·do·that·recently·on·an·exam.·It·was·not·as·fun·as·I·had·hoped.·But·that·is·okay.·I·hope·you·didn't·do·this·by·hand.·Here·is·the·flag·tho:·bronco{r@1l_f3nc3_cip3rs_r_cool}

Flag: bronco{r@1l_f3nc3_cip3rs_r_cool}

Rahhh-SA

rahhh-sa.png Writeup author: Bedi
import math

e = 65537
phi_n = 3424680
n = 3429719
ciphertext = [-53102, -3390264, -2864697, -3111409, -2002688, -2864697, -1695722, -1957072, -1821648, -1268305, -3362005, -712024, -1957072, -1821648, -1268305, -732380, -2002688, -967579, -271768, -3390264, -712024, -1821648, -3069724, -732380, -892709, -271768, -732380, -2062187, -271768, -292609, -1599740, -732380, -1268305, -712024, -271768, -1957072, -1821648, -3418677, -732380, -2002688, -1821648, -3069724, -271768, -3390264, -1847282, -2267004, -3362005, -1764589, -293906, -1607693]

def extended_euclidean(e, phi_n):
    A1, A2, A3 = 1, 0, phi_n
    B1, B2, B3 = 0, 1, e

    while True:
        if B3 == 0:
            return -1
        if B3 == 1:
            return B2 % phi_n
        Q = A3 // B3
        T1, T2, T3 = A1 - (Q * B1), A2 - (Q * B2), A3 - (Q * B3)
        A1, A2, A3 = B1, B2, B3
        B1, B2, B3 = T1, T2, T3

def decrypt(ciphertext, d, n):
    return [pow(c, d, n) for c in ciphertext]

d = extended_euclidean(e, phi_n)
decrypted_numbers = decrypt(ciphertext, d, n)
print("".join(chr(i) for i in decrypted_numbers))

Flag:bronco{m4th3m4t1c5_r34l1y_1s_qu1t3_m4g1c4l_raAhH!}

Mid PRNG

mid-prng Writeup author: ppp45
  • flag prefix is known
  • generator can only have states in range(0, 256), otherwise bytes() would fail

We download encrypted flag 1000 times

for run in {1..1000}; do
    nc bad-prng.nc.broncoctf.xyz 8000 >>history.txt
    echo >>history.txt
done

We use known flag prefix to recover pairs of consecutive states

def xor(b1, b2):
    return bytes(e1 ^ e2 for e1, e2 in zip(b1, b2))


with open("history.txt") as f:
    data = [bytes.fromhex(e) for e in f.readlines()]


prefix = b"bronco{"
dct = {}

for row in data:
    lst = list(xor(prefix, row))
    for a, b in zip(lst, lst[1:]):
        assert dct.setdefault(a, b) == b

for row in data:
    x = row[0] ^ prefix[0]
    key = bytearray([x])
    for i in range(len(row) - 1):
        try:
            key.append(dct[key[-1]])
        except KeyError:
            break
    else:
        flag = xor(key, row)
        print(f"{flag = }")
        break

Flag: bronco{0k_1ts_n0t_gr34t}

Homie Owes Me

homie-owes-me Writeup author: Lazarus

According to intel from Luigi:

1. Really likes being called a homie. Specifically, yoshiethehomie.
2. Likes to talk in "partial leetspeak."
3. Is a fan of "one, only one!" special character replacement.
4. Appends his 4-digit PIN to all his secure logins.
5. Loves BroncoCTF so much that he includes the flag format in his credentials.

I created a custom mask for hashcat:

hashcat -m 1400 -a 3 -O -1 o0 -2 s5$ -3 i1! -4 e3 d:\hash.txt bronco{y?1?2h?3?4th?4h?1m?3?4?d?d?d?d}

Flag: bronco{y0sh1eth3hom!e8778}