I quickly needed a lightweight DNS tunnelling daemon that would deploy easily. I found that Dns2tcp was just that and I had good results with this program when testing. Quickly googling for a way to chroot this program came back witout anything but it turned out to be a quick and simple thing to do.

Dns2tcp is a network tool designed to relay TCP connections through DNS traffic. Encapsulation is done on the TCP level, thus no specific driver is needed (i.e: TUN/TAP). hsc.fr/ressources/outils/dns2tcp/index.html.en

dns2tcp seems to be no longer maintained and there is probably better dns tunnelling software out there.

Server

$ curl http://www.hsc.fr/ressources/outils/dns2tcp/download/dns2tcp-0.5.2.tar.gz | tar xzv
$ cd dns2tcp-0.5.2
$ ./configure &&  make
$ vim server/dns2tcpd/dns2tcpd.conf
listen = 11.22.22.11
port = 53
user = nobody
chroot = /tmp
domain = sub.server.com
resources = ssh:127.0.0.1:22
key = secretpassphrase111

$ sudo server/dns2tcpd -f server/dns2tcpd.conf -d 3 -F
08:48:03 : Debug options.c:97   Add resource ssh:127.0.0.1 port 22
08:48:03 : Debug socket.c:55    Listening on 50.116.4.114:53 for domain sub.server.com
Starting Server v0.5.2...
08:48:03 : Debug main.c:132     Chroot to /tmp
08:48:03 : Debug main.c:142     Change to user nobody

I’ve downloaded and compiled dns2tcpd on my Debian server, see the README file that comes with the code for more details. After creating a configuration file the server starts fine, listening on the public ip 11.22.22.11 (nothing can connect to it yet because you run a firewall, right?!).

chrooting the server

Even though the process drops privileges I’m going to build a chroot jail for it [as something to do because why not? grsecurity has chroot hardenig]. Now to see what libraries the process is using. On another console (because the program above is running in the foreground):

$ sudo su
$ ps aux | grep dns2tcp -m1
nobody   21520  0.0  0.1  41864   824 pts/8    S+   02:01   0:00 server/dns2tcpd -f server/dns2tcpd.conf -d 3 -F

$ lsof -p 21520
COMMAND    PID   USER   FD   TYPE     DEVICE SIZE/OFF   NODE NAME
dns2tcpd 21520 nobody  cwd    DIR      202,0     4096     12 /tmp
dns2tcpd 21520 nobody  rtd    DIR      202,0     4096     12 /tmp
dns2tcpd 21520 nobody  txt    REG      202,0   138934 165243 /home/user/source/dns2tcp-0.5.2/server/dns2tcpd
dns2tcpd 21520 nobody  mem    REG      202,0    71432  92053 /lib/libresolv-2.11.3.so
dns2tcpd 21520 nobody  mem    REG      202,0    22036  92068 /lib/libnss_dns-2.11.3.so
dns2tcpd 21520 nobody  mem    REG      202,0    42580  92054 /lib/libnss_files-2.11.3.so
dns2tcpd 21520 nobody  mem    REG      202,0    38504  92047 /lib/libnss_nis-2.11.3.so
dns2tcpd 21520 nobody  mem    REG      202,0    79676  92051 /lib/libnsl-2.11.3.so
dns2tcpd 21520 nobody  mem    REG      202,0    30496  92069 /lib/libnss_compat-2.11.3.so
dns2tcpd 21520 nobody  mem    REG      202,0  1319176  92067 /lib/libc-2.11.3.so
dns2tcpd 21520 nobody  mem    REG      202,0   118060  92060 /lib/ld-2.11.3.so
dns2tcpd 21520 nobody    0u   CHR      136,8      0t0     11 /dev/pts/8
dns2tcpd 21520 nobody    1u   CHR      136,8      0t0     11 /dev/pts/8
dns2tcpd 21520 nobody    2u   CHR      136,8      0t0     11 /dev/pts/8
$ kill -9 21520

Building the chroot:

$ mkdir -p /opt/chroot_dns2tcp/dns2tcpd/{etc,tmp,dev,usr,lib}
$ mkdir  /opt/chroot_dns2tcp/dns2tcpd/usr/dns2tcpd/
$ mknod /opt/chroot_dns2tcpd/dns2tcpd/dev/null c 1 3
$ cp /lib/ld-linux.so.2 /lib/libc.so.6 /lib/libnss_dns.so.2 /lib/libnss_files.so.2 /lib/libnss_nis.so.2 /lib/libresolv.so.2 /opt/chroot_dns2tcp/dns2tcpd/lib/
$ cp /home/user/source/dns2tcp-0.5.2/server/dns2tcpd /home/user/source/dns2tcp-0.5.2/server/dns2tcpd.conf /opt/chroot_dns2tcp/dns2tcpd/usr/dns2tcpd/ -v
$ cp /etc/hosts /etc/localtime /opt/chroot_dns2tcp/dns2tcpd/etc/ -v

$ touch /opt/chroot_dns2tcp/dns2tcpd/etc/group
$ echo "nobody:x:65534:65534:nobody:/dev/null:/bin/false" >> /opt/chroot_dns2tcp/dns2tcpd/etc/group

$ touch /opt/chroot_dns2tcp/dns2tcpd/etc/passwd
$ echo "nobody:x:65534:65534:nobody:/dev/null:/bin/false" >> /opt/chroot_dns2tcp/dns2tcpd/etc/passwd

$ touch /opt/chroot_dns2tcpd/daemon.log
$ touch /opt/chroot_dns2tcpd/screen.rc

$ vim /opt/chroot_dns2tcp/dns2tcpd/etc/nsswitch.conf
passwd:         files
group:          files
shadow:         files
hosts:          files dns
networks:       files
protocols:      db files
services:       db files
ethers:         db files
rpc:            db files
netgroup:       nis

Hardening: If you use the PaX patch on your Linux kernel you have more hardening options:

$ pax -cC /opt/chroot_dns2tcp/dns2tcpd/usr/dns2tcpd/dns2tcpd
file /opt/chroot_dns2tcp/dns2tcpd/usr/dns2tcpd/dns2tcpd had a PT_GNU_STACK program header, converted
$ pax -MRXSPE /opt/chroot_dns2tcp/dns2tcpd/usr/dns2tcpd/dns2tcpd
$ paxctl -v /opt/chroot_dns2tcpd/dns2tcpd/usr/local/dns2tcpd/dns2tcpd
PaX control v0.5
Copyright 2004,2005,2006,2007 PaX Team

- PaX flags: P-S-M-X-E-R- [/opt/chroot_dns2tcpd/dns2tcpd/usr/local/dns2tcpd/dns2tcpd]
        PAGEEXEC is enabled
        SEGMEXEC is enabled
        MPROTECT is enabled
        RANDEXEC is enabled
        EMUTRAMP is enabled
        RANDMMAP is enabled

Using checksec.sh I found that further hardening can still be implemented (listed below). There has been a remote buffer overflow vulnerability in dns2tcp before - CVE-2008-03910.

$ ./checksec.sh --file /opt/chroot_dns2tcpd/dns2tcpd/usr/local/dns2tcpd/dns2tcpd
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
No RELRO        No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   /opt/chroot_dns2tcpd/dns2tcpd/usr/local/dns2tcpd/dns2tcpd
  • RELRO hardening: http://tk-blog.blogspot.com/2009/02/relro-not-so-well-known-memory.html
  • Stack Canaries: http://en.wikipedia.org/wiki/Buffer_overflow_protection#Canaries
  • position-independent executable - http://en.wikipedia.org/wiki/Position-independent_executable

File permissions:

$ chmod o-rwx /opt/chroot_dns2tcpd/ -R -v
$ chmod -w /opt/chroot_dns2tcpd/dns2tcpd/ -R -v
$ chattr +i /opt/chroot_dns2tcpd/dns2tcpd/* -R -v
$ chattr +a /opt/chroot_dns2tcpd/daemon.log

Starting the server This command will start the server and log the output. I start the server from inside of screen so I can logout from ssh and leave the process running. Here the screen config set by -c is blank because I can’t yet figure out how to stop screen from loading my regular .screenrc on startup.

$ screen -S dns2tcp -c /opt/chroot_dns2tcpd/screen.rc sh -c "/usr/sbin/chroot /opt/chroot_dns2tcpd/dns2tcpd /usr/local/dns2tcpd/dns2tcpd -f /usr/local/dns2tcpd/dns2tcpd.conf -d1 -F 2>&1 | tee -a  /opt/chroot_dns2tcpd/daemon.log"
02:02:46 : Debug options.c:97   Add resource ssh:127.0.0.1 port 22
02:02:46 : Debug socket.c:55    Listening on 11.22.22.11:53 for domain  sub.server.com
Starting Server v0.5.2...
02:02:46 : Debug main.c:132     Chroot to /tmp
09:02:46 : Debug main.c:142     Change to user nobody

When the server starts I get an message in my syslog from grsecurity because I have enabled logging of exec’s within chroots. I’ve also utilised the socket restrictions feature and blocked client sockets for the user nobody.

$ dmesg | grep -m1 dns2tcpd
grsec: From 100.XX.XXX.XXX: exec of /opt/chroot_dns2tcpd/dns2tcpd/usr/local/dns2tcpd/dns2tcpd within chroot by process /opt/chroot_dns2tcpd/dns2tcpd/usr/local/dns2tcpd/dns2tcpd[chroot:18125] uid/euid:0/0 gid/egid:0/0, parent /bin/bash[bash:6016] uid/euid:0/0 gid/egid:0/0

Client On your local linux machine download and compile the code.

$ vim /home/laptop/.ssh/config
Host dnstunnel
User user
Port 2828
HostName 127.0.0.1
IdentityFile /home/laptop/.ssh/private-key-file
LogLevel  QUIET
DynamicForward localhost:8080

$ dns2tcpc -d 3 -z  sub.server.com -k secretpassphrase111 -r ssh -l 2828 8.8.8.8
$ ssh dnstunnel

Have now connected to the ssh daemon listening on localhost of the remote server, with all of the traffic being sent/received over dns queries.

Creating session id: 0x8gd7 address = 202.180.64.10 (compression NOT wanted) delete_client 0x8gd7

The dns2tcp daemon will show clients connecting and disconnecting. The IP address shown here belongs to google because as you see above out client is using 8.8.8.8, one of googles DNS servers. If you are using this behind a captive portal and don’t specify a DNS server the client will look in resolv.conf.