3.5 KiB
ZCRX Broker
ZCRX Broker управляет буферами приёма без копирования. В раздатке есть ELF zcrx-broker без PIE. У сервиса собственный пул памяти. Два отложенных события могут вернуть один блок в список свободных дважды. Добиваемся выделения одного адреса двум объектам. Тогда запись в новый blob изменит поля действующего объекта прав.
Решение
Создаём niov view через пункт 1, label любой. Новый view имеет refs = 1. Для view 0 дважды вызываем 2) arm lifecycle event: сначала путь scrub, затем refill. Оба события запоминают одно и то же состояние с одной ссылкой.
В обоих снимках счётчик ссылок равен единице. Поэтому при выборе 3) drain lifecycle events сервис обрабатывает оба события и дважды возвращает один и тот же блок в пул. В списке свободных теперь две записи с одним адресом — double free в самописном аллокаторе.
Выделяем capability через пункт 4: она занимает первую запись. Вторую пока оставляем в пуле. У capability есть закодированный обработчик, который можно прочитать через 5) inspect capability, cap 0. Значение encoded_handler сохраняем числом в encoded_audit.
Обработчик по умолчанию — audit_handler, кодирование — XOR с pointer guard. Адреса функций берём из readelf -sW public/zcrx-broker, поскольку PIE выключен. Восстанавливаем guard и кодируем адрес экспорта:
ptr_guard = encoded_audit ^ audit_handler
encoded_export = export_handler ^ ptr_guard
Пункт 6) allocate blob заберёт вторую запись того же блока. Blob и живая capability окажутся в одной памяти. Сначала готовим 176-байтную подделку.
import struct
payload = struct.pack("<QQQ", 0x4341505245433031, encoded_export, 0x4558504f52542121)
payload += b"/broker/export\x00".ljust(48, b"\x00")
payload = payload.ljust(176, b"\x00")
Выбираем 6) allocate blob, на запрос blob bytes: отправляем строку 176, затем на blob payload: передаём payload без добавленного перевода строки. Его поля задают magic, encoded_handler, rights и нуль-терминированный путь. Пункт 7) commit capability, cap 0, декодирует обработчик тем же guard. Проверки проходят, управление уходит в export_handler, и сервис печатает флаг.
Флаг
caplag{double_returned_niov_shared_the_capability}