p67_0x09_a eulogy for format strings_by_captain planet

Download p67_0x09_A Eulogy for Format Strings_by_Captain Planet

If you can't read please download the document

Upload: xeno

Post on 22-Aug-2014

16 views

Category:

Documents


1 download

TRANSCRIPT

Volume 0x0e, Issue 0x43, Phile #0x09 of 0x10 |=-----------------------------------------------------------------------=| |=-------------------=[ A Eulogy for Format Strings ]=-------------------=| |=-----------------------------------------------------------------------=| |=-----------------------=[ by Captain Planet ]=-------------------------=| |=-----------------------------------------------------------------------=| Index ------[ ------[ ------[ ------[ Introduction Glibc's FORTIFY_SOURCE Bypassing FORTIFY_SOURCE Exploitation A. Dummy program B. CUPS lppasswd bug C. TODO- ASLR ------[ 4. Afterword ------[ 0. Introduction Today the Windows CRT disables %n by default [0]. And li ewise, glibc's FORTIFY_SOURCE patches provides protection mechanisms which render exploitation impossible. Objective-C isn't being considered, but i'm told you can have plenty of fun there too. Even format strings weren't a critically endangered species, they've been demoted to the class of infolea . The great thing about format strings of course was that they provided both a read and write primitive. They were the `spor ` of exploitation. ASLR? PIE? NX Stac /Heap? No problem, fmt had you covered. The story goes that around 2000 everybody was hunting down format strings. Just about everything was vulnerable. Chec out the TESO article in the lin s. It was pretty outrageous. CORE exploited pretty much everything with locales too [1]. But today, those days are long one. Unless of course you're hac ing edus, in which case you can still use locale bugs to pop root shells on PMOS technology. A few months ago something funny happened. A guy by the name of Ronald Volgers [2] had his way with CUPS lppasswd, which was shipped root setuid in Ubuntu and Debian. Nice find man! Locale bugs, oh yeah, awesome! Unfortunately, the aforementioned patch ma es fmt str exploitation quite unli ely. In detail, the FORTIFY_SOURCE provides two countermeasures against fmt strings. 1) 2) Format strings containing the %n specifier may not be located at a writeable address in the memory space of the application. When using positional parameters, all arguments within the range must be consumed. So to use %7$x, you must also use 1,2,3,4,5 and 6. 0. 1. 2. 3.

But thats o ay since the FORTIFY_SOURCE patch is not really all that complete. (-: Why? Because glibc is really really weird code. It amazes me that someone would travel all around the world and ta e credit for glibc when they did not even write it. Actually, it ma es perfect sense, nobody

==Phrac

Inc.==

with any dignity would admit to writing any part of glibc to public audiences. Don't get me wrong, glibc lets pretty good stuff happen to my computer. The code is pretty hard to loo at though, you wouldn't introduce her to your parents if you now what I mean... What you're about to read is slightly harder than writing a format string and a little bit easier than building glibc itself. (Glibc binaries are ideal for an ELF VX because of the difficulty of compiling them). Prequisites are understanding format string exploitation. They were last written about in phrac here [3] if you need a refresher. If you have never exploited a format string vuln, see the article by 'rebel' [6]. It is one of the most s illed and digestible discourses available. So lets dive right in. ------[ 1. Glibc's FORTIFY_SOURCE =========================================================================== WARNING: THE REST OF THIS ARTICLE INCLUDES GLIBC CODE WHICH MAY INDUCE CHEST PAIN, VOMITING, BLACKOUTS, or PERMANENT LOSS OF EYESIGHT. ALL ATTEMPTS TO KEEP KEEP GLIBC CODE TO A MINIMUM HAVE BEEN MADE BY THE AUTHOR. =========================================================================== "%49150u %4849$hn %1$*269158540$x %1$*13996$x %1073741824$d" Have you seen a format string li e that before? It ma es positional parameters loo less attractive, doesn't it? So how does this patch supposedly wor ? To turn it on the binary must be compiled with `-D_FORTIFY_SOURCE=2` enabled with an optimization level of at least -O2. This is li ely because of the compiler pass the patch is implemented at. So the following happens. 0x08048509 : mov 0x0804850d : movl 0x08048514 : call %ebx,0x4(%esp) $0x1,(%esp) 0x80483c4

First, calls to printf, etc get rerouted to __*_ch in your compiled binary and the first argument of :flag: is passed as 1. A. +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ File: libc/debug/printf_ch .c /* Write formatted output to stdout from the format string FORMAT. */ int ___printf_ch (int flag, const char *format, ...) { va_list ap; int done; _IO_acquire_loc _clear_flags2 (stdout); if (flag > 0) stdout->_flags2 |= _IO_FLAGS2_FORTIFY; va_start (ap, format); done = vfprintf (stdout, format, ap);

va_end (ap); if (flag > 0) stdout->_flags2 &= ~_IO_FLAGS2_FORTIFY; _IO_release_loc (stdout); return done; } +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ The function sets the _IO_FLAGS2_FORTIFY bit to ON in the FILE* structure to enable the FORTIFY chec s. This is sort of clever, as the bit will always get toggled on when entering dangerous functions. You can not universally disable the mechanism very easily. But this itself does not actually guarantee any ind of security. Under libio/libio.h the following secondary flags are defined: #define _IO_FLAGS2_MMAP 1 #define _IO_FLAGS2_NOTCANCEL 2 //fopen 'm' mmap access mode //open/read/write should not be used as thread cancellization points

Disabling the entire flags buffer should not be too much trouble, but may lead to some inconsistencies if the file stream pointer is opened with 'm' in the mode parameter. The astute reader will be wondering about functions such as vsnprintf, which require no file stream pointer. Well, glibc provides an o ay solution. A file stream pointer is made on the stac with a callbac that writes to a buffer instead of a file descriptor. This file stream pointer is then passed along to vfprintf. Now, with the _IO_FLAGS2_FORTIFY bit set, there are two protections that are enabled. B. Protection #1 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ File: libc/stdio-common/vfprintf.c LABEL (form_number): \ if (s->_flags2 & _IO_FLAGS2_FORTIFY) { if (! readonly_format) { size_t) extern int __readonly_area (const void *, \ \ \ \ \ \

attribute_hidden;

#ifdef _LIBC # define _IO_FLAGS2_FORTIFY 4 //enable fortify security chec s #endif #define _IO_FLAGS2_USER_WBUF 8 //wide buffer (2-byte) support fun #ifdef _LIBC # define _IO_FLAGS2_SCANF_STD 16 // %a support for scanf #endif

readonly_format = __readonly_area (format, ((STR_LEN (format) + 1) (CHAR_T))); } if (readonly_format < 0) ***\n"); \ __libc_fatal ("*** %n in writable segment detected \ \ * sizeof \

\

\

} \ +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

C. Protection #2 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ File: libc/stdio-common/vfprintf.c /* Determine the number of arguments the format string consumes. */ nargs = MAX (nargs, max_ref_arg); /* Allocate memory for the argument descriptions. */ args_type = alloca (nargs * sizeof (int)); memset (args_type, s->_flags2 & _IO_FLAGS2_FORTIFY ? '\xff' : '\0', nargs * sizeof (int)); args_value = alloca (nargs * sizeof (union printf_arg)); args_size = alloca (nargs * sizeof (int)); .. for (cnt = 0; cnt < nargs; ++cnt) .. switch (args_type[cnt]) .. case -1: /* Error case. Not all parameters appear in N$ format strings. We have no way to determine their type. */ assert (s->_flags2 & _IO_FLAGS2_FORTIFY); __libc_fatal ("*** invalid %N$ use detected ***\n"); } +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ The effect of this second patch is that all of the arg_types are set to -1 by default. If there are any argument holes in between which do not get processed, they are left as -1. So the effect is that: %4$x would be invalid but %4$x %2$x %1$x %3x would be o ay. To be honest, I do not really see this as some huge security improvement

If the memory detect pretty

format string payload containing a %n is located in a writeable area such as the stac or BSS or DATA or the heap, this patch will it and error out. Besides a DoS, this patch renders format strings harmless.

but more of a nuisance. It does not really stop infolea s much. Maybe they wanted to prevent people from exploiting 8 character format strings, because those are really common in the wargaming scene. ------[ 2. Bypassing FORTIFY_SOURCE Now, if you were paying attention, you saw a bunch of allocas in 1-A. That :nargs: variable, that is the calculated maximum number of arguments. If a fmt str has simple arguments, the value is just that number. But, if a fmt str uses width arguments or positional parameters (often called direct parameters in other format string articles), then those also factor into the maximum :nargs: value As an example in this string, %x %x %x %13981938$x, 13981938 is the :nargs: value being passed to the alloca functions in code snippet 1-C. Do not get too excited. This is not enough for generic control. Unfortunately, we can not do the same stac shifting as in [4] since we are in a context past the initial stac frame allocation. At the epilogue of the function, a base register will be used to collapse the stac , ma ing stac shifting less useful without being accompanied by memory clobbering. This is true of many of the architecture's C compilers. They pretty much all implement some sort of easy stac clean-up with a base register, so alloca itself is difficult to attac . Instead, it is the operations that use the allocated memory that must be exploited. The integer overflow can be used to trigger all sorts of memory trespasses. One other thing to do is shift the stac into the heap using the alloca. This also turns out to be difficult because of those memset operations. But we do have a loss of state. And as always, from a loss of state new opportunites arise. We are in the land of undefined. Hi mom and dad! This article will use one such trespass opportunity to bypass FORTIFY_SOURCE. It should be noted that others exist, but may be a bit harder to utilize than this one. > The first patch (1-B) can be disabled by clearing that IO_FLAGS2_FORTIFY bit in the file stream pointer. Typically it will be the only flag enabled on the file stream pointer. In the unli eley case that one of the other bits was set, for example _IO_FLAGS2_MMAP, inconsitencies may arise when the file stream pointer is closed. This may or may not affect exploitability.

The details of the allocas are a little bit more complex if you loo assembly. For our purposes, they roughly end up doing:

So the

ey constraints around :nargs: are as follows:

at the

We will now revisit the second part of the patch. It ma es any format string exploit less flexible. The loop on nargs has to be terminated early to avoid the assert and the libc_fatal when a "hole" in the arguments is discovered. By hole, I am referring to the code in (1-C) which chec s the :args_type: value against -1. Remember that the fortify source patch won't let you access %5$x without also accessing %1$? %2$? %3$? and %4?. That is what is meant by 'hole' in this context. The termination of that loop can coincidentally happen all by itself if the stac is aligned correctly. The loop will hit out of bounds of the alloca created buffers and self terminate when :nargs: is set to 0, provided that :nargs: is stored by the compiler on the stac . If it fails to do this, an assert() statemet will be triggered, preventing exploitation. Or, we can reuse the 4-byte NUL write can be used to bypass the loop reliably. One instance of a successful bypass can then be performed in two easy steps. 1. Turn off the IO_FORTIFY_SOURCE bit to allow %n from a writeable address 2. Set nargs=0 to s ip the value-filling loop. Note that bypassing the loop via #2 requires us to dig further down the stac to find our user input since the same loop is responsible for filling in the args_value array. If you have ever attempted to exploit a format string by truncating a pointer and reusing it as destination on glibc, you probably failed because of that args_value array. ------[ 3. Exploitation In standard phrac style we will first do this on a test binary and then on a real-world binary to disprove any accusations of academic tendencies, li e thought experiments. Feel free to s ip to part B. ------------[ A. Dummy Test Program for clarity Note: ASLR is disabled and the program has an executable stac . +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ //File: test.c //gcc -D_FORTIFY_SOURCE=2 -O2 int main(){ char buf[256]; fgets(buf, sizeof(buf), stdin); printf(buf); } +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ captain@planet:~/research/fmt/article$ ./a.out %n *** %n in writable segment detected *** Aborted captain@planet:~/research/fmt/article$ ./a.out %4$x *** invalid %N$ use detected *** Aborted Oh nooO! Scary format string protections are ma ing everything hurt.

ENABLE POWER MORPHING LINUX SHARING COMMUNITY POWER ---Alright remember the process ids. 1. Disable fortify source 2. Set nargs = 0 3. Enjoy the %n So first, lets figure out where that arbitrary 4-byte NUL write is on our system. We will pic some ridiculous desination, li e %1$*269168516$. If it doesn't crash, eep incrementing that by about 20000. So we'll send the following as our investigative payload. The first part should trigger the NUL write. The second part should eep the stac sane. %1$*269168516$x %1073741824$ %