
CakePHP 5.4.2 — высокий — удалённый — cookie — Cookie jar HTTP-клиента сопоставляет домены как суффикс регулярного выражения
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · cakephp-cookie-suffix
Класс: Утечка / подмена cookie Доступ: Удалённый
CakePHP 5.4.2 (887b641) — Cake Software Foundation
CookieCollection::findMatchingCookies использует preg_match('/' . preg_quote($domain) . '$/', $host). Это суффикс строки, а не domain-match по RFC 6265. Cookie для api.partner.com отправляется на evilapi.partner.com. Cookie для bank.com отправляется на notbank.com.
Cookie::createFromHeaderString() также копирует Domain= из Set-Cookie без проверки соответствия хосту ответа. addFromResponse от attacker.test с Domain=victim.internal сохраняет этот cookie и впоследствии прикрепляет его к victim.internal.
Приложение, использующее Cake\Http\Client с cookie jar по умолчанию для нескольких хостов, может утечь сессионный cookie партнёра на суффиксно-спутанный соседний хост или подсадить cookie на внутренний хост после запроса к URL атакующего. other.com не совпадает. Это не баг cookie браузера. Стандартный cakephp/app сам по себе не обращается ко второму хосту.
| ID | CVE пока нет |
| Класс | Утечка / подмена cookie (domain-match по суффиксу регулярного выражения) |
| Доступ | Удалённый (приложение использует cookie jar Http Client для нескольких хостов) |
| CWE | CWE-565, CWE-201 |
| CVSS | High: 8.2 CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N |
| Продукт | CakePHP |
| Затронуто | 5.4.2 (887b641de9f7aba06071f98f17edcbc573cbcf63) |
| Аутентификация | приложение использует Cake\Http\Client с cookie jar по умолчанию для нескольких хостов |
| Лицензия | GNU Affero GPL v3.0 |
| Лаборатория | только 127.0.0.1 |
Требуется приложение, которое сохраняет cookie Cake\Http\Client для более чем одного хоста (OAuth, вебхуки, URL, предоставленный пользователем, второй API).
| Привилегия | Метод / sink | Доказано в лаборатории или выведено |
|---|---|---|
| Утечка сохранённого cookie на суффиксно-спутанный соседний хост | CookieCollection::addToRequest для https://evilapi.partner.com/ после cookie с доменом api.partner.com | Доказано в лаборатории. В запросе Cookie — sid=SECRET. RFC 6265 не дал бы совпадения |
| Та же утечка для регистрируемого суффикса | Домен cookie bank.com, хост запроса notbank.com | Доказано в лаборатории. В запросе Cookie — sid=SECRET |
| Негативный контроль | Хост запроса other.com | Доказано в лаборатории. Заголовок Cookie отсутствует |
| Подмена cookie для внутреннего хоста | addFromResponse для Set-Cookie: sid=x; Domain=victim.internal от https://attacker.test/ | Доказано в лаборатории. Сохранённый домен — victim.internal; следующий запрос к этому хосту отправляет sid=x |
| Фиксация сессии / украденная сессия партнёра при следующем вызове Client | Подсаженный или утёкший sid, используемый как сессия API | Выведено. Лаборатория останавливается на оракуле заголовка Cookie |
| Отправка на тот же хост по-прежнему работает | Домен cookie api.partner.com для хоста api.partner.com | Доказано в лаборатории. Ожидаемо, не баг |
Закрыто по этому остатку: other.com не совпадает. Cookie jar браузера — это не данный баг. Без шелла. Не неаутентифицированный RCE на скелетном приложении.
Я зафиксировал cakephp/cakephp 5.4.2 после GHSA-2qh5, GHSA-fxf7, GHSA-vjqc и заметки о filename= в 5.4.2. Сопоставление cookie в Http Client — это суффикс регулярного выражения:
$pattern = '/' . preg_quote($domain, '/') . '$/';
if (!preg_match($pattern, $host)) {
continue;
}
addFromResponse передаёт хост запроса только как значение по умолчанию. Атрибут Domain= из Set-Cookie перезаписывает его и сохраняется.
Уже зафиксированные ошибочные пути: трактовка этого как бага cookie браузера; утверждение, что other.com тоже совпадает; раздувание оракула заголовка Cookie до ATO без приложения, использующего jar для нескольких хостов. Театр: обратный шелл.
python3 cakephp-cookie-suffix-Abraxas-Labs.py
или
cd lab
./run.sh
Git clone тега 5.4.2 (887b641de9f7aba06071f98f17edcbc573cbcf63) на PHP 8.3. Loopback-оракул HTTP на 127.0.0.1:19420. Дополнительный HTTP-источник не требуется; оракул запускает CookieCollection из зафиксированной версии.
sid=SECRET домен api.partner.com. addToRequest https://evilapi.partner.com/ -> Cookie: sid=SECRET.https://other.com/ -> заголовок Cookie отсутствует.https://api.partner.com/ по-прежнему отправляет sid=SECRET.bank.com -> https://notbank.com/ отправляет sid=SECRET.addFromResponse от https://attacker.test/ с Set-Cookie: sid=x; Domain=victim.internal; Path=/. Следующий https://victim.internal/ отправляет sid=x.SUCCESS cakephp-cookie-suffix suffix-leak=yes control-other=yes plant-domain=yes how=cakephp-5.4.2 CAKEPHP-COOKIE-SUFFIX-WITNESS
Domain-match по RFC 6265 (host === domain || str_ends_with($host, '.' . $domain)). Host-only cookie, когда Domain опущен. Отклонять Domain, который не проходит domain-match с хостом запроса. Отклонять публичные суффиксы.
887b641de9f7aba06071f98f17edcbc573cbcf63)src/Http/Cookie/CookieCollection.php · src/Http/Cookie/Cookie.phpGNU Affero GPL v3.0. См. LICENSE. Только loopback-лаборатория. Без гарантий.