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
  • Symmetry
  • Bean Counter
  • CTRIME
  1. Practice
  2. Cryptohack
  3. Symmetric Ciphers

Stream Ciphers

PreviousBlock Ciphers 1NextMathematics

Last updated 2 months ago

Symmetry

Challenge website:

OFB works like a basic stream cipher, using an XOR operation between the plaintext and a keystream to produce ciphertext. Because XOR is reversible, if you apply the same process to the ciphertext using the same keystream, you’ll get back the original plaintext.

In OFB sense, encrypting a plaintext twice will get you to the original plaintext.

import requests

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

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

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

# Get encrypted flag
ct = encrypt_flag()

# Encryption == Decryption
plaintext = ct[32:]
iv = ct[:32]
flag = encrypt(plaintext, iv)

print(bytes.fromhex(flag))

Flag: crypto{0fb_15_5ymm37r1c4l_!!!11!}

Bean Counter

Analyzing the code, we realize that the value for self.stup never changes. Consequently, all blocks are encrypted with the same key. We can get the key by XOR-ing the first block's ciphertext with its plaintext. The plaintext is known to us because the first 16 bytes of a PNG file is the same everywhere. After obtaining the key, we could just decrypt the rest of the blocks.

import requests

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

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

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()

# Encrypted image
encrypted = encrypt()

# Keystream is the same for all blocks
# To get key, XOR the 1st block ciphertext and the known plaintext
known_plaintext = "89504e470d0a1a0a0000000d49484452"
key = hex_xor(encrypted[:32], known_plaintext)

# XOR all blocks with the key to get the original image
encrypted_blocks = [encrypted[i:i+32] for i in range(0, len(encrypted), 32)]
plain = ""
for block in encrypted_blocks:
    plain += hex_xor(block, key)

# Create the resulting image
file = open("result.png", "wb")
file.write(bytes.fromhex(plain))
print("File result.png created!")

Flag: crypto{hex_bytes_beans}

CTRIME

In this challenge, we are given an oracle. The oracle takes our input and append it with the flag. Then, the combined data are compressed using zlib before being encrypted using the CTR mode.

from Crypto.Cipher import AES
from Crypto.Util import Counter
import zlib
import os

KEY = b"aaaaaaaaaaaaaaaa"
FLAG = "crypto{this_is_a_dummy_flag}"

def encrypt(plaintext):
    plaintext = bytes.fromhex(plaintext)
    iv = int.from_bytes(os.urandom(16), 'big')
    cipher = AES.new(KEY, AES.MODE_CTR, counter=Counter.new(128, initial_value=iv))
    compressed = zlib.compress(plaintext + FLAG.encode())
    encrypted = cipher.encrypt(compressed)
    return encrypted.hex()

random = "abcdefg"
known = "crypto{"
length_random = len(encrypt((random).encode().hex()))   # 82
length_known = len(encrypt((known).encode().hex()))     # 74

As we can see, encrypting crypto{ gave us shorter ciphertext, as the data was able to be compressed further because of the duplicate string. The logic for the exploit is that we could brute-force characters to append to known and choose the character that produces the shortest ciphertext.

However, in practice, it's not that simple. Most of the time, adding characters that are already in the flag should not even change the length of the original ciphertext. But of course, there are times where adding any character would result in adding the length of the ciphertext, no matter what character. For this, I make a modification to the code so that the code stops and ask for my own input on what I think the character should be (this would make sense if you run the code below). In addition, from trial and errors, I noticed that you should just shift the known characters instead of always appending them, because this can mess with the compression stuffs.

Anyways, here's the solver script.

import string
import requests

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

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

known = "crypto{"
length = len(encrypt((known).encode().hex()))
compiled = known

# Loop until the correct char is "}"
while True:
    correct_char = ""
    length = len(encrypt((known).encode().hex()))

    # Find a character who keeps the ciphertext length
    for c in string.printable:
        length_c = len(encrypt((known+c).encode().hex()))
        if length_c == length:
            print(f"Found!! c = {c} --- len_c = {length_c}")
            correct_char = c
            break

    # If it's not found / the string got longer anyways,
    # ... find the one that uniquely changes the length 
    # (the value different than the others)
    if correct_char == "":
        new_len = {}
        correct_char = []
        for c in string.printable:
            length_c = len(encrypt((known+c).encode().hex()))
            if length_c not in new_len.keys():
                new_len[length_c] = [c]
            else:
                new_len[length_c].append(c)
            print(f"c = {c}, length_c = {length_c}, length = {length}")

        # Check if a unique length exist
        try:
            assert (1 in [len(val) for val in new_len.values()])
            print("Unique character exists!")
        except:
            print("Unique character doesn't exist.")

        # Extract that unique character
        for val in new_len.values():
            if len(val) == 1:
                correct_char = val[0]
        
        # In case where a unique length doesn't exist, input the character manually
        inp = input("> ")
        if inp == "N":  # To cancel the whole process
            break
        else:
            correct_char = inp

    # Correct character is found, add it to compiled
    # Keep 'known' not more than 8 characters
    known += correct_char
    compiled += correct_char
    if len(known) >= 8:
        known = known[1:]
    print(known)

    # If end-of-flag found, stop code
    if correct_char == "}":
        break

# Print the flag
print(compiled)

Flag: crypto{CRIME_571ll_p4y5}

Challenge website:

Challenge website:

In zlib, there's a So, what happens if we input a known part of the flag to the encrypt function? Here, I replicated the code locally, and added a dummy flag for testing.

🏴
🌐
https://aes.cryptohack.org/symmetry/
https://aes.cryptohack.org/bean_counter/
https://aes.cryptohack.org/ctrime/
matching and replacement of duplicate strings with pointers.