COMMAND

    format bugs

SYSTEMS AFFECTED

    various platforms

PROBLEM

    Lamagra  Argamal  posted  following.   For  all  of  you who don't
    understand  these  kind  of  bugs,  they  are really really new so
    don't feel bad.  Lamagra  has written a small (ugly)  introduction
    into these kind of  bugs.  Don't expect  too much, but it  explain
    it good enough.

    The  concept  is  quite  simple:  when  a  *printf() function (eg.
    printf(char *fmt,...)) is  called and fmt  is user-supplied.   The
    user can put in  formatstrings %s %p %x  in the fmt. *printf  will
    then  "convert"  them  with  the  supplied  arguments.  Problem is
    *printf() doesn't  know where  it's arguments  stop.   When a  new
    formatstrings it  just reads  the next  thing on  the stack.  Lets
    have a look at an example.

    <++> ptest.c
    #include <stdarg.h>

    blaat(char *fmt,...)
    {
        va_list va;
        int i;
        char *addr;

        va_start(va,fmt);

        printf("---| begin |---\n");
        for(i = 0;i < 5;i++)
        {
	    addr = va_arg(va,char *);
	    printf("%p\n",addr);
        }
        printf("---|  end  |---\n");

        va_end(va);
    }

    main(int argc,char **argv)
    {
        char buf[8];
        char *prot = (char *)0x12345678;

        strncpy(buf,argv[1],8);

        blaat(argv[1]);
        printf(argv[1]);
        putchar('\n');
    }
    <-->

    darkstar:/tmp/temp# gcc ptest.c -optest
    darkstar:/tmp/temp# ptest blaat
    ---| begin |---
    0x12345678
    0x61616c62
    0xbfff0074
    0xbffffb24
    0x804855e
    ---|  end  |---
    blaat

    A simple argument makes  it print the 5  things on the top  of the
    stack before the call to blaat().  You can see 0x12345678 which is
    the content of 'prot' and the content of buf namely our  argument.
    The output  of printf()  is just  our argument.   Now lets  run it
    again with  some formatstrings  in the  argument, we  use %p since
    it doesn't crash the program.

        darkstar:/tmp/temp# ptest AAAA%p
        ---| begin |---
        0x12345678
        0x41414141
        0xbf007025
        0xbffffb24
        0x804855e
        ---|  end  |---
        AAAA0x12345678

    Now the  output of  printf() is  quite interesting.   It prints  4
    times 'A'  and the  %p is  replaced by  the address  on top of the
    stack.  When you'd add more %p's to the argument you'll see  every
    other element on the stack gets printed.

    How to exploit them?   First thing to do  is, check out the  other
    kinds of formatstrings: %c,  %f, %d, %s, %p,  %i, %n, etc.   %n is
    probably  the  most  interesting,  it  writes  the number of bytes
    printed to the location pointed to by it's argument.

    Simple example:

        int q;
        printf("AAAA%n",&q);
        q = 4 after that

    As we've seen  from our previous  tests, our argument  copied into
    buf, is on the stack aswell.  What if we replaced the AAAA with  a
    valid address and then wrote to that address using %n.  Lets see

        darkstar:/tmp/temp# gdb --exec=a.out --symbols=ptest
        GNU gdb 4.17
        Copyright 1998 Free Software Foundation, Inc.
        GDB is free software, covered by the GNU General Public License, and you are
        welcome to change it and/or distribute copies of it under certain conditions.
        Type "show copying" to see the conditions.
        There is absolutely no warranty for GDB.  Type "show warranty" for details.
        This GDB was configured as "i586-slackware-linux"...
        (gdb) r
        Starting program: /tmp/temp/a.out

        Program received signal SIGTRAP, Trace/breakpoint trap.
        Cannot remove breakpoints because program is no longer writable.
        It might be running in another process.
        Further execution is probably impossible.
        0x8048090 in ___crt_dummy__ ()
        (gdb) break *main+60
        Breakpoint 1 at 0x804821c: file ptest.c, line 30.
        (gdb) break *main+65
        Breakpoint 2 at 0x8048221: file ptest.c, line 30.
        (gdb) c
        Continuing.
        ---| begin |---
        0x12345678
        0xbffffb10
        0x6e257025
        0xbffffb00
        0x80480ee
        ---|  end  |---

        Breakpoint 1, 0x804821c in main (argc=2, argv=0xbffffba0) at ptest.c:30
        30          printf(argv[1]);
        (gdb) x/wx 0xbffffb10
        0xbffffb10:     0x00000000
        (gdb) c
        Continuing.

        Breakpoint 2, 0x8048221 in main (argc=2, argv=0xbffffba0) at ptest.c:30
        30          printf(argv[1]);
        (gdb) x/wx 0xbffffb10
        0xbffffb10:     0x0000000e
        (gdb) c
        Continuing.
        û ¿0x12345678

        Program exited with code 012.
        (gdb) q
        darkstar:/tmp/temp#

    a.out   is   a   simple   program   that   executes   ptest   with
    \x10\xfb\xff\xbf%p%n as  argument, as  you can  see %p  prints the
    first address and %n writes to 0xbfffb10.

    What  can  we  do  with  this,  you  ask.   Well you can overwrite
    anything  inside  the  program  (except  regions mapped PROT_WRITE
    like .text).  This can be many things.  In proftpd exploit Lamagra
    chose for  the saved  uid and  part of  the configs  in mem.   The
    daemon relinquishes rootpriviledges  for opening a  dataconnection
    (on  LIST,RETR,etc)  after  that  it  changes  back to the old uid
    (saved in mem).   The general idea  was to zero  that uid, open  a
    dataconnection and up with root priviledges inside proftpd.   With
    local access this is enough, you upload a backdoor and CHMOD  4755
    (-rwsr-xr-x) proftp allows suidsgid flags unlike wuftpd.  But with
    anonymous  access,  you  couldn't  write  to  disk  because of the
    configuration.  Simple thing to do is, corrupt the  configuration.
    In this case  corrupt the 'DenyAll'  setting.  Other  things to do
    are eg. change  the last byte  of a stackpointer,  data in memory.
    and lots more.

    An interesting thing in the new glibc version is that for  example
    %<long number>d works  even with snprintf(),  on older glibc's  it
    just crashes.

        *- note -*
        "%.5d",5 outputs 00005
        "%.200000d" outputs 200000 bytes
        *- note -*

    With this advantage you  could also change <saved  %eip>, function
    pointers, jmp_buf's etc etc.

    All this is very nice, but it can get really hard sometimes.  Some
    sort of userdefined string has  to be somewhere on the  stack when
    the function  is called.   This requires  some fiddling  and  some
    stackbacktracing.  Sometimes you have  to go back a few  functions
    before a  buffer is  in reach.   Conclusion: these  bugs are  big,
    sometimes....

    H.D.Moore   spent   some   time   going   over   a   handful    of
    daemons/priviledged  programs  that  he  suspected had issues with
    formatting characters in user-supplied data.  Many daemons log bad
    login attempts with the usernames to syslog.  If syslog is  called
    with  2  arguments  only  and  the  fmt  string being passed to it
    contains  user  data,  syslog  will  happily  expand  those format
    strings.  This could lead to garbled log messages or even  jumping
    to arbitrary code.  Here is an example of the right and wrong  way
    to log user supplied data to syslog:

        [WRONG] - soon to be disclosed daemon
        syslog(priority, userdata);

        [RIGHT] - OpenSSH 2.1.1p1
        syslog(priority, "%.500s", userdata);

SOLUTION

    This concludes this small introduction  into format bugs.  If  you
    want to play some more with  this kind of bugs, fiddle a  bit with
    the ptest program.   And maybe try  exploiting the ftp  program on
    bsd,  linux,  windows...  it  has  this  sort of hole in the QUOTE
    command.  This isn't a big thing but nice to play with.