![](https://habrastorage.org/getpro/habr/upload_files/1a3/2ae/139/1a32ae139aabb0ea4048aa1f37caaaf4.jpg)
Привет, Хабр! Давно я не публиковал свои похождения по CTF — пора это исправить! За прошедшее время мне удалось попробовать себя в трех турнирах. В ImaginaryCTF, посвященном кибербезопасности, Umasscybersec, адаптированном под Губку Боба, и n00bzCTF, который сделали для опытных n00bz.
Категорий задач на турнирах было много. Среди них — OSINT, Reverse, Forensics, Pwn и Web. Последнее направление для меня самое интересное, поэтому его я и выбрал. Что из этого получилось, рассказываю под катом!
Readme
Задание
Попробуйте прочитать файл file.txt.
readme.chal.imaginaryctf.org →
Решение
1. Перехожу по ссылке и убеждаюсь, что она работает. Открываю код страницы.
![](https://habrastorage.org/getpro/habr/upload_files/205/1e1/51f/2051e151f4b9a7e1f36b2f8e6862441a.png)
2. В коде страницы ничего интересного — надо искать глубже. Запускаю dirsearch.
![](https://habrastorage.org/getpro/habr/upload_files/bb0/cf6/fc9/bb0cf6fc98cea73476298ef311d39cd7.png)
Сервер отвечает ошибкой 404 на запросы страниц flag.txt и index.html. А в файле default.conf с настройками nginx находится такая конфигурация:
server {
listen 80 default_server;
listen [::]:80;
root /app/public;
location / {
if (-f $request_filename) {
return 404;
}
proxy_pass http://localhost:8000;
}
}
В директиве location есть условие проверки наличия файла. Если запрашиваемый в URL файл существует, то сервер возвращает ошибку 404. Результаты поиска dirsearch и эта конфигурация подтверждают, что на сервере есть оба файла: flag.txt, index.html. Необходимо как-то обойти это условие — есть несколько вариантов.
3. Вручную или с помощью модуля Intruder в burpsuite можно перебрать предлагаемые варианты нагрузок. В результате вместо /flag.txt можно запросить страницу по такой форме:
GET /flag.txt/.
4. Сервер вернул внятный ответ — флаг в кармане!
![](https://habrastorage.org/getpro/habr/upload_files/49c/400/bad/49c400badec71346df9ab689ca929477.png)
Journal
Задание
Дорогой дневник, в этом приложении нет LFI.
journal.chal.imaginaryctf.org →
Решение
На страницы находятся лишь ссылки на пять файлов с текстами. Однако наибольший интерес вызывает URL:
http://journal.chal.imaginaryctf.org/?file=file1.txt
Имя запрашиваемого файла передается в параметре file, который потенциально может быть уязвимым.
1. Смотрю приложенные конфигурационные файлы. Начинаю с index.php.
![](https://habrastorage.org/getpro/habr/upload_files/a06/e1a/c54/a06e1ac5478ac2cde377504fbbccafca.png)
В нем находятся те самые ссылки на текстовые файлы, а также некая функция assert, которая вместе с strpos проверяет наличие многоточия в имени запрашиваемого файла. Вероятно, так реализована защита от cmd-инъекции. Далее просто проверяется наличие файла и отдается содержимое, если условие истинно.
2. По логике кода понятно, что можно попробовать обратиться по пути flag.txt. Иду смотреть Dockerfile.
![](https://habrastorage.org/getpro/habr/upload_files/a03/785/82e/a0378582e73d88e809997fb07bd4d1c7.png)
Самая интересная строка в файле выглядит так:
RUN mv /flag.txt /flag-’tr -dc A-Za-z0-9 < /dev/random | head -c 20’.txt
В имя файла flag.txt добавляется двадцать случайных символов. При этом сам файл с флагом находится в корневом каталоге. Значит, чтобы добраться до ОС сервера, необходимо работать с функцией:
assert("strpos('$file', '..') === false") or die("Invalid file!");
3. Так как передаваемое в параметр file значение используется в функции strpos без какой-либо валидации, можно провести php-инъекцию. Она будет выглядеть следующим образом:
‘).system(“ls”);//
В результате строка кода примет такой вид:
assert("strpos('‘).system(“ls”);//', '..') === false") or die("Invalid file!");
Все, что будет после знака комментария «//», программа проигнорирует. До него будет выполнено strpos(‘’).system(‘ls’). Кроме того, должна появиться ошибка выполнения функции assert, т. к. она принимает два параметра.
4. Пробую подать нагрузку:
![](https://habrastorage.org/getpro/habr/upload_files/59f/bd4/9ae/59fbd49aeb25169b52ed471469ba1d09.png)
Все работает — файлы текущего каталога отдаются.
5. Поднимаюсь по файловой системе до корневого каталога, где должен находиться файл с флагом.
![](https://habrastorage.org/getpro/habr/upload_files/159/462/468/15946246870f33dcd6145a4beaec1292.png)
Действительно, файл есть. Брутить имя, конечно, было бы очень долгим занятием.
6. Читаю файл и получаю заветный флаг!
![](https://habrastorage.org/getpro/habr/upload_files/c84/57b/090/c8457b090378ae0d665067817d01dd00.png)
Passwordless
Задание
Устали от хранения паролей? Не беспокойтесь! Этот сверхзащищенный веб-сайт не содержит паролей!
Решение
На странице задания встречает такая форма ввода логина:
![](https://habrastorage.org/getpro/habr/upload_files/21e/cae/116/21ecae116b0b05cac8c916032cabf1bf.png)
Если ввести любое имя, то происходит редирект.
![](https://habrastorage.org/getpro/habr/upload_files/0b8/3eb/310/0b83eb31025d3e7e9a2663ace0297913.png)
1. В коде страницы нет ничего интересного — иду в исходники. В app.py есть следующий скрипт маршрутизации запроса:
@app.route('/<uid>')
def user_page(uid):
if uid != str(uuid.uuid5(leet,'admin123')):
return f'Welcome! No flag for you :('
else:
return flag
Флаг отдается только в случае, если перейти по ссылке:
str(uuid.uuid5(leet,'admin123'))
В самом начале файла задается значение переменной leet:
global leet=uuid.UUID('13371337-1337-1337-1337-133713371337')
2. Пробую сформировать целевую ссылку — для этого в IDLE выполняю часть кода из конфигурационного файла:
import uuid
leet=uuid.UUID('13371337-1337-1337-1337-133713371337')
print(uuid.uuid5(leet,'admin123'))
На выходе получаю следующую строку:
3c68e6cc-15a7-59d4-823c-e7563bbb326c
3. Используя полученную строку, собираю новую ссылку и пробую обратиться по ней:
http://24.199.110.35:40150/3c68e6cc-15a7-59d4-823c-e7563bbb326c
Готово — сервер возвращает флаг: n00bz{1337-13371337-1337-133713371337-1337}.
Holesome Birthday Party
Задание
Тебя только что пригласили на день рождения Губки Боба! Но он решил испытать вашу дружбу, прежде чем выдать входной билет. Сможешь ли ты доказать пройти испытания и заслужить право попасть на вечеринку?
holesomebirthdayparty.ctf.umasscybersec.org →
Решение
Губка Боб ждет на своем дне рождения только настоящих друзей — надо обязательно попасть на тусовку!
1. Перехожу по ссылке, вижу первое задание:
![](https://habrastorage.org/getpro/habr/upload_files/f2d/b49/14d/f2db4914d127805ac61c0afd3fef89e5.png)
Похоже, необходимо поработать с HTTP-запросом и в заголовке User-Agent указать «Bikini Bottom»:
User-Agent: Bikini Bottom
![](https://habrastorage.org/getpro/habr/upload_files/540/470/83b/54047083b1a0c473300723827b66ba16.png)
Отлично! Первое задание пройдено.
2. Далее необходимо поменять дату в запросе через заголовок Date. Гуглю — оказывается, у Губки Боба день рождения 14 июля. Меняю дату на нужную.
![](https://habrastorage.org/getpro/habr/upload_files/9f9/3cb/c06/9f93cbc064d15ac4bd32bc358587e5b2.png)
Готово — перехожу к следующему заданию.
3. Теперь Боб хочет учить французский язык, поэтому просит говорить соответствующе.
![](https://habrastorage.org/getpro/habr/upload_files/a00/de1/b3d/a00de1b3daf28ab39a8c7d1a074b0c1f.png)
В HTTP-запросе язык указывается в заголовке Accept-Language — меняю на fr:
![](https://habrastorage.org/getpro/habr/upload_files/21a/9c6/57c/21a9c657c17189b7ecd022db26724436.png)
Прекрасно, двигаюсь дальше.
![](https://habrastorage.org/getpro/habr/upload_files/f00/3c7/8ee/f003c78eee3c6f35d87881f11eebc95e.png)
4. Именинник просит захватить печенье со вкусом шоколадной крошки. Пойду навстречу и добавлю в HTTP-запрос Cookie с требуемым значением.
![](https://habrastorage.org/getpro/habr/upload_files/026/e4d/fc2/026e4dfc20c0d8b974f062160e21c682.png)
Губка Боб выдал билет на тусовку, но что-то в нем не так…
![](https://habrastorage.org/getpro/habr/upload_files/37a/2c1/76b/37a2c176b851c4e2b6cd5a9f9be35e63.png)
В Cookie прилетает новое значение (Login=eyJsb2dnZWRpbiI6IGZhbHNlfQ==) — логин в BASE-64 кодировке. У Burpsuite есть модуль Decode, воспользуюсь им:
![](https://habrastorage.org/getpro/habr/upload_files/77d/92d/4e3/77d92d4e383fa22310a4c3bf9290bb46.png)
Меняю на true и отдаю в заголовке. Финальный HTTP-запрос (с учетом предыдущих заданий) будет следующим:
![](https://habrastorage.org/getpro/habr/upload_files/25b/44b/55d/25b44b55d2c3ebfd400fa641b7e7aff8.png)
Флаг получен — вечеринка тусовка с грустным Сквидвардом началась!
![](https://habrastorage.org/getpro/habr/upload_files/a22/7de/577/a227de577abb908beb326c2b24f6708a.png)
CTF турниры помогают расширить знания о, казалось бы, понятных функциях языков программирования или конфигурации nginx. А также позволяют поработать со структурой протокола HTTP.
Надеюсь, подобный разбор заданий поможет начинающим игрокам в CTF понять способы подхода к процессу поиска флагов. До встречи в следующих частях!
MrB4el
Довольно милый CTF с почти минимум надмозга, характерного для игр последнего десятка лет.
Спасибо за разбор!