│ Category: Cryptography
│ Author: Imattas aka Zemi
│ Flag: byuctf{1t_4lw4ys_c0m3s_b4ck_t0_1_21bcd6}
│ Source Path: crypto/cycles
────────────────────────────────────────────────────────────────────────────────
--[ Challenge Description ]--
-- text --
I heard through the grapevine that you can't take a discrete log under a large enough, good prime. Well I made sure to pick a good prime, go ahead and try
[main.py] [cycles.txt]
────────────────────────────────────────────────────────────────────────────────
--[ Provided Materials ]--
- Repository folder
https://github.com/BYU-CSA/BYUCTF-2025/tree/main/crypto/cycles
- crypto/cycles/README.md
https://github.com/BYU-CSA/BYUCTF-2025/blob/main/crypto/cycles/README.md
- crypto/cycles/cycles.txt
https://github.com/BYU-CSA/BYUCTF-2025/blob/main/crypto/cycles/cycles.txt
- crypto/cycles/solve.py
https://github.com/BYU-CSA/BYUCTF-2025/blob/main/crypto/cycles/solve.py
────────────────────────────────────────────────────────────────────────────────
--[ Recon / Initial Analysis ]--
This page imports the BYUCTF 2025 source material for the Cryptography challenge
Cycles. The prompt is kept separate from the solve notes, and upstream artifacts
are linked so the challenge can be replayed from the original repository.
────────────────────────────────────────────────────────────────────────────────
--[ Vulnerability / Observation ]--
The useful observation comes from the imported solve notes below. I kept the
original technical path intact while normalizing the page metadata, challenge
grouping, and author attribution for the Volume 2 writeup archive.
────────────────────────────────────────────────────────────────────────────────
--[ Exploitation / Solution ]--
Source: crypto/cycles/README.md
https://github.com/BYU-CSA/BYUCTF-2025/blob/main/crypto/cycles/README.md
This challenge only involves knowing fermat's little theorem, which says that
the number, given g being prime and p being prime, any a such that g^a=1%p must
be a multiple of p-1. A little experimentation reveals this when poking around
with it. a-1 doesn't work, so you know it needs to be a multiple. The actual
multiple is under 50, though I'm unsure which it is. See my solve script for
more details. Running the solve takes less than 5 seconds.
See solve.py.
Flag - byuctf{1t_4lw4ys_c0m3s_b4ck_t0_1_21bcd6}
────────────────────────────────────────────────────────────────────────────────
--[ Full Exploit Script ]--
Source: crypto/cycles/solve.py
https://github.com/BYU-CSA/BYUCTF-2025/blob/main/crypto/cycles/solve.py
-- python --
from Crypto.Util.number import long_to_bytes
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
g = 3
P = 121407847840823587654648673057258513248172487324370407391241175652533523276605532412599555241774504967764519702094283197762278545483713873101436663001473945726106157159264352878998534133035299601861808839807763182625559052896295039354029361792893109774218584502647139466059910154701304129191164513825925289381
ciphertext = b'S\x00\xe7%\xcd\xec\xad\x9a\xe1lO\x80\xd6\r\xa5\x00\x19Y\x18\x7f\xa1\x9cx\x98\xb08n~-\rj2\xd4d\xd2\xda\xa6\xd0\r#7\xee-\\\xb4\x10\x98\x8f'
Hint = 1
a = P-1
for i in range(1,100):
key = long_to_bytes(a*i)[:16]
plaintext = AES.new(key, AES.MODE_ECB).decrypt(ciphertext)
if b'byuctf' in plaintext:
print(unpad(plaintext, AES.block_size))
quit()
────────────────────────────────────────────────────────────────────────────────
--[ Key Takeaways ]--
- The BYUCTF 2025 challenge material is preserved with local archive formatting.
- The page author is normalized to Imattas aka Zemi.
- The source repository remains linked for handouts, services, and solve
artifacts.