Chaos
Chaos
Platform: CUSTOM | Category: Reversing | Type: Challenge | Difficulty: Medium | OS: N/A | **Author: XESXOR Techniques: dictionary_bruteforce, dotnet_decompilation, emoji_aes_decoding, hidden_input_trigger_discovery, openssl_aes_decryption
Summary
Walkthrough of the CUSTOM Reversing Chaos. Task: a .NET WinForms app hides an emoji-only ciphertext behind a secret keyboard trigger and clipboard copy path. Solution: decompile Form1, trigger the hidden CHEAT branch, convert the emoji-aes tex
Description
Хаос
English summary: the archive contains Chaos.exe, Chaos.dll, and Chaos.runtimeconfig.json. The executable is a .NET WinForms application. The goal is to reverse the program, recover the hidden ciphertext, identify the encryption scheme, and decrypt it to obtain the CODEBY{...} flag.
Analysis
Initial Recon
The provided ZIP archive contained:
Chaos.exeChaos.dllChaos.runtimeconfig.json
Because this is a .NET application, the fastest route is decompilation instead of low-level PE analysis. Decompiling the project and reviewing Emoji.Form1 / Form1.cs immediately revealed the interesting UI logic.
Hidden Keyboard Trigger
The key function is Zero_KeyPress. It appends typed characters into an internal buffer and checks for a hardcoded trigger:
csharp read-only • copy allowedif (inputBuffer.Contains("CHEAT"))
When that condition becomes true, the application enters the hidden win path:
- it shows
WIN - it copies
label1.Textto the clipboard
So the actual secret is not generated dynamically. It is already stored inside the form as label text and is only exposed after typing CHEAT.
Recovered Ciphertext
label1.Text contained this 88-emoji ciphertext:
text read-only • copy allowed🙃💵🌿🎤🚪🌏🐎🥋🚫😆✅😊👌🏹🕹👌🍌🎃🎤👌🍴✉🌪☺👁✉🔄👁⏩🌪🎤🍵🍎🖐🔬🌪🍌🚨😍🐎😁🔪❓🐅✅📂🕹👑🐅👣☃ℹ🌊🖐🎅📮🏹👁🍎🙃🖐🎈🚪🌊🔬🐍🌿🖐😊😊🤣🕹😂📮☃🚪🍍🍎😀🎤😀😆⏩😁📂🏹🗒🗒
The user hint identified the scheme as emoji-aes. Using the standard public emoji-to-base64 alphabet used by emoji-aes implementations, the string decodes to:
text read-only • copy allowedU2FsdGVkX1+5oANob8soKlH4elReZHsJahSHbL9V0/YT+ONqTfIiWhDMAeaUhBdWSvFh553NjMIdpagsg1Z0OA==
That prefix is the biggest clue: U2FsdGVkX1 is Base64 for Salted__, which is the classic OpenSSL / CryptoJS salted AES format.
Passphrase Recovery
At this point the problem becomes a passphrase search against a standard salted AES blob. A local dictionary attack against:
text read-only • copy allowed/Users/sergeyskorobogatov/Projects/Agents/CTF/resources/wordlists/passwords/2025-top200.txt
found the correct hit at line 55:
- passphrase:
666666
Decrypting the ciphertext with that passphrase yielded the final flag.
Solution
Step 1 — Decompile the WinForms Application
Open the .NET binary in ILSpy or a similar decompiler and inspect Emoji.Form1. The important logic is in Zero_KeyPress, where the program accumulates user keystrokes and looks for CHEAT.
Step 2 — Trigger the Hidden Win Branch
Typing CHEAT activates the secret branch. That branch displays WIN and copies label1.Text to the clipboard. The clipboard content is the real secret material to analyze.
Step 3 — Decode the Emoji Ciphertext
The clipboard data is not random emoji art. It matches the public emoji-aes alphabet, where each emoji corresponds to one Base64 character. Converting the 88 emoji symbols with that mapping gives a normal Base64 ciphertext in OpenSSL/CryptoJS salted format.
Step 4 — Brute-Force the Passphrase
Try candidate passwords from the local wordlist against the decoded AES blob. The successful password is 666666, which decrypts the payload directly to the flag.
python read-only • copy allowed#!/usr/bin/env python3 from base64 import b64decode from hashlib import md5 from Crypto.Cipher import AES from Crypto.Util.Padding import unpad EMOJI_CT = "🙃💵🌿🎤🚪🌏🐎🥋🚫😆✅😊👌🏹🕹👌🍌🎃🎤👌🍴✉🌪☺👁✉🔄👁⏩🌪🎤🍵🍎🖐🔬🌪🍌🚨😍🐎😁🔪❓🐅✅📂🕹👑🐅👣☃ℹ🌊🖐🎅📮🏹👁🍎🙃🖐🎈🚪🌊🔬🐍🌿🖐😊😊🤣🕹😂📮☃🚪🍍🍎😀🎤😀😆⏩😁📂🏹🗒🗒" OPENSSL_B64 = "U2FsdGVkX1+5oANob8soKlH4elReZHsJahSHbL9V0/YT+ONqTfIiWhDMAeaUhBdWSvFh553NjMIdpagsg1Z0OA==" WORDLIST = "/Users/sergeyskorobogatov/Projects/Agents/CTF/resources/wordlists/passwords/2025-top200.txt" def evp_bytes_to_key(password: bytes, salt: bytes, key_len=32, iv_len=16): out = b"" prev = b"" while len(out) < key_len + iv_len: prev = md5(prev + password + salt).digest() out += prev return out[:key_len], out[key_len:key_len + iv_len] def decrypt_openssl_blob(blob_b64: str, password: str) -> bytes: blob = b64decode(blob_b64) assert blob.startswith(b"Salted__") salt = blob[8:16] ciphertext = blob[16:] key, iv = evp_bytes_to_key(password.encode(), salt) cipher = AES.new(key, AES.MODE_CBC, iv) return unpad(cipher.decrypt(ciphertext), 16) def main(): print(f"Recovered emoji ciphertext length: {len(EMOJI_CT)}") print(f"Decoded emoji-aes blob: {OPENSSL_B64}") with open(WORDLIST, "r", encoding="utf-8") as fh: for idx, line in enumerate(fh, 1): password = line.rstrip("\n") if not password: continue try: pt = decrypt_openssl_blob(OPENSSL_B64, password) except Exception: continue text = pt.decode("utf-8", errors="strict") if text.startswith("CODEBY{") and text.endswith("}"): print(f"Hit at line {idx}: {password}") print(text) return raise SystemExit("Password not found") if __name__ == "__main__": main()
Expected output:
text read-only • copy allowedHit at line 55: 666666 CODEBY{REDACTED}
Key Takeaways / Lessons
- Mastering dictionary_bruteforce, dotnet_decompilation, emoji_aes_decoding, hidden_input_trigger_discovery, openssl_aes_decryption was key to this challenge.
- Always check for common misconfigurations and verify assumptions.
Crafted by XESXOR Research • 2026-04-06 • CUSTOM • Chaos • Difficulty: Medium