crypto::ctf

crypto RSA · AES classical · modern
IdentifyClassicalXOR RSAAES modesHashes ECCTools
01Identify the Cipher
By ciphertext appearance
Looks likeLikely cipher
Only A-Z, spacesClassical (Caesar, Vigenère, sub)
Base64 stringEncoded, not encrypted — decode first
Hex stringXOR, AES-ECB raw, or just encoding
Large decimal n,e,cRSA
Two large hex numbersECC (r,s) ECDSA signature
Repeating blocks (hex)AES-ECB — block pattern visible
Dots/dashesMorse code
Binary groupsBinary → ASCII
Numbers 0-25Caesar / affine substitution
Mixed symbolsSubstitution / pigpen / Polybius
Quick decode pipeline
# Always try these first
echo "<string>" | base64 -d
echo "<hex>"    | xxd -r -p
echo "<string>" | tr 'A-Za-z' 'N-ZA-Mn-za-m'   # ROT13

# CyberChef "Magic" — auto-detect
# https://gchq.github.io/CyberChef/#recipe=Magic

# dcode.fr — cipher identifier
# https://www.dcode.fr/cipher-identifier

# Python quick decode
python3 -c "
import base64, binascii
s = 'your_string'
print(base64.b64decode(s))       # base64
print(bytes.fromhex(s))          # hex
print(base64.b32decode(s))       # base32
"
02Classical Ciphers
Caesar / ROT
# Brute force all 26 shifts
python3 -c "
ct = 'KHOOR ZRUOG'
for shift in range(26):
    print(shift, ''.join(chr((ord(c)-65-shift)%26+65) if c.isupper()
          else chr((ord(c)-97-shift)%26+97) if c.islower()
          else c for c in ct))
"

# ROT13 (shift=13)
echo "cvpbPGS{..." | tr 'A-Za-z' 'N-ZA-Mn-za-m'

# ROT47 (printable ASCII)
echo "E:4@r%u..." | tr '!-~' 'P-~!-O'
Vigenère
# Find key length with Index of Coincidence
# or Kasiski test — use online tools

# Python solve with known key
python3 -c "
ct = 'LXFOPVEFRNHR'
key = 'LEMON'
pt = ''
for i,c in enumerate(ct):
    if c.isalpha():
        shift = ord(key[i%len(key)].upper()) - 65
        pt += chr((ord(c.upper())-65-shift)%26+65)
    else: pt += c
print(pt)
"

# Automated: use quipqiup.com or dcode.fr
Frequency analysis
# Monoalphabetic substitution
python3 -c "
from collections import Counter
ct = 'your ciphertext here'
freq = Counter(c.upper() for c in ct if c.isalpha())
for ch, n in freq.most_common():
    print(ch, n)
# English: E T A O I N S H R D L C U M W F G Y P B V K J X Q Z
"

# quipqiup.com — automated sub cipher solver
# cryptogram-solver.com

# Known plaintext: if you know part of plaintext
# map known chars → build substitution alphabet
Other classical
CipherSolve
AtbashA↔Z, B↔Y: tr 'A-Za-z' 'ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba'
Rail fencedcode.fr/rail-fence-cipher
Playfairdcode.fr/playfair-cipher
PolybiusMap pairs to 5×5 grid
BaconA/B groups → 5 bits → letter
Columnar trans.Reorder columns, read rows
Morsedcode.fr or CyberChef
AffineE(x)=ax+b mod 26, brute a∈{1,3,5,7…}
03XOR
XOR operations
# Single-byte XOR brute force
python3 -c "
ct = bytes.fromhex('1b37373331363f78151b7f2b783431333d78397828372d363c78373e783a393b3736')
for key in range(256):
    pt = bytes(b ^ key for b in ct)
    if all(32 <= c < 127 for c in pt):
        print(hex(key), pt)
"

# Repeating-key XOR (Vigenère-like)
python3 -c "
ct = bytes.fromhex('...')
key = b'ICE'
pt = bytes(ct[i] ^ key[i%len(key)] for i in range(len(ct)))
print(pt)
"

# XOR two ciphertexts (same key → key cancels)
# ct1 XOR ct2 = pt1 XOR pt2
python3 -c "
c1 = bytes.fromhex('...')
c2 = bytes.fromhex('...')
print(bytes(a^b for a,b in zip(c1,c2)))
"
Find XOR key length
# Hamming distance method
python3 -c "
def hamming(a, b):
    return sum(bin(x^y).count('1') for x,y in zip(a,b))

ct = bytes.fromhex('...')
for ks in range(2, 41):
    score = hamming(ct[:ks], ct[ks:2*ks]) / ks
    print(ks, score)
# Lowest score = most likely key length
"

# Once key length known → single-byte XOR each column
python3 -c "
ct = bytes.fromhex('...')
ks = 3  # key size
key = []
for i in range(ks):
    col = ct[i::ks]
    best = max(range(256), key=lambda k:
        sum(chr(b^k) in 'etaoinshrdlu ' for b in col))
    key.append(best)
print(bytes(key))
"
04RSA Attacks
Basics & small e
# RSA: c = m^e mod n,  m = c^d mod n
# d = modular inverse of e mod phi(n)

# e=3, small m: cube root (no mod needed)
python3 -c "
import gmpy2
c = 0xdeadbeef...
m, exact = gmpy2.iroot(c, 3)
if exact: print(bytes.fromhex(hex(m)[2:]))
"

# e=65537 with small n: factor with factordb
# http://factordb.com/  — paste n

# Factor small n directly
python3 -c "
from sympy import factorint
n = 0x...
print(factorint(n))
"

# Once p,q known → decrypt
python3 -c "
from Crypto.Util.number import inverse, long_to_bytes
p,q,e,c = ...,...,...,...
phi = (p-1)*(q-1)
d = inverse(e, phi)
m = pow(c, d, p*q)
print(long_to_bytes(m))
"
Common RSA attacks
AttackConditionTool
Fermat factoringp,q close togetherCustom script
Wiener's attackd small (d < n^0.25)owiener lib
Common factorTwo n share a primegcd(n1,n2)
Broadcast attackSame m, e=3, 3 different nCRT + cube root
Padding oraclePKCS#1 v1.5Custom
Low public expe=3, small mCube root
Boneh-Durfeed < n^0.292SageMath script
factordbn already factored onlinefactordb.com
RsaCtfTool
# Swiss army knife for RSA CTF challenges
RsaCtfTool.py --publickey pub.pem --uncipherfile cipher.bin
RsaCtfTool.py -n <n> -e <e> --uncipher <c>

# Try all attacks at once
RsaCtfTool.py -n <n> -e <e> --uncipher <c> --attack all

# Multiple public keys (common modulus)
RsaCtfTool.py --publickey "*.pem" --attack all

# Fermat factoring manually
python3 -c "
import gmpy2
n = 0x...
a = gmpy2.isqrt(n) + 1
while True:
    b2 = a*a - n
    b, exact = gmpy2.isqrt_rem(b2)
    if exact == 0: break
    a += 1
p,q = int(a-b), int(a+b)
print(p, q)
"
05AES Modes & Attacks
Mode identification & attacks
ModeIdentifyAttack
ECBIdentical 16-byte blocks in CTCut-and-paste, chosen plaintext weak
CBCIV prepended, no block repeatPadding oracle, bit-flip
CTRKeystream XOR, no paddingReused nonce → XOR CTs
OFBLike CTR, stream cipherReused IV → XOR
CFBFeedback modeBit-flipping
GCMAuthenticated, 12-byte nonceNonce reuse → auth forgery
ECB cut-and-paste / oracle
# ECB detection: submit 32 identical bytes
# If bytes 0-15 == bytes 16-31 → ECB mode
python3 -c "
ct = encrypt(b'A' * 32)
print('ECB!' if ct[0:16] == ct[16:32] else 'Not ECB')
"

# Byte-at-a-time ECB decryption
# Encrypt: [A*15 + unknown_byte] → compare with
# all [A*15 + guess] until match

# CBC bit-flip: flip bit in CT block i
# → predictably flips bit in PT block i+1
python3 -c "
ct = bytearray(ciphertext)
# target offset in block i+1 = offset + 16
ct[offset] ^= ord('a') ^ ord('A')  # change 'a' → 'A' in next block
"
CTR / reused nonce
# If nonce reused: ct1 XOR ct2 = pt1 XOR pt2
# Known plaintext → recover keystream → decrypt all
python3 -c "
# If you know pt1 (or part of it):
ks = bytes(a^b for a,b in zip(ct1, pt1))  # keystream
pt2 = bytes(a^b for a,b in zip(ct2, ks))
print(pt2)
"

# Padding oracle (CBC) — pwntools has one
# Or use: padbuster, POET
padbuster http://target/decrypt "<ciphertext>" 16
06Hash Attacks
Crack & identify
# Identify hash type
hashid <hash>
hash-identifier <hash>
# 32 hex = MD5, 40 = SHA1, 64 = SHA256, 128 = SHA512

# Crack with hashcat
hashcat -m 0    hash.txt rockyou.txt  # MD5
hashcat -m 100  hash.txt rockyou.txt  # SHA1
hashcat -m 1400 hash.txt rockyou.txt  # SHA256
hashcat -m 1800 hash.txt rockyou.txt  # SHA512crypt

# Online rainbow tables
# crackstation.net / hashes.com / md5decrypt.net

# Length extension attack (MD5, SHA1, SHA256)
hash_extender --data original --secret-length 16 \
              --append ";admin=true" --signature <hash>
Hash length extension
# Vulnerable: HMAC(key || msg) using MD5/SHA1/SHA256
# You know: hash(key||msg), len(key), msg
# You can forge: hash(key||msg||padding||append)

# hlextend Python library
python3 -c "
import hlextend
sha = hlextend.new('sha256')
new_msg, new_hash = sha.extend(
    b';admin=true',        # data to append
    b'original_message',   # original data
    16,                    # secret key length
    'original_hash_hex'    # known hash
)
print(new_msg.hex(), new_hash)
"
07ECC & Misc
ECC basics & attacks
# ECDSA signature: (r, s) with nonce k
# Nonce reuse: same k → same r
# k = (z1-z2) * modInverse(s1-s2, n)
# d = (s*k - z) * modInverse(r, n)

python3 -c "
from Crypto.Util.number import inverse
# if r1 == r2 (same nonce k used):
k = (z1 - z2) * inverse(s1 - s2, n) % n
d = (s1 * k - z1) * inverse(r1, n) % n
print(hex(d))  # private key
"

# Invalid curve attack, small subgroup
# Use SageMath for discrete log on weak curves
Misc attacks & tools
ScenarioTool/Approach
Unknown cipherCyberChef Magic, dcode.fr
RSA all-in-oneRsaCtfTool.py
Prime factoringfactordb.com, yafu, msieve
Discrete logSageMath discrete_log()
AES padding oraclepadbuster, pwntools
Hash crackinghashcat, john, crackstation
XOR key findingxortool, CyberChef
PRNG predictionz3, randcrack (Mersenne)
LCG predictionKnown outputs → solve for params
CRYPTO CHECKLIST →  ① Decode first (base64, hex, rot13) before assuming encryption  ② Count ciphertext length — multiple of 16? → block cipher  ③ Repeating blocks → ECB mode  ④ Large n,e,c integers → RSA → try RsaCtfTool  ⑤ XOR: brute single byte, check for English  ⑥ Classical text: frequency analysis + dcode.fr  ⑦ Hash: identify length → crackstation → hashcat