转载

DNS Shell初体验

背景介绍

我们遇到过各种各样的 Shell,从协议上来看,最开始基于 TCP、UDP 的 Shell,到后来基于ICMP 的 Shell 。从依托工具上看,有 nc 反弹、telnet 反弹、SSH 端口转发等手段,极度猥琐的甚至还有 利用 awk 的反弹 Shell 。从语言上看,各种流行的语言都能用来写后门,从bash 到 3P(Perl Python PHP)再到 Ruby 和 Java ,大牛总是可以根据不同的环境情况选择不同的 Shell 来利用。

各种 Shell 都有它自己的优点和缺点,采用 TCP 和 UDP 的虽然功能强大,但是却受到了防火墙和杀毒软件的严格监控,Ruby 和 Java 写成的又不一定有相应的运行环境。

主角登场

我们今天介绍一个利用 DNS 协议进行通信的反弹 Shell,和 ICMP 反弹 Shell 的原理几乎相同,只是传输的协议变为了 DNS。

使用 DNS 请求来伪装通信进行命令控制带来的好处不言而喻,不管你做了多么严格的网络控制,你也要满足至少对一个服务器发起的 DNS 查询请求,那么就可以被攻击者恶意利用。利用 DNS 的想法并不新奇,在黑帽大会上也多次提到过 DNS 隧道,之前的 DNS 域传送漏洞也是一个利用方法,今天我们选取一个开源的工具进行学习–

DNShell

有关于 DNS 的相关细节不再赘述,有想了解详细内容的移步相关的 RFC 文档,如 RFC 4034 RFC 3755 等等。

准备工作

如作者所说,这是一个使用 Python 编写的、利用 DNS 作为命令控制信道的反弹 Shell。对于 Python 库的准备就不再多说了,依赖的环境应该是 Python 2.7,因为其在服务器端的代码中使用了 raw_input .format() 。众所周知, raw_input 函数在 Python3 中被砍掉了,而 str.format() 则是在 Python 2.6 才加入的函数。 DNS Shell初体验

上图是我的 Package 页

from Crypto.Cipher import AES 引入错误,如果在装了 Crypto 后还是错误,就需要装 pycrypto 这个库。

如果想修改代码到 Python3 下运行,遇到 import dns.resolver 引入错误,是需要装 dnspython3 的。

DNS 查询

简单介绍一下如何利用 Python 来进行 DNS 查询,这也是核心的方法,DNS 作为信道进行隐蔽通信的核心就是把要传递的数据作为 DNS 请求的 hostname 部分。

  1. import dns . resolver

  2. myResolver = dns . resolver .Resolver()

  3. myAnswers = myResolver . query (“google.com”, “A”)

  4. for data in myAnswers :

  5. print data

先创建一个实例,然后查询 Google.com 的 A 记录。以同样的方式,我们可以执行对 MX 和NS 的查询,只需要改动 A 的那部分就可以了,很简单。

当用它来进行反向 DNS 查找(主机名到 IP) 时,就不是简单的输入 IP 地址直接执行 A 记录查找。我们需要执行 PTR 查找,查找时要将待查找的 IP 地址逆向书写,并将 “.in addr.arpa”追加到它后面。

例如,为解析 IP 地址为 114.124.134.3 的主机名,我们使用的代码是:

myAnswers = myResolver.query("3.134.124.114.in-addr.arpa", "PTR")

DNS 解析程序也给我们指定我们自己的域名服务器的选项。这可以通过使用:

1. myResolver = dns . resolver .Resolver()

2. myResolver . nameservers = ['8.8.8.8', '8.8.4.4']

这里也引出了一种对抗的方法,我们将在后面进行说明。

分析与问题

DNS Shell初体验

上图将 DNShell 的一次命令控制过程进行了展示,由于两个文件代码量并不大,我们不进行逐段的详细解释和分析了,只对一些地方进行简要阐述,想彻底弄懂或者改写代码的请自行研究。

先运行服务器端,再执行被控制端,这一点在 GitHub 的项目主页上作者也有提醒。

Python 有两个内建的模块用于处理命令行参数,一个是 getopt 另一个是 optparse ,作者在这个代码中使用的是 optparse 模块用来解析命令行参数。

监听的端口是常见的 DNS 服务器端口 53,如果你的服务器恰好搭建了 DNS 服务,或者有程序占用这个端口,你就无法对这个端口进行监听了,必须先停止占用端口的程序。

程序使用了 base64 进行编码解码,使用 AES 进行加密解密,在程序两端都要更改密钥和向量来保证加密的安全性。

NXT 资源记录通过在域中创建所有字面上的所有者名称链,指出某个名称在域中不存在。它们同时也指出,一个已有名称当前有什么资源记录类型。

在加解密的时候,因为 AES 是分块加密的,在加解密时作者使用 lambda 表达式这种匿名函数来实现,十分简洁。

执行命令用的传统 subprocess 子进程的方法,如果要改进代码的话,这里还有提升空间可以解决很多问题,比如执行命令时的权限问题、 Windows 和 Linux 的命令不尽相同的问题等。

如果被控制端需要放在 Windows 上运行,不仅要考虑到其没有 Python 运行环境是否打包成exe 文件才能运行的问题、是否触发 UAC 的验证引起用户警觉,还有应该使用加壳、加密等手段来绕过像管家、360 等杀软的问题。

Nullege 是一个查询源代码和文档的好地方,和谷歌配合使用疗效显著。如果你对其中的某些函数感到陌生或者困惑,不但可以查官方文档,也可以在这里查找很多示例的源代码增进理解。

实际部署

DNS Shell初体验

服务器端放在了一台 VPS 上,如上图所示。

DNS Shell初体验

在被控制端脚本执行后,服务器端会出现一个 SHELL 的提示行,我们在这里输入命令

ipconfig -all

DNS Shell初体验

在被控制端,我们可以清楚的看到,经过解码和解密,已经成功得到了 ipconfig -all

这个命令,(需要注意的是,我对代码进行了些微的改动,可能导致行号和作者版本的行号不相同,不过这并不影响什么)

在调试器中查看执行命令的

stdout 可以看到

DNS Shell初体验

即远程的命令可以成功执行。

DNS Shell初体验

我们再输入退出的指令 quit 来测试一下

DNS Shell初体验

被控制端直接退出了,也完成了远程的命令。接着我们用 Wireshark 进行抓包

DNS Shell初体验

可以看到这条发送出去的 DNS 请求

DNS Shell初体验

返回的响应中,我们也确实看到了携带的数据

对抗方法

在 DNS 查询小节中,我们讲到了在查询时指定域名服务器的方法。这也是对抗使用 DNS 请求作为隐蔽通信信道的方法,在可能的情况下,使用自己搭建的 DNS 服务器,这样就可以直接得出:除了这一台 DNS 服务器要与外界进行 DNS 请求交互,其余服务器任何试图与外界DNS 服务器发起的请求都是恶意的。 

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

原文  http://www.freebuf.com/sectool/110815.html
正文到此结束
Loading...