##Task
Figure out the logic of Super Secured SCADA Python Server and get the flags! ip: 119.81.178.102:1337
#300 SCADA Server 1 [network]
-
Подключаемся к серверу и пытаемся ввести некоторые стандартные команды. Мне удалось найти только
help
(который не выводил ничего полезного) иexit
. На Всё остальное получаем следующий ответ:Unknown command: ИМЯ_КОМАНДЫ
-
Автоматически переберем команды. Код команды смотри в brute_commands.py.
-
Находим команду
get
. Пробуем. Получаем ответ:No such file or directory
Очевидно, после команды должен указываться файл или директория для открытия. Пробуемget .
, получаем ответIs a directory
. -
Вспоминаем, что сервер на питоне. Перебираем стандартные имена, типа
__init__.py
,main.py
и... И находим server.py!
Первое задание решено.
#100 SCADA Server 2 [network]
Смотрим код server.py. После беглого просмотра видим
открытие файла private/flag_ki_ctf_2016_telnet.txt
. Очевидно, там флаг.
Мы помним, что команда get не дает нам открывать директории. Однако
если попробовать get flag_ki_ctf_2016_telnet.txt
, то выяснится,
что в текущей директории тоже есть файл с таким названием.
Задание решено.
#700 SCADA Server 3 [network]
- Находим в коде server.py обработку команды
login
. Работает это следующим образом: 0. Пользователь посылает команду login вместе с логином и паролем, разделенными пробелом. 0. Сервер объединяет логин и пароль символом:
и подает на вход функцииcalc_hash
. 0. Если результатcalc_hash
равен0xC84E20E52E25E5E8
, сервер возвращает флаг. - Функция calc_hash содержится в модуле
custom_crypt
. Получаем его с помощью командыget custom_crypt.py
.
Посмотрим содержимое функции:
def calc_hash(crc, s):
for byte in s:
crc = crc64_tab[(crc ^ ord(byte))&0xFF] ^ ((crc << 8)&0xFFFFFFFFFFFFFFFF)
return crc
Для простоты восприятия разобьем содержимое цикла на несколько строк:
def calc_hash(crc, s):
for byte in s:
tmp1 = (crc ^ ord(byte)) & 0xFF
tmp2 = crc64_tab[tmp1]
crc = tmp2 ^ ((crc << 8) & 0xFFFFFFFFFFFFFFFF)
return crc
Замечаем следующие вещи:
- Из-за сдвига crc на 8 бит влево (<< 8) и последующего логического умножения (&) на каждой итерации мы теряем 1 байт из старшей части.
- crc умножается на 8 байт, поэтому при обратном прохождении мы можем предсказать только 8 итераций функции.
- Т.к. crc сдвигается на байт влево, то 1 байт из младшей части заполнен нулям. Затем он складывается по модулю 2 (XOR) с tmp2, а это значит, что последние 8 баит tmp2 не изменяются, поэтому у итогового crc и tmp2 будет совпадать старший байт.
Исходя из сказанного выше, можно задать следующие требования к обратной функции: 0. Мы можем предсказать только последние 8 байт текста. Соответственно, после 8 итераций алгоритм должен завершать свою работу. 0. Мы можем отыскать требуемый tmp2 по младшему байту. tmp2 берется из crc64_tab, у некоторых значений которого последний байт совпадает. Исходя из этого необходимо рассматривать несколько вариантов, в зависимости от значения tmp2, выбранного на каждой итерации. 0. Также, очевидно, логин и пароль должны состоять только из печатных символов.
С учётом перечисленных требований напишем обратную функцию: crypt_hack.py.
В итоге получаем строку VOplmH2n
. Однако это только последние 8
символов. Переберём остальные через основную функцию:
crypt_brute.py
Получаем ответ:
login b hVOplmH2n
Отправляем полученную команду серверу. Задание решено.