
phpMyAdmin - Alta 8.1 - POST /index.php autenticado - Bypass de 2FA
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · PMA-5.2.3-WebAuthn-2FA
phpMyAdmin 5.2.3 - phpMyAdmin
Tarball padrão. web-auth/webauthn-lib é suggest / require-dev. composer install --no-dev não o instala. createServer portanto usa CustomServer. Essa classe faz o parse de response.signature (o campo precisa existir e decodificar) e nunca faz verificação COSE dele. Ela nunca usa a credentialPublicKey armazenada. O próprio CustomServerTest do produto aceita uma assertion sem chave pública.
Depois da senha do MySQL, uma assinatura WebAuthn inválida que ainda corresponda a challenge, origin, rpIdHash e user-present é uma sessão completa do phpMyAdmin. Ou seja, o banco de dados.
| ID | ainda sem CVE |
| CWE | CWE-347, CWE-287 |
| CVSS | Alto: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| Produto | phpMyAdmin |
| Afetado | 5.2.3 tarball padrão (web-auth/webauthn-lib ausente) |
| Autenticação | Senha do MySQL, depois WebAuthn ignorado |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1 |
Ele ainda precisa da senha do MySQL (login por cookie). Depois dessa senha, ele faz POST de webauthn_request_response com challenge correspondente, host de origin, rpIdHash, user-present, id de credencial registrada e uma assinatura inválida. Ele entra na interface normal. O 2FA sumiu. Isso é RCE sobre os dados, não um shell PHP no host.
Se você realmente instalou web-auth/webauthn-lib, você está no WebauthnLibServer e não tem esse bug.
Eu li createServer, depois parseAndValidateAssertionResponse, depois o teste unitário que não passa uma chave pública. As verificações estruturais são reais: id de credencial em allowCredentials, clientData.type == webauthn.get, challenge corresponde à sessão, host de origin, rpIdHash, user-present. A assinatura é parseada e ignorada.
O primeiro cliente que apontei para isso foi educado. Ele fez POST de um JSON WebAuthn sem uma sessão de senha prévia. Página de login. Ele fez POST de route=/ com uma assinatura que nem sequer satisfazia as verificações estruturais. Você permanece no 2FA. webauthn_request_response ainda está no HTML.
Caminhos errados já registrados: Webauthn\Server presente (classe de servidor errada); challenge não era o desta sessão; host de origin incompatível; flag user-present desmarcada; id de credencial não estava em allowCredentials; não autenticado, sem senha do MySQL; um reverse shell. Teatro. O indício é o campo de 2FA sumido e uma página de navegação do phpMyAdmin.
Bugs de cliente parecem bugs de produto. Um helper que faz shadow de http.client nunca envia um pacote. .recv() em um HTTPResponse não é .read(). Menciono isso uma vez porque custou tempo e pareceu, por um minuto, que o phpMyAdmin estava bem.
Então: POST de username/password/token. Extraia o challenge da sessão da página de 2FA. Faça POST da assertion forjada. still_2fa=False landed=True. O Dockerfile do lab copia a 5.2.3, composer install --no-dev, depois deleta vendor/web-auth para que o CustomServer seja o que está em jogo. O último php -r sai com 1 se Webauthn\Server existir. A imagem deve falhar o build se a lib ainda estiver lá. init.sql registra WebAuthn 2FA para appuser sem uma chave de hardware.
cd lab
./run.sh
Aponte apenas para http://127.0.0.1:18080. Coloque o phpMyAdmin 5.2.3 como src/ ao lado do Dockerfile primeiro.
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
Instale web-auth/webauthn-lib para que createServer use a lib, ou faça a verificação COSE real de response.signature em relação à chave pública armazenada em CustomServer. Até então, desative o 2FA WebAuthn ou não exponha este phpMyAdmin.
CustomServer::parseAndValidateAssertionResponse · WebAuthn::createServer · CustomServerTestGNU Affero GPL v3.0. Consulte LICENSE. Apenas laboratório em loopback. Sem garantias.