ctfs
  • 👋Hello!
  • 🏴Practice
    • 🌐Cryptohack
      • Introduction
      • General
        • Encoding
        • XOR
        • Mathematics
        • Data Formats
      • Symmetric Ciphers
        • How AES Works
        • Symmetric Starter
        • Block Ciphers 1
        • Stream Ciphers
      • Mathematics
        • Modular Math
        • Lattices
      • RSA
        • Starter
        • Primes Part 1
        • Public Exponent
    • 🌐PortSwigger
      • Path Traversal
      • File Upload
      • SSRF Attacks
    • 🌐TryHackMe
      • Basic Skills
      • Linux
      • Penetration Testing
      • Networking
      • OSINT
  • 🚩Competitions
    • 2025
      • 🇮🇩GKSK#9 Osintathon
        • Mudik Lebaran (100 pts)
        • Foto Patung (100 pts)
        • Kolektor Komik (100 pts)
        • Tolong Aku (100 pts)
        • Kencan Pertama (100 pts)
        • Nama Si Pelaku (100 pts)
        • Cekidot (100 pts)
        • Ledakan! (100 pts)
        • 🎹🎶 (100 pts)
        • Batu Besar (100 pts)
        • Komentar (100 pts)
        • Ini dimana? (100 pts)
        • Koordinat Foto Misterius (100 pts)
        • Bianglalaaa (100 pts)
        • Aku Hacker (100 pts)
        • Anjazzz (100 pts)
        • Dikirim Kakakku (129 pts)
        • Ingfo Loker (154 pts)
        • MISSING 00 (100 pts)
        • MISSING 01 (154 pts)
        • Siapa Aku? (154 pts)
      • 🇮🇩IFEST 13
        • Ququerer (250 pts)
        • Silent Trace (370 pts)
        • Nugas (Solved After Event)
        • Free Flag (280 pts)
        • Brute (Solved After Event)
        • Web V1 (Solved After Event)
        • Bypass (Solved After Event)
        • Orbiter (Solved After Event)
      • 🌐OSINT Combine (Wildlife)
        • Getting Started (100 pts)
        • Proper Poppy (100 pts)
        • Legendary Beasts (200 pts)
        • Shadow Fleet (200 pts)
        • Proper Poppy II (200 pts)
        • Not So Smug Smuggler (200 pts)
        • Icy (200 pts)
        • Forest Pals (200 pts)
        • Safari Time II (200 pts)
        • Sneaky! (200 pts)
        • Hello Friend (300 pts)
        • Busy As A (300 pts)
        • Get Rotated! (300 pts)
        • High Seas (300 pts)
        • Nocturnal (300 pts)
        • Safari Time (400 pts)
        • Peak Weather (400 pts)
        • Singsong (400 pts)
        • Falling Fell (500 pts)
        • Kitty Cats (500 pts)
      • 🇮🇩RECURSION
        • let him cook
        • Basic Math
        • Favourite Number
        • Zarrar Cipher (100 pts)
        • paBlue Team (100 pts)
        • [🩸] I wish I was there on December 21, 2024 (100 pts)
        • Small House (200 pts)
        • [🩸] Mission Difference (456 pts)
    • 2024
      • 🌐Santa Claus CTF
        • Complete Picture
        • Day 1 - Big Bang
        • Day 2 - The Summer Job
        • Day 3 - The Visitors
        • Day 4 - Happy Birthday
        • Day 5 - Say My Name
        • Day 6 - Say "Cheese"
        • Day 7 - Revealing Pixels
        • Day 8 - Connecting The Dots
        • Day 9 - 404 Not Found
        • Day 10 - Breaking News
        • Day 11 - Ayrton Santa
        • Day 12 - Lost and Found
        • Day 13 - Planespotting
        • Day 14 - Santa Surveillance
        • Day 15 - Shaken, Not Stirred
        • Day 16 - Status Update
        • Day 17 - Waste ...of Time
        • Day 18 - Lost in Translation
        • Day 19 - Santa's Clones
        • Day 20 - Losing Tracks
        • Day 21 - Sing my Song
        • Day 22 - Eagle Eye
        • Day 23 - Distances Matters
        • Day 24 - Mastermind
      • 🌐Cyber Jawara International
        • Stone Game (100 pts)
        • prepare the tools (176 pts)
        • Persona (484 pts)
      • 🌐OSMOSIS Precon CTF
        • 1 The art of espionage
        • # 2 The Hack
        • # 3 The rabbit hole
        • # 4 The Association
        • # 6 Where is number 5
        • # 5 Who is it
        • Too many Layers
        • The prize
      • 🇮🇩Intechfest
        • Sanity Check (100 pts)
        • Alin (113 pts)
        • GerakSendiri (106 pts)
        • Details (100 pts)
      • 🇮🇩COMPFEST 16
        • Let's Help John! (100 pts)
        • money gone, wallet also gone (100 pts)
        • head’s up! (493 pts)
        • CaRd (304 pts)
        • Sanity Check (100 pts)
      • 🇮🇩Gemastik
        • Baby AES (451 pts)
        • Baby Structured (100 pts)
      • 🇮🇩Technofair 11
        • Kenangan
        • Xorban
        • Marsha
        • Siap Tempur!!
        • eftipi
        • kurang berarti
        • DUMPling
        • Malicious
      • 🌐DIVER OSINT
        • chiban
      • 🇮🇩GKSK#8 Osintathon
        • Sport Location
        • Meklaren lu warna apa boss ?
        • Postcode
        • Rumah Minang
        • Latihan
        • Anak Misterius
        • Travelling Anywhere
        • The Thief
        • Danger Watch
        • Misteri Ruang Angkasa
        • Fun Walk
        • I am Late
        • My Oshi
        • Wellcome to my Youtube Channel
        • Pesan Tersembunyi Wingdings
        • Salah Fokus
        • Apa itu GKSK?
        • Foto Bersejarah
        • Picture
        • Nostalgia Child
        • oldschool
        • Summer Olympic
      • 🇮🇩Techcomfest
        • pemanasan
        • crackable
        • Kuli-ah forensik
    • 2023
      • 🇮🇩Cyber Jawara
        • daruma
      • 🇮🇩NCW
        • Simple (220 pts)
        • wangsaf (320 pts)
        • Sillyville Saga (220 pts)
        • Freminhelp (Solved after event)
      • 🇮🇩Hology 6
      • 🇮🇩SlashRoot 7
        • Summary (441 pts)
        • eeee (480 pts)
        • Zebra Cross (409 pts)
        • Waka Waka eh eh (185 pts)
        • ANABUL (250 pts)
      • 🇮🇩COMPFEST 15
        • not simply corrupted (316 pts)
        • Artificial secret (356 pts)
      • 🇮🇩Gemastik
        • easy AES
        • k-1
        • Gen Z
      • 🇮🇩TechnoFair 10
        • RSA Bwang
        • Marsah
        • rapsodi
        • Pengen Merch JKT 😢
        • space mono
        • file pemberian fans
        • bantu aku mencari sebuah rahasia
    • 2022
      • 🇮🇩NCW
        • sabeb64 (331 pts)
        • cakemath (451 pts)
        • Downloader (244 pts)
        • 199 passcode (Solved after event)
      • 🇮🇩TEDCTF
      • 🇮🇩Gemastik
      • 🇮🇩OSCCTF
      • 🇮🇩ARA
  • 🪦Old Hello
Powered by GitBook
On this page
  • ECB CBC WTF
  • ECB Oracle
  • Flipping Cookie
  • Lazy CBC
  • Triple DES
  1. Practice
  2. Cryptohack
  3. Symmetric Ciphers

Block Ciphers 1

PreviousSymmetric StarterNextStream Ciphers

Last updated 2 months ago

ECB CBC WTF

Challenge website:

Take each block separately. Decrypt the block (using ECB decryption), then XOR the result with the previous block's ciphertext.

import requests

url = "https://aes.cryptohack.org/ecbcbcwtf/"

def encrypt_flag():
    response = requests.get(url + "encrypt_flag/")
    return response.json()['ciphertext']

def decrypt(ciphertext):
    response = requests.get(url + "decrypt/" + ciphertext)
    return response.json()['plaintext']

def hex_xor(hex1, hex2):
	hex1 = bytes.fromhex(hex1)
	hex2 = bytes.fromhex(hex2)
	res = b""
	for x, y in zip(hex1, hex2):
		res += (x^y).to_bytes()
	return res.hex()


ciphertext = encrypt_flag()
CT_NO_OF_BLOCKS = len(ciphertext)//32	# 3 -- The first block is iv

plaintext = ""
for i in range(1, CT_NO_OF_BLOCKS):
	cipher_block = ciphertext[i*32:(i+1)*32]
	xored_block = decrypt(cipher_block)
	plain_block = hex_xor(xored_block, ciphertext[(i-1)*32:i*32])
	plaintext += plain_block

print(bytes.fromhex(plaintext))

Flag: crypto{3cb_5uck5_4v01d_17_!!!!!}

ECB Oracle

ECB mode encrypts the same block to the ciphertext. Using the oracle, we can try encrypting 15 bytes of known data, leaving the last byte to be the first byte from the flag. Then, we can brute-force all the possible bytes and match the resulting ciphertext to the one we got with the unknown last byte from the flag. The ciphertext that matches would reveal what the first character of the flag is. To get the next characters, we can put the known byte of the flag into the encryption input, and still leaving the last byte to be the second character of the flag.

Using the approach explained above, we wouldn't be able to read the whole flag if the flag length is longer than 16. That's why knowing the flag length is important. We can use the oracle to encrypt a few bytes of data, adding one after each. When the ciphertext shows an additional block of data, that's when we stop.

# Increment plaintext length until new block is added
def get_flag_length(buffer=b'\x00'.hex()):
	buffer_len = 1
	ct_len = len(encrypt(buffer*buffer_len))//2
	new_ct_len = -1
	while ct_len >= new_ct_len:
		buffer_len += 1
		new_ct_len = len(encrypt(buffer*buffer_len))//2
	flag_len = ct_len - (buffer_len - 1) - 1 	# 1 is padding
	return flag_len

# Result: 25
# Therefore, we need 2 blocks as buffer
# We will do the encryption & brute-forcing stuffs on the 2nd block

Now here's the solver code.

import requests

url = "https://aes.cryptohack.org/ecb_oracle/"

def encrypt(plaintext):
    response = requests.get(url + "encrypt/" + plaintext)
    return response.json()['ciphertext']

# Leaving 1 byte for the flag's byte
buff_len = 31

# Store found characters here
flag = b""

# Main code
for _ in range(buff_len):
	buffer = (b"\x00"*buff_len).hex()
	ct = bytes.fromhex(encrypt(buffer))
	ct2_real = ct[16:32]		# 2nd block of the ciphertext

	# Try all possible bytes
	for x in range(256):
		guess = buffer + flag.hex() + f"{x:02x}"
		ct2_guess = bytes.fromhex(encrypt(guess))[16:32]
		if ct2_guess == ct2_real:
			flag += x.to_bytes()
			buff_len -= 1
			break

	print(flag)

Flag: crypto{p3n6u1n5_h473_3cb}

Flipping Cookie

This is a basic case of CBC mode bit flipping attack. Here, we know the structure of the plaintext. When decrypting in CBC, the decryption result is XOR-ed with the previous block's ciphertext (for the first block, with the iv). We can reverse the process (XOR-ing the known plaintext and the iv) to get the data after decryption (we will call it xored here). Then, we XOR xored with the new plaintext that we crafted. The result is then put as the new iv. This way, when the decryption happens, the block will decrypts to the new plaintext instead of the original plaintext.

import requests

url = "https://aes.cryptohack.org/flipping_cookie/"

def get_cookie():
    response = requests.get(url + "get_cookie/")
    return response.json()['cookie']

def check_admin(cookie, iv):
    response = requests.get(url + "check_admin/" + cookie + "/" + iv)
    try:
    	return response.json()['flag']
    except:
    	return response.json()['error']

def hex_xor(hex1, hex2):
	hex1 = bytes.fromhex(hex1)
	hex2 = bytes.fromhex(hex2)
	res = b""
	for x, y in zip(hex1, hex2):
		res += (x^y).to_bytes()
	return res.hex()

# Get the cookie
full_cookie = get_cookie()
iv = full_cookie[:32]
cookie = full_cookie[32:]

# Flip the bytes
plain = b"admin=False;expi".hex()
new_plain = b"admin=True;expir".hex()
xored = hex_xor(iv, plain)
new_iv = hex_xor(xored, new_plain)

# Get flag
print(check_admin(cookie, new_iv))

Flag: crypto{4u7h3n71c4710n_15_3553n714l}

Lazy CBC

In this implementation of CBC, the key itself is used as the IV. Our goal is to leak the key using two available functionalities:

  • encrypt() – Encrypts any input using the same key used for encrypting the flag.

  • receive() – Takes a ciphertext as input and gives you the plaintext but only if the plaintext is unprintable.

To exploit the system, we begin by encrypting a block of empty bytes (00000000000000000000000000000000). This gives us the ciphertext 33a689f7d458770d1c1744e5ae5e38a2.

Since XOR-ing any value with zero leaves it unchanged, this ciphertext is essentially the encrypted key (kind of like using ECB mode). If we could decrypt it directly, we'd obtain the key. Putting the ciphertext into receive() would only give us the original empty bytes, because using CBC, the decryption result is XOR-ed with the IV, or in this case the key.

To work around this, we could construct a new ciphertext, putting two of the ciphertext we have side-by-side, and adding an invalid block at the end to trick receive() into giving us the decryption result.

ciphertext + ciphertext + invalid block
33a689f7d458770d1c1744e5ae5e38a233a689f7d458770d1c1744e5ae5e38a2000000000000000000000000000000ff

Due to how CBC decryption works, the second block's plaintext will be the key XOR-ed with the previous block's ciphertext. Reversing the XOR operation will finally give us the key. And with the key, we can decrypt the flag.

Here is the full solver script.

import requests

url = "https://aes.cryptohack.org/lazy_cbc/"

def encrypt(plaintext):
    response = requests.get(url + "encrypt/" + plaintext)
    return response.json()['ciphertext']

def get_flag(key):
    response = requests.get(url + "get_flag/" + key)
    try:
    	return response.json()['plaintext']
    except:
    	return response.json()['error']

def receive(ciphertext):
    response = requests.get(url + "receive/" + ciphertext)
    try:
    	return response.json()['success']
    except:
    	return response.json()['error'].split(": ")[-1]

def hex_xor(hex1, hex2):
	hex1 = bytes.fromhex(hex1)
	hex2 = bytes.fromhex(hex2)
	res = b""
	for x, y in zip(hex1, hex2):
		res += (x^y).to_bytes()
	return res.hex()

# Prepare the parameters
empty_bytes = "00000000000000000000000000000000"
invalid_block = "000000000000000000000000000000ff"
ciphertext = encrypt(empty_bytes)

# Get key and flag
dec_key = receive(ciphertext + ciphertext + invalid_block)[32:64]
key = hex_xor(dec_key, ciphertext)
flag = bytes.fromhex(get_flag(key))

print(flag)

Flag: crypto{50m3_p30pl3_d0n7_7h1nk_IV_15_1mp0r74n7_?}

Triple DES

Ek1(Ek2(m)) = m

Now, since we're dealing with triple DES, we can use a pair of weak keys for the first two encryption. For the third encryption, use half of the pair. Then, we encrypt the encrypted flag with a new set of keys. The first key being the other half, and the second and third key being another pair of semi-weak keys. In the end, we will be left with the flag's plaintext.

import requests

url = "https://aes.cryptohack.org/triple_des/"

def encrypt(key, plaintext):
    response = requests.get(url + "encrypt/" + key + "/" + plaintext)
    return response.json()['ciphertext']

def encrypt_flag(key):
    response = requests.get(url + "encrypt_flag/" + key)
    return response.json()['ciphertext']

# Prepare the keys
DES_SEMI_WEAK_KEY_1 = "011f011f010e010e"
DES_SEMI_WEAK_KEY_2 = "1f011f010e010e01"
key1 = DES_SEMI_WEAK_KEY_1 + DES_SEMI_WEAK_KEY_2 + DES_SEMI_WEAK_KEY_1
key2 = DES_SEMI_WEAK_KEY_2 + DES_SEMI_WEAK_KEY_1 + DES_SEMI_WEAK_KEY_2

# Encrypt the flag
flag = encrypt_flag(key1)
flag = encrypt(key2, flag)

print(bytes.fromhex(flag))

Flag: crypto{n0t_4ll_k3ys_4r3_g00d_k3ys}

Challenge website:

Challenge website:

Challenge website:

Challenge website:

DES is known to have several weak keys and semi-weak keys (more on ). These keys can make DES encryption act as decryption. For example using a pair of semi-weak keys, this property holds:

🏴
🌐
https://aes.cryptohack.org/ecb_oracle/
https://aes.cryptohack.org/flipping_cookie/
https://aes.cryptohack.org/lazy_cbc/
https://aes.cryptohack.org/triple_des/
https://en.wikipedia.org/wiki/Weak_key
https://aes.cryptohack.org/ecbcbcwtf/