Sploitus

Exploit for CVE-2026-66917

githubexploit Β· 2026-08-23

Exploit Code

README168 lines
## https://sploitus.com/exploit?id=44FBF26F-2C6B-57D1-98CC-7EEDE10A0B52
# IDOR + Stored XSS via Broken Object-Level Authorization in JoomGallery

**JoomGallery ≀ 4.3.0 β€” Editor-Role User Hijacks Any Gallery Image and Stores XSS Payload, Enabling Admin Session Takeover**

![CVE](https://img.shields.io/badge/CVE-CVE--2026--66917-green)
![CVSS v4.0](https://img.shields.io/badge/CVSS_v4.0-8.6-red)
![CWE-639](https://img.shields.io/badge/CWE--639-Broken_Object_Level_Authorization-orange)
![CWE-79](https://img.shields.io/badge/CWE--79-Cross--site_Scripting-orange)
![Affected](https://img.shields.io/badge/Affected-4.0.0_–_4.3.0-red)
![Researcher](https://img.shields.io/badge/Researcher-Toan_Le-blue)

---

## SUMMARY

`UserimageController::save()` in JoomGallery checks `checkACL('edit', ...)` instead of `checkACL('edit.own', ...)`. An Editor-role user can POST to `task=userimage.save&id=N` for any image regardless of ownership (IDOR β€” CWE-639). Because Editor-role carries `core.edit` globally, the authorization check passes for every image ID on the site, including images owned by administrators.

Combined with a missing `$this->escape()` call in the frontend image template, an Editor can store an XSS payload in any image title β€” including admin-owned images β€” causing JavaScript execution in every visitor's browser. This enables full admin session hijack and site-wide compromise.

---

## AFFECTED VERSIONS

| COMPONENT                     | VULNERABLE     | TESTED ON                                                  | FIXED |
| ----------------------------- | -------------- | ---------------------------------------------------------- | ----- |
| JoomGallery (com_joomgallery) | 4.0.0 – 4.3.0 | Joomla 5.4.7 + JoomGallery 4.3.0-stable (PHP 8.2 / Apache) | 4.4.0 |

---

## VULNERABILITY DETAILS

**Type:** Broken Object-Level Authorization / IDOR (CWE-639) chained with Stored Cross-Site Scripting (CWE-79)
**Authentication required:** Low-privilege Editor account

### Root Cause 1 β€” IDOR (CWE-639)

**File:** `components/com_joomgallery/src/Controller/UserimageController.php`

The `save()` action performs an ACL check using `edit` permission instead of `edit.own`. The `edit` permission is granted to all Editor-role users globally, so the check succeeds for any image ID regardless of who created it.

**USERIMAGECONTROLLER.PHP β€” VULNERABLE CODE (LINE 145)**

```php
// Vulnerable
if (!$this->checkACL('edit', 'image', $recordId, $parent_id, true)) { ... }
```

Because `core.edit` is held globally by the Editor group, the condition evaluates to `false` for every image ID, granting unrestricted write access. On a successful save, the model additionally updates `created_by` to the attacker's user ID, silently transferring ownership of the image to the attacker.

### Root Cause 2 β€” Stored XSS (CWE-79)

**File:** `components/com_joomgallery/tmpl/image/default.php`

The frontend image template echoes `$this->item->title` without HTML-encoding it into the `alt` attribute context. Joomla's `JInput` `STRING` filter does not strip double-quote characters, so a payload containing `"` breaks out of the attribute and injects arbitrary event handlers.

**DEFAULT.PHP β€” VULNERABLE CODE (LINES 64, 78)**

```php
// Vulnerable
item->title; ?>" ...>
```

The payload `abc" onmouseover="alert(document.domain);" x="` is stored in `jos_joomgallery.title` and injected raw into the HTML attribute on every page render. No sanitization occurs at the storage or display layer.

---

## PROOF OF CONCEPT

### Attack Chain Overview

1. Admin creates a gallery image (ID=3). Owner = Administrator. Published and Approved.
2. Attacker logs in as Editor. Extracts session cookie and CSRF token from the login page.
3. Attacker fetches a fresh CSRF token from the JoomGallery component.
4. **IDOR:** Editor POSTs `task=userimage.save&id=3` with XSS payload in `jform[title]`. ACL check passes (`core.edit`, not `edit.own`). Server returns HTTP 303 β€” not 403.
5. Title updated with unescaped payload. `created_by` transferred to attacker's user ID.
6. XSS payload stored unescaped in `jos_joomgallery.title`.
7. Any visitor (or admin) browses the gallery frontend. Template renders `alt="abc" onmouseover="alert(document.domain);"`. XSS fires. Admin session captured β†’ full site compromise.

---

#### 1. Admin Creates Gallery Image β€” Owner: Administrator, ID = 3

Admin creates `admin_image` via JoomGallery backend (Joomla 5.4.7). Image is Published, Approved, and owned by **Administrator**

![Step 1](images/s1-step1-admin-creates-image-id3-owner-administrator.png)

---

#### 2. Editor Extracts CSRF Token from Login Page

`GET /index.php/component/users/login` β€” response JSON contains `"csrf.token":"a68c2b3a..."`. Token captured for the subsequent login POST.

![Step 2](images/s1-step2-editor-extracts-csrf-token-login-page.png)

---

#### 3. Editor Logs In β€” HTTP 303, `joomla_user_state=logged_in`

`POST /index.php/component/users/login` with CSRF token and Editor credentials. Response: HTTP 303 and `Set-Cookie: joomla_user_state=logged_in`. Session cookie captured.

![Step 3](images/s1-step3-editor-login-http-303-logged-in.png)

---

#### 4. Editor Extracts Fresh JoomGallery CSRF Token

`GET /index.php?option=com_joomgallery` with session cookie. Response contains a new `"csrf.token":"2d96934b..."` for use in the save request.

![Step 4](images/s1-step4-editor-extracts-joomgallery-csrf-token.png)

---

#### 5. IDOR + XSS Injection β€” Server Accepts with HTTP 303

Editor POSTs to `option=com_joomgallery&task=userimage.save&id=3` with `jform[title]` set to:

```
abc" onmouseover="alert(document.domain);" x="
```

Server returns HTTP 303 (not 403), confirming the IDOR. `Location` header shows the XSS payload in the redirect URL, confirming the title was accepted and saved.

![Step 5](images/s1-step5-idor-xss-injection-http-303-accepted.png)

---

#### 6. Ownership Transferred β€” Owner Changed to Editor User

JoomGallery backend shows image ID=3 now has Owner: `Editor User`. The `created_by` field was silently updated in the database during the unauthorized save.

![Step 6](images/s1-step6-ownership-transferred-editor-user.png)

---

#### 7. Database Confirms Payload Stored Unescaped

SQL query on `jos_joomgallery` confirms the XSS payload is stored  β€” `"` is stored as a raw double-quote, not as `"`. No sanitization occurred at the storage layer.

![Step 7](images/s1-step7-db-payload-stored-unescaped.png)

---

#### 8. XSS Fires on Gallery Frontend

Any user visiting `/index.php/component/joomgallery/gallery` triggers the payload. The browser's `alert()` dialog confirms JavaScript execution in the victim's origin (`document.domain`).

![Step 8](images/s1-step8-xss-fires-gallery-frontend.png)

---

## IMPACT

1. **Unauthorized Image Modification (IDOR):** An Editor-role user can modify any gallery image on the site, including images owned by administrators, without ownership validation.
2. **Silent Ownership Transfer:** Every unauthorized save silently reassigns `created_by` to the attacker, permanently altering the audit trail.
3. **Stored XSS β€” Persistent Execution:** The injected JavaScript payload executes in every visitor's browser on each gallery page load, affecting unauthenticated visitors and administrators alike.
4. **Admin Session Hijack β†’ Full Site Compromise:** A malicious script in the `alt` attribute can exfiltrate the administrator's session cookie, granting the attacker full backend access and control over the entire Joomla installation.
5. **Scope Change:** The XSS executes in the victim's browser origin (CVSS `S:C`), crossing the trust boundary between the attacker's low-privilege session and the victim's high-privilege session.

---

## REFERENCES

- **CVE:** https://vulners.com/cve/CVE-2026-66917
- **NVD:** https://nvd.nist.gov/vuln/detail/CVE-2026-66917
- **GitHub Advisory:** https://github.com/advisories/GHSA-8vxv-gf52-gm5h
- **Vendor Repository:** https://github.com/JoomGalleryfriends/JoomGallery
- **Vendor New Release Note:** https://www.joomgalleryfriends.net/en/blog/joomgallery-4-en/joomgallery-4-4-0.html