
phpMyAdmin - Alta 8.1 - Autenticado POST /index.php - Omisión de 2FA
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · PMA-5.2.3-WebAuthn-2FA
phpMyAdmin 5.2.3 - phpMyAdmin
Tarball por defecto. web-auth/webauthn-lib es suggest / require-dev. composer install --no-dev no lo instala. Por lo tanto, createServer usa CustomServer. Esa clase analiza response.signature (el campo debe existir y decodificarse) y nunca la verifica con COSE. Nunca usa la credentialPublicKey almacenada. El propio CustomServerTest del producto acepta una aserción sin clave pública.
Después de la contraseña de MySQL, una firma WebAuthn basura que aún coincida con el challenge, el origin, el rpIdHash y el user-present es una sesión completa de phpMyAdmin. Eso es la base de datos.
| ID | aún sin CVE |
| CWE | CWE-347, CWE-287 |
| CVSS | Alta: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| Producto | phpMyAdmin |
| Afectado | 5.2.3 tarball por defecto (web-auth/webauthn-lib ausente) |
| Autenticación | Contraseña de MySQL, luego WebAuthn omitido |
| Licencia | GNU Affero GPL v3.0 |
| Laboratorio | solo 127.0.0.1 |
Todavía necesita la contraseña de MySQL (inicio de sesión por cookie). Tras esa contraseña, envía por POST webauthn_request_response con un challenge coincidente, el host de origin, rpIdHash, user-present, el id de credencial registrada y una firma basura. Llega a la interfaz normal. El 2FA ha desaparecido. Eso es RCE sobre los datos, no una shell PHP en el host.
Si realmente instalaste web-auth/webauthn-lib, estás en WebauthnLibServer y no en este fallo.
Leí createServer, luego parseAndValidateAssertionResponse, luego la prueba unitaria que no pasa una clave pública. Las comprobaciones estructurales son reales: id de credencial en allowCredentials, clientData.type == webauthn.get, el challenge coincide con la sesión, el host de origin, rpIdHash, user-present. La firma se analiza y se ignora.
El primer cliente que apunté a esto fue educado. Envió por POST un JSON de WebAuthn sin una sesión de contraseña previa. Página de inicio de sesión. Envió por POST route=/ con una firma que ni siquiera satisfacía las comprobaciones estructurales. Te quedas en el 2FA. webauthn_request_response sigue en el HTML.
Callejones sin salida ya registrados: Webauthn\Server presente (clase de servidor equivocada); challenge que no es el de esta sesión; discrepancia en el host de origin; flag user-present desactivado; id de credencial no presente en allowCredentials; sin autenticar, sin contraseña de MySQL; una reverse shell. Teatro. La señal es el campo de 2FA desaparecido y una página de navegación de phpMyAdmin.
Los fallos del cliente parecen fallos del producto. Un helper que ensombrece http.client nunca envía un paquete. .recv() sobre un HTTPResponse no es .read(). Lo menciono una vez porque costó tiempo y pareció, durante un minuto, que phpMyAdmin estaba bien.
Luego: POST de username/password/token. Extraer el challenge de sesión de la página de 2FA. POST de la aserción falsificada. still_2fa=False landed=True. El Dockerfile del laboratorio copia 5.2.3, composer install --no-dev, y luego elimina vendor/web-auth para que CustomServer sea el que esté en juego. El último php -r sale con 1 si existe Webauthn\Server. La imagen debe fallar la compilación si la librería sigue ahí. init.sql inscribe el 2FA WebAuthn para appuser sin una clave de hardware.
cd lab
./run.sh
Apunta solo a http://127.0.0.1:18080. Coloca phpMyAdmin 5.2.3 como src/ junto al Dockerfile primero.
password-login status=200 webauthn=True
2fa-post status=200 still_2fa=False landed=True
SUCCESS PMA-WEBAUTHN-BYPASS WebAuthn 2FA accepted with garbage signature
Instala web-auth/webauthn-lib para que createServer utilice la librería, o verifica realmente la firma COSE de response.signature contra la clave pública almacenada en CustomServer. Hasta entonces, desactiva el 2FA de WebAuthn o no expongas este phpMyAdmin.
CustomServer::parseAndValidateAssertionResponse · WebAuthn::createServer · CustomServerTestGNU Affero GPL v3.0. Consulta LICENSE. Solo para laboratorio en loopback. Sin garantía.