Summary
Poweradmin: OIDC sub collation bypass in Poweradmin leading to account takeover
Preface
Poweradmin maps OIDC identities into local users through oidc_user_links.oidc_subject plus provider_id. In the MySQL schema, the OIDC link table explicitly uses utf8mb4_unicode_ci, which is case-insensitive and accent-insensitive. OIDC sub is a stable external subject identifier and should be matched byte-for-byte within the issuer/provider scope.
The confirmed local PoC used two different OIDC users:
- Victim subject:
victim-login - Attacker subject:
victím-login(í, U+00ED)
MySQL reported those two subjects as equal under utf8mb4_unicode_ci. After the victim linked their OIDC account, the attacker authenticated to the same provider with the attacker's own password and Poweradmin resolved the session to the victim's local account.
Server Info
- Application: Poweradmin
- Version:
targets/poweradmingite1f9c9a - Database: MySQL
8.4.10,character_set_server=utf8mb4,collation_server=utf8mb4_unicode_ci - Access Permissions: Any user who can create or control an account in the connected OIDC provider
- Auth Method: OIDC generic provider
- Tools: Docker Compose, local OIDC provider, Python PoC harness
Affected Entry Point:
GET /oidc/login?provider=generic
GET /oidc/callback?code=...&state=...
Relevant request properties:
- Authentication: valid OIDC authorization code flow
- Trigger: attacker OIDC account has a
subthat collides with a victim's linkedsub - Vulnerable field: OIDC
substored and looked up asoidc_user_links.oidc_subject
Root Cause Analysis
part0, oidc_user_links.oidc_subject uses an accent-insensitive collation
The MySQL schema defines the OIDC link table with utf8mb4_unicode_ci:
-- sql/poweradmin-mysql-db-structure.sql:341-356
CREATE TABLE `oidc_user_links` (
`user_id` INT(11) NOT NULL,
`provider_id` VARCHAR(50) NOT NULL,
`oidc_subject` VARCHAR(255) NOT NULL,
...
UNIQUE KEY `unique_subject_provider` (`oidc_subject`, `provider_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
In the PoC database:
Field Type Collation
provider_id varchar(50) utf8mb4_unicode_ci
oidc_subject varchar(255) utf8mb4_unicode_ci
SELECT 'victim-login' = 'victím-login' COLLATE utf8mb4_unicode_ci;
-- accent_collision = 1
part1, OIDC sub flows into a normal SQL equality lookup
Poweradmin reads the subject from OIDC userinfo data. If no custom subject mapping is configured, it uses the sub claim:
// lib/Application/Service/OidcService.php:424-459
$resourceOwner = $provider->getResourceOwner($token);
$userData = $resourceOwner->toArray();
...
subject: $userData[$mapping['subject'] ?? 'sub'] ?? '',
The callback passes the resulting OidcUserInfo to provisioning:
// lib/Application/Service/OidcService.php:269-291
$userInfo = $this->getUserInfo($provider, $token, $providerId);
$userId = $this->userProvisioningService->provisionUser($userInfo, $providerId);
Provisioning first tries to find an existing user by subject:
// lib/Application/Service/UserProvisioningService.php:86-90
$existingUserId = $authMethod === self::AUTH_METHOD_SAML
? $this->findUserBySamlSubject($userInfo->getSubject(), $providerId)
: $this->findUserByOidcSubject($userInfo->getSubject(), $providerId);
The lookup is normal SQL equality evaluated under the column's weak collation:
// lib/Application/Service/UserProvisioningService.php:145-149
$stmt = $this->db->prepare("
SELECT user_id FROM oidc_user_links
WHERE oidc_subject = ? AND provider_id = ?
");
$stmt->execute([$subject, $providerId]);
When the attacker authenticates with sub = victím-login, MySQL matches the existing row for oidc_subject = victim-login and returns the victim's user_id.
part2, The returned user ID becomes the authenticated session
After provisioning returns the matched user_id, Poweradmin fetches the database username for that user and stores the matched user ID in the session:
// lib/Application/Service/OidcService.php:307-317
$databaseUsername = $this->userProvisioningService->getDatabaseUsername($userId);
$this->setSessionValue('userlogin', $databaseUsername);
// lib/Application/Service/OidcService.php:360-371
$this->setSessionValue('userid', $userId);
...
$this->setSessionValue('authenticated', true);
The attacker's OIDC password is validated by the IdP, but the local user_id selected by Poweradmin comes from the weak-collation SQL lookup.
Security Impact
An attacker who can register or control an OIDC principal with an accent/collation variant of a victim's OIDC subject can authenticate with the attacker's own IdP credentials and obtain a Poweradmin session for the victim's local account.
The confirmed PoC used distinct OIDC usernames, subjects, emails, and passwords. The attacker did not know or modify the victim's password.
Reproduction
1. Start the local Poweradmin OIDC lab
sudo -n docker compose -p poweradminoidcpoc -f poc/work/poweradmin-oidc-collation/docker-compose.yml up -d
The lab uses MySQL utf8mb4_unicode_ci and a local OIDC provider with two real login accounts:
Victim:
username/sub: victim-login
email: [email protected]
password: VictimPassword123!
Attacker:
username/sub: victím-login
email: [email protected]
password: AttackerPassword123!
2. Log in once as the victim through OIDC
Authenticate through GET /oidc/login?provider=generic using:
username: victim-login
password: VictimPassword123!
Poweradmin creates the victim local user and OIDC link:
users:
id username fullname email
2 victim-login Victim Poweradmin [email protected]
oidc_user_links:
id user_id provider_id oidc_subject
1 2 generic victim-login
3. Log in as the attacker OIDC user
Authenticate from a separate browser session with:
username: victím-login
password: AttackerPassword123!
Observed result from poc/work/poweradmin-oidc-collation/run_poc.py:
{
"attacker_login": {
"selected_idp_user": "attacker",
"selected_idp_username": "victím-login",
"has_session_cookie": true,
"home_contains_victim_username": true,
"home_contains_attacker_username": false
},
"attacker_resolved_to_victim": true
}
Database evidence after both logins:
version charset_server collation_server
8.4.10 utf8mb4 utf8mb4_unicode_ci
accent_collision
1
users:
id username fullname email
2 victim-login Victim Poweradmin [email protected]
oidc_user_links:
id user_id provider_id oidc_subject oidc_subject_hex
1 2 generic victim-login 76696374696D2D6C6F67696E
The application audit log also records all OIDC login events as the victim user:
user:victim-login operation:login_success auth_method:oidc
No local user or OIDC link was created for victím-login.
Patches
Fixed in 4.2.5, 4.3.4, and 4.4.0. OIDC and SAML subject identifiers are now matched byte-for-byte. The fix includes a database migration that changes the collation of the identity link columns, so upgrading requires running the SQL update script for your database in the sql/ directory.
Acknowledge / Credit
whale120 (@whale120_tw), working with DEVCORE Internship Program
Impact
The application does not adequately verify the identity of a user, device, or process before granting access. Typical impact: unauthorized access to functions or data reserved for authenticated parties.
GHSA-CMWH-G2H8-C222 has a CVSS score of 8.1 (High). The vector is network-reachable, low privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (4.2.5, 4.3.4); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Treat OIDC subject identifiers as byte-exact strings.
For MySQL, migrate the OIDC mapping identifiers to a binary or byte-preserving collation:
ALTER TABLE oidc_user_links
MODIFY COLUMN provider_id varchar(50)
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
MODIFY COLUMN oidc_subject varchar(255)
CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL;
Also make lookup queries byte-preserving so patched application code protects existing deployments before schema migrations are complete:
$stmt = $this->db->prepare("
SELECT user_id FROM oidc_user_links
WHERE BINARY oidc_subject = BINARY ?
AND BINARY provider_id = BINARY ?
");
Review related identity and authorization lookups:
findUserByEmail()usesusers.email = ?and can be impacted whenlink_by_emailis enabled.findPermissionTemplateByName()usesperm_templ.name = ?for SSO permission template mapping.findGroupByName()usesuser_groups.name = ?for SSO group mapping.
Those fields should either be intentionally documented as case/accent-insensitive or migrated/looked up with byte-preserving semantics where they represent security boundaries.
Frequently Asked Questions
- What is GHSA-CMWH-G2H8-C222? GHSA-CMWH-G2H8-C222 is a high-severity improper authentication vulnerability in poweradmin/poweradmin (composer), affecting versions >= 4.1.0, < 4.2.5. It is fixed in 4.2.5, 4.3.4. The application does not adequately verify the identity of a user, device, or process before granting access.
- How severe is GHSA-CMWH-G2H8-C222? GHSA-CMWH-G2H8-C222 has a CVSS score of 8.1 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which versions of poweradmin/poweradmin are affected by GHSA-CMWH-G2H8-C222? poweradmin/poweradmin (composer) versions >= 4.1.0, < 4.2.5 is affected.
- Is there a fix for GHSA-CMWH-G2H8-C222? Yes. GHSA-CMWH-G2H8-C222 is fixed in 4.2.5, 4.3.4. Upgrade to this version or later.
- Is GHSA-CMWH-G2H8-C222 exploitable, and should I be worried? Whether GHSA-CMWH-G2H8-C222 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether GHSA-CMWH-G2H8-C222 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix GHSA-CMWH-G2H8-C222?
- Upgrade
poweradmin/poweradminto 4.2.5 or later - Upgrade
poweradmin/poweradminto 4.3.4 or later
- Upgrade