Райтап на задачу «Зона риска»

lol

Автор:

lol

6 ноября 2026

7

06.11.2026
Зона риска
Категория:

Rev

Задача «Зона риска»

Kr3s5

Автор:

Kr3s5

Первая кровь:
motgyl
Сложность:
easy
Дата публикации:
23.05.2026
Решений:
43
Рейтинг:
5/5
Ссылка:
Решать

По классике смотрим на файл через file:

Решение задачи «Зона риска»: иллюстрация 1

Он стрипнутый((( Хочется плакать, но мы не сдаёмся и идём на разведку в гидру. В main мы видим следующее:

Решение задачи «Зона риска»: иллюстрация 2

То есть условно нам нужно ей сказать флаг, тогда она нас похвалит))) Что ж, постараемся. На входе у нас запрашивают те самые «заветные слова» для Пикси и копируют эти 27 байт в буфер.

Решение задачи «Зона риска»: иллюстрация 3

Отлично! Мы нашли keygen, теперь нужно разобраться, как он работает, а именно:

На входе ей передаётся указатель на буфер, далее он делает два прохода:

Первый проход — псевдослучайные числа.

for (int i = 0; i < 27; i++) {
    srand(i);
    raw[i] = rand() % 100;
}

То есть для каждого индекса i:

  1. Вызывается srand(i);

  2. Берётся rand();

  3. Результат берётся по модулю 100.

Как раз-таки из-за srand(i) ключ детерминированный:

Решение задачи «Зона риска»: иллюстрация 4

То есть сырые значения у нас получаются такие:

53 53 5a 2e 01 4b 29 4d 60 0f 5f 17 3c 5a 53 5d
2c 41 62 31 0b 38 46 02 49 3d 39

А вот уже второй проход — это перемешка с накоплением:

int sum = 0;
 
for (int i = 0; i < 27; i++) {
    sum += raw[i];
    key[i] = (raw[i] + sum) % 100;
}

Или же:

sum_i = sum_{i-1} + raw_i
key_i = (raw_i + sum_i) % 100

Из-за данных манипуляций каждый байт ключа зависит от всех предыдущих. И уже после второго прогона ключ равен:

42 31 2e 30 04 35 3c 49 58 16 61 30 2d 41 29 2c
27 19 38 38 1d 1e 0e 30 5c 29 5e

Это и есть наш key[27], который потом использует encrypt:

Решение задачи «Зона риска»: иллюстрация 5

Если упростить энкрипт, то мы получим что-то по типу такого:

input[i] --(+ key[i])--> t --(rol на key[i]&7)--> r --(xor key[i%10])--> target[i]

Отсюда мы знаем две из трёх переменных, а именно:

  1. Target[i] — те самые 27 байт из rodata;

  2. Key[i] — это уже 27 байт, которые заполняет keygen.

По факту известно всё, кроме флага. Поэтому мы идём в обратную сторону, отталкиваясь от известного, путь выглядит примерно так:

target[i] --(xor key[i%10])--> r --(ror на key[i]&7)--> t --(- key[i])--> input[i]

Поэтому мы можем провести полный цикл расшифровки, используя буквально 2 строчки на пайтоне:

flag = bytes(
    (ror8(target[i] ^ key[i % 10], key[i] & 7) - key[i]) & 0xff
    for i in range(27)
)

Имея абсолютно все данные на руках, можем написать простенький солвер на пайтоне и получить флажок:

key = bytes.fromhex(
    "42312e3004353c49581661302d41292c271938381d1e0e305c295e"
)

target = bytes.fromhex(
    "507c4b43815a4712d04bdd511a7133c0f56ccf7e336e8fafe87826"
)

def ror8(v, n):
    n &= 7
    return ((v >> n) | (v << (8 - n))) & 0xff

flag = bytes(
    (ror8(target[i] ^ key[i % 10], key[i] & 7) - key[i]) & 0xff
    for i in range(27)
)

print(flag)
print(flag.decode().rstrip())
Решение задачи «Зона риска»: иллюстрация 6

Важный момент, который представлен в main: он ожидает 27 байт, включая \n. А сам флаг состоит из 26 символов.

Решение задачи «Зона риска»: иллюстрация 7

Ну а вот и наш долгожданный флажок)))

Если вы нашли опечатку или ошибку, пожалуйста, выделите фрагмент текста и нажмите Ctrl+Enter

Также по теме:

Райтап на задачу «Bogosort»

/Разборы задач

Райтап на задачу «Bogosort»

Восстанавливаем флаг из значений синуса и анаграммы, не дожидаясь случайной сортировки.
Прочитать

5

Райтап на задачу «ЗаБАГованная башня»

/Разборы задач

Райтап на задачу «ЗаБАГованная башня»

Решаем ханойскую башню, исследуем сборку ключа и разбираем патч исполняемого файла.
Прочитать

4