
Stored XSS via User-Agent in Admin Order View in PhocaCart
PhocaCart ≤ 6.1.7 — Unauthenticated Attacker Stores XSS Payload via Checkout. Executes in Admin Browser Context
PhocaCart 6.1.7 for Joomla stores the raw HTTP User-Agent header from checkout requests in the orders database table when the store_user_agent configuration option is enabled. The stored value is subsequently rendered without HTML encoding into the administrator order edit view via itemText() in Adminview.php.
An unauthenticated attacker can place an order with a crafted User-Agent containing a JavaScript payload. When an administrator opens the order in the backend, the payload executes in the admin browser context — enabling session hijacking, credential theft, or unauthorized administrative actions.
The vulnerability requires store_user_agent = 1 (non-default) to be enabled. Because this option is a deliberate administrator choice to collect browser statistics, affected shops are those where the shop owner has enabled the feature — making exploitation fully silent from the attacker's perspective.
| COMPONENT | VULNERABLE | TESTED ON | FIXED |
|---|---|---|---|
| PhocaCart (com_phocacart) | 5.0.0 – 6.1.7 | Joomla 5.4.7 + PhocaCart 6.1.7 + MariaDB 10.6 | 6.1.8 |
Type: Stored XSS (CWE-79) — admin-targeted
Authentication required: None to inject. Administrator to trigger
CSRF token required: Yes (standard Joomla CSRF token required for checkout)
File: administrator/components/com_phocacart/libraries/Phoca/Render/Adminview.php:382
When store_user_agent = 1, the method PhocacartUtils::getUserAgent() returns $_SERVER['HTTP_USER_AGENT'] without any sanitization. This raw string is stored in #__phocacart_orders (truncated at 200 characters — insufficient to prevent XSS). When an administrator views the order in the backend, itemText() concatenates the stored value directly into the HTML response with no call to htmlspecialchars().
Data flow:
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 — STORE PHASE (VULNERABLE SOURCE)
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 — RENDER PHASE (VULNERABLE SINK)
// No htmlspecialchars() applied:
$output .= ' <div class="controls ' . $class . '">' . $item . '</div>' . "\n";
Called from administrator/components/com_phocacart/views/phocacartorder/tmpl/edit.php:84:
echo $r->itemText($this->itemcommon->user_agent, Text::_('COM_PHOCACART_USER_AGENT'), '', 'user_agent');
The itemText() function passes $item (the stored user_agent value) directly into the HTML output with no encoding, making it the primary sink. Fixing it at the sink level protects all callers of itemText().
The attack requires completing a full checkout flow with a malicious User-Agent header. The CSRF token must be obtained first since Joomla's checkout controller validates it.
In Joomla admin, navigate to Components → Phoca Cart → Options → Main tab → Statistics Options. Set Store User Agent Information = Yes and save.

The attacker visits the shop frontend to extract a valid Joomla CSRF token required by the checkout controller. The token appears in the HTML source or JavaScript config of any shop page.

The attacker adds a product to cart via the AJAX checkout endpoint using a benign User-Agent. This transitions the checkout session to the next state.

The attacker submits billing information to advance the checkout state machine.

The attacker selects a shipping method to proceed to payment.

The attacker selects a payment method (e.g., Cash on Delivery).

Critical injection step. The attacker places the final order via task=checkout.order with the User-Agent header set to the XSS payload. The checkout.order task reads raw HTTP_USER_AGENT and stores it unescaped in #__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
Result: Order created. Database stores user_agent = '<script>alert(document.domain)</script>'.

Administrator navigates to Components → Phoca Cart → Orders. The injected order appears in the list. XSS is not yet triggered at this stage.

When the administrator clicks the order to open the edit view, the stored user_agent is rendered unescaped via itemText(). JavaScript executes immediately.
Trigger URL:
/administrator/index.php?option=com_phocacart&view=phocacartorder&layout=edit&id=<ORDER_ID>
