Skip to content
KitploitKITPLOIT
FerramentasExploitsBlog
Log in
Enviar
FerramentasExploitsBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
CVE-2021-3156 — CVE-2021-3156: POC, Docker e Análise | Kitploit
Ferramentas/GitHubGitHub/chenaotian/cve-2021-3156
Escalada de PrivilégiosAnálise de VulnerabilidadesAnálise de CódigoExploraçãoEngenharia ReversaDepuradoresFuzzingTestes de PenetraçãoAprendizado e EducaçãoExploração de BináriosLabs e Prática
11214há 4 anosAinda não revisado

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar
GitHub
chenaotian/cve-2021-3156

CVE-2021-3156

CVE-2021-3156: POC, Docker e Análise

Ver Repositório

CVE-2021-3156

[toc]

Resumo da vulnerabilidade

ID da vulnerabilidade: CVE-2021-3156

Pontuação da vulnerabilidade:

Produto afetado: linux sudo

Versões afetadas: 1.8.2-1.8.31sp12; 1.9.0-1.9.5sp1

Condições de exploração: linux local; sudo com suid e executável

Efeito da exploração: Escalada local de privilégios

Obter código-fonte: https://www.sudo.ws/getting/source/

Configuração do ambiente

Ambiente Docker: chenaotian/cve-2021-3156

Docker que montei, fornece:

  1. sudo compilado manualmente com suporte a depuração de código-fonte
  2. glibc com símbolos de depuração
  3. gdb e os plugins do gdb pwngdb & pwndbg
  4. exp.c e o exploit compilado com sucesso

Tudo está no diretório /root:

image-20220124223312224

  • O diretório exp é onde estão o código-fonte do exploit e o binário compilado, pode ser executado diretamente neste Docker
  • glibc-2.27 é o diretório dos fontes da versão do libc neste ambiente
  • sudo-1.8.21 é o diretório dos fontes do sudo neste ambiente, usei este para compilar.

Testando o exploit:``` cd exp su test ./exp whoami

O conteúdo relacionado à depuração é visto mais adiante em [alguns comandos de depuração](#一些调试命令)


## Princípio da vulnerabilidade

payload de acionamento de vulnerabilidade```shell
sudoedit -s '\' `python3 -c "print('A'*80)"`

Análise do código fonte (sudo-1.8.21): Primeiro, a função main em sudo.c (sudo.c: 133):```c int main(int argc, char *argv[], char *envp[]) { int nargc, ok, status = 0; char **nargv, **env_add; char **user_info, **command_info, **argv_out, **user_env_out; struct sudo_settings *settings; struct plugin_container *plugin, *next; sigset_t mask; debug_decl_vars(main, SUDO_DEBUG_MAIN)

··· ···
··· ···

/* Parse command line arguments. */
//在这里处理输入参数,设置sudo_mode
sudo_mode = parse_args(argc, argv, &nargc, &nargv, &settings, &env_add);

··· ···
··· ···
    
switch (sudo_mode & MODE_MASK) {
··· ···
··· ···
case MODE_EDIT:
case MODE_RUN:
    ok = policy_check(&policy_plugin, nargc, nargv, env_add,
	&command_info, &argv_out, &user_env_out);
    ··· ···
    ··· ···
}

··· ···
··· ···

}

- Primeiro, chame a função parse_args para processar os parâmetros que inserimos. Na verdade, aqui inserimos apenas um `-s`, nada para definir, defina sudo_mode como MODE_EDIT e MODE_SHELL.
- Então, dependendo do sudo_mode, MODE_EDIT chamará policy_check.

Em seguida, está em sudo.c a função policy_check (sudo.c: 1136):```c
static int
policy_check(struct plugin_container *plugin, int argc, char * const argv[],
    char *env_add[], char **command_info[], char **argv_out[],
    char **user_env_out[])
{
    ··· ···
    ··· ···
    ret = plugin->u.policy->check_policy(argc, argv, env_add, command_info,
	argv_out, user_env_out);
    ···
}

A função de callback plugin->u.policy->check_policy foi chamada. É possível depurar para ver a função real desta função:

image-20220123113326096

O que é chamado é a função sudoers_policy_check (policy.c: 760) em policy.c:```c static int sudoers_policy_check(int argc, char * const argv[], char *env_add[], char **command_infop[], char **argv_out[], char **user_env_out[]) { ··· ···

exec_args.argv = argv_out;
exec_args.envp = user_env_out;
exec_args.info = command_infop;

ret = sudoers_policy_main(argc, argv, 0, env_add, &exec_args);
··· ···
··· ···

}

Em seguida, a função sudoers_policy_main em sudoers.c foi chamada (sudoers.c: 224):```c
int
sudoers_policy_main(int argc, char * const argv[], int pwflag, char *env_add[],
    void *closure)
{
    ··· ···
    ··· ···

    /*
     * Make a local copy of argc/argv, with special handling
     * for pseudo-commands and the '-i' option.
     */
    if (argc == 0) {
	··· ···
    } else {
	/* Must leave an extra slot before NewArgv for bash's --login */
	NewArgc = argc;
	NewArgv = reallocarray(NULL, NewArgc + 2, sizeof(char *));
	··· ···
	}
	memcpy(++NewArgv, argv, argc * sizeof(char *));
	NewArgv[NewArgc] = NULL;
	··· ···
	}
    }
	··· ···
    cmnd_status = set_cmnd();
    ··· ···
    ··· ···
    ··· ···
}

Aqui são definidas algumas variáveis globais, NewArgc e NewArgv como a seguir, que são na verdade os parâmetros passados.

image-20220123113819116

Em seguida, entra na função set_cmnd em sudoers.c (sudoers.c: 796):```c static int set_cmnd(void) { ··· ··· ··· ···

/* set user_args */
if (NewArgc > 1) {
    char *to, *from, **av;
    size_t size, n;

    /* Alloc and build up user_args. */
    //根据参数总长度计算size, 后续malloc 申请,没有问题
    for (size = 0, av = NewArgv + 1; *av; av++)
	size += strlen(*av) + 1;
    if (size == 0 || (user_args = malloc(size)) == NULL) {
	sudo_warnx(U_("%s: %s"), __func__, U_("unable to allocate memory"));
	debug_return_int(-1);
    }
    if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {
	/*
	 * When running a command via a shell, the sudo front-end
	 * escapes potential meta chars.  We unescape non-spaces
	 * for sudoers matching and logging purposes.
	 */
     //将所有参数拷贝到一起放到堆中,逻辑是遇到'\'加非空格类型字符则只拷贝非空格字符
     //但这里\x00 并不算空格类型字符
     //他没有考虑参数如果只有一个'\'或以'\'结尾并且下两个字符后就是另一个字符串情况
	for (to = user_args, av = NewArgv + 1; (from = *av); av++) {
	    while (*from) {
		if (from[0] == '\\' && !isspace((unsigned char)from[1]))
		    from++;
		*to++ = *from++;
	    }
	    *to++ = ' ';
	}
	*--to = '\0';
    } 
    ··· ···
}
}
··· ···
··· ···

}

O estouro também ocorre aqui, como pode ser visto nos comentários do código. O estouro de heap ocorre ao copiar para o heap. A intenção original deste trecho de código é copiar todos os parâmetros de NewArgv para o heap, separados por espaços, e quando encontrar `\+caractere não espaço`, copiar apenas esse caractere.

**Mas ele não considerou uma situação: se um elemento de NewArgv termina com `\`, então temos a estrutura `\+\x00`, e `\x00` não é um caractere de espaço (incrivelmente). Isso significa que, após copiar `\x00` para o heap, a variável `from` é incrementada (duas vezes em um loop) e passa direto pela condição de término do while (`\x00`), fazendo com que o programa pense que os parâmetros não foram totalmente copiados e continue copiando até encontrar o próximo `\x00`.**

Nesse cenário, pode-se ver que `\+\x00` é imediatamente seguido pelo próximo parâmetro `A*80`, então a cópia continuará até o final de `A*80`. Mas não esqueça que, em seguida, o parâmetro `A*80` ainda será processado normalmente, resultando em uma segunda cópia. Portanto, `A*80` é copiado duas vezes no total, mas o chunk foi alocado com base no tamanho de apenas uma string `A*80`, excedendo em muito o tamanho alocado.

image-20220123113907744

Então ocorre o estouro, antes da cópia:

image-20220123114036691

Depois da cópia:

image-20220123114137794

O caminho geral de disparo da vulnerabilidade é (ao depurar, basta definir pontos de interrupção nestas funções):

- sudo.c : main
  - sudo.c : policy_check
    - policy.c : sudoerrs_policy_check
      - sudoers.c : sudoers_policy_main
        - sudoers.c : set_cmnd
          - sudoers.c : 859

## Princípio de Exploração da Vulnerabilidade

Referência: [blasty/CVE-2021-3156](https://github.com/blasty/CVE-2021-3156). **Mas o método de layout de heap dele é difícil de encontrar; aqui analisamos detalhadamente o método de layout de heap.** Usando a variável de ambiente `LC_*` para organizar o heap, o chunk que transborda irá sobrescrever exatamente a estrutura `service_user` que a função `nss_load_library` precisa carregar para a biblioteca compartilhada (so). Sobrescrevendo a string do nome da biblioteca nessa estrutura, fazemos o programa carregar a biblioteca que especificamos, permitindo a execução de código arbitrário.
Baixar ferramenta