Какой ты: writeup
Ссылка на задание http://f8tasks.ru/challenges#%D0%90%20%D0%BA%D0%B0%D0%BA%D0%BE%D0%B9%20%D1%81%D0%B5%D0%B3%D0%BE%D0%B4%D0%BD%D1%8F%20%D1%82%D1%8B?-33
Какой ты
| Category | Crypto |
| Difficulty | Easy |
| Cipher / Vulnerability | Nibble substitution (hex encoding) |
| Flag | flag{emoji_to_ascii} |
Recon
А какой сегодня ты?
😀😀 😀😂 😀🤩 😀😌 😌😈 😀😬 😀😭 😀🥴 😀🤬 😀🤠 😬🥴 😌😡 😀🥴 😬🥴 😀🤩 😌🥺 😀🥺 😀🤠 😀🤠 😌😭
Артефакт — строка из 20 токенов, каждый ровно 2 эмодзи, разделены пробелами. Ключевые наблюдения:
- в первой позиции — только 3 разных символа (
😀 😌 😬), во второй — 12 - асимметрия «мало / много» при длине пары → nibble-split: старший полубайт (0–7) и младший (0–f)
- 20 токенов = 20 байт — типичная длина CTF-флага
Nibble Substitution
Каждый byte флага записан в hex как два полубайта; каждый полубайт заменён эмодзи из независимой таблицы.
Формула: emoji_hi + emoji_lo → high_nibble || low_nibble → byte
Восстановление через Known-plaintext (префикс flag{):
Первые 5 байт в hex: 66 6c 61 67 7b.
| Токен | hex-байт | emoji_hi → nibble | emoji_lo → nibble |
|---|---|---|---|
😀😀 | 66 | 😀 → 6 | 😀 → 6 |
😀😂 | 6c | 😀 → 6 | 😂 → c |
😀🤩 | 61 | 😀 → 6 | 🤩 → 1 |
😀😌 | 67 | 😀 → 6 | 😌 → 7 |
😌😈 | 7b | 😌 → 7 | 😈 → b |
5 токенов дают 😀 → 6, 😌 → 7 для HIGH_NIBBLE и 5 значений LOW_NIBBLE — достаточно для гипотезы. Оставшиеся значения добираются из следующих токенов без противоречий.
Итоговые таблицы:
HIGH_NIBBLE:
| Эмодзи | nibble |
|---|---|
😀 | 6 |
😌 | 7 |
😬 | 5 |
LOW_NIBBLE:
| Эмодзи | nibble | Эмодзи | nibble |
|---|---|---|---|
😀 | 6 | 😭 | d |
😂 | c | 🥴 | f |
🤩 | 1 | 🤬 | a |
😌 | 7 | 🤠 | 9 |
😈 | b | 😡 | 4 |
😬 | 5 | 🥺 | 3 |
Decryption
| Шаг | Токен | hex | ASCII | Шаг | Токен | hex | ASCII |
|---|---|---|---|---|---|---|---|
| 1 | 😀😀 | 66 | f | 11 | 😬🥴 | 5f | _ |
| 2 | 😀😂 | 6c | l | 12 | 😌😡 | 74 | t |
| 3 | 😀🤩 | 61 | a | 13 | 😀🥴 | 6f | o |
| 4 | 😀😌 | 67 | g | 14 | 😬🥴 | 5f | _ |
| 5 | 😌😈 | 7b | { | 15 | 😀🤩 | 61 | a |
| 6 | 😀😬 | 65 | e | 16 | 😌🥺 | 73 | s |
| 7 | 😀😭 | 6d | m | 17 | 😀🥺 | 63 | c |
| 8 | 😀🥴 | 6f | o | 18 | 😀🤠 | 69 | i |
| 9 | 😀🤬 | 6a | j | 19 | 😀🤠 | 69 | i |
| 10 | 😀🤠 | 69 | i | 20 | 😌😭 | 7d | } |
Hex-строка: 666c61677b656d6f6a695f746f5f61736369697d → flag{emoji_to_ascii}
Automation
TOKENS = "😀😀 😀😂 😀🤩 😀😌 😌😈 😀😬 😀😭 😀🥴 😀🤬 😀🤠 😬🥴 😌😡 😀🥴 😬🥴 😀🤩 😌🥺 😀🥺 😀🤠 😀🤠 😌😭".split()
HIGH_NIBBLE = {
"😀": "6",
"😌": "7",
"😬": "5",
}
LOW_NIBBLE = {
"😀": "6",
"😂": "c",
"🤩": "1",
"😌": "7",
"😈": "b",
"😬": "5",
"😭": "d",
"🥴": "f",
"🤬": "a",
"🤠": "9",
"😡": "4",
"🥺": "3",
}
def decode(tokens: list[str]) -> str:
hex_bytes = "".join(HIGH_NIBBLE[first] + LOW_NIBBLE[second] for first, second in tokens)
return bytes.fromhex(hex_bytes).decode()
if __name__ == "__main__":
print(decode(TOKENS))
python3 solve.py
# flag{emoji_to_ascii}
Key Takeaways
- Nibble substitution. Один байт кодируется двумя символами из независимых алфавитов. Признак — асимметрия количества уникальных символов в первой и второй позиции пары.
- Known-plaintext attack. Стандартный префикс
flag{(5 байт) немедленно восстанавливает большую часть подстановочной таблицы без перебора. - Structural analysis. Длина токена, счётчик уникальных символов по позиции и сравнение с hex/base64/morse — стандартный первый шаг при анализе нестандартных артефактов.
