Skip to main content

Всеми забытый: writeup

Ссылка на задачу http://f8tasks.ru/challenges#%D0%92%D1%81%D0%B5%D0%BC%D0%B8%20%D0%B7%D0%B0%D0%B1%D1%8B%D1%82%D1%8B%D0%B9...-18

Всеми забытый

CategoryCrypto
DifficultyEasy
EncodingBase58
Flagflag{forg3t_br0th3r}

Recon

«Всеми забытый… Думай, что с этим можно сделать… Он был такой же как все его братья»

Единственный артефакт задачи — строка:

2RmC5owouixWFsJsWW5jKTgBSBS4

Первичный анализ:

  • длина 28 символов
  • только буквы и цифры — нет +, /, =, -, _
  • нет символов 0, O, I, l

Формулировка «братья» прямо указывает на семейство base-кодировок. Отсутствие 0/O/I/l — характерный признак: именно эти символы исключены в Base58 как визуально неоднозначные.


Base58 Identification: анализ алфавита

Сравнение популярных base-кодировок по структурным признакам:

КодировкаАлфавитПаддингСпецсимволы
Base64A–Z, a–z, 0–9, +, /=+, /
Base32A–Z, 27=нет
Base620–9, A–Z, a–zнетнет
Base581–9, A–Z без O, a–z без 0/I/lнетнет

Отличие Base58 от Base62: оба используют только буквы и цифры, но Base58 исключает 0, O, I, l. Наша строка содержит J (есть в Base58) и не содержит 0, O, I, l — полное совпадение.

Алгоритм декодирования — накапливаемая сумма по основанию 58:

num = 0
для каждого символа:  num = num × 58 + index(символ в алфавите)

Ручной расчёт первых четырёх символов строки 2RmC...:

#СимволИндексnum после шага
0211
1R241 × 58 + 24 = 82
2m4482 × 58 + 44 = 4800
3C114800 × 58 + 11 = 278411

После прохода всех 28 символов получаем большое целое число, которое конвертируется в байты: num.to_bytes((num.bit_length() + 7) // 8, "big").


Decryption

ШагДействиеДетали
1Проверить алфавитвсе символы строки входят в Base58
2Построить числоnum = 0; for ch: num = num*58 + idx(ch)
3Конвертировать в байтыnum.to_bytes((num.bit_length()+7)//8, "big")
4Декодировать UTF-8decoded.decode("utf-8")

Результат:

flag{forg3t_br0th3r}

Automation

#!/usr/bin/env python3

ALPHABET58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"
ENCODED = "2RmC5owouixWFsJsWW5jKTgBSBS4"


def b58decode(value: str) -> bytes:
    number = 0
    for char in value:
        if char not in ALPHABET58:
            raise ValueError(f"invalid Base58 character: {char!r}")
        number = number * 58 + ALPHABET58.index(char)

    decoded = number.to_bytes((number.bit_length() + 7) // 8, "big")
    leading_zeros = len(value) - len(value.lstrip("1"))
    return b"\x00" * leading_zeros + decoded


def main() -> None:
    decoded = b58decode(ENCODED)
    print(decoded.decode("utf-8"))


if __name__ == "__main__":
    main()
python3 solve.py
# flag{forg3t_br0th3r}

Key Takeaways

  1. Кодирование ≠ шифрование. Base58 полностью обратимо без ключа: это смена представления, а не защита. Флаг не спрятан — он просто записан другим алфавитом.

  2. Алфавит — главная улика. Отсутствие 0, O, I, l однозначно выделяет Base58 среди похожих кодировок. Анализ набора символов сужает гипотезы быстрее любого инструмента.

  3. Исключённые символы — не случайность. Base58 придумали для Bitcoin-адресов, чтобы люди не путали похожие символы при ручном вводе. Понимание контекста происхождения формата помогает быстрее его опознать.