Los Caballeros

"Sobre seguridad, programación, modding, frikismo, etc... "

Debugging BoF con Valgrind

Twitter icon Twitter icon
Valgrind es un conjunto de herramientas libres que ayuda en la depuración de problemas de memoria y rendimiento de programas.

Asi lo define wikipedia.

Ahora pues esta tool actúa como una especie de monitor de recursos, es decir por ejemplo cuando detectamos un desborde o un error similar esta tool nos puede ayudar a determinar la naturaleza del mismo (determinar detalles de bug que encontramos por pura suerte xD), así que comencemos:

para comenzar entre las opciones que utilizaremos del valgrind están:

--log-file=<file> log messages to <file> (tal cual, crea un log de la ejecución a un archivo)
--log-socket=ipaddr:port log messages to socket ipaddr:port (lo mandamos por un socket tcp)

Del mismo modo se pueden crear logs xml's para poder leerlos mas fácilmente con algún parser (o programar nuestro propia tool para analizar dichos "eventos").

--leak-check=full (establecemos el nivel de "leaks" que se van a comprobar, en este caso nos interesan todos :P...)

Bien ahora comencemos:

xianur0@Zer0-Null:~$ valgrind --log-file=log.txt --leak-check=full -v gtmess

de este modo vamos a generar un reporte del gtmess, se inicia la aplicación y ahora nos toca causar el BoF manualmente, en este caso vamos a intentar conectarnos con un usuario de 512 de largo sin @, para ver que pasa...

y al obtener el desborde, vamos a ver un poco del que paso, en este caso no nos vamos a guiar del stacktrace que nos devolverá la aplicación, vamos a analizar los logs (ya que nos facilitan un tanto la vida...)


==11520== Memcheck, a memory error detector
==11520== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==11520== Using Valgrind-3.6.0.SVN-Debian and LibVEX; rerun with -h for copyright info
==11520== Command: gtmess
==11520== Parent PID: 11373
==11520==
--11520--
--11520-- Valgrind options:
--11520-- --suppressions=/usr/lib/valgrind/debian-libc6-dbg.supp
--11520-- --log-file=log.txt
--11520-- --leak-check=full
--11520-- -v
--11520-- Contents of /proc/version:
--11520-- Linux version 2.6.32-26-generic (buildd@rothera) (gcc version 4.4.3 (Ubuntu 4.4.3-4ubuntu5) ) #48-Ubuntu SMP Wed Nov 24 09:00:03 UTC 2010
--11520-- Arch and hwcaps: X86, x86-sse1-sse2
--11520-- Page sizes: currently 4096, max supported 4096
--11520-- Valgrind library directory: /usr/lib/valgrind
--11520-- Reading syms from /lib/ld-2.11.1.so (0x4000000)
--11520-- Reading debug info from /lib/ld-2.11.1.so ..
--11520-- .. CRC mismatch (computed d005eb74 wanted 53dc8daf)
--11520-- Reading debug info from /usr/lib/debug/lib/ld-2.11.1.so ..
--11520-- Reading syms from /usr/local/bin/gtmess (0x8048000)
--11520-- Reading syms from /usr/lib/valgrind/memcheck-x86-linux (0x38000000)
--11520-- object doesn't have a dynamic symbol table
--11520-- Reading suppressions file: /usr/lib/valgrind/debian-libc6-dbg.supp
--11520-- Reading suppressions file: /usr/lib/valgrind/default.supp
--11520-- REDIR: 0x40160b0 (index) redirected to 0x3803e9b3 (vgPlain_x86_linux_REDIR_FOR_index)
--11520-- Reading syms from /usr/lib/valgrind/vgpreload_core-x86-linux.so (0x401f000)
--11520-- Reading syms from /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so (0x4022000)
==11520== WARNING: new redirection conflicts with existing -- ignoring it
--11520-- new: 0x040160b0 (index ) R-> 0x04025c30 index
--11520-- REDIR: 0x4016290 (strlen) redirected to 0x4026070 (strlen)
--11520-- Reading syms from /lib/i686/cmov/libssl.so.0.9.8 (0x4043000)
--11520-- Reading debug info from /lib/i686/cmov/libssl.so.0.9.8 ..
--11520-- .. CRC mismatch (computed 7b8b88b0 wanted b921d0c8)
--11520-- object doesn't have a symbol table
--11520-- Reading syms from /lib/i686/cmov/libcrypto.so.0.9.8 (0x408c000)
--11520-- Reading debug info from /lib/i686/cmov/libcrypto.so.0.9.8 ..
--11520-- .. CRC mismatch (computed 85170666 wanted 720fbd0f)
--11520-- object doesn't have a symbol table
--11520-- Reading syms from /lib/libncurses.so.5.7 (0x41de000)
--11520-- Reading debug info from /lib/libncurses.so.5.7 ..
--11520-- .. CRC mismatch (computed 99b3a921 wanted 788a2112)
--11520-- object doesn't have a symbol table
--11520-- Reading syms from /lib/tls/i686/cmov/libpthread-2.11.1.so (0x4216000)
--11520-- Reading debug info from /lib/tls/i686/cmov/libpthread-2.11.1.so ..
--11520-- .. CRC mismatch (computed 38da17dd wanted d3835c81)
--11520-- Reading debug info from /usr/lib/debug/lib/tls/i686/cmov/libpthread-2.11.1.so ..
--11520-- Reading syms from /lib/tls/i686/cmov/libc-2.11.1.so (0x422f000)
--11520-- Reading debug info from /lib/tls/i686/cmov/libc-2.11.1.so ..
--11520-- .. CRC mismatch (computed 0893b039 wanted f05c11b3)
--11520-- Reading debug info from /usr/lib/debug/lib/tls/i686/cmov/libc-2.11.1.so ..
--11520-- Reading syms from /lib/tls/i686/cmov/libdl-2.11.1.so (0x4389000)
--11520-- Reading debug info from /lib/tls/i686/cmov/libdl-2.11.1.so ..
--11520-- .. CRC mismatch (computed 6be1e729 wanted de5b14b0)
--11520-- Reading debug info from /usr/lib/debug/lib/tls/i686/cmov/libdl-2.11.1.so ..
--11520-- Reading syms from /lib/libz.so.1.2.3.3 (0x438e000)
--11520-- Reading debug info from /lib/libz.so.1.2.3.3 ..
--11520-- .. CRC mismatch (computed 53909dc3 wanted dc0e37c9)
--11520-- object doesn't have a symbol table
--11520-- REDIR: 0x42a2140 (strcmp) redirected to 0x401f44c (_vgnU_ifunc_wrapper)
--11520-- REDIR: 0x42a1fd0 (index) redirected to 0x4025ba0 (index)
--11520-- REDIR: 0x42a33a0 (memchr) redirected to 0x40267b0 (memchr)
--11520-- REDIR: 0x42a2b10 (rindex) redirected to 0x4025ae0 (rindex)
--11520-- REDIR: 0x42a27f0 (__GI_strlen) redirected to 0x4026050 (__GI_strlen)
--11520-- REDIR: 0x42a52b0 (strchrnul) redirected to 0x4027510 (strchrnul)
--11520-- REDIR: 0x42a8e00 (__GI_strncmp) redirected to 0x40265c0 (__GI_strncmp)
--11520-- REDIR: 0x42a2a30 (strncpy) redirected to 0x4026270 (strncpy)
--11520-- REDIR: 0x429ef40 (malloc) redirected to 0x4024e9b (malloc)
--11520-- REDIR: 0x42a3f20 (memcpy) redirected to 0x401f44c (_vgnU_ifunc_wrapper)
--11520-- REDIR: 0x433f000 (__memcpy_ssse3) redirected to 0x40267f0 (memcpy)
--11520-- REDIR: 0x429ee60 (free) redirected to 0x4024ab5 (free)
--11520-- REDIR: 0x42a2190 (__GI_strcmp) redirected to 0x4026690 (__GI_strcmp)
--11520-- REDIR: 0x429fec0 (realloc) redirected to 0x4024f4a (realloc)
--11520-- REDIR: 0x42a28b0 (strnlen) redirected to 0x4025fb0 (strnlen)
--11520-- REDIR: 0x42a3c20 (stpcpy) redirected to 0x40270a0 (stpcpy)
--11520-- REDIR: 0x42a2200 (strcpy) redirected to 0x40260b0 (strcpy)
--11520-- REDIR: 0x42a51e0 (rawmemchr) redirected to 0x4027540 (rawmemchr)
--11520-- REDIR: 0x42a2730 (strlen) redirected to 0x401f44c (_vgnU_ifunc_wrapper)
--11520-- REDIR: 0x42a2770 (__strlen_sse2) redirected to 0x4026030 (strlen)
--11520-- REDIR: 0x429e660 (calloc) redirected to 0x402417f (calloc)
--11520-- REDIR: 0x4347240 (__strcmp_ssse3) redirected to 0x4026630 (strcmp)
--11520-- REDIR: 0x42a29e0 (strncmp) redirected to 0x401f44c (_vgnU_ifunc_wrapper)
--11520-- REDIR: 0x43486b0 (__strncmp_ssse3) redirected to 0x4026550 (strncmp)
--11520-- REDIR: 0x42a3930 (memset) redirected to 0x401f44c (_vgnU_ifunc_wrapper)
--11520-- REDIR: 0x433def0 (__memset_sse2) redirected to 0x4027420 (memset)
--11520-- Reading syms from /lib/libgcc_s.so.1 (0x57fe000)
--11520-- Reading debug info from /lib/libgcc_s.so.1 ..
--11520-- .. CRC mismatch (computed 75b2cc22 wanted fffc9534)
--11520-- object doesn't have a symbol table
==11520==
==11520== Process terminating with default action of signal 6 (SIGABRT)
==11520== at 0x4259651: raise (raise.c:64)
==11520== by 0x425CA81: abort (abort.c:92)
==11520== by 0x429049C: __libc_message (libc_fatal.c:189)
==11520== by 0x431138F: __fortify_fail (fortify_fail.c:32)
==11520== by 0x43102C9: __chk_fail (chk_fail.c:29)
==11520== by 0x430F5F9: __strcat_chk (strcat_chk.c:51)
==11520== by 0x804EDEB: log_in (string3.h:146)
==11520== by 0x8051D7C: main (gtmess.c:2589)
--11520-- Discarding syms at 0x5800370-0x5818738 in /lib/libgcc_s.so.1 due to munmap()
==11520==
==11520== HEAP SUMMARY:
==11520== in use at exit: 497,443 bytes in 8,777 blocks
==11520== total heap usage: 11,671 allocs, 2,894 frees, 889,703 bytes allocated
==11520==
==11520== Searching for pointers to 8,777 not-freed blocks
==11520== Checked 17,412,980 bytes
==11520==
==11520== 136 bytes in 1 blocks are possibly lost in loss record 190 of 306
==11520== at 0x402425F: calloc (vg_replace_malloc.c:467)
==11520== by 0x4010CDB: _dl_allocate_tls (dl-tls.c:300)
==11520== by 0x421C2E2: pthread_create@@GLIBC_2.1 (allocatestack.c:561)
==11520== by 0x8050C2D: interval_init (gtmess.c:423)
==11520== by 0x8051736: main (gtmess.c:2492)
==11520==
==11520== 136 bytes in 1 blocks are possibly lost in loss record 191 of 306
==11520== at 0x402425F: calloc (vg_replace_malloc.c:467)
==11520== by 0x4010CDB: _dl_allocate_tls (dl-tls.c:300)
==11520== by 0x421C2E2: pthread_create@@GLIBC_2.1 (allocatestack.c:561)
==11520== by 0x8063707: msnftp_init (xfer.c:740)
==11520== by 0x805173B: main (gtmess.c:2493)
==11520==
==11520== LEAK SUMMARY:
==11520== definitely lost: 0 bytes in 0 blocks
==11520== indirectly lost: 0 bytes in 0 blocks
==11520== possibly lost: 272 bytes in 2 blocks
==11520== still reachable: 497,171 bytes in 8,775 blocks
==11520== suppressed: 0 bytes in 0 blocks
==11520== Reachable blocks (those to which a pointer was found) are not shown.
==11520== To see them, rerun with: --leak-check=full --show-reachable=yes
==11520==
==11520== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 27 from 10)
--11520--
--11520-- used_suppression: 27 dl-hack3-cond-1
==11520==
==11520== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 27 from 10)


podremos ver que se hacen algunas cosas con memoria y demás, pero en este momento la parte que nos interesa es la siguiente:

==11520==
==11520== Process terminating with default action of signal 6 (SIGABRT)
==11520== at 0x4259651: raise (raise.c:64)
==11520== by 0x425CA81: abort (abort.c:92)
==11520== by 0x429049C: __libc_message (libc_fatal.c:189)
==11520== by 0x431138F: __fortify_fail (fortify_fail.c:32)
==11520== by 0x43102C9: __chk_fail (chk_fail.c:29)
==11520== by 0x430F5F9: __strcat_chk (strcat_chk.c:51)
==11520== by 0x804EDEB: log_in (string3.h:146)
==11520== by 0x8051D7C: main (gtmess.c:2589)

vamos a ir analizando de abajo hacia arriba, aunque primero que nada hay que denotar que la aplicación termino con "signal 6" (SIGABRT), esto es la aplicación que ejecutamos con valgrind (gtmess) lo cual a simple vista nos esta diciendo que hubo un desborde y que se necesito abortar la aplicación, pero despues miramos lo siguiente:

==11520== by 0x8051D7C: main (gtmess.c:2589)

se cargo el main de gtmess.c y se llamo a algo en la linea 2589

==11520== by 0x804EDEB: log_in (string3.h:146)

ese algo que se llamo es log_in():

                            log_in(0);

vayamos a log_in() que se encuentra en la linea 2071, de esto podemos notar que se hizo algo con un string (puesto que nos esta marcado que llamo a string3.h linea 146).

Para entender un poco mas de que se hizo vamos a ese archivo y a esa linea, tenemos lo siguiente:

__extern_always_inline char *
__NTH (strcat (char *__restrict __dest, __const char *__restrict __src))
{
return __builtin___strcat_chk (__dest, __src, __bos (__dest));
}

que tal cual es lo que nos dijo valgrind arriba:

==11520== by 0x430F5F9: __strcat_chk (strcat_chk.c:51)

es decir se llamo a la función strcat(), y en esta parte hubo un error por que se llamo a:

==11520== by 0x43102C9: __chk_fail (chk_fail.c:29)

tenemos que ahí estuvo el desborde, ahora regresemos a gtmess.c linea 2071:

int log_in(int startup)
{
int r, retry;
if (msn.nfd != -1) {
msg(C_ERR, "You are already logged in\n");
return 0;
}
if (startup) {
if (!msn.login[0]) return 0;
} else {
if (!(r = get_string(C_EBX, 0, "Login as: ", msn.login, SML))) return 0;
if (r == 1) {
if (strchr(msn.login, '@') == NULL) strcat(msn.login, "@hotmail.com");
Strcpy(msn.nick, msn.login, SML);
msn.pass[0] = 0;
draw_status(1);
}
}
if (startup) {
if (!msn.pass[0]) return 0;
} else if (!get_string(C_EBX, 1, "Password: ", msn.pass, SML)) return 0;
msg2(C_MNU, "Logging in ...");

retry = Config.max_retries;
while (retry >= 0) {
while ((r = do_login()) == 1);
if (r == 0) return 0;
msn.nfd = -1;

msg(C_ERR, "Failed to connect to server\n");
if (r == -400) break; /* do not retry when passport authentication fails */
if (retry) msg(C_ERR, "Retrying %d more time%s ...\n", retry, (retry > 1)? "s": ZS);
--retry;
}
return -1;
}


ya que ubicamos aproximadamente que es lo que causo el desborde nos toca detectar exactamente en que linea se causo el desborde con strcat(), localicemos todos los strcat(), en este caso no deseo complicar mucho las cosas de modo que solo veremos lo simple, así que queda ir haciendo un "mapeo de ejecución" para ver que se ejecuto y donde se detuvo, para esto realmente nos bastaría con llenar de printf() e ir imprimiendo según se vayan ejecutando las cosas, en mi caso quedo algo como esto:

Code:
int log_in(int startup)
{
int r, retry;
if (msn.nfd != -1) {
msg(C_ERR, "You are already logged in\n");
return 0;
}
if (startup) {
if (!msn.login[0]) return 0;
} else {
if (!(r = get_string(C_EBX, 0, "Login as: ", msn.login, SML))) return 0;
if (r == 1) {
printf("en la linea 2083\n");
if (strchr(msn.login, '@') == NULL) strcat(msn.login, "@hotmail.com");
printf("en la linea 2085\n");
Strcpy(msn.nick, msn.login, SML);
printf("en la linea 2087\n");
msn.pass[0] = 0;
draw_status(1);
}
}
if (startup) {
if (!msn.pass[0]) return 0;
} else if (!get_string(C_EBX, 1, "Password: ", msn.pass, SML)) return 0;
msg2(C_MNU, "Logging in ...");

retry = Config.max_retries;
while (retry >= 0) {
while ((r = do_login()) == 1);
if (r == 0) return 0;
msn.nfd = -1;

msg(C_ERR, "Failed to connect to server\n");
if (r == -400) break; /* do not retry when passport authentication fails */
if (retry) msg(C_ERR, "Retrying %d more time%s ...\n", retry, (retry > 1)? "s": ZS);
--retry;
}
return -1;
}

compilamos y volvemos a ejecutar, ya no necesitamos a valgrind, únicamente veremos que es lo que nos retorna en la terminal:

en la linea 2083
Terminado (killed)
xianur0@Zer0-Null:~$

así que lo ultimo fue la linea 2083, vayamos a esa linea:

y nos encontramos con:

            if (strchr(msn.login, '@') == NULL) strcat(msn.login, "@hotmail.com");

ya encontramos exactamente donde fue el BoF, es decir únicamente tenemos que buscar de cuanto es msn.login (la cual es una msn_t):


src/client/nserver.h:29:


Code:
typedef struct {
/* must be set by user */
char login[SML], pass[SML];
char notaddr[SML];

/* personal state */
char nick[SML];
char psm[SML];
msn_stat_t status;
int inbox, folders;
/* privacy mode */
char BLP; /* all other users: (A)llow; (B)lock */
char GTC; /* prompt when other users add you: (A)lways; (N)ever */
/* lists */
msn_glist_t GL; /* group list */
msn_clist_t CL; /* contact list (forward, reverse, allow, block) */

char hlogin[SML]; /* highlighted contact in CL */
char hgid[SNL]; /* highlighted group in CL */
int dhid; /* delta pos. of highlighted contact */

/*msn_clist_t IL; /* initial status list */
/* unsigned int SYN; /* syn list version */
int list_count; /* number of LST responses */

int flskip; /* FL list line skip (for display) */
pthread_mutex_t lock;

FILE *fp_log; /* log file pointer */
char fn_log[SML]; /* log file name */


/* session */
unsigned int tid;
int nfd;
int in_syn; /* SYN in progress */
pthread_t thrid;
} msn_t;

tenemos que el campo login tiene de tamaño SML:

src/client/msn.h:30:

#define SML 512

es decir 512, y listo, ya tenemos ubicado y "analizado" el bug.

Saludos!

No hay comentarios: