
Хранимый XSS через User-Agent в просмотре заказов администратора в PhocaCart
PhocaCart ≤ 6.1.7 — Неаутентифицированный злоумышленник сохраняет XSS-пейлоад через оформление заказа. Выполняется в контексте браузера администратора
PhocaCart 6.1.7 для Joomla сохраняет необработанный HTTP-заголовок User-Agent из запросов оформления заказа в таблицу базы данных заказов, когда включён параметр конфигурации store_user_agent. Сохранённое значение затем выводится без HTML-кодирования в представление редактирования заказа администратора через itemText() в Adminview.php.
Неаутентифицированный злоумышленник может оформить заказ с поддельным User-Agent, содержащим JavaScript-пейлоад. Когда администратор открывает заказ в админпанели, пейлоад выполняется в контексте браузера администратора, что позволяет перехватить сессию, похитить учётные данные или совершить несанкционированные административные действия.
Для уязвимости требуется включённый параметр store_user_agent = 1 (не по умолчанию). Поскольку этот параметр является осознанным выбором администратора для сбора статистики браузеров, уязвимы те магазины, где владелец включил эту функцию, что делает эксплуатацию полностью незаметной со стороны злоумышленника.
| КОМПОНЕНТ | УЯЗВИМЫЕ ВЕРСИИ | ПРОТЕСТИРОВАНО НА | ИСПРАВЛЕНО |
|---|---|---|---|
| PhocaCart (com_phocacart) | 5.0.0 – 6.1.7 | Joomla 5.4.7 + PhocaCart 6.1.7 + MariaDB 10.6 | 6.1.8 |
Тип: Хранимый XSS (CWE-79) — направлен на администратора
Требуется аутентификация: Не требуется для внедрения. Требуется администратор для срабатывания
Требуется CSRF-токен: Да (стандартный CSRF-токен Joomla требуется для оформления заказа)
Файл: administrator/components/com_phocacart/libraries/Phoca/Render/Adminview.php:382
Когда store_user_agent = 1, метод PhocacartUtils::getUserAgent() возвращает $_SERVER['HTTP_USER_AGENT'] без какой-либо санитизации. Эта необработанная строка сохраняется в #__phocacart_orders (обрезается до 200 символов — недостаточно для предотвращения XSS). Когда администратор просматривает заказ в админпанели, itemText() конкатенирует сохранённое значение напрямую в HTML-ответ без вызова htmlspecialchars().
Поток данных:
Attacker sends checkout request:
User-Agent: <script>alert(document.domain)</script>
utils.php:334 PhocacartUtils::getUserAgent()
└─ return (string) $_SERVER['HTTP_USER_AGENT'] // raw, no sanitization
order.php:413 $d['user_agent'] = substr($user_agent, 0, 200)
└─ INSERT INTO #__phocacart_orders (user_agent) VALUES ('<script>...</script>')
Admin opens order in backend:
edit.php:84 echo $r->itemText($this->itemcommon->user_agent, ...)
Adminview.php:382 '<div class="controls >' . $item . '</div>'
// → NO htmlspecialchars()
→ Browser renders: <div><script>alert(document.domain)</script></div>
→ XSS fires in administrator's browser
ORDER.PHP — ЭТАП СОХРАНЕНИЯ (УЯЗВИМЫЙ ИСТОЧНИК)
administrator/components/com_phocacart/libraries/phocacart/order/order.php:424
$user_agent = PhocacartUtils::getUserAgent(); // reads $_SERVER['HTTP_USER_AGENT'] raw
$d['user_agent'] = substr($user_agent, 0, 200); // stored in DB unfiltered
ADMINVIEW.PHP:382 — ЭТАП ВЫВОДА (УЯЗВИМЫЙ ПРИЁМНИК)
// No htmlspecialchars() applied:
$output .= ' <div class="controls ' . $class . '">' . $item . '</div>' . "\n";
Вызывается из administrator/components/com_phocacart/views/phocacartorder/tmpl/edit.php:84:
echo $r->itemText($this->itemcommon->user_agent, Text::_('COM_PHOCACART_USER_AGENT'), '', 'user_agent');
Функция itemText() передаёт $item (сохранённое значение user_agent) напрямую в HTML-вывод без кодирования, что делает её основным приёмником. Исправление на уровне приёмника защищает всех вызывающих itemText().
Для атаки требуется пройти полный процесс оформления заказа с вредоносным заголовком User-Agent. Сначала необходимо получить CSRF-токен, поскольку контроллер оформления заказа Joomla проверяет его.
В админке Joomla перейдите в Components → Phoca Cart → Options → Main tab → Statistics Options. Установите Store User Agent Information = Yes и сохраните.

Злоумышленник посещает витрину магазина, чтобы извлечь действующий CSRF-токен Joomla, требуемый контроллером оформления заказа. Токен присутствует в HTML-исходнике или JavaScript-конфигурации любой страницы магазина.

Злоумышленник добавляет товар в корзину через AJAX-эндпоинт оформления заказа, используя безобидный User-Agent. Это переводит сессию оформления в следующее состояние.

Злоумышленник отправляет платёжные данные, чтобы продвинуть конечный автомат оформления заказа.

Злоумышленник выбирает способ доставки, чтобы перейти к оплате.

Злоумышленник выбирает способ оплаты (например, оплата при доставке).

Критический шаг внедрения. Злоумышленник размещает финальный заказ через task=checkout.order, устанавливая заголовок User-Agent в XSS-пейлоад. Задача checkout.order читает необработанный HTTP_USER_AGENT и сохраняет его без экранирования в #__phocacart_orders.user_agent.
POST /index.php/shop/checkout HTTP/1.1
Host: TARGET
User-Agent: <script>alert(document.domain)</script>
Content-Type: application/x-www-form-urlencoded
phcheckouttac=1&task=checkout.order&option=com_phocacart&return=&<CSRF_TOKEN>=1
Результат: заказ создан. База данных хранит user_agent = '<script>alert(document.domain)</script>'.

Администратор переходит в Components → Phoca Cart → Orders. Внедрённый заказ появляется в списке. На этом этапе XSS ещё не срабатывает.

Когда администратор щёлкает по заказу, чтобы открыть представление редактирования, сохранённый user_agent выводится без экранирования через itemText(). JavaScript выполняется немедленно.
URL срабатывания:
/administrator/index.php?option=com_phocacart&view=phocacartorder&layout=edit&id=<ORDER_ID>
