From owner-freebsd-security Sun Apr 13 10:50:57 1997 Return-Path: Received: (from root@localhost) by freefall.freebsd.org (8.8.5/8.8.5) id KAA15884 for security-outgoing; Sun, 13 Apr 1997 10:50:57 -0700 (PDT) Received: from pdx1.world.net (pdx1.world.net [192.243.32.18]) by freefall.freebsd.org (8.8.5/8.8.5) with ESMTP id KAA15879 for ; Sun, 13 Apr 1997 10:50:55 -0700 (PDT) Received: from suburbia.net (suburbia.net [203.4.184.1]) by pdx1.world.net (8.7.5/8.7.3) with SMTP id KAA08040 for ; Sun, 13 Apr 1997 10:52:55 -0700 (PDT) Received: (qmail 937 invoked by uid 110); 13 Apr 1997 17:42:48 -0000 MBOX-Line: From owner-bugtraq@NETSPACE.ORG Sun Apr 13 17:37:32 1997 remote from suburbia.net Delivered-To: proff@SUBURBIA.NET Received: (qmail 766 invoked from network); 13 Apr 1997 17:37:25 -0000 Received: from brimstone.netspace.org (128.148.157.143) by suburbia.net with SMTP; 13 Apr 1997 17:37:25 -0000 Received: from netspace.org ([128.148.157.6]) by brimstone.netspace.org with ESMTP id <32898-29443>; Sun, 13 Apr 1997 13:35:17 -0400 Received: from NETSPACE.ORG by NETSPACE.ORG (LISTSERV-TCP/IP release 1.8c) with spool id 3444988 for BUGTRAQ@NETSPACE.ORG; Sun, 13 Apr 1997 13:20:08 -0400 Received: from brimstone.netspace.org (brimstone [128.148.157.143]) by netspace.org (8.8.5/8.8.2) with ESMTP id NAA17692 for ; Sun, 13 Apr 1997 13:19:40 -0400 Received: from netspace.org ([128.148.157.6]) by brimstone.netspace.org with ESMTP id <32770-29445>; Sun, 13 Apr 1997 13:22:09 -0400 Approved-By: aleph1@UNDERGROUND.ORG Received: from sun1.ideal.ru (fsite.24h.dialup.ru [194.87.18.254]) by netspace.org (8.8.5/8.8.2) with ESMTP id IAA28809 for ; Sun, 13 Apr 1997 08:05:21 -0400 Received: (from solar@localhost) by sun1.ideal.ru (8.8.3/8.7.3) id QAA06271; Sun, 13 Apr 1997 16:06:40 -0300 (GMT) MIME-Version: 1.0 Content-Type: text/plain; charset=US-ASCII Content-Transfer-Encoding: 7BIT Message-ID: <199704131906.QAA06271@sun1.ideal.ru> Date: Sun, 13 Apr 1997 16:06:38 -0300 Reply-To: solar@SUN1.IDEAL.RU From: Solar Designer Subject: 2nd Linux kernel patch to remove stack exec X-To: linux-kernel@vger.rutgers.edu To: BUGTRAQ@NETSPACE.ORG Sender: owner-security@FreeBSD.ORG X-Loop: FreeBSD.org Precedence: bulk Hello! I include an improved version of my patch in this message. The main difference from the old one is that no programs at all should be broken by using it (this includes GCC trampolines). If some programs still get broken, please let me know. I'd like to thank everyone who replied to my previous message pointing out the problem, I'll now answer the common stuff at once. About GCC trampolines -- yes, there is a problem, but in reality it turns out to be quite easy to solve; also, nested functions, and especially those which address gets passed somewhere else, are not common in real world applications -- one of the reasons is that it's a GNU C extension. Since most programs will never use the trampolines, it makes sense to run them with non-executable stack, and enable stack execution permission for those that really need it. This can be done automatically, by modifying the GPF handler to switch back to the huge code segment (which covers the stack) and re-executing the instruction, unless it was a RET. Since most buffer overflows can only be exploited by overwriting the return address, this will still make them unexploitable (RET has to be the instruction to pass the control onto the stack), while C programs will normally only use CALL, and it is extremely unlikely that some code will use RET for that purpose (this can never happen for pure C programs compiled with GCC). Note that such emulation won't make the things run any slower since only one GPF per entire process life may get generated (after that the stack remains executable for this entire process). About me breaking the entire signal handling -- wrong, I handle this case specially from the very beginning, by temporary switching to the huge code segment for the time of signal handler execution. This leaves potential buffer overflows in signal handlers exploitable, but there seems to be no other simple way for the kernel to put the necessary return code in user program's address space (remember, signal handlers have to return with a plain RET, but they need to return to the kernel, so some extra code in the user space is required, which would get jumped to by the RET, and jump into the kernel). About [not] including the patch in Linux kernel release -- the patch was not intended to be included in standard kernel distribution, at least not right now, when it hasn't been tested widely enough. Anyway, it might be reasonable to include it there after some testing is done, as a configurable experimental feature. A possible new question -- why can't an exploit be made such a way, so the GPF handler would enable execution permission on the stack? This is due to most buffer overflow vulnerabilities allowing to only overwrite the function return address, and not some other pointer which would get jumped to. No matter if the custom code would contain a CALL, since it has to be put onto the stack, and GPF would happen when attempting to execute the RET, before the control has a chance to get to the CALL. However, I admit there're some rare buffer overflow cases which will remain exploitable -- these are when the vulnerable function uses function pointers, and keeps them on its stack. I only know one such example -- SuperProbe. Also, in some cases the custom code may be put somewhere else in user program's address space, not on the stack, or the program may already have some suitable code in it (I already mentioned that in my previous message). Anyway, I believe my patch makes most buffer overflows (well, at least some of them for sure, which is enough to be worth using) unexploitable. Another possible new question -- what if the GPF is caused by some bug in a program? Well, in that case my patched handler will still switch to the huge code segment, and attempt to re-execute the instruction, which will cause the GPF again. This time the handler will do what it used to do earlier -- terminate the program with a SIGSEGV. I actually tested that, seems to work fine (exactly the same as it did without my patch), including the case when running under gdb. As usual, any bug reports are welcome. Finally, someone might wonder if the patch is still useful, when it got that fallback in the GPF handler. While using libc5, it is unlikely the fallback will ever happen (even if it does, only that single process will be running with the stack being executable), so the patch prevents many overflows from being exploitable (I actually ensured that many overflow exploits stopped working, well, except for my SuperProbe exploit that I mentioned above), so the patch is useful. However, things are likely to change with glibc... To enable/disable execution permission on the stack in your programs (who would need that, with such a GPF handler?), the following can be used: #include [...] /* Switch to huge code segment => executable stack */ asm("ljmp %0,$1f\n1:\n" : : "i" (USER_HUGE_CS)); /* Switch to truncated code segment => non-executable stack */ asm("ljmp %0,$1f\n1:\n" : : "i" (USER_CS)); If someone really uses these, it might be reasonable to make such macros in asm/segment.h, so the stuff looks more readable. And now the patch... (to make it work with 2.1.x, change "cs" to "xcs" for signal.c and traps.c). diff -u --recursive /extra/linux-2.0.30/arch/i386/kernel/head.S linux/arch/i386/kernel/head.S --- /extra/linux-2.0.30/arch/i386/kernel/head.S Sat Apr 12 10:41:59 1997 +++ linux/arch/i386/kernel/head.S Sat Apr 12 10:44:58 1997 @@ -402,7 +402,7 @@ .quad 0xc0c392000000ffff /* 0x18 kernel 1GB data at 0xC0000000 */ .quad 0x00cbfa000000ffff /* 0x23 user 3GB code at 0x00000000 */ .quad 0x00cbf2000000ffff /* 0x2b user 3GB data at 0x00000000 */ - .quad 0x0000000000000000 /* not used */ + .quad 0x00cafa000000ffff /* 0x33 user 2.75GB code */ .quad 0x0000000000000000 /* not used */ .fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */ #ifdef CONFIG_APM diff -u --recursive /extra/linux-2.0.30/arch/i386/kernel/signal.c linux/arch/i386/kernel/signal.c --- /extra/linux-2.0.30/arch/i386/kernel/signal.c Sat Apr 12 10:41:59 1997 +++ linux/arch/i386/kernel/signal.c Sat Apr 12 10:44:58 1997 @@ -214,7 +214,7 @@ /* Set up registers for signal handler */ regs->esp = (unsigned long) frame; regs->eip = (unsigned long) sa->sa_handler; - regs->cs = USER_CS; regs->ss = USER_DS; + regs->cs = USER_HUGE_CS; regs->ss = USER_DS; regs->ds = USER_DS; regs->es = USER_DS; regs->gs = USER_DS; regs->fs = USER_DS; regs->eflags &= ~TF_MASK; diff -u --recursive /extra/linux-2.0.30/arch/i386/kernel/traps.c linux/arch/i386/kernel/traps.c --- /extra/linux-2.0.30/arch/i386/kernel/traps.c Sat Apr 12 10:41:59 1997 +++ linux/arch/i386/kernel/traps.c Sun Apr 13 07:22:44 1997 @@ -198,6 +198,14 @@ return; } die_if_kernel("general protection",regs,error_code); + if (regs->cs == USER_CS && get_seg_byte(USER_DS, (char *)regs->eip) != 0xC3) { +/* + * Switch to the original huge code segment (and allow code execution on the + * stack for this entire process), unless the faulty instruction is a RET. + */ + regs->cs = USER_HUGE_CS; + return; + } current->tss.error_code = error_code; current->tss.trap_no = 13; force_sig(SIGSEGV, current); diff -u --recursive /extra/linux-2.0.30/include/asm-i386/segment.h linux/include/asm-i386/segment.h --- /extra/linux-2.0.30/include/asm-i386/segment.h Sat Apr 12 10:41:37 1997 +++ linux/include/asm-i386/segment.h Sat Apr 12 10:44:58 1997 @@ -4,7 +4,8 @@ #define KERNEL_CS 0x10 #define KERNEL_DS 0x18 -#define USER_CS 0x23 +#define USER_HUGE_CS 0x23 +#define USER_CS 0x33 #define USER_DS 0x2B #ifndef __ASSEMBLY__ Signed, Solar Designer