/Разборы задач
6 ноября 2026
Райтап на задачу «Bogosort»
Восстанавливаем флаг из значений синуса и анаграммы, не дожидаясь случайной сортировки.5
7

Rev
Задача «Зона риска»
По классике смотрим на файл через file:
Он стрипнутый((( Хочется плакать, но мы не сдаёмся и идём на разведку в гидру. В main мы видим следующее:
То есть условно нам нужно ей сказать флаг, тогда она нас похвалит))) Что ж, постараемся. На входе у нас запрашивают те самые «заветные слова» для Пикси и копируют эти 27 байт в буфер.
Отлично! Мы нашли keygen, теперь нужно разобраться, как он работает, а именно:
На входе ей передаётся указатель на буфер, далее он делает два прохода:
Первый проход — псевдослучайные числа.
for (int i = 0; i < 27; i++) {
srand(i);
raw[i] = rand() % 100;
}То есть для каждого индекса i:
Вызывается srand(i);
Берётся rand();
Результат берётся по модулю 100.
Как раз-таки из-за srand(i) ключ детерминированный:
То есть сырые значения у нас получаются такие:
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:
Если упростить энкрипт, то мы получим что-то по типу такого:
input[i] --(+ key[i])--> t --(rol на key[i]&7)--> r --(xor key[i%10])--> target[i]Отсюда мы знаем две из трёх переменных, а именно:
Target[i] — те самые 27 байт из rodata;
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())Важный момент, который представлен в main: он ожидает 27 байт, включая \n. А сам флаг состоит из 26 символов.
Ну а вот и наш долгожданный флажок)))