Entry Methods
After defining metadata and options, implement the entry method that the framework calls. Its name depends on the module type.
Reference table
Section titled “Reference table”| Type | Entry point | When it is called |
|---|---|---|
| exploit | exploit | run / exploit command |
| exploit / some auxiliary | check | check command or AutoCheck |
| auxiliary (ordinary) | run | run |
| auxiliary + Scanner | run_host(ip) | Once per host (run is provided by the mixin) |
| auxiliary + Scanner check | check_host(ip) | Check for each host |
| post | run | run (requires SESSION) |
check (optional but recommended)
Section titled “check (optional but recommended)”This method only determines whether a target is vulnerable and does not send a payload (or uses only minimal proof). It returns CheckCode::*. Return a CheckCode rather than using fail_with.
| CheckCode | Meaning |
|---|---|
Unknown | Insufficient information (for example, a timeout) |
Safe | Not vulnerable / not applicable |
Detected | Service found, but vulnerability status unknown |
Appears | Passive evidence, such as a version, suggests vulnerability |
Vulnerable | Behavior confirms vulnerability |
Unsupported | Check is unsupported |
def check res = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'index.php') ) return CheckCode::Unknown('no response') if res.nil? return CheckCode::Safe('not ExampleApp') unless res.body.include?('ExampleApp')
if res.body =~ /Version: 1\.[0-2]/ return CheckCode::Appears('ExampleApp 1.0-1.2 detected') end
CheckCode::Detected('ExampleApp found, version unknown')endFor Scanner modules, use:
def check_host(ip) # Check each ip CheckCode::SafeendPrefer vprint_* for messages, and avoid asserting “Vulnerable” yourself without enough evidence. The framework displays the CheckCode.
exploit
Section titled “exploit”This is the body of an exploit. A common flow is connect → exploit the vulnerability → deliver the payload → call handler if needed.
def exploit res = send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'ping'), 'vars_post' => { 'ip' => "127.0.0.1;#{payload.encoded}" } ) fail_with(Failure::Unreachable, 'no response') if res.nil?endWhen using CmdStager:
def execute_command(cmd, _opts = {}) send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'ping'), 'vars_get' => { 'ip' => "127.0.0.1;#{cmd}" } )end
def exploit if target['Arch'] == ARCH_CMD execute_command(payload.encoded) else execute_cmdstager(linemax: 1500) endendIn the TCP template (example.rb), the flow is connect → sock.put → handler.
Use fail_with(Failure::..., message) on failure. See Response Handling and fail_with for the constants.
run (auxiliary / post)
Section titled “run (auxiliary / post)”def run print_status("Running against #{peer}") # Process print_good('Done')endIn a post module, session is available.
def run print_status(cmd_exec('id'))endrun_host (Scanner)
Section titled “run_host (Scanner)”def run_host(ip) res = send_request_cgi('method' => 'GET', 'uri' => '/') return if res.nil? print_good("#{ip}:#{rport} #{res.code}") report_service(host: ip, port: rport, name: 'http', info: res.headers['Server'])endDo not define run yourself and override Scanner’s behavior. Keep host-specific state in local variables.
Relationship to AutoCheck
Section titled “Relationship to AutoCheck”With prepend Msf::Exploit::Remote::AutoCheck, check runs automatically before exploit / run. See AutoCheck for details.
Debug output
Section titled “Debug output”| Method | Purpose |
|---|---|
print_status | Ordinary progress [*] |
print_good | Success [+] |
print_error | Error [-] |
print_warning | Warning |
vprint_* | Only when VERBOSE true |
Next is Saving Results.

