Files
2026-09-17 00:50:07 +03:00

4.6 KiB
Raw Permalink Blame History

Launchlet

Web medium

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. Пробуем предъявить найденный токен:

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, сохраняя гостевой ключ и сессию:

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.

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-контекстом и возвращает третий флаг. Проверка идентификатора действия есть, а привязки аргументов к разрешённой области нет. В этой ветке, как и при чтении заметок, параметры клиента позволяют выбрать чужие данные.

Солвер.

Все этапы

Этап Флаг
1 caplag{preview_manifest_kept_the_receipt}
2 caplag{anon_key_needs_real_rls}
3 caplag{server_actions_need_scope_checks}