What would you like to learn?

Try PowerShell, networks, XSS, or Step 138

Browse the full curriculum →

Penetration testing

Step 119. Mastering netcat — The Swiss Army Knife of Networking

Step 119Estimated practice · 3 hours

Level 2 — Introduction to Security and the Basics of Attack Skills | Difficulty ★★★☆☆ | Estimated time: 3 hours

Prerequisites: Step 118 complete. You’ve built bind shells and reverse shells, and you know what a listener is. You understand ports and banner grabbing (Step 79).

  • What you need: two Linux lab terminals (a single Kali machine is enough; if you have two VMs, try it between Kali ↔ MS2 as well)
  • Caution: ⚠️ All exercises in this chapter are for your own lab and legal platforms only. Applying them to unauthorized systems is a crime.

In Step 118 we already passed shells back and forth with netcat (hereafter nc). But nc is not a shell tool — it is a TCP/UDP pipe itself: it merely pours whatever you type on the keyboard onto the network, and pours whatever arrives from the network onto the screen. That simplicity is its strength. When you’ve broken into a server with no tools on it mid-penetration, nc is the last weapon standing for moving files, sweeping ports, and talking to services directly. Today, without shells, we get nc’s five remaining faces into our hands — chat, file transfer, port scanning, banner grabbing, and manual HTTP.


1. Learning Objectives

By the end of this chapter, you will be able to:

  • Choose between nc’s two modes (listener/client) appropriately for the situation
  • Chat between two nc instances and transfer files with redirection
  • Run a makeshift port scan with -zv and interpret the output
  • Receive banners that arrive the moment you connect, and read service names and versions
  • Craft an HTTP request by hand and send it with a combination of printf and nc

2. Background Knowledge — Today’s Tools and Concepts

Today’s Tools at a Glance

Category Details
Language/environment A Linux shell (Kali recommended; macOS/Ubuntu work the same)
Today’s commands nc -l -p, nc target port, nc -zv, nc -w, printf pipes
Concepts needed Listeners and clients, redirection (>, <), banners, HTTP request format
Today’s artifact An nc usage note — the transferred file, scan results, collected banners

2-1. nc’s Two Faces — The Waiting Side and the Knocking Side

nc is always one of two things. nc -l -p 5555 is a listener — it opens door 5555 and waits. nc targetIP 5555 is a client — it knocks on that door. From the moment a connection is made, the distinction vanishes: type on either side and characters flow to the other’s screen.

In Step 118, the bind shell was "the victim is the listener," and the reverse shell was "the attacker is the listener." In today’s exercises, we pour pure data instead of shells (-e).

2-2. Today’s Flags at a Glance

Flag Meaning
-l Listener mode (listen)
-p port The port number to open
-v Verbose output — tells you whether connections succeed or fail
-z Scan mode — tests connections without sending data
-w seconds Wait limit — disconnects after this much time
-k Keep the listener alive even when a guest disconnects (keep accepting)

Check which kind of nc you have. The family whose first line of nc -h says OpenBSD netcat (Kali, Ubuntu default) has no -e option — a safer version at the cost of shell attachment (measured 2026-09-09: OpenBSD netcat (Debian patchlevel 1.226-1ubuntu2)). MS2’s traditional nc supports -e. Even with the same nc name, different families have different options — that is today’s hidden theme.

2-3. Banner Grabbing and Manual Protocols

In Step 79 we received banners with Python. With nc it’s simpler — connect and the banner just flows onto the screen. And nc is not only a "receiving" tool. For protocols like HTTP where the guest must speak first, we can type the first words ourselves and send them. Talking to a web server without a browser shows you HTTP in the raw.


3. Follow Along

3-1. Chat — The Simplest Communication

Open two terminals.

Input (terminal 1 — the receiving side)

nc -l -p 5555

Input (terminal 2 — the sending side)

nc 127.0.0.1 5555

Now type characters and press Enter on either side. They appear verbatim on the other’s screen. Sent through a pipe, one line goes straight through like this (measured 2026-09-09):

printf "Hello, server! My first message over the networkn" | nc -w 1 127.0.0.1 5555

Terminal 1’s screen:

Hello, server! My first message over the network

How to read it: nc doesn’t care what the characters are. It merely moves bytes as-is. -w 1 means "disconnect after 1 second of waiting," preventing the problem of a connection that never ends when sending through a pipe. That chatting works means you can pour arbitrary data in both directions — and when that data is shell commands, that’s what a shell was.

Why: shells, file transfer, proxies — every application of nc is a variation on this simple pipe. Get the principle into your hands first.

3-2. File Transfer — The Magic of Redirection

If you turn the characters that appeared on screen during chat into a file (>), that’s receiving a file. Conversely, if you pour a file into nc (<), that’s sending a file.

Input (terminal 1 — the receiving side)

nc -l -p 5556 > got.txt

Input (terminal 2 — the sending side)

printf "This is the secret file's contents.nSecond line: moved with netcat.n" > secret.txt
nc -w 1 127.0.0.1 5556 < secret.txt

Verification (measured 2026-09-09):

md5sum secret.txt got.txt
be44d17924f67dd152d1eac8ee51fb6a  secret.txt
be44d17924f67dd152d1eac8ee51fb6a  got.txt

How to read it: the hashes are completely identical — the bytes crossed over without a single one wrong. This is exactly the classic technique used, in environments with no tools, for pulling out collected files after a penetration or pushing attack tools in.

Why: the receiving side saves with > and the sending side reads with < — just memorize the directions. Note, though: even when the transfer finishes, nc doesn’t know "the file’s end." You must attach -w 1 or cut it with Ctrl+C (Wall 1).

3-3. A Makeshift Port Scan — -zv

Let’s imitate Step 79’s scanner with a single line of nc. First, deliberately open a door (terminal 1: nc -l -p 5555), then scan from terminal 2.

Input

nc -zv -w 1 127.0.0.1 5550-5560

Output (measured 2026-09-09):

nc: connect to 127.0.0.1 port 5550 (tcp) failed: Connection refused
nc: connect to 127.0.0.1 port 5551 (tcp) failed: Connection refused
nc: connect to 127.0.0.1 port 5552 (tcp) failed: Connection refused
nc: connect to 127.0.0.1 port 5553 (tcp) failed: Connection refused
nc: connect to 127.0.0.1 port 5554 (tcp) failed: Connection refused
Connection to 127.0.0.1 5555 port [tcp/*] succeeded!
nc: connect to 127.0.0.1 port 5556 (tcp) failed: Connection refused
(rest omitted)

How to read it: -z is scan mode, which tests connections without sending data; -v is verbose mode, which speaks the results. Only port 5555 says succeeded! and the rest say Connection refused — the same open/closed judgment we learned in Step 79. If you want only the open ones, filter like nc -zv -w 1 127.0.0.1 5550-5560 2>&1 | grep succeeded (the output comes out on standard error, so 2>&1 is needed).

Why: this is the minimal function for "quickly sweeping a port range" in environments without nmap. It falls short of nmap in speed and information, but the nc you know well exists everywhere.

3-4. Banner Grabbing — The Self-Introduction That Comes the Moment You Connect

First, let’s build a test banner server in Python and see for ourselves.

Input (banner_server.py)

import socket
srv = socket.socket()
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("127.0.0.1", 2222))
srv.listen(1)
conn, addr = srv.accept()
conn.sendall(b"SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13rn")
conn.close(); srv.close()

Input

python3 banner_server.py &   # terminal 1
timeout 2 nc 127.0.0.1 2222  # terminal 2

Output (measured 2026-09-09):

SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13

How to read it: the moment we connected, one line appeared. It contains everything — the protocol (SSH-2.0), the program (OpenSSH), the version (9.6p1), the distribution (Ubuntu). A real SSH server also sends a business card in exactly the same format first when you connect.

Doing the same thing toward the lab’s actual MS2 (Screen example — verify in your own lab):

nc MS2_IP 21
220 (vsFTPd 2.3.4)
nc MS2_IP 22
SSH-2.0-OpenSSH_4.7p1 Debian-8ubuntu1

How to read it: vsFTPd 2.3.4 — the very version into which we planted a backdoor in Step 117. A single banner line tells you "which vulnerabilities to search for." From a defender’s perspective, it also means a banner that gives away the version plainly is itself a guide for attackers.

3-5. Manual HTTP — Talking to a Web Server Without a Browser

Open a Python web server, and send a request with nc instead of a browser.

Input

mkdir -p /tmp/websrv && cd /tmp/websrv
echo "<h1>My lab web server</h1>" > index.html
python3 -m http.server 8080 --bind 127.0.0.1 &   # terminal 1
printf "GET / HTTP/1.0rnrn" | nc -w 2 127.0.0.1 8080   # terminal 2

Output (measured 2026-09-09):

HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.3
Date: Wed, 09 Sep 2026 06:09:55 GMT
Content-type: text/html
Content-Length: 27
Last-Modified: Wed, 09 Sep 2026 06:09:55 GMT

<h1>My lab web server</h1>

How to read it: all we sent was a single line, GET / HTTP/1.0, plus an empty line (rnrn). The server responded in order: a status line (200 OK), headers, an empty line, the body. This is the raw form of what a browser does every time. The header Server: SimpleHTTP/0.6 Python/3.12.3 is also a kind of banner — the web server’s business card.

Why: rn is HTTP’s line-break rule (CRLF), and you signal the end of a request with a single empty line. Someone who has written this format by hand won’t be intimidated later when looking at the request/response panels of web vulnerability tools.


4. Missions & Exercises

Mission — Complete Your nc Usage Notes

Using Kali and MS2 (or 127.0.0.1 to itself if you don’t have them), perform the following and record each result on a single notes page:

  1. Exchange a conversation of at least three lines via chat between two terminals
  2. Create an arbitrary text file, transfer it with nc, and confirm the md5sums match on both sides
  3. Sweep ports 20–100 of MS2 (or 127.0.0.1) with nc -zv and filter out only the open ports with grep
  4. Connect to two of the open ports, receive their banners, and write the service names and versions in your notes
  5. Manually send a GET / request to MS2’s port 80 (or the local web server from 3-5) and copy the response’s status line and Server header

Exercises

Exercise 1. State the options and formats that create nc’s listener mode and client mode, respectively.

Exercise 2. In file transfer, what do the > in the receiving side’s nc -l -p 5556 > got.txt and the < in the sending side’s nc 127.0.0.1 5556 < secret.txt each do?

Exercise 3. Distinguish the roles of -z and -v in an nc -zv scan, and explain why the output coming out on standard error makes 2>&1 necessary together with grep.

Exercise 4. Suppose banner grabbing yielded vsFTPd 2.3.4. Explain what an attacker and a defender each do with this single line.


Answers & completion criteria · expand/collapse

5. Model Answers & Completion Criteria

Mission Model Answer

A notes example (values vary by environment):

[1] Chat: terminal1 nc -l -p 5555 / terminal2 nc 127.0.0.1 5555 — bidirectional confirmed
[2] File transfer: secret.txt 77 bytes → got.txt, md5sum match confirmed
[3] Scan: nc -zv -w 1 MS2_IP 20-100 2>&1 | grep succeeded
    → 21, 22, 23, 25, 53, 80 open (example — varies by lab)
[4] Banners: port 21 vsFTPd 2.3.4 / port 22 OpenSSH_4.7p1
[5] HTTP: "HTTP/1.1 200 OK" + Server header copied down

How to verify: ① Do the two md5sum lines for the file transfer match? ② Did you filter out only the open ports from the scan results? ③ Did you write down even the version numbers from the banners? ④ Can you read the HTTP response’s status line (200 OK, and so on)?

Exercise Answers

Answer 1. A listener is nc -l -p port (opens a door and waits); a client is nc targetIP port (knocks on the door). After connection, either side can send to the other.

Answer 2. > is output redirection, which writes the bytes nc receives from the network to a file instead of the screen; < is input redirection, which pours a file’s contents into nc as its input. Shell redirection meets nc and becomes file transfer.

Answer 3. -z is scan mode, which tests only whether a connection is established without sending data; -v is verbose mode, which outputs success/failure as sentences. Because nc issues its result messages to standard error, not standard output, you must merge standard error into standard output with 2>&1 to pass them through a pipe to grep.

Answer 4. The attacker searches for "vsFTPd 2.3.4 vulnerabilities" and finds Step 117’s backdoor exploit — the banner is the table of contents of the attack. The defender looks at the same banner, realizes "this version is a known vulnerable version," and updates it, or changes the configuration so the banner hides the version — the banner is an update checklist.

Completion Criteria Checklist

  • [ ] I can freely open and connect with both modes, listener and client
  • [ ] I transferred a file with nc and confirmed the md5sums match
  • [ ] I ran a range scan with nc -zv and filtered out only the open ports
  • [ ] I read service names and versions from banners
  • [ ] I succeeded at a manual HTTP request with printf ... | nc
  • [ ] I can explain why -w is needed (connections don’t close automatically)
  • [ ] I checked my nc’s family (whether it’s OpenBSD) and the presence of -e

6. Common Pitfalls & Fixes

Wall 1. The file transfer finished but nc won’t end

Symptom: the file is fully sent, but both terminals wait vacantly.
Cause: nc doesn’t know "the file’s end." The connection is alive; it simply hasn’t received a signal to close.
Fix: attach -w 1 on the sending side, or cut with Ctrl+C after checking. Building the habit of verifying transfer completion with file size and md5sum gives you peace of mind.

Wall 2. Only Connection refused appears

Symptom (the 2026-09-09 measured message verbatim): nc: connect to 127.0.0.1 port 5556 (tcp) failed: Connection refused
Cause: there’s no listener on that port. Check whether you started the listener first, and whether the port numbers match on both sides.
Fix: the receiving side (nc -l -p port) must be up first. In a scan, this message is the normal judgment result meaning "closed."

Wall 3. -e doesn’t work

Symptom: nc -l -p 4444 -e /bin/bash fails with an option error.
Cause (measured 2026-09-09): Kali/Ubuntu’s default nc is the OpenBSD family, with no -e. You can confirm by the absence of e in the option list of nc -h.
Fix: if you need shell attachment, use traditional nc or ncat, or use the FIFO pipe workaround. Today’s chat, transfer, and scanning are all possible without -e.

Wall 4. No response at all to a manual HTTP request

Symptom: you sent printf "GET / HTTP/1.0nn" | nc ... and there’s silence.
Cause: HTTP’s line break is CRLF (rn). With only n, the server can’t recognize that the request has ended and keeps waiting.
Fix: use rn like printf "GET / HTTP/1.0rnrn", and be sure to send the empty line at the end (two in a row). If it’s still stuck, set a wait limit with -w 2.

Wall 5. I filtered with grep but no lines come out

Symptom: nc -zv 127.0.0.1 1-100 | grep succeeded is completely empty.
Cause: nc’s result messages go out on standard error (stderr). Pipes pass only standard output.
Fix: merge with 2>&1, like nc -zv -w 1 127.0.0.1 1-100 2>&1 | grep succeeded. And if truly no ports are open, an empty result is normal — first open a door with nc -l -p and test.


7. Summary

Today’s Concepts

Concept One-line explanation
Listener nc -l -p port — the side that opens a door and waits
Client nc target port — the side that knocks on the door
Banner The self-introduction (name, version) a service sends the moment you connect
Makeshift scan Testing only connection establishment with -zv to judge open/closed
Manual protocol Typing a protocol’s first words by hand to converse with a service
nc family differences OpenBSD nc has no -e — same name, different options

Today’s Commands

Command What it does
nc -l -p 5555 Open a listener on port 5555
nc 127.0.0.1 5555 Connect to a listener (chat)
nc -l -p 5556 > got.txt Save incoming data to a file
nc -w 1 target 5556 < secret.txt Transfer a file (ends after 1 second of waiting)
nc -zv -w 1 target 20-100 Makeshift scan of a port range
printf "GET / HTTP/1.0rnrn" | nc target 80 A manual HTTP request
md5sum file1 file2 Verify transfer integrity

An Instinct More Important Than Commands

The ability to wield nc is knowing with your body that "a network is, in the end, a flow of bytes." Today we confirmed that chat, file transfer, and shells are all the same pipe — which is why defenders watch whether tools like nc exist on internal servers and whether abnormal listeners are open (ss -tlnp).

And as we saw in banner grabbing, services often reveal their version just by connecting. Why an attacker’s reconnaissance is fast, why a defender reduces version exposure — the training of reading the same single line from two positions is the eye of this entire Level.


Once every box is checked, Step 119 is complete.

ONE STEP FURTHER

Finished this lesson?

Check the completion criteria, then mark your progress.

Something wrong with this page or a link? Let us know.
Next