19 марта в рамках Московского Технологического Марафона прошел четвёртый этап Кибертона - соревнования по информационной безопасности в формате task-based CTF. Разбор некоторых заданий - в этой статье.
Заказчик попросил нас провести анализ его нового инновационного сайта.
Для этого он спрятал секретную информацию в файл flag.txt в корневой директории приложения.
Сможете ли вы обнаружить уязвимость и узнать содержимое этого файла?
Заходим на сайт, видим богатый(нет) функционал:
Внимательно изучаем красивых котиков ссылки:
?image_name=cat1.jpg
?image_name=cat2.png
?image_name=cat3.jpg
...
Замечаем, что это GET запрос в аргументы которого передаётся название файла. Это наводит на мысль о LFI (local file inclusion) уязвимости. Пробуем передать в параметр ../../../../etc/passwd
и получаем интересное сообщение от сайта:
Hacking attempt discovered. Viewing files upper than application directory is not permitted. This incident will be reported.
Ага, значит сервер запрещает выходить выше корневой директории приложения. Но так это нам и не надо, по условию flag.txt, который нам нужен, как раз находится там. Подбираем кол-во ../
пока сервер не перестанет сообщать о попытке взлома. В нашем случае корневая директория находится на один уровень выше наших файлов с котиками.
Переходим по ссылке http://45.143.94.76:3200/?image_name=../flag.txt
и получаем флаг.
Заказчик продолжает развивать свой сайт.
Теперь ещё более инновационный! И безопасный! (или нет)
Переходим на сайт и оцениваем масштаб инноваций:
Кнопка Sign in
оказывается не рабочей, зато теперь можно выбрать категорию котиков!
обращаем внимание на запрос:
http://45.143.94.76:3201/?cat_type=sad_cats
а также на структуру html страницы:
img src="static/sad_cats/cat3.jpg" alt="cool_cat" ...
...
img src="static/sad_cats/cat1.jpg" alt="cool_cat" ...
Теперь еще более внимательно:
?cat_type=sad_cats
src="static/sad_cats
/cat3.jpg"
Делаем вывод, что сервер рендерит все файлы в директории, которая передаётся в параметре GET запроса. Пробуем подставить вместо sat_cats
параметр ./
(отображение текущей директории) и получаем:
img src="static/./evil_cats" alt="cool_cat" ../
img src="static/./sad_cats" alt="cool_cat" ../
img src="static/./future_cats" alt="cool_cat" ../
...
img src="static/./fat_cats" alt="cool_cat" ...
Сервер пытается отрендерить буквально все, даже директории.
Стоп, вы тоже это видите?
Directory traversal уязвимость!
static/./future_cats
. Это же кошки из будущего! Смотрим что находится в этой директории:
url/?cat_type=future_cats
img src="static/future_cats/cat1.jpg" alt="cool_cat" ...
img src="static/future_cats/edff72b33bfc4973.txt" alt="cool_cat" ...
Замечаем подозрительный файл edff72b33bfc4973.txt
, переходим по ссылке site_url/static/future_cats/edff72b33bfc4973.txt
и получаем флаг.
Заказчик исправил все ранее найденные уязвимости и продолжает внедрять фичи!
Теперь он полностью уверен, что его сайт в абсолютной безопасности!
Убеди его в обратном.
Переходим по ссылке, видим старый добрый сайт с котиками, вроде ничего не изменилось. А нет, добавились формы входа и регистрации:
Сразу пробуем ковычку, но безрезультатно:
Username contains invalid characters!
Перебором понимаем, что запрещены ещё символы [ ] _
Пробуем зарегестрировать простого пользователя, например DIO
Обратим внимание на то, что появилась сессия
eyJ1c2VybmFtZSI6eyIgdCI6WyJESU8iLCJrYWJhY2hraSJdfX0.XnYRtQ.YDaC7kG-_ki8BLYn8lKPPlLoI1M
Декодируем её:
{
"username": {
" t": [
"DIO",
"kabachki"
]
}
}
Возвращаемся на главную страницу и видим Welcome, DIO
, а значит наш юзернейм подставился в html страницу.
<a href="http://tiny.cc/d7hilz">Welcome, DIO</a>
Препдполагаем SSTI (Server Side Template Injection) уязвимость. Учитывая, что сервер написан на фласке (видно в заголовках HTTP ответа - Server: Werkzeug/1.0.0 Python/3.7.4
) скорее всего шаблонизатором(движок, который рендерит html на бэкенде) является jinja.
Пробуем зарегестрировать юзера {{34.5*2}}
и:
Nice. Как говорится, вижу уязвимость
. Дальше усиленно гуглим и пробуем вывести все, что можем, учитывая фильтрацию символов при регистрации. Например {{config.items()}}
выводит список конфигурационных переменных:
Там и находился флаг.
P.S. внимательные участники заметили, что секретный ключ фласка также был доступен, и тем самым можно было подписать сессию и отправить уязвимость без фильтрации символов, что привело бы к получению доступа к серверу (RCE
)
Тот самый, уязвимый участок кода:
# let's pretend all html is processed with render_template_string
user = render_template_string(session["username"][0])
INSANE INSANE INSANE
https://yadi.sk/d/GM5yHpy-sW6oPw
скачиваем app.apk файл, запускаем его на устройстве(опционально):
Продолжаем расследование. Берём apktool
dex2jar
и jdgui
.
в итоге получаем исходный код приложения:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ImageView imageView = findViewById(R.id.imageDisplay);
imageView.setImageResource(R.drawable.hello);
// imageView.setImageResource(R.drawable.flag);
}
Замечаем комментарий imageView.setImageResource(R.drawable.flag);
. Идем в папку с ресурсами, которую нам дал apktool, находим файл flag.png
:
Вот это поворот! Ну стега и стега, смотрим что там внутри. Обращаем внимание на exif поля:
Artist - Ti dumal eto stega
Copyright - no eto bila ya
OwnerName - Crypta
ImageDescription - jppni://nmipotqz.ags/UiM1Wa3r
jppni://nmipotqz.ags/UiM1Wa3r
подозрительно похоже на url адрес, чем-то зашифрованный. Пробуем разные классические шифры(например на www.dcode.fr), в итоге подходит аффинный. (Догадаться можно было по остальным exif меткам: city - Affine
)
Получаем ссылку на пастебин:
https://pastebin.com/QsA1Uc3x
В ней находится код на эзотерическом языке программирования brainfuck. Вбиваем его в любой онлайн декодер и получаем:
ti dumal eto misc, no eto bil ya, admin!
ssh cyberthon@194.87.95.137 Ricardo2020
Заходим по ssh на сервер и сразу видим файлы:
data.txt
task.txt
Читаем task.txt - Найди в файле data.txt уникальную строчку
Решение - cat data.txt | sort | uniq -u
Вывод - 0KLRiyDQtNGD0LzQsNC7INGN0YLQviDQsNC00LzQuNC9PyDQndC+INGN0YLQviDQsdGL0Lsg0Y8sINCy0LXQsSEKaHR0cDovLzQ1LjE0My45NC43Njo0MjA4Lw==
Декодим base64:
Ты думал это админ? Но это был я, веб!
http://45.143.94.76:4208/
На сайте видим:
Here you get 624 random numbers. Can you predict 625 one?
624 числа через |
Но как же нам отгадать 625 число? Мы же не можем видеть будущее...
Или можем? Все дело в том, что random в питоне является псевдослучайным, а значит, набрав достаточно данных мы можем восстановить исходное состояние системы!
Код на питоне (понадобится библиотека randcrack
):
from randcrack import RandCrack
rc = RandCrack()
numbers = [] # все числа с сайта
for number in numbers:
rc.submit(number)
print(rc.predict_randint(0, 4294967291)) # верхней границей выбираем самое большое число выборки
Вводим предсказанное число и получаем флаг.