← Back to Writeups
CUSTOMMediumCrypto

Кастом (Custom)

XESXOR9/10/20263 min read
#crypto#custom#medium#known-plaintext-attack-using-f#substitution-cipher-reversal#leetspeak-pattern-recognition

Кастом (Custom)

Platform: hackerlab | Category: Crypto | Type: Challenge | Difficulty: Medium | OS: NA | Author: XESXOR | Date: 2026-01-02 | Status: Solved Techniques: Known Plaintext Attack using flag format, Substitution cipher reversal, Leetspeak pattern recognition

Summary

Files: - 5d41402abc4b2a76b9719d911017c592.py - encryption script - encrypted.txt - encrypted text

Кастом (Custom) — hackerlab

Description

"Иногда полезно придумывать что-то своё. Это развивает креативность в человеке."

(Translation: "Sometimes it's useful to create something of your own. It develops creativity in a person.")

Files:

  • 5d41402abc4b2a76b9719d911017c592.py - encryption script
  • encrypted.txt - encrypted text

Analysis

Encryption Script

python
import string
import random
import sys

def generate_key(entrypoint):
    random.seed(entrypoint)
    letters = list(string.ascii_lowercase)
    key = list(letters)
    random.shuffle(key)
    return dict(zip(letters, key))
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

def encrypt(key, message):
    cipher = ""
    for c in message:
        if c in key:
            cipher += key[c]
        else:
            cipher += c
    return cipher

entrypoint = sys.argv[0][::-1].upper()
key = generate_key(entrypoint)

FLAG = "CODEBY{REDACTED}".lower()

cipher = encrypt(key, FLAG)

with open('encrypted.txt', 'w') as file:
    file.write(cipher)

Key Observations

  1. Substitution cipher - simple letter substitution, numbers and symbols are not encrypted
  2. Seed depends on filename - sys.argv[0][::-1].upper() = reversed script name
  3. Filename = MD5("hello") - 5d41402abc4b2a76b9719d911017c592
  4. Encrypted text: sgwahu{sfj70d_s1pm3v_1j_4kj0_fj3xf1} ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Solution Approach

Instead of bruteforcing the seed (which would require knowing the exact filename at runtime), used Known Plaintext Attack:

  1. Flag starts with codeby{ → ciphertext starts with sgwahu{
  2. This gives initial mapping: s→c, g→o, w→d, a→e, h→b, u→y

Solution

Step 1: Building Initial Mapping

Ciphertext: sgwahu{sfj70d_s1pm3v_1j_4kj0_fj3xf1}
Known:      codeby{..............................}

Mapping: s→c, g→o, w→d, a→e, h→b, u→y

Step 2: Partial Decryption

Applying the known mapping:

sgwahu{sfj70d_s1pm3v_1j_4kj0_fj3xf1}
codeby{c.s70d_c1.h3._1s_4.s0_.s3.f1}

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Step 3: Recognizing Leetspeak Patterns

Analyzing the partially decrypted text:

  • c.s70d → cus70m (custom) → f→u
  • c1.h3. → c1ph3r (cipher) → p→p, m→h, v→r
  • 1s → 1s (is) → j→s
  • 4.s0 → 4ls0 (also) → k→l
  • .s3.f1 → us3fu1 (useful) → x→f

Step 4: Complete Mapping

python
cipher_to_plain = {
    's': 'c', 'g': 'o', 'w': 'd', 'a': 'e', 'h': 'b', 'u': 'y',
    'f': 'u', 'j': 's', 'd': 'm', 'p': 'p', 'm': 'h', 'v': 'r',
    'k': 'l', 'x': 'f'
}

ciphertext = "sgwahu{sfj70d_s1pm3v_1j_4kj0_fj3xf1}"
plaintext = ""
for c in ciphertext:
    if c in cipher_to_plain:
        plaintext += cipher_to_plain[c]
    else:
        plaintext += c
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

print(plaintext)  # codeby{REDACTED}

Decrypted Message

REDACTED
custom cipher is also useful

The message matches the task description about the usefulness of creating something of your own!

Alternative Approach

Could have tried to recover the seed:

  1. Filename: 5d41402abc4b2a76b9719d911017c592.py
  2. Reversed: yp.295c26f8e710119d9179b67a2b4cba20414d5
  3. Uppercase: YP.295C26F8E710119D9179B67A2B4CBA20414D5

But Known Plaintext Attack turned out to be simpler and more reliable.

Lessons

‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

  • Custom cipher ≠ secure cipher - a custom cipher does not mean a secure one
  • Known Plaintext Attack is effective against substitution ciphers
  • Leetspeak helps guess words even with partial decryption
  • The filename (MD5 hash) was a red herring ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍