Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
CVE-2021-3156-Heap-Based-Buffer-Overflow-in-Sudo-Baron-Samedit- | Kitploit
Herramientas/GitHubGitHub/sornphut/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit-
Escalada de PrivilegiosAnálisis de VulnerabilidadesExplotaciónPruebas de PenetraciónPapers e InvestigaciónAprendizaje y EducaciónExplotación de Binarios
GitHubsornphut/cve-2021-3156-heap-based-buffer-overflow-in-sudo-baron-samedit-

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

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

Ver Repositorio
hace 1 añoAún no revisado

Aviso de seguridad de Qualys

Baron Samedit: Desbordamiento de búfer basado en montón en Sudo (CVE-2021-3156)

======================================================================== Contenido

Resumen Análisis Explotación Agradecimientos Cronología

======================================================================== Resumen

Descubrimos un desbordamiento de búfer basado en montón en Sudo (https://www.sudo.ws/). Esta vulnerabilidad:

  • es explotable por cualquier usuario local (usuarios normales y usuarios del sistema, sudoers y no sudoers), sin autenticación (es decir, el atacante no necesita conocer la contraseña del usuario);

  • se introdujo en julio de 2011 (commit 8255ed69) y afecta a todas las versiones heredadas de 1.8.2 a 1.8.31p2 y a todas las versiones estables de 1.9.0 a 1.9.5p1, en su configuración predeterminada.

Desarrollamos tres exploits diferentes para esta vulnerabilidad y obtuvimos privilegios de root completos en Ubuntu 20.04 (Sudo 1.8.31), Debian 10 (Sudo 1.8.27) y Fedora 33 (Sudo 1.9.2). Otros sistemas operativos y distribuciones probablemente también sean explotables.

======================================================================== Análisis

Si se ejecuta Sudo para lanzar un comando en modo "shell" (shell -c comando):

  • ya sea mediante la opción -s, que establece el flag MODE_SHELL de Sudo;

  • o mediante la opción -i, que establece los flags MODE_SHELL y MODE_LOGIN_SHELL de Sudo;

entonces, al comienzo de main() de Sudo, parse_args() reescribe argv (líneas 609-617), concatenando todos los argumentos de la línea de comandos (líneas 587-595) y escapando todos los metacaracteres con barras invertidas (líneas 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 }

Más adelante, en sudoers_policy_main(), set_cmnd() concatena los argumentos de la línea de comandos en un búfer basado en montón "user_args" (líneas 864-871) y desescapa los metacaracteres (líneas 866-867), "para fines de coincidencia y registro de 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 }

Desafortunadamente, si un argumento de la línea de comandos termina con un solo carácter de barra invertida, entonces:

  • en la línea 866, "from[0]" es el carácter de barra invertida, y "from[1]" es el terminador nulo del argumento (es decir, no es un carácter de espacio);

  • en la línea 867, "from" se incrementa y apunta al terminador nulo;

  • en la línea 868, el terminador nulo se copia al búfer "user_args", y "from" se incrementa de nuevo y apunta al primer carácter después del terminador nulo (es decir, fuera de los límites del argumento);

  • el bucle "while" en las líneas 865-869 lee y copia caracteres fuera de los límites al búfer "user_args".

En otras palabras, set_cmnd() es vulnerable a un desbordamiento de búfer basado en montón, porque los caracteres fuera de los límites que se copian al búfer "user_args" no se incluyeron en su tamaño (calculado en las líneas 852-853).

En teoría, sin embargo, ningún argumento de la línea de comandos puede terminar con un solo carácter de barra invertida: si MODE_SHELL o MODE_LOGIN_SHELL está establecido (línea 858, una condición necesaria para alcanzar el código vulnerable), entonces MODE_SHELL se establece (línea 571) y parse_args() ya escapó todos los metacaracteres, incluidas las barras invertidas (es decir, escapó cada barra invertida con una segunda barra invertida).

En la práctica, sin embargo, el código vulnerable en set_cmnd() y el código de escape en parse_args() están rodeados por condiciones ligeramente 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)) {

Nuestra pregunta, entonces, es: ¿podemos establecer MODE_SHELL y ya sea MODE_EDIT o MODE_CHECK (para alcanzar el código vulnerable) pero no el MODE_RUN predeterminado (para evitar el código de escape)?

La respuesta, al parecer, es no: si establecemos MODE_EDIT (opción -e, línea 361) o MODE_CHECK (opción -l, líneas 423 y 519), entonces parse_args() elimina MODE_SHELL de los "valid_flags" (líneas 363 y 424) y sale con un error si especificamos un flag no válido como MODE_SHELL (líneas 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);

Pero encontramos una escapatoria: si ejecutamos Sudo como "sudoedit" en lugar de "sudo", entonces parse_args() establece automáticamente MODE_EDIT (línea 270) pero no restablece los "valid_flags", y los "valid_flags" incluyen MODE_SHELL de forma predeterminada (líneas 127 y 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 }

En consecuencia, si ejecutamos "sudoedit -s", establecemos tanto MODE_EDIT como MODE_SHELL (pero no MODE_RUN), evitamos el código de escape, alcanzamos el código vulnerable y desbordamos el búfer basado en montón "user_args" mediante un argumento de la línea de comandos que termina con un solo carácter de barra invertida:


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

Desde el punto de vista de un atacante, este desbordamiento de búfer es ideal:

  • controlamos el tamaño del búfer "user_args" que desbordamos (el tamaño de nuestros argumentos concatenados de la línea de comandos, en las líneas 852-854);

  • controlamos de forma independiente el tamaño y el contenido del desbordamiento en sí (a nuestro último argumento de la línea de comandos le siguen convenientemente nuestras primeras variables de entorno, que no se incluyen en el cálculo del tamaño de las líneas 852-853);

  • incluso podemos escribir bytes nulos en el búfer que desbordamos (todo argumento de la línea de comandos o variable de entorno que termine con una sola barra invertida escribe un byte nulo en "user_args", en las líneas 866-868).

Por ejemplo, en un Linux amd64, el siguiente comando asigna un búfer "user_args" de 24 bytes (un chunk de montón de 32 bytes) y sobrescribe el campo size del siguiente chunk con "A=a\0B=b\0" (0x00623d4200613d41), su campo fd con "C=c\0D=d\0" (0x00643d4400633d43), y su campo bk con "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 <---- user_args buffer ----> size fd bk

======================================================================== Explotación

Debido a que Sudo llama a funciones de localización al comienzo mismo de su función main():


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

y pasa cadenas de traducción (a través de la función gettext() y la macro _()) a funciones de cadena de formato 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 la fascinante técnica de halfdog de https://www.halfdog.net/Security/2017/LibcRealpathBufferUnderflow/ y transformar el desbordamiento de búfer basado en montón de Sudo en un exploit de cadena de formato. Más precisamente:

  • en la línea 154, en setlocale(), asignamos con malloc() y liberamos con free() varias variables de entorno LC (LC_CTYPE, LC_MESSAGES, LC_TIME, etc.), creando así pequeños agujeros al comienzo mismo del montón de Sudo (chunks fast o tcache libres);

  • en la línea 155, bindtextdomain() asigna con malloc() una estructura binding, que contiene un puntero dirname al nombre de un directorio que contiene archivos de catálogo ".mo" y, por lo tanto, cadenas de traducción;

  • en set_cmnd(), asignamos con malloc() el búfer "user_args" en uno de los agujeros al comienzo del montón de Sudo, y desbordamos este búfer, sobrescribiendo así el puntero dirname de la estructura binding;

  • en la línea 301 (por ejemplo), gettext() (a través de la macro _()) carga nuestra propia cadena de traducción desde el dirname sobrescrito -- en otras palabras, controlamos la cadena de formato que se pasa a sudo_printf().

Para implementar esta técnica inicial, escribimos un brute-forcer rudimentario que ejecuta Sudo dentro de gdb, desborda el búfer "user_args" y selecciona aleatoriamente los siguientes parámetros:

  • las variables de entorno LC que pasamos a Sudo, y su longitud (usamos la configuración regional "C.UTF-8" y añadimos un "@modifier" aleatorio);

  • el tamaño del búfer "user_args" que desbordamos;

  • el tamaño del desbordamiento en sí;

  • si pasamos por el código de autenticación de Sudo (opción -A o -n) o no (opción -u #realuid).

Desafortunadamente, esta técnica inicial falló; nuestro brute-forcer fue capaz de sobrescribir el puntero dirname de la estructura 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

pero LC_MESSAGES siempre era la configuración regional "C" predeterminada (no "C.UTF-8"), lo que desactiva la traducción de cadenas en gettext() (es decir, gettext() devuelve la cadena de formato original, no la nuestra).

Afortunadamente, sin embargo, nuestro brute-forcer produjo docenas de fallos únicos de Sudo y backtraces de gdb; entre estos, tres llamaron nuestra atención, y finalmente explotamos los tres.

======================================================================== 1/ Sobrescritura de struct sudo_hook_entry

El primer fallo que llamó nuestra atención es:


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

Increíblemente, la función process_hooks_getenv() de Sudo falló (en la línea 108) porque sobrescribimos directamente un puntero a función, getenv_fn (un miembro de una estructura sudo_hook_entry basada en montón):


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 explotar esta sobrescritura de struct sudo_hook_entry, observamos que:

  • la llamada a getenv_fn (en la línea 108) es compatible con una llamada a execve():

    . name ("SYSTEMD_BYPASS_USERDB") es compatible con el argumento pathname de execve();

    . &val (un puntero a un puntero NULL) es compatible con argv de execve();

    . hook->closure (un puntero NULL) es compatible con envp de execve();

  • podemos derrotar a ASLR sobrescribiendo parcialmente el puntero a función getenv_fn (que apunta a la función sudoers_hook_getenv() en la biblioteca compartida sudoers.so); y afortunadamente, el comienzo de sudoers.so contiene una llamada a execve() (o 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 leer /dev/kmsg (dmesg) como un usuario sin privilegios en Ubuntu y, por lo tanto, obtener información detallada sobre nuestros fallos de Sudo.

En consecuencia, adoptamos la siguiente estrategia:

  • Primero, forzamos por fuerza bruta los parámetros del exploit hasta que sobrescribimos getenv_fn con una dirección de espacio de usuario no válida (por encima de 0x800000000000) -- hasta que observamos una falla de protección general en el lugar de la llamada a getenv_fn:

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

  • A continuación, reutilizamos estos parámetros del exploit pero sobrescribimos getenv_fn con un patrón regular de direcciones de espacio de usuario válidas (por debajo de 0x800000000000) pero no mapeadas -- en este ejemplo, getenv_fn es el vigésimo segundo puntero que sobrescribimos (0x32 es '2', parte de nuestro patrón):

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

  • Por último, sobrescribimos parcialmente getenv_fn (sobrescribimos sus dos bytes menos significativos con 0x8a00, el desplazamiento de execv() en sudoers.so, y su tercer byte con 0x00, el terminador nulo de user_args en set_cmnd()) hasta derrotar a ASLR -- tenemos una buena probabilidad de sobrescribir getenv_fn con la dirección de execv() después de 2^(3*8-12) = 2^12 = 4096 intentos, ejecutando así nuestro propio binario, llamado "SYSTEMD_BYPASS_USERDB", como root.

Probamos con éxito este primer exploit en Ubuntu 20.04.

======================================================================== 2/ Sobrescritura de struct service_user

El segundo fallo que llamó nuestra atención es:


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

La función nss_load_library() de glibc falló (en la línea 344) porque sobrescribimos el puntero "library", un miembro de una estructura service_user basada en montón:


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 transformar fácilmente esta sobrescritura de struct service_user en una ejecución arbitraria de código:

  • sobrescribimos ni->library con un puntero NULL, para entrar en el bloque de las líneas 330-342, evitar el fallo en la línea 344 y entrar en el bloque de las líneas 344-359;

  • sobrescribimos ni->name (un array de caracteres, inicialmente "systemd") con "X/X";

  • las líneas 353-357 construyen el nombre de una biblioteca compartida "libnss_X/X.so.2" (en lugar de "libnss_systemd.so.2");- en la línea 359, cargamos nuestra propia biblioteca compartida "libnss_X/X.so.2" desde el directorio de trabajo actual y ejecutamos nuestro constructor _init() como root.

Probamos con éxito este segundo exploit en Ubuntu 20.04, Debian 10 y Fedora 33.

======================================================================== 3/ sobrescritura de def_timestampdir

Nuestro tercer exploit no se deriva de uno de los fallos de Sudo, sino de una observación casual: durante nuestra fuerza bruta, Sudo creó docenas de nuevos directorios en nuestro directorio de trabajo actual (AAAAAA, AAAAAAAAA, etc.). Cada uno de estos directorios pertenece a root y contiene solo un pequeño archivo, con el nombre de nuestro propio usuario: el archivo timestamp de Sudo -- evidentemente sobrescribimos def_timestampdir, el nombre del directorio de timestamps de Sudo.

Si sobrescribimos def_timestampdir con el nombre de un directorio que no existe todavía, entonces podemos competir contra ts_mkdirs() de Sudo, crear un enlace simbólico a un archivo arbitrario y:

3a/ bien hacer chown() de este archivo arbitrario a usuario root y grupo root;

3b/ bien abrir (o crear) este archivo arbitrario como root, y escribir una struct timestamp_entry en él.

No pudimos transformar 3a/ en privilegios root completos (por ejemplo, si hacemos chown() de nuestro propio binario SUID a root, entonces el kernel elimina automáticamente el bit SUID de nuestro binario). Si usted, querido lector, encuentra una solución a este problema, ¡por favor publíquela en la lista de correo pública oss-security!

Finalmente, pudimos transformar 3b/ en privilegios root completos, pero inicialmente nos enfrentamos a dos problemas:

  • timestamp_open() de Sudo elimina nuestro enlace simbólico arbitrario si el archivo al que apunta es más antiguo que el tiempo de arranque. Pudimos resolver este primer problema creando un archivo timestamp muy antiguo (desde la época de Unix), esperando a que timestamp_open() lo elimine, y compitiendo contra timestamp_open() para crear nuestro enlace simbólico arbitrario final.

  • No controlamos el contenido de la struct timestamp_entry que se escribe en el archivo arbitrario. Hasta donde sabemos, solo controlamos tres bytes (un ID de proceso o una struct timespec), y no pudimos transformar esta escritura de tres bytes en privilegios root completos. Si usted, querido lector, encuentra una solución a este problema, ¡por favor publíquela en la lista de correo pública oss-security!

Sin embargo, pudimos sortear este segundo problema abusando de un pequeño fallo en timestamp_lock() de Sudo. Si ganamos las dos competiciones contra ts_mkdirs() y timestamp_open(), y si nuestro enlace simbólico arbitrario apunta a /etc/passwd, entonces este archivo se abre como root, y:


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)

  • en la línea 644, los primeros 0x38 bytes de /etc/passwd ("root❌0:0:...") se leen en una struct timestamp_entry basada en la pila, entry;

  • en la línea 652, entry.type es 0x783a (":x"), no TS_LOCKEXCL;

  • en las líneas 657 y 318, se escriben entry->size bytes de la entry basada en la pila en /etc/passwd, pero entry->size es en realidad 0x746f ("ot"), no sizeof(struct timestamp_entry).

Como resultado, escribimos el contenido completo de la pila de Sudo en /etc/passwd (incluyendo nuestros argumentos de línea de comandos y nuestras variables de entorno): inyectamos un usuario arbitrario en /etc/passwd y, por lo tanto, obtenemos privilegios root completos. Probamos con éxito este tercer exploit en Ubuntu 20.04.

Nota: este pequeño fallo en timestamp_lock() se corrigió en enero de 2020 mediante el commit 586b418a, pero esta corrección no se trasladó a las versiones heredadas.

======================================================================== Agradecimientos

Agradecemos a Todd C. Miller por su profesionalismo, rápida respuesta y meticulosa atención a cada detalle de nuestro informe. También agradecemos a los miembros de distros@openwall.

======================================================================== Cronología

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

2021-01-19: Aviso y parches enviados a distros@openwall.

2021-01-26: Fecha de publicación coordinada (6:00 PM UTC).

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