
MySQL Community Server 26.7.0 - Alta - SET ROLE ALL EXCEPT resíduo FILE - gravação mysqld UID
abraxaslabs.tech · github.com/abraxas · @abraxas_null · [email protected] · mysql-set-role-leftover
Write-up: https://abraxaslabs.tech/research/mysql-set-role-leftover
Classe: Privilégio residual (FILE como UID do mysqld; não é LPE de SO) Alcance: Remoto
MySQL Community Server mysqld 26.7.0 (06a5c1c) - Oracle
SET ROLE ALL EXCEPT rfile (ou SET ROLE DEFAULT sem padrões) limpa a lista de roles ativas, então checkout_access_maps() retorna sem set_master_access(user->access). CURRENT_ROLE() é NONE. Bits globais estáticos que a role agregou (FILE, PROCESS, SUPER, ...) permanecem em m_master_access. check_access lê esse resíduo. Lab: o FILE residual grava INTO OUTFILE como o UID do mysqld.
SET ROLE NONE é o caminho que restaura ACL_USER::access. Privilégios dinâmicos (SYSTEM_USER) são removidos. O resíduo em nível de banco já está fechado (Bug#35386565). Contas padrão não têm roles.
| ID | ainda sem CVE |
| Classe | Privilégio residual (FILE como UID do mysqld; não é LPE de SO) |
| Alcance | Remoto (SQL autenticado) |
| CWE | CWE-269 |
| CVSS | Alto: 8.1 CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N |
| Produto | MySQL Community Server mysqld |
| Afetado | 26.7.0 (06a5c1c99c377fc41b2eba1ea244e8b220bdc3c8) |
| Autenticação | autenticado; a vítima recebe uma role que carrega ACLs globais estáticas (FILE no lab) |
| Licença | GNU Affero GPL v3.0 |
| Lab | apenas 127.0.0.1. Arquivo testemunha. Gravação de FILE residual. |
Possuir uma conta à qual foi concedida uma role com FILE (ou outro bit global estático). Executar SET ROLE nessa role e depois SET ROLE ALL EXCEPT nela. CURRENT_ROLE() reporta NONE. SELECT 'witness' INTO OUTFILE ainda grava como o UID do mysqld sob secure_file_priv.
Uma aplicação que faz sandbox com SET ROLE ALL EXCEPT dangerous_role ou SET ROLE DEFAULT e depois confia em CURRENT_ROLE() ainda mantém esses bits estáticos. SET ROLE NONE de fato os remove.
O resíduo não concede FILE à conta que nunca o teve por meio de uma role. Ele falha em remover FILE após a desativação.
Mesma caça ao 26.7.0 que os três resíduos de cliente. O daemon padrão não publicado Crit/High estava vazio. ACL / roles ainda tinham esse retorno antecipado de role vazia.
void Security_context::checkout_access_maps(void) {
if (m_acl_map != nullptr) {
get_global_acl_cache()->return_acl_map(m_acl_map);
m_acl_map = nullptr;
}
if (m_active_roles.size() == 0) return;
// ...
}
activate_role_all (TODOS EXCETO) e chamada vazia de activate_role_default que faz esse checkout. activate_role_none então executa set_master_access(user->access).
Caminho errado já registrado: o primeiro SELECT note FROM lab.t INTO OUTFILE remanescente retornou 1142 (cache de db_acl() 0). O SELECT 'witness' INTO OUTFILE constante é o oráculo de FILE.
cd lab
./run.sh
Imagem mysql:26.7.0. Publicado 127.0.0.1:18630. A role rfile tem FILE. A vítima tem GRANT SELECT ON lab.* e GRANT rfile, file_priv=N, sem role padrão. Login outfile 1227. SET ROLE rfile outfile ok. SET ROLE ALL EXCEPT rfile faz CURRENT_ROLE() NONE; gravações outfile remanescentes escrevem /var/lib/mysql-files/MYSQL-SET-ROLE-LEFTOVER-WITNESS. SET ROLE NONE volta a ser 1227.
SUCCESS mysql-set-role-leftover before-file=denied with-role=yes leftover-role=NONE leftover-file=yes after-none=denied dump=26.7.0 image=mysql:26.7.0 MYSQL-SET-ROLE-LEFTOVER-WITNESS
Quando m_active_roles está vazio, checkout_access_maps() deve chamar set_master_access(user->access) da mesma forma que SET ROLE NONE já faz. ALL EXCEPT / DEFAULT vazio não deve manter o mapa agregado anterior.
06a5c1c99c377fc41b2eba1ea244e8b220bdc3c8)sql/auth/sql_security_ctx.cc checkout_access_mapssql/auth/roles.cc activate_role_all / activate_role_none