Step 119. Mastering netcat — The Swiss Army Knife of Networking
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
-zvand 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
printfand 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 network\n" | 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-3ubuntu13\r\n")
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.0\r\n\r\n" | 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 (\r\n\r\n). 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: \r\n 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:
- Exchange a conversation of at least three lines via chat between two terminals
- Create an arbitrary text file, transfer it with nc, and confirm the md5sums match on both sides
- Sweep ports 20–100 of MS2 (or 127.0.0.1) with
nc -zvand filter out only the open ports with grep - Connect to two of the open ports, receive their banners, and write the service names and versions in your notes
- 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.
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 -zvand 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
-wis 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.0\n\n" | nc ... and there’s silence.
Cause: HTTP’s line break is CRLF (\r\n). With only \n, the server can’t recognize that the request has ended and keeps waiting.
Fix: use \r\n like printf "GET / HTTP/1.0\r\n\r\n", 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.0\r\n\r\n" | 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. Click the checkbox in the sidebar to save your progress.