2

I'm new to buffer overflows and I'm trying to understand exactly what I'm doing before using any premade and easy to go scripts. My goal is to spawn a shell, so I found the asm code to do an execve("/bin/sh"). The shellcode comes from shell-storm. :

char *shellcode = "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
          "\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80";

Going through tutorials and stack documentation, I understood that the stack goes downward (from high addresses to low addresses when we add data on it).

For now let's say that this shellcode is stored in a local buffer starting at 0x00.

  1. Will we have the shellcode stored like that :

    0x00 31 c0 50 68
    0x04 2f 2f 73 68 
    0x08 .....
    

    meaning that in the stack it will looks like :

    0x08 .....
    0x04 2f 2f 73 68 
    0x00 31 c0 50 68
    
  2. When a scanf("%d",myInt) is performed I understand that if we give "9" and that &myInt = 0x04 we will have 0x04 : 09 00 00 00 . Means that the "09" is at the address 0x04. Even if addresses are only accessible when they are a multiple of 4 and given that this is little endian.

  3. Now how does the scanf works with a %s ? Does he read chars four by four and store the first in the (little endian) lowest 0x04 address and the 4th in the 0x07?

So the real question is:

Considering that we usually reverse the address that we want to see taking the place of the stored IP. Should we do the same for our shellcode ? When we want to overwrite the IP register in the stack by 0x01020304 we commonly write in our payload \x04\03\02\01 because we write in the stack upward, meaning that we hit the least significant bit of an address first.

1 Answer 1

0
  1. No, most operating systems today are little endian, the string are reversed in memory. To see this, compile in 32 bits a code like:
    // test.c
    #include <stdio.h>

    int main(){
        char buf[8] = "ABCDEFGH";
        int n = 9;
        printf("%s\n", buf);
        printf("%d\n", n);
        return 0;
    }

with:

$ apt install gcc-multilib
$ gcc -g test.c -o test -m32

Then run the binary in gdb, break before printf execution and look at the stack:

$ gdb ./test -q 
Reading symbols from ./test...done.
(gdb) disas main
Dump of assembler code for function main:
   [...]
   0x000011db <+50>:    sub    $0xc,%esp
   0x000011de <+53>:    lea    -0x14(%ebp),%eax
   0x000011e1 <+56>:    push   %eax
   0x000011e2 <+57>:    call   0x1040 <puts@plt>
   [...]
End of assembler dump.
// printf is replaced by puts system call here

(gdb) b *main+57
Breakpoint 1 at 0x11e2: file test.c, line 6.

(gdb) r
Starting program: /root/test 

Breakpoint 1, 0x565561e2 in main () at test.c:6
6       printf("%s\n", buf);

(gdb) x/20x $esp
0xffffd280: 0xffffd294  0x56559000  0xffffd35c  0x565561c0
0xffffd290: 0x00000001  0x44434241  0x48474645  0x00000009
0xffffd2a0: 0xffffd2c0  0x00000000  0x00000000  0xf7deab41
0xffffd2b0: 0xf7faa000  0xf7faa000  0x00000000  0xf7deab41
0xffffd2c0: 0x00000001  0xffffd354  0xffffd35c  0xffffd2e4

ASCII code for 'A' is 0x41, 'B' 0x42, ..., so in the second line you can see that ABCDEFGH is stored as:

0x94: DCBA
0x98: HGFE
0x9B: 0x00000009

Note that the end of the string is in a higher address.

  1. Scanf store the input string in the same way:
$ cat test.c

#include <stdio.h>

int main(){
    char input[8];
    scanf("%s\n", input);
    printf("%s\n", input);
    return 0;
}

$ gcc -g test.c -o test -m32
$ gdb ./test -q

(gdb) disas main
   [...]
   0x000011d4 <+43>:    call   0x1050 <__isoc99_scanf@plt>
   0x000011d9 <+48>:    add    $0x10,%esp
   0x000011dc <+51>:    sub    $0xc,%esp
   0x000011df <+54>:    lea    -0x10(%ebp),%eax
   0x000011e2 <+57>:    push   %eax
   0x000011e3 <+58>:    call   0x1030 <puts@plt>
   [...]

(gdb) b * main+58
Breakpoint 1 at 0x11e3: file test.c, line 6.

(gdb) r < <(echo "ABCDEFG")
Starting program: /root/test < <(echo "ABCDEFG")

Breakpoint 1, 0x565561e3 in main () at test.c:6
6       printf("%s\n", input);
(gdb) x/20x $esp
0xffffd280: 0xffffd298  0xffffd298  0xffffd35c  0x565561c0
0xffffd290: 0x00000001  0xffffd354  0x44434241  0x00474645
0xffffd2a0: 0xffffd2c0  0x00000000  0x00000000  0xf7deab41
0xffffd2b0: 0xf7faa000  0xf7faa000  0x00000000  0xf7deab41
0xffffd2c0: 0x00000001  0xffffd354  0xffffd35c  0xffffd2e4
(gdb) 

  1. Because of that you have to reverse the address you want to put in eip via an input string to have a correct address in memory (eg: 0xdeadbeef becomes "\xef\xbe\xad\de").

A shellcode is a set of assembly instruction you will store in memory, the following string is ready for an input, you don't need to modify it:

"\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69"
"\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80"

After input you will have a sequence of opcodes in memory like in code segment.
The stack grows down but strings "grow normally" (remember "ABCDEFGH") and eip get incremented after each instruction so it will follow the order of the instructions in the string:

Disassembly:
0:  31 c0                   xor    eax,eax
2:  50                      push   eax

String Literal:
"\x31\xC0\x50"

Don't worry about little endian, it is just the way data are stored, it just matters when you want to craft an address (overwrite of saved eip for example).

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .