
CakePHP 5.4.2 — высокий — удалённый — cookie — Cookie jar HTTP-клиента сопоставляет домены как суффикс регулярного выражения
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · cakephp-cookie-suffix
Разбор: https://abraxaslabs.tech/research/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 по regex-суффиксу) |
| Доступ | Удалённый (приложение использует cookie jar Http Client для разных хостов) |
| CWE | CWE-565, CWE-201 |
| CVSS | Высокий: 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 не подвержены этой ошибке. Без shell. Не неаутентифицированный RCE на скелетном приложении.
Я зафиксировал cakephp/cakephp 5.4.2 после GHSA-2qh5, GHSA-fxf7, GHSA-vjqc и замечания о filename= в 5.4.2. Сопоставление cookie в Http Client — это regex-суффикс:
$pattern = '/' . preg_quote($domain, '/') . '$/';
if (!preg_match($pattern, $host)) {
continue;
}
addFromResponse передаёт хост запроса только как значение по умолчанию. Атрибут Domain= из Set-Cookie перезаписывает его и сохраняется.
Уже зафиксированные неверные повороты: трактовка этого как бага браузерных cookie; утверждение, что other.com тоже совпадает; раздувание оракула заголовка Cookie до ATO без приложения, использующего хранилище cookie между хостами. Театральщина: reverse shell.
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-origin не требуется; оракул запускает 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
Сопоставление доменов по RFC 6265 (host === domain || str_ends_with($host, '.' . $domain)). Cookies только для хоста, если Domain опущен. Отклонять Domain, который не соответствует домену запроса. Отклонять публичные суффиксы.
887b641de9f7aba06071f98f17edcbc573cbcf63)src/Http/Cookie/CookieCollection.php · src/Http/Cookie/Cookie.phpGNU Affero GPL v3.0. См. LICENSE. Только для лаборатории на loopback. Без гарантий.