Крипто-квиз (Crypto Quiz)
Крипто-квиз (Crypto Quiz)
Platform: bug-makers | Category: Crypto | Type: Challenge | Difficulty: Easy | OS: NA | Author: XESXOR | Date: 2026-05-25 | Status: Solved Techniques: api_interaction, base64_decoding, hex_to_utf8, morse_decoding, rot13_decoding, single_byte_xor_bruteforce
Summary
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 the stated multi-byte key 'BUG'.
Крипто-квиз (Crypto Quiz) — Bug Makers CTF
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 questionsGET /api/question/{id}— get a specific question with ciphertextPOST /api/check-answer— submit an answer for the current questionPOST /api/next-question— advance to the next questionGET /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 read-only • copy allowedimport 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 read-only • copy allowedimport 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 read-only • copy allowedhex_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 read-only • copy allowed#!/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 read-only • copy allowed#!/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()))