Первым делом просканируем порты на 10.124.249.9 с помощью nmap.

Nmap scan report for 10.124.249.9
Host is up, received syn-ack (0.0074s latency).
Scanned at 2024-09-16 15:11:41 EDT for 17s
Not shown: 65531 closed tcp ports (conn-refused)
PORT     STATE SERVICE REASON  VERSION
22/tcp   open  ssh     syn-ack OpenSSH 8.4p1 Debian 5+deb11u1 (protocol 2.0)
80/tcp   open  http    syn-ack nginx 1.25.1
3306/tcp open  mysql   syn-ack MySQL 5.7.43
8082/tcp open  http    syn-ack Apache httpd 2.4.57 ((Debian))
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

На машинке 4 открытых порта ssh, вебчик, инстанс мускуля и phpmyadmin (админка для управления бд).

Information disclosure

После первичного изучения сайта corp-wiki.standalone.stf понимаем, что нам необходимо получить какую-то учетку. Так как свою создать пока не получается в связи с отсутствием invite code, будем брутить чужие!) Посмотрев авторов статьей и побрутив учетки стандартным листом, находим первую точку входа - креды:

  • emiwil:1234

Перебрав параметр id (см. скриншот выше), выясняем, что у нас есть доступ к публикациям roblee и michjohn

  1. У michjohn мы видим статью “Report about bug in software”

Внутри которой находится какой-то стектрейс с url-ом, видимо для какого-то локального сервиса: http://searchserver:8000/api/data. Он нам понадобится чуть позже.

File "main.py", line 47, in <module>
    data = fetch_data("http://searchserver:8000/api/data")
  File "main.py", line 23, in fetch_data
    response = requests.get(url)
  File "/usr/local/lib/python3.9/site-packages/requests/api.py", line 76, in get
    return request('get', url, params=params, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/requests/api.py", line 61, in request
    return session.request(method=method, url=url, **kwargs)
  File "/usr/local/lib/python3.9/site-packages/requests/sessions.py", line 516, in request
    prep = self.prepare_request(req)
  1. У roblee находим статью “Request: Get access back!“.

В ней лежит какой-то пароль:

Данный пароль к вики не подходит, но мы узнали ранее, что у сайта торчит phpmyadmin на 8082 порту.

Логинимся в phpmyadmin

Попробуем залогиниться в phpmyadmin с кредами, которые нашли у roblee.

Отлично! У нас получилось, теперь мы можем создать invite code, чтобы зарегистрировать своего пользователя. Здесь есть один момент, если у инвайта в поле isUsed значение 0 - его можно использовать, если 1 - использовать нельзя, логично. Поэтому новый инвайт лучше использовать как можно скорее, пока кто-то хитрый не стащил :)

Поиск дальнейших векторов

Пока первая половина нашей команды пыталась понять, как раскрутить инвайт коды, вторая во всю ломала интерфесйс вики.

Даже получилось залить php cmd shell, но он не отработал. А кто сказал, что будет легко?

Вики было чень плохо…

Также по пути встречались подозрительные XSSки других участников:

Практически все формы ввода на сайте были уязвимы к XSS’ам, но применить это так и не удалось. Когда-нибудь маленький XSS вырастет в большой Steal admin Cookie и станет полноценным вектором Киберполигона… Но не в этот раз.

В какой-то момент вики совсем поплохело.

WARNING! Видео небезопасно для людей, страдающих эпилепсией.

SSRF в подгрузке аватарок

Итак, возращаемся к решению. Мы обнаружили, что после создания инвайта появляется возможность регистрировать новых пользователей. У новых пользователей добавляется функционал изменения слогана, имени пользователя и картинки пользователя. Первые две функции оказались неуязвимы, а вот если при смене аватара обратиться на какой-то адрес, будь то http listener или внутренний адрес, сайт будет вести себя странно.

Поведение у этого сервиса такое:

  1. мы можем передать в поле url адрес, и сервис обратится к нему без фильтрации пользовательского ввода;
  2. отправится POST запрос на сервер;
  3. либо аватарка будет подгружена, либо мы получим ошибку “This is not a picture, check the link please.”

Оказалось, мы можем отправлять запросы локальной файловой системе и читать файлы:

На этом моменте можно быстренько реализовать SSRF

Идём дальше. Если мы обратимся ко внутреннему сервису searchserver (адрес которого засветился в стектрейсе выше), то получим подсказку, что раскрутить вектор предстоит через API:

После чего пробуем побрутить пути для searchserver и находим файлик openapi.json, который расскажет нам, что этот сервис использует питоновский фреймворк Fast API и даже отдаст один путь!)

Когда мы направляемся по пути, который нам отдал openapi.json /api/articles/search/req, нас встречает ошибка:

<span class="debug">
    {"detail": [{"loc":["query","req"],"msg":"field required","type":"value_error.missing"}]}
</span>

Это дает нам понять, что не хватает одного query параметра req, передав в который подстроку, мы получаем лист из интов (правда нам всегда возвращается не полный ответ, а только кусок). Как мы выяснили позже, возвращается список id статей, в заголовках которых встречается подстрока из параметра req.

SQL injection в req параметре

Достаточно долго мы отчаянно пытались набрутить новый endpoint или вытащить что-то из существующего, пока не решили пихнуть кавычку во всё тот же req:)))

Жаль только корп-вики оказалась очень нежной, поэтому все попытки сканирования sqlmap’ом заканчивались падением сервера.

Пришлось крутить лапками ручками. Ну первым делом пробуем union based sql injection и пытаемся вытащить таблички. Предварительно дважды проэнкодив с помощью urlencoded наш payload:

' union select table_name from information_schema.columns -- -

Сначала мы можем реализовать уязвимость:

SQLi на узле corp-wiki.standalone.stf (10.124.249.9) Получите содержимое ячейки flag из таблицы secret.

Потому что она тоже в параметре req:

f'Union  select FLAG from secret --;

Также отправляем в urlencoded на два раза:

Как и было описано ранее, мы видим не весь ответ сервера, а только его часть, а значит для получения названий нужных нам табличек можем воспользоваться OFFSET и LIMIT

Найдя таблицу users и посмотрев, что в ней есть колонки name и pass, составим финальный запрос для извлечения кредов админа

' union select concat(name, ':', pass) from users limit 1 offset 0 --;

Заходим с этими кредами на аккаунт админа и видим, как в одной из статей будет лежать нужный нам флаг для критического события!

На этом мучения на wiki.standalone.stf закончились, всем спасибо!)

Tags:

#standoff#writeup