While writing my "strcpy overflow tutorial", I was about to use my custom made shellcode. But then I realized, If it would have been me reading that tutorial, I surely would have wanted to know from where the h3ll that cryptic code came from. So, I decided to write this small tutorial showing how to write a very basic shellcode. This tutorial is not intended to be exhaustive. It just shows you the hood, its you who will have to pick up the hood and explore whats under it.
Lab Setup
- C/C++ compiler : lcc-win32
- Assembler : nasm
- arwin : A little script to find functions in a dll. You can find it here.
- Debugger : Immunity Debugger
- Perl: ActivePerl-5.12.4
- A piece of code to test our shellcode
Lets Begin
What we want to do today
By the end of this tutorial we will try to make a working shellcode (which you can use when you go through "Strcpy BOF" tutorial ;) ). In addition to that we will also take a look at few techniques which can be used to make your shellcode a bit more reliable. So lets see what our goal state is:
By the end we would be able to write a shellcode which would invoke a popup as shown above. We would be calling the "MessageBox" method to invoke this popup.
Lets analyse the working of above program by loading its exe in a debugger (Immunity Debugger)For example
If we look at the details of "MessageBox" method here at msdn we find out that this method takes 4 arguments.As we can see in the image above, these 4 arguments are pushed onto the stack and then a call to "MessageBox" is made. Also, it should be noted that the 2nd and 3rd arguments are pointer to strings "Hello World" and "Shellcode" respectively.
So this is what we want our shellcode to do:
1. push the strings "Hello World" and "Shellcode" to the stack.
2. push the arguments "0" , "pointer to HELLO WORLD", "pointer to SHELLCODE" , 0 onto the stack.
3. call MessageBox.
We will try to find the MessageBox method in " user32.dll " using arwin as shown below:
*** Remember the method name is case sensitive ***
Now, lets write the shellcode ( the assembly version ;) )
PUSH 0X00202021 ;PUSH "HELLO WORLD"
PUSH 0X00202065 ;PUSH "SHELLCODE"
XOR EAX,EAX ;MAKE EAX=0
PUSH EAX ;PUSH ALL FOUR ARGUMENTS
CALL ESI ;CALLTO MESSAGEBOX
Now we need to convert this assembly version of shellcode into the hex version which we can use in our C application to test this shellcode. We can do so using nasm as shown below:
After using nasm we get a binary file. Now we need to convert this binary file into a hex file. We do so again by using a script (pveReadBin.pl) written by Peter Van Eeckhoutte of the "corelan team" .
#!/usr/bin/perl
# Perl script written by Peter Van Eeckhoutte
# http://www.corelan.be:8800
# This script takes a filename as argument
# will read the file
# and output the bytes in \x format
#
if ($#ARGV ne 0) {
print " usage: $0 ".chr(34)."filename".chr(34)."\n";
exit(0);
}
#open file in binary mode
print "Reading ".$ARGV[0]."\n";
open(FILE,$ARGV[0]);
binmode FILE;
my ($data, $n, $offset, $strContent);
$strContent="";
my $cnt=0;
while (($n = read FILE, $data, 1, $offset) != 0) {
$offset += $n;
}
close(FILE);
print "Read ".$offset." bytes\n\n";
my $cnt=0;
my $nullbyte=0;
print chr(34);
for ($i=0; $i < (length($data)); $i++)
{
my $c = substr($data, $i, 1);
$str1 = sprintf("%01x", ((ord($c) & 0xf0) >> 4) & 0x0f);
$str2 = sprintf("%01x", ord($c) & 0x0f);
if ($cnt < 8)
{
print "\\x".$str1.$str2;
$cnt=$cnt+1;
}
else
{
$cnt=1;
print chr(34)."\n".chr(34)."\\x".$str1.$str2;
}
if (($str1 eq "0") && ($str2 eq "0"))
{
$nullbyte=$nullbyte+1;
}
}
print chr(34).";\n";
print "\nNumber of null bytes : " . $nullbyte."\n";
Use the shellcode generated by this script in your C application and Compile & Execute.
AND VOILA!!!
Now we have our first working shellcode. After pressing the OK button it is most likely to crash as we have not yet managed to exit cleanly via our shellcode.
But still we want our shellcode to exit cleanly. So what can we do about it?
Well like we called the "MessageBox" method, we can also call the "ExitProcess" method. Again we will use the arwin program to search the address of "ExitProcess" method in "Kernel32.dll".
As we can see here that ExitProcess method takes one argument i.e. the exit code, so before calling this method we need to push this argument onto the stack. We need to modify our code accordingly as shown below:
PUSH 0X00202021 ;PUSH "HELLO WORLD"
PUSH 0X00202065 ;PUSH "SHELLCODE"
XOR EAX,EAX ;MAKE EAX=0
PUSH EAX ;PUSH ALL FOUR ARGUMENTS
CALL ESI ;CALLTO MESSAGEBOX
XOR EAX,EAX ;MAKE EAX=0
PUSH EAX ;PUSH THE ARGUMENT
MOV EAX,0X7656BBE2 ;STORE ADDRESS OF ExitProcess
JMP EAX ;JUMP TO ExitProcess
Again, you need to convert this assembly code into hex form using arwin and pveReadBin.pl. Compile & Execute. Here you go, now you have a working shellcode which can exit cleanly and you are going to use it in strcpy BOF tutorial (most probably ;) ).
But wait, before being too happy you need to take another look at the generated shellcode:
"\x68\x21\x20\x20\x00\x68\x72\x6c"
"\x64\x21\x68\x6f\x20\x57\x6f\x68"
"\x48\x65\x6c\x6c\x89\xe3\x68\x65"
"\x20\x20\x00\x68\x6c\x63\x6f\x64"
"\x68\x53\x68\x65\x6c\x89\xe1\x31"
"\xc0\x50\x53\x51\x50\xbe\x11\xea"
"\x22\x76\xff\xd6\x31\xc0\x50\xb8"
"\xe2\xbb\x56\x76\xff\xe0";
Did you notice anything about the highlighted bytes in the generated shellcode? These wont let your shellcode work with the string functions as these are null bytes and as you might already know that null bytes are used as string terminators. So we need to remove these null bytes so as to make our shellcode work with string functions.
Well, we have a very simple technique to do so :)
We will not push these null bytes directly onto the stack, we will modify our shellcode so that these null bytes gets generated themselves by our shellcode. And how do we do it?
Lets see:
Now if we look at our assembly code, we find that these two null bytes corresponds to below mentioned instructions:
PUSH 0X00202021 ;PUSH "HELLO WORLD"
PUSH 0X00202065 ;PUSH "SHELLCODE"
What if we subtract 0x11111111 from 0x00202021( =0XEF0F0F10), move it to EBX, add 0x11111111 to EBX. This way we bypass the problem of null bytes and the result we get is the same as in the previous case.
And similarly, we can handle the 2nd null byte as well.
Finally, we get this:
[BITS 32]
MOV EBX,0XEF0F0F10
ADD EBX,0X11111111
PUSH EBX
PUSH 0X21646C72
PUSH 0X6F57206F
PUSH 0X6C6C6548
MOV EBX,ESP
MOV ECX,0XEF0F0F54
ADD ECX,0X11111111
PUSH ECX
PUSH 0X646F636C
PUSH 0X6C656853
MOV ECX,ESP
XOR EAX,EAX
PUSH EAX
PUSH EBX
PUSH ECX
PUSH EAX
MOV ESI,0X7622EA11
CALL ESI
XOR EAX,EAX
PUSH EAX
MOV EAX,0X7656BBE2 ;Address of ExitProcess
JMP EAX
Convert to hex using arwin and pveReadBin.pl again.
Reading shellcode - nullfree.bin
Read 76 bytes
"\xbb\x10\x0f\x0f\xef\x81\xc3\x11"
"\x11\x11\x11\x53\x68\x72\x6c\x64"
"\x21\x68\x6f\x20\x57\x6f\x68\x48"
"\x65\x6c\x6c\x89\xe3\xb9\x54\x0f"
"\x0f\xef\x81\xc1\x11\x11\x11\x11"
"\x51\x68\x6c\x63\x6f\x64\x68\x53"
"\x68\x65\x6c\x89\xe1\x31\xc0\x50"
"\x53\x51\x50\xbe\x11\xea\x22\x76"
"\xff\xd6\x31\xc0\x50\xb8\xe2\xbb"
"\x56\x76\xff\xe0";
Number of null bytes : 0
- Perl: ActivePerl-5.12.4
- A piece of code to test our shellcode
char code[]=
"PASTE YOUR SHELLCODE HERE";
int main(int argc, char**argv)
{
int (*func)();
func = (int(*)()) code;
(int)(*func)();
}
Lets Begin
First of all we should know what a shellcode is. Whenever a vulnerability is found in a piece of code/application, it needs to be exploited to alter the actual behavior of the application. After the attacker has taken control of the application, (s)he needs to make that application do something useful. This something useful can be simply spawning a calculator or opening a port so that attacker can remotely connect to victim's machine. This code which does "something useful" for the attacker is known as shellcode. In other words we can say that the actual code which attacker wants to run after exploiting the vulnerability is known as shellcode.
What we want to do today
By the end of this tutorial we will try to make a working shellcode (which you can use when you go through "Strcpy BOF" tutorial ;) ). In addition to that we will also take a look at few techniques which can be used to make your shellcode a bit more reliable. So lets see what our goal state is:
By the end we would be able to write a shellcode which would invoke a popup as shown above. We would be calling the "MessageBox" method to invoke this popup.
Lets analyse the working of above program by loading its exe in a debugger (Immunity Debugger)For example
If we look at the details of "MessageBox" method here at msdn we find out that this method takes 4 arguments.As we can see in the image above, these 4 arguments are pushed onto the stack and then a call to "MessageBox" is made. Also, it should be noted that the 2nd and 3rd arguments are pointer to strings "Hello World" and "Shellcode" respectively.
So this is what we want our shellcode to do:
1. push the strings "Hello World" and "Shellcode" to the stack.
2. push the arguments "0" , "pointer to HELLO WORLD", "pointer to SHELLCODE" , 0 onto the stack.
3. call MessageBox.
Now lets see how this can be accomplished.
PUSHING STRINGS ON THE STACK: We will be using a perl script written by Peter Van Eeckhoutte of the "corelan team" to convert strings to hex.
PUSHING STRINGS ON THE STACK: We will be using a perl script written by Peter Van Eeckhoutte of the "corelan team" to convert strings to hex.
#!/usr/bin/perl# Perl script written by Peter Van Eeckhoutte# http://www.corelan.be:8800# This script takes a string as argument# and will produce the opcodes# to push this string onto the stack#if ($#ARGV ne 0) {print " usage: $0 ".chr(34)."String to put on stack".chr(34)."\n";exit(0);}#convert string to bytesmy $strToPush=$ARGV[0];my $strThisChar="";my $strThisHex="";my $cnt=0;my $bytecnt=0;my $strHex="";my $strOpcodes="";my $strPush="";print "String length : " . length($strToPush)."\n";print "Opcodes to push this string onto the stack :\n\n";while ($cnt < length($strToPush)){ $strThisChar=substr($strToPush,$cnt,1); $strThisHex="\\x".ascii_to_hex($strThisChar); if ($bytecnt < 3) { $strHex=$strHex.$strThisHex; $bytecnt=$bytecnt+1; } else { $strPush = $strHex.$strThisHex; $strPush =~ tr/\\x//d; $strHex=chr(34)."\\x68".$strHex.$strThisHex.chr(34). " //PUSH 0x".substr($strPush,6,2).substr($strPush,4,2). substr($strPush,2,2).substr($strPush,0,2);
$strOpcodes=$strHex."\n".$strOpcodes; $strHex=""; $bytecnt=0; } $cnt=$cnt+1;}#last lineif (length($strHex) > 0){ while(length($strHex) < 12) { $strHex=$strHex."\\x20"; } $strPush = $strHex; $strPush =~ tr/\\x//d; $strHex=chr(34)."\\x68".$strHex."\\x00".chr(34)." //PUSH 0x00". substr($strPush,4,2).substr($strPush,2,2).substr($strPush,0,2); $strOpcodes=$strHex."\n".$strOpcodes;}else{ #add line with spaces + null byte (string terminator) $strOpcodes=chr(34)."\\x68\\x20\\x20\\x20\\x00".chr(34). " //PUSH 0x00202020"."\n".$strOpcodes;}print $strOpcodes;
sub ascii_to_hex ($){ (my $str = shift) =~ s/(.|\n)/sprintf("%02lx", ord $1)/eg; return $str;}
For example:
We have just generated hex for pushing the strings onto the stack, but the MessageBox method expects pointer to these strings. So, after pushing each of these strings onto the stack we will store ESP in any of the registers and when we need to push the arguments before the method call, we will use these stored pointers.
WRITING ACTUAL SHELLCODE:
Below mentioned will be the flow of our shellcode:
1. push text "Shellcode" to the stack
2. save pointer to this string i.e. ESP to register EBX
3. push caption "Hello World!!" to the stack
4. again save ESP to ECX
5. XOR EAX - to make EAX=0 so that we can use it for arguments having value=0
6. push EAX - 1st argument
7. push EBX - 2nd argument (pointer to the text "Shellcode")
8. push ECX - 3rd argument (pointer to caption "Hello World!!" )
9. push EAX - 4th argument
10. call <address of MessageBox> - for this we need to know the address of MessageBox first.
You can run this Perl script and redirect the output to a text file using redirection ( > ) and use it for yourself.We have just generated hex for pushing the strings onto the stack, but the MessageBox method expects pointer to these strings. So, after pushing each of these strings onto the stack we will store ESP in any of the registers and when we need to push the arguments before the method call, we will use these stored pointers.
WRITING ACTUAL SHELLCODE:
Below mentioned will be the flow of our shellcode:
1. push text "Shellcode" to the stack
2. save pointer to this string i.e. ESP to register EBX
3. push caption "Hello World!!" to the stack
4. again save ESP to ECX
5. XOR EAX - to make EAX=0 so that we can use it for arguments having value=0
6. push EAX - 1st argument
7. push EBX - 2nd argument (pointer to the text "Shellcode")
8. push ECX - 3rd argument (pointer to caption "Hello World!!" )
9. push EAX - 4th argument
10. call <address of MessageBox> - for this we need to know the address of MessageBox first.
GETTING ADDRESS OF "MESSAGEBOX" METHOD
We can use a C program written by "steve hanna" to find the address of MessageBox method. But before we search for the address, we need to know where to search. To accomplish this we would again load the exe in the debugger and take a look at the log. In this log we can see all the dll's that were loaded before executing the program.
We will try to find the MessageBox method in " user32.dll " using arwin as shown below:
*** Remember the method name is case sensitive ***
Now, lets write the shellcode ( the assembly version ;) )
[BITS 32]
PUSH 0X21646C72
PUSH 0X6F57206F
PUSH 0X6C6C6548
MOV EBX,ESP ;SAVE THE POINTER TO STRINGPUSH 0X00202065 ;PUSH "SHELLCODE"
PUSH 0X646F636C
PUSH 0X6C656853
MOV ECX,ESP ;SAVE POINTER TO STRINGXOR EAX,EAX ;MAKE EAX=0
PUSH EAX ;PUSH ALL FOUR ARGUMENTS
PUSH EBX
PUSH ECX
PUSH EAX
MOV ESI,0X7622EA11 ;STORE ADDRESS OF MESSAGEBOXCALL ESI ;CALLTO MESSAGEBOX
Now we need to convert this assembly version of shellcode into the hex version which we can use in our C application to test this shellcode. We can do so using nasm as shown below:
After using nasm we get a binary file. Now we need to convert this binary file into a hex file. We do so again by using a script (pveReadBin.pl) written by Peter Van Eeckhoutte of the "corelan team" .
#!/usr/bin/perl
# Perl script written by Peter Van Eeckhoutte
# http://www.corelan.be:8800
# This script takes a filename as argument
# will read the file
# and output the bytes in \x format
#
if ($#ARGV ne 0) {
print " usage: $0 ".chr(34)."filename".chr(34)."\n";
exit(0);
}
#open file in binary mode
print "Reading ".$ARGV[0]."\n";
open(FILE,$ARGV[0]);
binmode FILE;
my ($data, $n, $offset, $strContent);
$strContent="";
my $cnt=0;
while (($n = read FILE, $data, 1, $offset) != 0) {
$offset += $n;
}
close(FILE);
print "Read ".$offset." bytes\n\n";
my $cnt=0;
my $nullbyte=0;
print chr(34);
for ($i=0; $i < (length($data)); $i++)
{
my $c = substr($data, $i, 1);
$str1 = sprintf("%01x", ((ord($c) & 0xf0) >> 4) & 0x0f);
$str2 = sprintf("%01x", ord($c) & 0x0f);
if ($cnt < 8)
{
print "\\x".$str1.$str2;
$cnt=$cnt+1;
}
else
{
$cnt=1;
print chr(34)."\n".chr(34)."\\x".$str1.$str2;
}
if (($str1 eq "0") && ($str2 eq "0"))
{
$nullbyte=$nullbyte+1;
}
}
print chr(34).";\n";
print "\nNumber of null bytes : " . $nullbyte."\n";
AND VOILA!!!
But still we want our shellcode to exit cleanly. So what can we do about it?
Well like we called the "MessageBox" method, we can also call the "ExitProcess" method. Again we will use the arwin program to search the address of "ExitProcess" method in "Kernel32.dll".
As we can see here that ExitProcess method takes one argument i.e. the exit code, so before calling this method we need to push this argument onto the stack. We need to modify our code accordingly as shown below:
[BITS 32]
PUSH 0X21646C72
PUSH 0X6F57206F
PUSH 0X6C6C6548
MOV EBX,ESP ;SAVE THE POINTER TO STRINGPUSH 0X00202065 ;PUSH "SHELLCODE"
PUSH 0X646F636C
PUSH 0X6C656853
MOV ECX,ESP ;SAVE POINTER TO STRINGXOR EAX,EAX ;MAKE EAX=0
PUSH EAX ;PUSH ALL FOUR ARGUMENTS
PUSH EBX
PUSH ECX
PUSH EAX
MOV ESI,0X7622EA11 ;STORE ADDRESS OF MESSAGEBOXCALL ESI ;CALLTO MESSAGEBOX
XOR EAX,EAX ;MAKE EAX=0
PUSH EAX ;PUSH THE ARGUMENT
MOV EAX,0X7656BBE2 ;STORE ADDRESS OF ExitProcess
JMP EAX ;JUMP TO ExitProcess
Again, you need to convert this assembly code into hex form using arwin and pveReadBin.pl. Compile & Execute. Here you go, now you have a working shellcode which can exit cleanly and you are going to use it in strcpy BOF tutorial (most probably ;) ).
But wait, before being too happy you need to take another look at the generated shellcode:
"\x68\x21\x20\x20\x00\x68\x72\x6c"
"\x64\x21\x68\x6f\x20\x57\x6f\x68"
"\x48\x65\x6c\x6c\x89\xe3\x68\x65"
"\x20\x20\x00\x68\x6c\x63\x6f\x64"
"\x68\x53\x68\x65\x6c\x89\xe1\x31"
"\xc0\x50\x53\x51\x50\xbe\x11\xea"
"\x22\x76\xff\xd6\x31\xc0\x50\xb8"
"\xe2\xbb\x56\x76\xff\xe0";
Did you notice anything about the highlighted bytes in the generated shellcode? These wont let your shellcode work with the string functions as these are null bytes and as you might already know that null bytes are used as string terminators. So we need to remove these null bytes so as to make our shellcode work with string functions.
Well, we have a very simple technique to do so :)
We will not push these null bytes directly onto the stack, we will modify our shellcode so that these null bytes gets generated themselves by our shellcode. And how do we do it?
Lets see:
Now if we look at our assembly code, we find that these two null bytes corresponds to below mentioned instructions:
PUSH 0X00202021 ;PUSH "HELLO WORLD"
PUSH 0X00202065 ;PUSH "SHELLCODE"
What if we subtract 0x11111111 from 0x00202021( =0XEF0F0F10), move it to EBX, add 0x11111111 to EBX. This way we bypass the problem of null bytes and the result we get is the same as in the previous case.
And similarly, we can handle the 2nd null byte as well.
Finally, we get this:
[BITS 32]
MOV EBX,0XEF0F0F10
ADD EBX,0X11111111
PUSH EBX
PUSH 0X21646C72
PUSH 0X6F57206F
PUSH 0X6C6C6548
MOV EBX,ESP
MOV ECX,0XEF0F0F54
ADD ECX,0X11111111
PUSH ECX
PUSH 0X646F636C
PUSH 0X6C656853
MOV ECX,ESP
XOR EAX,EAX
PUSH EAX
PUSH EBX
PUSH ECX
PUSH EAX
MOV ESI,0X7622EA11
CALL ESI
XOR EAX,EAX
PUSH EAX
MOV EAX,0X7656BBE2 ;Address of ExitProcess
JMP EAX
Convert to hex using arwin and pveReadBin.pl again.
Reading shellcode - nullfree.bin
Read 76 bytes
"\xbb\x10\x0f\x0f\xef\x81\xc3\x11"
"\x11\x11\x11\x53\x68\x72\x6c\x64"
"\x21\x68\x6f\x20\x57\x6f\x68\x48"
"\x65\x6c\x6c\x89\xe3\xb9\x54\x0f"
"\x0f\xef\x81\xc1\x11\x11\x11\x11"
"\x51\x68\x6c\x63\x6f\x64\x68\x53"
"\x68\x65\x6c\x89\xe1\x31\xc0\x50"
"\x53\x51\x50\xbe\x11\xea\x22\x76"
"\xff\xd6\x31\xc0\x50\xb8\xe2\xbb"
"\x56\x76\xff\xe0";
Number of null bytes : 0
As we can see above, this time our shellcode is null-free i.e. it does not contain any null bytes.
Put it into your C application, Compile & Execute & Yippeee....
Now, we have made our shellcode a bit more reliable, as I said in the beginning ;) .
Now you are good to go to use this shellcode in string related functions as well ( as well as my strcpy BOF tutorial :) ).
Thanks for your patience while reading this tutorial (Hope you didn't bang your head against the keyboard :P )









No comments:
Post a Comment