
Auditor de funções libc em tempo de execução que detecta condições de corrida de acesso a arquivos e vulnerabilidades de symlink ao interceptar syscalls do sistema de arquivos via LD_PRELOAD, registrando potenciais caminhos de escalonamento de privilégios.
O PathAuditor é uma ferramenta criada para encontrar vulnerabilidades relacionadas a acesso a arquivos, auditando funções da libc.
A ideia é basicamente a seguinte:
Estamos usando LD_PRELOAD para interceptar todas as chamadas de biblioteca relacionadas ao sistema de arquivos e registrar quaisquer violações encontradas no syslog.
Este não é um produto oficial do Google.
Vejamos um exemplo do tipo de vulnerabilidade que esta ferramenta pode detectar. O CVE-2019-3461 foi um bug no tmpreaper, uma ferramenta que percorre /tmp e exclui arquivos antigos. Geralmente é executado como um cron job como root. Como ele não quer excluir arquivos fora de tmp, usava o seguinte código para verificar se um diretório é um ponto de montagem:
if (S_ISDIR (sb.st_mode)) {
char *dst;
if ((dst = malloc(strlen(ent->d_name) + 3)) == NULL)
message (LOG_FATAL, "malloc falhou.\n");
strcpy(dst, ent->d_name);
strcat(dst, "/X");
rename(ent->d_name, dst);
if (errno == EXDEV) {
free(dst);
message (LOG_VERBOSE,
"Arquivo em dispositivo diferente ignorado: `%s/%s'\n",
dirname, ent->d_name);
continue;
}
// [...]
Em resumo, este código chama rename("/tmp/foo", "/tmp/foo/x") que retornará
EXDEV se "/tmp/foo" for um ponto de montagem.
O PathAuditor sinalizaria esta chamada como uma potencial vulnerabilidade se "/tmp/foo"
for de propriedade de qualquer usuário que não seja root.
Para entender o porquê, precisamos pensar no que acontece no kernel quando a chamada de sistema rename é executada (simplificado):
"/tmp/foo" para o primeiro argumento."/tmp/foo/x" para o segundo argumento.Aqui existe uma condição de corrida, pois "/tmp/foo" será resolvido duas vezes. Se for
controlado pelo usuário, ele pode substituí-lo por um arquivo diferente a qualquer momento.
Em particular, queremos que "/tmp/foo" seja um diretório inicialmente para passar na
verificação if(S_ISDIR) no código do tmpreaper. Em seguida, o substituímos por um arquivo
pouco antes do código entrar na chamada de sistema. Quando o kernel resolver o primeiro argumento,
verá um arquivo com conteúdo controlado pelo usuário.
Agora o substituímos novamente, desta vez com um link simbólico para um diretório arbitrário no
mesmo sistema de arquivos. O kernel resolverá o caminho uma segunda vez, seguirá o link
simbólico e moverá o arquivo controlado para um diretório escolhido.
A restrição do mesmo sistema de arquivos ocorre porque rename não funciona entre sistemas de arquivos. Mas em algumas distribuições Linux, /tmp é apenas uma pasta na raiz por padrão, e você poderia usar este bug para mover um arquivo para /etc/cron, que será executado como root.
Para testar, você precisa compilar libpath_auditor.so com o bazel e carregá-lo em um binário usando LD_PRELOAD. Qualquer violação será registrada no syslog, portanto certifique-se de que ele esteja em execução.
bazel build //pathauditor/libc:libpath_auditor.so
LD_PRELOAD=/caminho/para/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar
tail /var/log/syslog
Também é possível executar isso em todos os processos do sistema adicionando-o ao /etc/ld.so.preload. Porém, vale ressaltar que isso é recomendado apenas em sistemas de teste, pois pode causar instabilidade.
Como início rápido, você pode testar o contêiner docker fornecido com este projeto:
docker build -t pathauditor-example .
docker run -it pathauditor-example
# LD_PRELOAD=/pathauditor/bazel-bin/pathauditor/libc/libpath_auditor.so cat /tmp/foo/bar
# cat /var/log/syslog