← Back to Writeups
CUSTOMEasyCrypto

Крипто-квиз (Crypto Quiz)

XESXOR9/2/20263 min read
#crypto#custom#easy#api_interaction#base64_decoding#hex_to_utf8#morse_decoding#rot13_decoding#single_byte_xor_bruteforce

Крипто-квиз (Crypto Quiz)

Platform: CUSTOM | Category: Crypto | Type: Challenge | Difficulty: Easy | OS: N/A | **Author: XESXOR Techniques: api_interaction, base64_decoding, hex_to_utf8, morse_decoding, rot13_decoding, single_byte_xor_bruteforce

Summary

Walkthrough of the CUSTOM Crypto Крипто-квиз (Crypto Quiz). Task: Web-based crypto quiz with 5 questions on basic ciphers (base64, ROT13, hex, Morse, XOR). Solution: Decoded each cipher; the XOR question required single-byte brute-force (key 0x0B) instead of t

Description

Тестирование по базовым шифрам и кодированию данных. Тест из пяти вопросов.

English summary: A web-based quiz at http://31.207.77.216:4026 testing knowledge of basic ciphers and data encoding. Five questions must be answered correctly in sequence to receive the flag.

Analysis

The application is an Express.js backend serving a single-page quiz app with the following API:

  • GET /api/questions — list all questions
  • GET /api/question/{id} — get a specific question with ciphertext
  • POST /api/check-answer — submit an answer for the current question
  • POST /api/next-question — advance to the next question
  • GET /api/final-message — retrieve the flag (only after all 5 correct answers) ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

Session-based progress tracking via connect.sid cookie ensures questions must be answered sequentially. The flag is only returned from /api/final-message when all 5 answers are marked correct in the session.

Solution

Question 1 — Base64

  • Cipher type: base64
  • Ciphertext: VGV4dCBpbiBiYXNlNjQgZm9ybWF0IQ==
  • Answer: Text in base64 format!

Standard base64 decode:

python
import base64
base64.b64decode("VGV4dCBpbiBiYXNlNjQgZm9ybWF0IQ==").decode()
# → 'Text in base64 format!'

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

Question 2 — ROT13

  • Cipher type: rot13
  • Ciphertext: Guvf grkg vf rapelcgrq hfvat EBG13
  • Answer: This text is encrypted using ROT13
python
import codecs
codecs.decode("Guvf grkg vf rapelcgrq hfvat EBG13", "rot_13")
# → 'This text is encrypted using ROT13'

Question 3 — Hex (UTF-8 Russian text)

  • Cipher type: hex
  • Ciphertext: d0 ad d1 82 d0 be d1 82 20 d1 82 d0 b5 d0 ba d1 81 d1 82 20 d0 b2 20 d1 85 d0 b5 d0 ba d1 81 d0 b0 d1 85
  • Answer: Этот текст в хексах (Russian: "This text is in hex") ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍
python
hex_str = "d0 ad d1 82 d0 be d1 82 20 d1 82 d0 b5 d0 ba d1 81 d1 82 20 d0 b2 20 d1 85 d0 b5 d0 ba d1 81 d0 b0 d1 85"
bytes.fromhex(hex_str.replace(" ", "")).decode("utf-8")
# → 'Этот текст в хексах'

Question 4 — Morse Code

  • Cipher type: morse
  • Ciphertext: .... . .-.. .-.. ---
  • Answer: HELLO

Standard Morse code lookup: H E L L O.

Question 5 — XOR (the tricky one)

  • Cipher type: xor
  • Ciphertext: Nehdono+~xbel+SDY
  • Stated key: BUG
  • Answer: Encoded using XOR ‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

The stated key "BUG" is misleading. Multi-byte XOR with the cycling key B, U, G produces non-printable characters. The actual encryption used a single-byte XOR key of 0x0B (decimal 11).

python
#!/usr/bin/env python3
ciphertext = b"Nehdono+~xbel+SDY"

# Brute-force all 256 single-byte XOR keys
for key in range(256):
    result = bytes([b ^ key for b in ciphertext])
    try:
        decoded = result.decode('ascii')
        if decoded.isprintable():
            print(f"Key 0x{key:02x} ({key:3d}): {decoded}")
    except:
        pass
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Key 0x0B → "Encoded using XOR"

Output shows key 0x0B produces the only readable English text: Encoded using XOR.

Getting the Flag

After submitting all 5 correct answers via /api/check-answer, the /api/final-message endpoint returned the flag.

python
#!/usr/bin/env python3
import urllib.request
import json

BASE = "http://31.207.77.216:4026"

# Create session
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor())

answers = {
    1: "Text in base64 format!",
    2: "This text is encrypted using ROT13",
    3: "Этот текст в хексах",
    4: "HELLO",
    5: "Encoded using XOR",
}
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

for qid, answer in answers.items():
    data = json.dumps({"questionId": qid, "answer": answer}).encode()
    req = urllib.request.Request(
        f"{BASE}/api/check-answer",
        data=data,
        headers={"Content-Type": "application/json"},
    )
    resp = opener.open(req)
    print(f"Q{qid}: {json.loads(resp.read())}")

    if qid < 5:
        req = urllib.request.Request(
            f"{BASE}/api/next-question",
            data=b"{}",
            headers={"Content-Type": "application/json"},
        )
        opener.open(req)
‍​‌‌​​‌​​​‌‌​​‌​​​​‌‌​‌​​​​‌‌‌​​‌​​‌‌​​​‌​​‌‌‌​​​​​‌‌​‌‌​​​‌‌​​​​‍

# Get the flag
resp = opener.open(f"{BASE}/api/final-message")
print(json.loads(resp.read()))

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

Key Takeaways / Lessons

  • Mastering api_interaction, base64_decoding, hex_to_utf8, morse_decoding, rot13_decoding, single_byte_xor_bruteforce was key to this challenge.
  • Always check for common misconfigurations and verify assumptions.

Crafted by XESXOR Research • 2026-05-25 • CUSTOM • Крипто-квиз (Crypto Quiz) • Difficulty: Easy