50 lines
3.6 KiB
Markdown
50 lines
3.6 KiB
Markdown
<h1 align="center">FlowBase</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>
|
||
|
||
В платформе автоматизации FlowBase собираем потоки из нод и запускаем их вебхуками. Флаг лежит в `/flag.txt`, доступном служебному пользователю `flowsvc`. В сервисе открыта регистрация. Начинаем с создания учётной записи.
|
||
|
||
## Решение
|
||
|
||
`robots.txt` подсказывает каталог `/dl/`. Файл, записанный в этот каталог, можно будет забрать через веб. Регистрируемся через `POST /api/register` с JSON-полями `username` и `password`. Из поля `token` ответа получаем bearer-токен и передаём его в `Authorization: Bearer <token>` при чтении потоков и их версий. `GET /api/flows` показывает только потоки текущего пользователя. Сразу после регистрации список пуст.
|
||
|
||
Зато `GET /api/flows/{id}/versions` владельца не проверяет. Перебираем числовые ID от 1 до 40 с полученным токеном и находим чужой `backup-daily`. В JSON версий находим ноду `system.run` и берём `webhook_uuid` соответствующей версии. Проверяем её параметры.
|
||
|
||
| Поле | Почему интересно |
|
||
|---|---|
|
||
| `owner = admin` | Поток принадлежит администратору |
|
||
| `webhook_uuid` | Адрес запуска потока |
|
||
| Нода `system.run` | Выполняет `curl $CURL_OPTS $BACKUP_URL` |
|
||
|
||
IDOR дал UUID вебхука. Проверка его ключа отключена через `WebhookAuthEnable=false`, поэтому `POST /webhooks/{uuid}/run` запускает поток без авторизации. Тело запроса позволяет задать `env` для нод.
|
||
|
||
Сервис проверяет, что первое слово команды равно `curl`, но аргументы берёт из переданных переменных. Подставляем локальный источник и файл назначения:
|
||
|
||
```json
|
||
{
|
||
"env": {
|
||
"BACKUP_URL": "file:///flag.txt",
|
||
"CURL_OPTS": "-s -o /app/static/dl/leak.txt"
|
||
}
|
||
}
|
||
```
|
||
|
||
Отправляем этот JSON в `POST /webhooks/{uuid}/run` с `Content-Type: application/json`, без заголовка авторизации. На сервере получается:
|
||
|
||
```bash
|
||
curl -s -o /app/static/dl/leak.txt file:///flag.txt
|
||
```
|
||
|
||
Процесс `flowsvc` может прочитать флаг и записать файл в каталог статики. Вывод ноды в ответ вебхука не попадает. Сохранённый результат забираем через `GET /dl/leak.txt`.
|
||
|
||
Доступ к версиям чужого потока раскрывает UUID, которого достаточно для запуска. Переданные переменные окружения становятся аргументами `curl`, позволяя скопировать закрытый файл в доступный каталог.
|
||
|
||
[Солвер](solve/solve.py).
|
||
|
||
## Флаг
|
||
|
||
`CAPLAG{flowb4s3_webhook_def4ult_0ff}`
|