Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
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-Heap-Based-Buffer-Overflow-in-Sudo-Baron-Samedit- — Análise técnica e desenvolvimento de exploit para CVE-2021-3156, um buffer overflow baseado em heap no Sudo, incluindo três exploits funcionais para escalonamento de privilégios locais nas principais distribuições Linux. | Kitploit
Ferramentas/GitHubGitHub/sornphut/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit-
Escalada de PrivilégiosAnálise de VulnerabilidadesExploraçãoTestes de PenetraçãoPapers e PesquisaAprendizado e EducaçãoExploração de Binários
GitHubsornphut/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit-

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

CVE-2021-3156-Heap-Based-Buffer-Overflow-in-Sudo-Baron-Samedit-

Análise técnica e desenvolvimento de exploit para CVE-2021-3156, um buffer overflow baseado em heap no Sudo, incluindo três exploits funcionais para escalonamento de privilégios locais nas principais distribuições Linux.

Ver Repositório
2há 1 anoAinda não revisado

Qualys Security Advisory

Baron Samedit: Estouro de buffer baseado em heap no Sudo (CVE-2021-3156)

======================================================================== Conteúdo

Resumo Análise Exploração Agradecimentos Cronologia

======================================================================== Resumo

Descobrimos um estouro de buffer baseado em heap no Sudo (https://www.sudo.ws/). Esta vulnerabilidade:

  • é explorável por qualquer usuário local (usuários normais e usuários do sistema, sudoers e não-sudoers), sem autenticação (ou seja, o invasor não precisa saber a senha do usuário);

  • foi introduzida em julho de 2011 (commit 8255ed69) e afeta todas as versões legadas de 1.8.2 a 1.8.31p2 e todas as versões estáveis de 1.9.0 a 1.9.5p1, em sua configuração padrão.

Desenvolvemos três exploits diferentes para esta vulnerabilidade e obtivemos privilégios totais de root no Ubuntu 20.04 (Sudo 1.8.31), Debian 10 (Sudo 1.8.27) e Fedora 33 (Sudo 1.9.2). Outros sistemas operacionais e distribuições provavelmente também são exploráveis.

======================================================================== Análise

Se o Sudo for executado para rodar um comando no modo "shell" (shell -c comando):

  • seja através da opção -s, que define a flag MODE_SHELL do Sudo;

  • ou através da opção -i, que define as flags MODE_SHELL e MODE_LOGIN_SHELL do Sudo;

então, no início da main() do Sudo, parse_args() reescreve argv (linhas 609-617), concatenando todos os argumentos da linha de comando (linhas 587-595) e escapando todos os metacaracteres com barras invertidas (linhas 590-591):


571 if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) { 572 char **av, *cmnd = NULL; 573 int ac = 1; ... 581 cmnd = dst = reallocarray(NULL, cmnd_size, 2); ... 587 for (av = argv; *av != NULL; av++) { 588 for (src = *av; src != '\0'; src++) { 589 / quote potential meta characters */ 590 if (!isalnum((unsigned char)*src) && *src != '_' && *src != '-' && *src != '$') 591 *dst++ = '\'; 592 *dst++ = *src; 593 } 594 -c cmnd */ ... 603 av = reallocarray(NULL, ac + 1, sizeof(char *)); ... 609 av[0] = (char plugin may override shell */ 610 if (cmnd != NULL) { 611 av[1] = "-c"; 612 av[2] = cmnd; 613 } 614 av[ac] = NULL; 615 616 argv = av; 617 argc = ac; 618 }

Mais tarde, em sudoers_policy_main(), set_cmnd() concatena os argumentos da linha de comando em um buffer baseado em heap "user_args" (linhas 864-871) e desescapa os metacaracteres (linhas 866-867), "para fins de correspondência e registro do sudoers":


819 if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) { ... 852 for (size = 0, av = NewArgv + 1; *av; av++) 853 size += strlen(*av) + 1; 854 if (size == 0 || (user_args = malloc(size)) == NULL) { ... 857 } 858 if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) { ... 864 for (to = user_args, av = NewArgv + 1; (from = *av); av++) { 865 while (*from) { 866 if (from[0] == '\' && !isspace((unsigned char)from[1])) 867 from++; 868 *to++ = *from++; 869 } 870 *to++ = ' '; 871 } ... 884 } ... 886 }

Infelizmente, se um argumento da linha de comando termina com um único caractere de barra invertida, então:

  • na linha 866, "from[0]" é o caractere de barra invertida, e "from[1]" é o terminador nulo do argumento (ou seja, não é um caractere de espaço);

  • na linha 867, "from" é incrementado e aponta para o terminador nulo;

  • na linha 868, o terminador nulo é copiado para o buffer "user_args", e "from" é incrementado novamente e aponta para o primeiro caractere após o terminador nulo (ou seja, fora dos limites do argumento);

  • o loop "while" nas linhas 865-869 lê e copia caracteres fora dos limites para o buffer "user_args".

Em outras palavras, set_cmnd() é vulnerável a um estouro de buffer baseado em heap, porque os caracteres fora dos limites que são copiados para o buffer "user_args" não foram incluídos em seu tamanho (calculado nas linhas 852-853).

Em teoria, no entanto, nenhum argumento da linha de comando pode terminar com um único caractere de barra invertida: se MODE_SHELL ou MODE_LOGIN_SHELL estiver definido (linha 858, uma condição necessária para atingir o código vulnerável), então MODE_SHELL está definido (linha 571) e parse_args() já escapou todos os metacaracteres, incluindo barras invertidas (ou seja, escapou cada barra invertida com uma segunda barra invertida).

Na prática, no entanto, o código vulnerável em set_cmnd() e o código de escape em parse_args() são cercados por condições ligeiramente diferentes:


819 if (sudo_mode & (MODE_RUN | MODE_EDIT | MODE_CHECK)) { ... 858 if (ISSET(sudo_mode, MODE_SHELL|MODE_LOGIN_SHELL)) {

versus:


571 if (ISSET(mode, MODE_RUN) && ISSET(flags, MODE_SHELL)) {

Nossa pergunta, então, é: podemos definir MODE_SHELL e MODE_EDIT ou MODE_CHECK (para atingir o código vulnerável) mas não o MODE_RUN padrão (para evitar o código de escape)?

A resposta, ao que parece, é não: se definirmos MODE_EDIT (opção -e, linha 361) ou MODE_CHECK (opção -l, linhas 423 e 519), então parse_args() remove MODE_SHELL das "valid_flags" (linhas 363 e 424) e sai com um erro se especificarmos uma flag inválida como MODE_SHELL (linhas 532-533):


358 case 'e': ... 361 mode = MODE_EDIT; 362 sudo_settings[ARG_SUDOEDIT].value = "true"; 363 valid_flags = MODE_NONINTERACTIVE; 364 break; ... 416 case 'l': ... 423 mode = MODE_LIST; 424 valid_flags = MODE_NONINTERACTIVE|MODE_LONG_LIST; 425 break; ... 518 if (argc > 0 && mode == MODE_LIST) 519 mode = MODE_CHECK; ... 532 if ((flags & valid_flags) != flags) 533 usage(1);

Mas encontramos uma brecha: se executarmos o Sudo como "sudoedit" em vez de "sudo", então parse_args() define automaticamente MODE_EDIT (linha 270) mas não redefine "valid_flags", e as "valid_flags" incluem MODE_SHELL por padrão (linhas 127 e 249):


127 #define DEFAULT_VALID_FLAGS (MODE_BACKGROUND|MODE_PRESERVE_ENV|MODE_RESET_HOME|MODE_LOGIN_SHELL|MODE_NONINTERACTIVE|MODE_SHELL) ... 249 int valid_flags = DEFAULT_VALID_FLAGS; ... 267 proglen = strlen(progname); 268 if (proglen > 4 && strcmp(progname + proglen - 4, "edit") == 0) { 269 progname = "sudoedit"; 270 mode = MODE_EDIT; 271 sudo_settings[ARG_SUDOEDIT].value = "true"; 272 }

Consequentemente, se executarmos "sudoedit -s", então definimos tanto MODE_EDIT quanto MODE_SHELL (mas não MODE_RUN), evitamos o código de escape, atingimos o código vulnerável e estouramos o buffer baseado em heap "user_args" através de um argumento da linha de comando que termina com um único caractere de barra invertida:


sudoedit -s '' perl -e 'print "A" x 65536' malloc(): corrupted top size Aborted (core dumped)

Do ponto de vista de um invasor, esse estouro de buffer é ideal:

  • controlamos o tamanho do buffer "user_args" que estouramos (o tamanho dos nossos argumentos de linha de comando concatenados, nas linhas 852-854);

  • controlamos independentemente o tamanho e o conteúdo do estouro em si (nosso último argumento de linha de comando é convenientemente seguido pelas nossas primeiras variáveis de ambiente, que não são incluídas no cálculo de tamanho nas linhas 852-853);

  • podemos até escrever bytes nulos no buffer que estouramos (todo argumento da linha de comando ou variável de ambiente que termina com uma única barra invertida escreve um byte nulo em "user_args", nas linhas 866-868).

Por exemplo, em um Linux amd64, o seguinte comando aloca um buffer "user_args" de 24 bytes (um chunk de heap de 32 bytes) e sobrescreve o campo de tamanho do próximo chunk com "A=a\0B=b\0" (0x00623d4200613d41), seu campo fd com "C=c\0D=d\0" (0x00643d4400633d43) e seu campo bk com "E=e\0F=f\0" (0x00663d4600653d45):


env -i 'AA=a' 'B=b' 'C=c' 'D=d' 'E=e' 'F=f' sudoedit -s '1234567890123456789012'

--|--------+--------+--------+--------|--------+--------+--------+--------+-- | | |12345678|90123456|789012.A|A=a.B=b.|C=c.D=d.|E=e.F=f.| --|--------+--------+--------+--------|--------+--------+--------+--------+-- size <---- buffer user_args ----> size fd bk

======================================================================== Exploração

Como o Sudo chama funções de localização no início da sua função main():


154 setlocale(LC_ALL, ""); 155 bindtextdomain(PACKAGE_NAME, LOCALEDIR); 156 textdomain(PACKAGE_NAME);

e passa strings de tradução (através da função gettext() e da macro _()) para funções de formatação de string, como:


301 sudo_printf(SUDO_CONV_ERROR_MSG, _("%s is not in the sudoers " 302 "file. This incident will be reported.\n"), user_name);

inicialmente queríamos reutilizar a técnica fascinante do halfdog de https://www.halfdog.net/Security/2017/LibcRealpathBufferUnderflow/ e transformar o estouro de buffer baseado em heap do Sudo em um exploit de string de formato. Mais precisamente:

  • na linha 154, em setlocale(), alocamos (malloc()) e liberamos (free()) várias variáveis de ambiente LC (LC_CTYPE, LC_MESSAGES, LC_TIME, etc), criando assim pequenos buracos no início do heap do Sudo (chunks fast ou tcache livres);

  • na linha 155, bindtextdomain() aloca (malloc()) uma struct binding, que contém um ponteiro dirname para o nome de um diretório que contém arquivos de catálogo ".mo" e, portanto, strings de tradução;

  • em set_cmnd(), alocamos o buffer "user_args" em um dos buracos no início do heap do Sudo e estouramos esse buffer, sobrescrevendo assim o ponteiro dirname da struct binding;

  • na linha 301 (por exemplo), gettext() (através da macro _()) carrega nossa própria string de tradução do dirname sobrescrito -- em outras palavras, controlamos a string de formato que é passada para sudo_printf().

Para implementar esta técnica inicial, escrevemos um brute-forcer rudimentar que executa o Sudo dentro do gdb, estoura o buffer "user_args" e seleciona aleatoriamente os seguintes parâmetros:

  • as variáveis de ambiente LC que passamos para o Sudo e seu comprimento (usamos a locale "C.UTF-8" e anexamos um "@modifier" aleatório);

  • o tamanho do buffer "user_args" que estouramos;

  • o tamanho do próprio estouro;

  • se passamos pelo código de autenticação do Sudo (opção -A ou -n) ou não (opção -u #realuid).

Infelizmente, esta técnica inicial falhou; nosso brute-forcer conseguiu sobrescrever o ponteiro dirname da struct binding:


Program received signal SIGSEGV, Segmentation fault.

0x00007f6e0dde1ea9 in __dcigettext (domainname=domainname@entry=0x7f6e0d9cc020 "sudoers", msgid1=msgid1@entry=0x7f6e0d9cc014 "user NOT in sudoers", msgid2=msgid2@entry=0x0, plural=plural@entry=0, n=n@entry=0, category=5) at dcigettext.c:619

=> 0x7f6e0dde1ea9 <__dcigettext+1257>: cmpb $0x2f,(%rax)

rax 0x4141414141414141 4702111234474983745

mas LC_MESSAGES era sempre a locale padrão "C" (não "C.UTF-8"), o que desabilita a tradução de strings em gettext() (ou seja, gettext() retorna a string de formato original, não a nossa).

Felizmente, no entanto, nosso brute-forcer produziu dezenas de crashes únicos do Sudo e backtraces do gdb; entre estes, três chamaram nossa atenção e eventualmente exploramos todos os três.

======================================================================== 1/ Sobrescrita de struct sudo_hook_entry

O primeiro crash que chamou nossa atenção é:


Program received signal SIGSEGV, Segmentation fault.

0x000056291a25d502 in process_hooks_getenv (name=name@entry=0x7f4a6d7dc046 "SYSTEMD_BYPASS_USERDB", value=value@entry=0x7ffc595cc240) at ../../src/hooks.c:108

=> 0x56291a25d502 <process_hooks_getenv+82>: callq *0x8(%rbx)

rbx 0x56291c1df2b0 94734565372592

0x56291c1df2b0: 0x4141414141414141 0x4141414141414141

Incrivelmente, a função process_hooks_getenv() do Sudo travou (na linha 108) porque sobrescrevemos diretamente um ponteiro de função, getenv_fn (um membro de uma struct sudo_hook_entry baseada em heap):


99 int 100 process_hooks_getenv(const char *name, char **value) 101 { 102 struct sudo_hook_entry *hook; 103 char *val = NULL; ... 107 SLIST_FOREACH(hook, &sudo_hook_getenv_list, entries) { 108 rc = hook->u.getenv_fn(name, &val, hook->closure);

Para explorar esta sobrescrita de struct sudo_hook_entry, observamos que:

  • a chamada para getenv_fn (na linha 108) é compatível com uma chamada para execve():

    . name ("SYSTEMD_BYPASS_USERDB") é compatível com o argumento pathname de execve();

    . &val (um ponteiro para um ponteiro NULL) é compatível com argv de execve();

    . hook->closure (um ponteiro NULL) é compatível com envp de execve();

  • podemos derrotar o ASLR sobrescrevendo parcialmente o ponteiro de função getenv_fn (que aponta para a função sudoers_hook_getenv() na biblioteca compartilhada sudoers.so); e felizmente, o início de sudoers.so contém uma chamada para execve() (ou execv()):


0000000000008a00 execv@plt: 8a00: f3 0f 1e fa endbr64 8a04: f2 ff 25 65 55 05 00 bnd jmpq *0x55565(%rip) # 5df70 <execv@GLIBC_2.2.5> 8a0b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)

  • podemos ler /dev/kmsg (dmesg) como um usuário não privilegiado no Ubuntu e, portanto, obter informações detalhadas sobre nossos crashes do Sudo.

Consequentemente, adotamos a seguinte estratégia:

  • Primeiro, fazemos brute-force dos parâmetros do exploit até sobrescrevermos getenv_fn com um endereço inválido do espaço do usuário (acima de 0x800000000000) -- até observarmos uma falha de proteção geral no local da chamada de getenv_fn:

sudoedit[15904] general protection fault ip:55e9b645b502 sp:7ffe53d6fa40 error:0 in sudo[55e9b644e000+1a000] ^^^

  • Em seguida, reutilizamos esses parâmetros do exploit, mas sobrescrevemos getenv_fn com um padrão regular de endereços de espaço de usuário válidos (abaixo de 0x800000000000) mas não mapeados -- neste exemplo, getenv_fn é o 22º ponteiro que sobrescrevemos (0x32 é '2', parte do nosso padrão):

sudoedit[15906]: segfault at 323230303030 ip 0000323230303030 sp 00007ffeeabf2868 error 14 in sudo[55b036c16000+5000] ^^^^

  • Por último, sobrescrevemos parcialmente getenv_fn (sobrescrevemos seus dois bytes menos significativos com 0x8a00, o offset de execv() em sudoers.so, e seu terceiro byte com 0x00, o terminador nulo de user_args em set_cmnd()) até derrotarmos o ASLR -- temos uma boa chance de sobrescrever getenv_fn com o endereço de execv() após 2^(3*8-12) = 2^12 = 4096 tentativas, executando assim nosso próprio binário, chamado "SYSTEMD_BYPASS_USERDB", como root.

Testamos com sucesso este primeiro exploit no Ubuntu 20.04.

======================================================================== 2/ Sobrescrita de struct service_user

O segundo crash que chamou nossa atenção é:


Program received signal SIGSEGV, Segmentation fault.

0x00007f6bf9c294ee in nss_load_library (ni=ni@entry=0x55cf1a1dd040) at nsswitch.c:344

=> 0x7f6bf9c294ee <nss_load_library+46>: cmpq $0x0,0x8(%rbx)

rbx 0x41414141414141 18367622009667905

A função nss_load_library() da glibc travou (na linha 344) porque sobrescrevemos o ponteiro "library", um membro de uma struct service_user baseada em heap:


327 static int 328 nss_load_library (service_user ni) 329 { 330 if (ni->library == NULL) 331 { ... 338 ni->library = nss_new_service (service_table ?: &default_table, 339 ni->name); ... 342 } 343 344 if (ni->library->lib_handle == NULL) 345 { 346 / Load the shared library. / 347 size_t shlen = (7 + strlen (ni->name) + 3 348 + strlen (__nss_shlib_revision) + 1); 349 int saved_errno = errno; 350 char shlib_name[shlen]; 351 352 / Construct shared object name. */ 353 __stpcpy (__stpcpy (__stpcpy (_"), 355 ni->name), 356 ".so"), 357 __nss_shlib_revision); 358 359 ni->library->lib_handle = __libc_dlopen (shlib_name);

Podemos facilmente transformar esta sobrescrita de struct service_user em uma execução de código arbitrário:

  • sobrescrevemos ni->library com um ponteiro NULL, para entrar no bloco nas linhas 330-342, evitar o crash na linha 344 e entrar no bloco nas linhas 344-359;

  • sobrescrevemos ni->name (um array de caracteres, inicialmente "systemd") com "X/X";

  • as linhas 353-357 constroem o nome de uma biblioteca compartilhada "libnss_X/X.so.2" (em vez de "libnss_systemd.so.2");- na linha 359, carregamos nossa própria biblioteca compartilhada "libnss_X/X.so.2" do diretório de trabalho atual e executamos nosso construtor _init() como root.

Testamos com sucesso este segundo exploit no Ubuntu 20.04, Debian 10, e Fedora 33.

======================================================================== 3/ sobrescrita de def_timestampdir

Nosso terceiro exploit não é derivado de um dos crashes do Sudo, mas de uma observação casual: durante nossa força bruta, o Sudo criou dezenas de novos diretórios em nosso diretório de trabalho atual (AAAAAA, AAAAAAAAA, etc). Cada um desses diretórios pertence ao root e contém apenas um pequeno arquivo, nomeado após nosso próprio usuário: o arquivo de timestamp do Sudo -- nós evidentemente sobrescrevemos def_timestampdir, o nome do diretório de timestamp do Sudo.

Se sobrescrevermos def_timestampdir com o nome de um diretório que não existe, então podemos competir contra o ts_mkdirs() do Sudo, criar um link simbólico para um arquivo arbitrário, e:

3a/ seja chown() este arquivo arbitrário para o usuário root e grupo root;

3b/ seja abrir (ou criar) este arquivo arbitrário como root, e escrever um struct timestamp_entry nele.

Não conseguimos transformar 3a/ em privilégios totais de root (por exemplo, se fizermos chown() do nosso próprio binário SUID para root, então o kernel automaticamente remove o bit SUID do nosso binário). Se você, caro leitor, encontrar uma solução para este problema, por favor publique-a na lista de discussão pública oss-security!

Eventualmente, conseguimos transformar 3b/ em privilégios totais de root, mas inicialmente enfrentamos dois problemas:

  • O timestamp_open() do Sudo exclui nosso link simbólico arbitrário se o arquivo ao qual ele aponta for mais antigo que o tempo de boot. Conseguimos resolver este primeiro problema criando um arquivo de timestamp muito antigo (desde a época Unix), esperando até que o timestamp_open() o exclua, e competindo contra o timestamp_open() para criar nosso link simbólico final e arbitrário.

  • Não controlamos o conteúdo do struct timestamp_entry que é escrito no arquivo arbitrário. Até onde sabemos, controlamos apenas três bytes (um ID de processo ou um struct timespec), e não conseguimos transformar esta escrita de três bytes em privilégios totais de root. Se você, caro leitor, encontrar uma solução para este problema, por favor publique-a na lista de discussão pública oss-security!

No entanto, conseguimos contornar este segundo problema abusando de um bug menor no timestamp_lock() do Sudo. Se vencermos as duas corridas contra ts_mkdirs() e timestamp_open(), e se nosso link simbólico arbitrário apontar para /etc/passwd, então este arquivo é aberto como root, e:


65 struct timestamp_entry { 66 unsigned short version; /* version number / 67 unsigned short size; / entry size / 68 unsigned short type; / TS_GLOBAL, TS_TTY, TS_PPID */ .. 78 };

305 static ssize_t 306 ts_write(int fd, const char *fname, struct timestamp_entry *entry, off_t offset) 307 { ... 318 nwritten = pwrite(fd, entry, entry->size, offset); ... 350 }

619 bool 620 timestamp_lock(void *vcookie, struct passwd *pw) 621 { 622 struct ts_cookie *cookie = vcookie; 623 struct timestamp_entry entry; ... 644 nread = read(cookie->fd, &entry, sizeof(entry)); 645 if (nread == 0) { ... 652 } else if (entry.type != TS_LOCKEXCL) { ... 657 if (ts_write(cookie->fd, cookie->fname, &entry, 0) == -1)

  • na linha 644, os primeiros 0x38 bytes de /etc/passwd ("root❌0:0:...") são lidos em um struct timestamp_entry baseado na pilha, entry;

  • na linha 652, entry.type é 0x783a (":x"), não TS_LOCKEXCL;

  • nas linhas 657 e 318, entry->size bytes do entry baseado na pilha são escritos em /etc/passwd, mas entry->size é na verdade 0x746f ("ot"), não sizeof(struct timestamp_entry).

Como resultado, escrevemos todo o conteúdo da pilha do Sudo em /etc/passwd (incluindo nossos argumentos de linha de comando e nossas variáveis de ambiente): nós injetamos um usuário arbitrário em /etc/passwd e, portanto, obtemos privilégios totais de root. Testamos com sucesso este terceiro exploit no Ubuntu 20.04.

Nota: este bug menor no timestamp_lock() foi corrigido em janeiro de 2020 pelo commit 586b418a, mas esta correção não foi retroportada para versões legadas.

======================================================================== Agradecimentos

Agradecemos a Todd C. Miller por seu profissionalismo, resposta rápida e atenção meticulosa a cada detalhe em nosso relatório. Agradecemos também aos membros de distros@openwall.

======================================================================== Cronograma

2021-01-13: Aviso enviado para Todd.Miller@sudo.

2021-01-19: Aviso e patches enviados para distros@openwall.

2021-01-26: Data de lançamento coordenada (6:00 PM UTC).

Baixar ferramenta
dst++ = ' '; 595 } ... 600 ac += 2; /
)user_details.shell; /
stpcpy (shlib_name, 354 "libnss