{"answers":{"affected":"4 open source packages ship an affected version, including poweradmin/poweradmin.","exploited":"There is no known exploitation of GHSA-cmwh-g2h8-c222 in the wild and no public exploit code.","fix":"Upgrade poweradmin/poweradmin to a fixed version for your release line: 4.2.5 or 4.3.4.","what":"GHSA-cmwh-g2h8-c222 is an authentication bypass vulnerability in poweradmin/poweradmin, published on 24 Jul 2026. Any signed-in user can exploit it over the network, with no action from the victim."},"fix_prompt":"Check this codebase for GHSA-cmwh-g2h8-c222 and fix it if it is affected.\n\nWhat it is: GHSA-cmwh-g2h8-c222 is an authentication bypass vulnerability in poweradmin/poweradmin, published on 24 Jul 2026. Any signed-in user can exploit it over the network, with no action from the victim.\nExploitation: There is no known exploitation of GHSA-cmwh-g2h8-c222 in the wild and no public exploit code.\n\nAffected packages and fixed versions:\n- poweradmin/poweradmin (Packagist): affected from 4.1.0; fixed in 4.2.5\n- poweradmin/poweradmin (Packagist): affected from 4.1.0; fixed in 4.2.5\n- poweradmin/poweradmin (Packagist): affected from 4.3.0; fixed in 4.3.4\n- poweradmin/poweradmin (Packagist): affected from 4.3.0; fixed in 4.3.4\n\nTask:\n1. Search the dependency manifests and lockfiles (for example package.json, package-lock.json, requirements.txt, poetry.lock, uv.lock, pom.xml, build.gradle, go.mod, Cargo.lock, Gemfile.lock) and any Dockerfiles for the affected packages, including transitive dependencies.\n2. If an affected version is in use, upgrade it to the fixed version for its release line with the project's package manager, so the lockfile is regenerated. Prefer the smallest upgrade that fixes it.\n3. If there is no fix, or the upgrade would break the build, do not guess: explain the risk and the safest workaround instead.\n4. Run the tests and summarise what changed and why.\n5. If nothing in the project is affected, say so and change nothing.\n\nReference: https://insights.sbomify.com/GHSA-cmwh-g2h8-c222/ (Markdown: https://insights.sbomify.com/GHSA-cmwh-g2h8-c222/index.md)","id":"GHSA-cmwh-g2h8-c222","markdown":"https://insights.sbomify.com/GHSA-cmwh-g2h8-c222/index.md","name":"GHSA-cmwh-g2h8-c222","record":{"aliases":[],"analysis":{"component_type":"web_application","fix_type":"patch","impacts":["account_takeover"],"network_reachable":"yes","non_default_config":false,"product_category":"Poweradmin web application","requires_auth":"low_privilege","summary":"Attacker with OIDC account can take over victim's Poweradmin account by exploiting case-insensitive database collation on subject identifiers.","user_interaction":"not_required","vuln_class":"auth_bypass"},"analysis_generated_by":"qwen3.8-flash-next","assigner":null,"cpe":[],"cvss":{"score":null,"severity":null,"source":null,"vector":null,"version":null},"cwes":[],"description":"## Preface\n\nPoweradmin 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.\n\nThe confirmed local PoC used two different OIDC users:\n\n- Victim subject: `victim-login`\n- Attacker subject: `victím-login` (`í`, U+00ED)\n\nMySQL 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.\n\n## Server Info\n\n- **Application:** Poweradmin\n- **Version:** `targets/poweradmin` git `e1f9c9a`\n- **Database:** MySQL `8.4.10`, `character_set_server=utf8mb4`, `collation_server=utf8mb4_unicode_ci`\n- **Access Permissions:** Any user who can create or control an account in the connected OIDC provider\n- **Auth Method:** OIDC generic provider\n- **Tools:** Docker Compose, local OIDC provider, Python PoC harness\n\n**Affected Entry Point:**\n\n```text\nGET /oidc/login?provider=generic\nGET /oidc/callback?code=...\u0026state=...\n```\n\nRelevant request properties:\n\n- Authentication: valid OIDC authorization code flow\n- Trigger: attacker OIDC account has a `sub` that collides with a victim's linked `sub`\n- Vulnerable field: OIDC `sub` stored and looked up as `oidc_user_links.oidc_subject`\n\n## Root Cause Analysis\n\n### part0 — `oidc_user_links.oidc_subject` uses an accent-insensitive collation\n\nThe MySQL schema defines the OIDC link table with `utf8mb4_unicode_ci`:\n\n```sql\n-- sql/poweradmin-mysql-db-structure.sql:341-356\nCREATE TABLE `oidc_user_links` (\n  `user_id` INT(11) NOT NULL,\n  `provider_id` VARCHAR(50) NOT NULL,\n  `oidc_subject` VARCHAR(255) NOT NULL,\n  ...\n  UNIQUE KEY `unique_subject_provider` (`oidc_subject`, `provider_id`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;\n```\n\nIn the PoC database:\n\n```text\nField          Type          Collation\nprovider_id    varchar(50)   utf8mb4_unicode_ci\noidc_subject   varchar(255)  utf8mb4_unicode_ci\n\nSELECT 'victim-login' = 'victím-login' COLLATE utf8mb4_unicode_ci;\n-- accent_collision = 1\n```\n\n### part1 — OIDC `sub` flows into a normal SQL equality lookup\n\nPoweradmin reads the subject from OIDC userinfo data. If no custom `subject` mapping is configured, it uses the `sub` claim:\n\n```php\n// lib/Application/Service/OidcService.php:424-459\n$resourceOwner = $provider-\u003egetResourceOwner($token);\n$userData = $resourceOwner-\u003etoArray();\n...\nsubject: $userData[$mapping['subject'] ?? 'sub'] ?? '',\n```\n\nThe callback passes the resulting `OidcUserInfo` to provisioning:\n\n```php\n// lib/Application/Service/OidcService.php:269-291\n$userInfo = $this-\u003egetUserInfo($provider, $token, $providerId);\n$userId = $this-\u003euserProvisioningService-\u003eprovisionUser($userInfo, $providerId);\n```\n\nProvisioning first tries to find an existing user by subject:\n\n```php\n// lib/Application/Service/UserProvisioningService.php:86-90\n$existingUserId = $authMethod === self::AUTH_METHOD_SAML\n    ? $this-\u003efindUserBySamlSubject($userInfo-\u003egetSubject(), $providerId)\n    : $this-\u003efindUserByOidcSubject($userInfo-\u003egetSubject(), $providerId);\n```\n\nThe lookup is normal SQL equality evaluated under the column's weak collation:\n\n```php\n// lib/Application/Service/UserProvisioningService.php:145-149\n$stmt = $this-\u003edb-\u003eprepare(\"\n    SELECT user_id FROM oidc_user_links\n    WHERE oidc_subject = ? AND provider_id = ?\n\");\n$stmt-\u003eexecute([$subject, $providerId]);\n```\n\nWhen 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`.\n\n### part2 — The returned user ID becomes the authenticated session\n\nAfter provisioning returns the matched `user_id`, Poweradmin fetches the database username for that user and stores the matched user ID in the session:\n\n```php\n// lib/Application/Service/OidcService.php:307-317\n$databaseUsername = $this-\u003euserProvisioningService-\u003egetDatabaseUsername($userId);\n$this-\u003esetSessionValue('userlogin', $databaseUsername);\n```\n\n```php\n// lib/Application/Service/OidcService.php:360-371\n$this-\u003esetSessionValue('userid', $userId);\n...\n$this-\u003esetSessionValue('authenticated', true);\n```\n\nThe attacker's OIDC password is validated by the IdP, but the local `user_id` selected by Poweradmin comes from the weak-collation SQL lookup.\n\n## Security Impact\n\nAn 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.\n\nThe confirmed PoC used distinct OIDC usernames, subjects, emails, and passwords. The attacker did not know or modify the victim's password.\n\n## Reproduction\n\n### 1. Start the local Poweradmin OIDC lab\n\n```bash\nsudo -n docker compose -p poweradminoidcpoc -f poc/work/poweradmin-oidc-collation/docker-compose.yml up -d\n```\n\nThe lab uses MySQL `utf8mb4_unicode_ci` and a local OIDC provider with two real login accounts:\n\n```text\nVictim:\n  username/sub: victim-login\n  email: victim.poweradmin@example.com\n  password: VictimPassword123!\n\nAttacker:\n  username/sub: victím-login\n  email: attacker.poweradmin@example.com\n  password: AttackerPassword123!\n```\n\n### 2. Log in once as the victim through OIDC\n\nAuthenticate through `GET /oidc/login?provider=generic` using:\n\n```text\nusername: victim-login\npassword: VictimPassword123!\n```\n\nPoweradmin creates the victim local user and OIDC link:\n\n```text\nusers:\nid  username      fullname            email\n2   victim-login  Victim Poweradmin   victim.poweradmin@example.com\n\noidc_user_links:\nid  user_id  provider_id  oidc_subject\n1   2        generic      victim-login\n```\n\n### 3. Log in as the attacker OIDC user\n\nAuthenticate from a separate browser session with:\n\n```text\nusername: victím-login\npassword: AttackerPassword123!\n```\n\nObserved result from `poc/work/poweradmin-oidc-collation/run_poc.py`:\n\n```json\n{\n  \"attacker_login\": {\n    \"selected_idp_user\": \"attacker\",\n    \"selected_idp_username\": \"victím-login\",\n    \"has_session_cookie\": true,\n    \"home_contains_victim_username\": true,\n    \"home_contains_attacker_username\": false\n  },\n  \"attacker_resolved_to_victim\": true\n}\n```\n\nDatabase evidence after both logins:\n\n```text\nversion  charset_server  collation_server\n8.4.10   utf8mb4         utf8mb4_unicode_ci\n\naccent_collision\n1\n\nusers:\nid  username      fullname            email\n2   victim-login  Victim Poweradmin   victim.poweradmin@example.com\n\noidc_user_links:\nid  user_id  provider_id  oidc_subject  oidc_subject_hex\n1   2        generic      victim-login   76696374696D2D6C6F67696E\n```\n\nThe application audit log also records all OIDC login events as the victim user:\n\n```text\nuser:victim-login operation:login_success auth_method:oidc\n```\n\nNo local user or OIDC link was created for `victím-login`.\n\n## Recommended Fix\n\nTreat OIDC subject identifiers as byte-exact strings.\n\nFor MySQL, migrate the OIDC mapping identifiers to a binary or byte-preserving collation:\n\n```sql\nALTER TABLE oidc_user_links\n  MODIFY COLUMN provider_id varchar(50)\n    CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,\n  MODIFY COLUMN oidc_subject varchar(255)\n    CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL;\n```\n\nAlso make lookup queries byte-preserving so patched application code protects existing deployments before schema migrations are complete:\n\n```php\n$stmt = $this-\u003edb-\u003eprepare(\"\n    SELECT user_id FROM oidc_user_links\n    WHERE BINARY oidc_subject = BINARY ?\n      AND BINARY provider_id = BINARY ?\n\");\n```\n\nReview related identity and authorization lookups:\n\n- `findUserByEmail()` uses `users.email = ?` and can be impacted when `link_by_email` is enabled.\n- `findPermissionTemplateByName()` uses `perm_templ.name = ?`","ecosystems":["Packagist"],"exploits":{"exploitdb":null,"first_seen":null,"github_poc":null,"max_poc_stars":null,"metasploit":null,"nuclei":null,"refs":[],"tier":null},"has_fix":true,"headline":"Poweradmin: OIDC `sub` collation bypass in Poweradmin leading to account takeover","id":"GHSA-cmwh-g2h8-c222","indexable":true,"kev":null,"kind":"advisory","links":{"ecosystem":"Packagist","product":null,"products":[],"type":"auth_bypass","year":"2026"},"n_packages":4,"n_references":null,"packages":[{"ecosystem":"Packagist","fixed":["4.2.5"],"introduced":["4.1.0"],"last_affected":[],"package":"poweradmin/poweradmin","purl":null,"source":"GHSA-cmwh-g2h8-c222"},{"ecosystem":"Packagist","fixed":["4.2.5"],"introduced":["4.1.0"],"last_affected":[],"package":"poweradmin/poweradmin","purl":"pkg:composer/poweradmin/poweradmin","source":"GHSA-cmwh-g2h8-c222"},{"ecosystem":"Packagist","fixed":["4.3.4"],"introduced":["4.3.0"],"last_affected":[],"package":"poweradmin/poweradmin","purl":null,"source":"GHSA-cmwh-g2h8-c222"},{"ecosystem":"Packagist","fixed":["4.3.4"],"introduced":["4.3.0"],"last_affected":[],"package":"poweradmin/poweradmin","purl":"pkg:composer/poweradmin/poweradmin","source":"GHSA-cmwh-g2h8-c222"}],"products":null,"published":"2026-07-24","related":[],"sources":[{"class":"attribution","licence":"CC BY 4.0","name":"GitHub Advisory Database","prefix":"GHSA"}],"ssvc":{"automatable":null,"exploitation":null,"technical_impact":null},"state":null,"updated":null,"vendors":null},"summary":"Attacker with OIDC account can take over victim's Poweradmin account by exploiting case-insensitive database collation on subject identifiers.","url":"https://insights.sbomify.com/GHSA-cmwh-g2h8-c222/"}