通过 DNS 服务器隧道传输 IPv4 数据,以绕过防火墙限制,并为渗透测试提供隐蔽的网络访问。
这是一款可以让你通过 DNS 服务器隧道传输 IPv4 数据的软件。它可用于互联网访问被防火墙限制、但允许 DNS 查询的各种场景。
编译 iodine 需要 meson。
在 build 目录中执行以下命令进行编译:
meson setup build
cd build
ninja
要构建并运行测试,你需要 check 库。在构建目录中运行 ninja test 来启动它们。
在你自己的局域网中试用一下吧!按照以下简单步骤操作:
./iodined -f 10.0.0.1 test.com。
如果你已经使用了 10.0.0.0 网络,请使用另一个内部网络,例如
172.16.0.0。./iodine -f -r 192.168.0.1 test.com。
将 192.168.0.1 替换为你服务器的 IP 地址。10.0.0.2,服务器拥有 10.0.0.1。要通过中继域名服务器实际使用它,请参见下文。
注意:服务器和客户端必须使用完全相同的协议。在大多数情况下,这意味着运行相同版本的 iodine。遗憾的是,实现向后和向前协议兼容通常是不可行的。
要使用此隧道,你需要控制一个真实域名(例如 mydomain.com),
以及一台拥有公网 IP 地址的服务器来运行 iodined。如果此服务器
已经运行了 DNS 程序,请更改其监听端口,然后使用 iodined 的
-b 选项让 iodined 转发 DNS 请求。(请注意,此过程
不建议在生产环境中使用,因为 iodined 的 DNS 转发
并非完全透明,例如区域传送将无法工作。)
或者,你可以将 DNS 服务器中的子域转发到 iodined,
此时 iodined 必须运行在不同的端口上(-p)。
然后,将一个子域(例如 t1.mydomain.com)委派给 iodined 服务器。
如果你使用 BIND 管理域名,请将以下两行添加到区域文件中:
t1 IN NS t1ns.mydomain.com. ; 注意末尾的点!
t1ns IN A 10.15.213.99
NS 行是将 t1 子域的查询路由到 t1ns 服务器所需的全部内容。我们为子域使用短名称,以便尽可能为数据流量保留更多空间。NS 行的末尾是你的 iodined 服务器的名称。这可以是任何名称,指向任何位置,但在这种情况下,它很容易保留在同一个区域文件中。它必须是一个名称(不是 IP 地址),并且该名称本身必须具有 A 记录(不是 CNAME)。
如果你的 iodined 服务器拥有动态 IP,请使用动态 DNS 提供商。只需将 NS 行指向它,并省略 A 行:
t1 IN NS myname.mydyndnsprovider.com. ; 注意末尾的点!
然后重新加载或重启你的域名服务器程序。现在,任何以 t1.mydomain.com 结尾的域名的 DNS 查询都将发送到你的 iodined 服务器。
最后在你的服务器上启动 iodined。第一个参数是隧道内部的 IP 地址,可以来自你尚未使用的任何范围(例如 192.168.99.1),第二个参数是分配的子域(在本例中为 t1.mydomain.com)。使用 -f 选项将使 iodined 在前台运行,这在测试时很有帮助。iodined 将打开一个虚拟接口("tun 设备"),并且还将开始在 UDP 端口 53 上监听 DNS 查询。可以在命令行上输入密码(-P pass),也可以在服务器启动后输入。现在一切已为客户端准备就绪。
如果你有可能从意外环境中使用 iodine 隧道,请使用 -c 选项启动 iodined。
此示例情况下的结果命令行:
./iodined -f -c -P secretpassword 192.168.99.1 t1.mydomain.com
所有设置已完成,只需启动 iodine。它接受一个或两个参数,第一个是本地中继 DNS 服务器(可选),第二个是你使用的域名(t1.mydomain.com)。如果你不指定第一个参数,将查询系统当前的 DNS 设置。
如果允许向任意计算机发送 DNS 查询,你可以直接将 iodined 服务器的地址作为第一个参数(在示例中为:t1ns.mydomain.com 或 10.15.213.99)。在这种情况下,也可能出现允许向任意计算机的 DNS 端口(53 UDP)发送_任意_流量的情况。Iodine 会检测到这一点,并在可能的情况下切换到原始 UDP 隧道。若要在任何情况下强制使用 DNS 隧道,请使用 -r 选项(在你自己网络内测试时尤其有用)。
客户端的隧道接口将获得一个与服务器相近的 IP(在本例中为 192.168.99.2 或 .3 等)以及合适的 MTU。以命令行选项的形式输入与服务器相同的密码,或在客户端启动后输入。使用 -f 选项将使 iodine 客户端保持在前台运行。
在此示例情形下得到的命令行,添加 -r 会强制使用 DNS 隧道,即使原始 UDP 隧道可行:
./iodine -f -P secretpassword t1.mydomain.com
现在,从任意一端你都应该能够 ping 通隧道另一端的 IP 地址。在本例中,从 iodine 客户端执行 ping 192.168.99.1,从 iodine 服务器执行 ping 192.168.99.2。
隧道内的数据仅支持 IPv4。
服务器默认同时监听 IPv4 和 IPv6 的传入请求。使用 -4 或 -6 选项可仅监听一种协议。原始模式将尝试使用与登录时相同的协议。
客户端可以使用 IPv4 或 IPv6 名称服务器连接到 iodined。中继名称服务器会在需要时自动在协议之间进行转换。使用 -4 或 -6 选项可强制客户端为其 DNS 查询使用特定的 IP 版本。
如果你的服务器监听 IPv6 且可访问,请在其 DNS 设置中为其添加一条 AAAA 记录。扩展上述示例后如下所示:
t1 IN NS t1ns.mydomain.com. ; note the dot!
t1ns IN A 10.15.213.99
t1ns IN AAAA 2001:db8::1001:99
可以通过 DNS 隧道路由所有流量。为此,首先通过有线/无线接口为 iodine 使用的名称服务器添加一条主机路由,以默认网关作为网关。然后在 DNS 隧道内用 iodined 服务器的 IP 地址替换默认网关,并将服务器配置为执行 NAT。
但是请注意,隧道内的数据流量完全没有加密,外部各方可以相对容易地读取和更改。为了获得最大安全性,请通过 DNS 隧道运行 VPN(=双重隧道),或使用安全 shell(SSH)访问,最好配合端口转发。后者也可用于网页浏览,只需在服务器上运行一个 Web 代理(例如 Privoxy)。
iodined 服务器会回复针对隧道域子域发送的 NS 请求。如果你的 iodined 子域是 t1.mydomain.com,请发送针对 foo123.t1.mydomain.com 的 NS 请求,以查看委派是否正常工作。
dig 是一个很好的工具:
% dig -t NS foo123.t1.mydomain.com
ns.io.citronna.de.
此外,iodined 服务器会针对任何受支持的请求类型,回复以 'z' 开头的请求,例如:
dig -t TXT z456.t1.mydomain.com
dig -t SRV z456.t1.mydomain.com
dig -t CNAME z456.t1.mydomain.com
在所有这些情况下,回复都应看起来像乱码文本。
在 macOS 10.6 及更高版本上,iodine 支持操作系统内置的原生 utun 设备——使用 -d utunX。
DNS 响应分片大小通常会被自动探测以获得最大带宽。若要强制指定特定值(并加快速度),请使用 -m 选项。
DNS 主机名通常使用到其最大长度,即 255 个字符。
已发现一些 DNS 中继在应答全长查询时相当不可靠,在反复尝试时
对分片大小自动探测给出差异极大(且大多非常糟糕)的结果。在这些情况下,使用 -M 开关
将 DNS 主机名长度缩减到例如 200 个字符,这会使这些 DNS 中继稳定得多。这对于某些“去优化”
DNS 中继也很有用,它们会用查询的两份完整副本填充响应,留给下游数据的空间非常少
(同样不支持 EDNS0)。-M 开关可以用一些上游带宽换取下游带宽。请注意,
-M 的最小值约为 100,因为该协议只能将数据包(最大 1200 字节)拆分为 16 个分片,
要求每个分片至少有 75 个真实数据字节。
上游数据以 Base32 进行 gzip 编码后发送;如果中继服务器支持域名中的大小写混合和 +,则使用 Base64;
如果支持 _ 代替,则使用 Base64u;如果支持高字节值字符,则使用 Base128。
这种上游编码是自动检测的。DNS 协议允许每个数据包一个查询,一个查询最多 256 个字符。
每个域名部分最多 63 个字符。因此你的域名和子域名应尽可能短,
以允许最大的上游吞吐量。
支持多种 DNS 请求类型,其中 NULL 和 PRIVATE 类型预计能提供最大的下游带宽。
PRIVATE 类型在私有使用范围内使用值 65399。其他可用类型按带宽递减顺序为 TXT、SRV、
MX、CNAME 和 A(返回 CNAME)。
通常会自动检测并使用“最佳”请求类型。然而,DNS 中继可能对例如 NULL 和 TXT 施加限制,
使得 SRV 或 MX 实际上成为最佳选择。这不会被自动检测,但可以使用 -T 选项强制指定。
建议尝试各种替代方案,尤其是当自动检测的请求类型提供的下游分片大小小于 200 字节时。
请注意,SRV、MX 和 A(返回 CNAME)查询可能/将会导致“智能”缓存名称服务器进行
额外查找以获取实际 IP 地址,这可能会减慢速度或完全失败。
非 NULL/PRIVATE 查询的 DNS 响应可以使用与上游数据相同的编解码器进行编码。这通常也会
自动检测,但不会进行完全详尽的测试,因此在选择更高级的编解码器时可能不会注意到某些问题。
在这种情况下,你会在分片大小自动探测中看到失败/损坏。特别是,已发现一些 DNS 中继
仅当返回主机名(SRV、MX、CNAME、A)超过约 180 个字符时才会将其改为小写。
在这些及类似情况下,使用 -O 选项尝试其他下游编解码器;Base32 应该总是有效。
现在的正常操作是服务器在下一个 DNS 请求到来之前_不_应答 DNS 请求,即所谓的“惰性”。
这样,当需要发送新的下游数据时,服务器总是手边有一个 DNS 请求。
这大大提高了(交互式)性能和延迟,并允许默认将静默 ping 请求放慢到 4 秒间隔,
甚至可能更慢。事实上,现在 ping 的主要目的是强制对上一个 ping 进行应答,
并防止 DNS 服务器超时(根据 RFC1035,通常至少 5-10 秒)。一些 DNS 服务器更不耐烦,
在没有隧道数据流量的时段会给出 SERVFAIL 错误(超时)。在这些情况下所有数据仍应能通过,
但 iodine 无论如何都会将 ping 间隔减少到 1 秒(-I1)以减少错误消息的数量。这对于像
dnsadvantage.com(ultradns)这样非常不耐烦的 DNS 中继可能没有帮助,它们会在 1 秒甚至更短时间内超时。
但数据仍能通过,你可以忽略 SERVFAIL 错误。
如果你在没有任何中间 DNS 服务器的本地网络上运行,请尝试
-I 50(iodine 和 iodined 在静默 60 秒后关闭连接)。
你唯一会注意到速度变慢的时候,是 DNS 回复数据包丢失时;
此时 iodined 服务器必须等待新的 ping 来重新发送数据。你可以
通过产生一些上行流量(按键、ping)来加速这一过程。如果这种情况
经常发生,请检查你的网络是否存在瓶颈,和/或使用 -I1 运行。
延迟应答在 lazy 模式下会导致一些“运营商级”商业
DNS 中继反复向 iodined 服务器重新发送相同的 DNS 查询。
如果该 DNS 中继实际上是由一组并行服务器实现的,
重复请求甚至可能来自多个来源。这种效应
只会在 iodined 服务器的网络流量中可见,不会
影响客户端的连接。Iodined 会注意到这些重复请求,并在
(其时机到来时)将相同的应答同时发送给原始查询和
最新的重复请求。之后,完整应答会被短暂缓存。
更晚到达服务器的延迟重复请求,会收到一个
iodine 客户端将忽略的回复(如果它真的到达那里的话)。
如果遇到问题,请尝试使用 tcpdump 或 ethereal/wireshark 等网络监控工具
检查流量,并确保中继 DNS 服务器
没有缓存该响应。缓存的错误消息可能意味着你在
服务器之前启动了客户端。服务器上的 -D(以及 -DD)选项
也可以显示接收和发送的查询。
如果你的特定接口上的 53 端口被一个并不使用它的应用程序占用,
请在 iodined 上使用 -p 指定备用端口(例如 -p 5353),
并使用例如 iptables(在 Linux 上)来转发流量:
iptables -t nat -A PREROUTING -i eth0 -p udp --dport 53 -j DNAT --to :5353
(由 Tom Schouten 提供)
Iodined 会拒绝来自超过 60 秒未活动(数据/ping)的客户端的数据。 同样,当 60 秒内未收到任何下行数据时,iodine 将退出。 如果发生长时间网络中断或类似情况,只需重新启动 iodine(重新登录), 可能需要多次,直到你取回原来的 IP 地址。完成后,只需等待一段时间, 你最终会看到隧道 TCP 流量从中断前的位置继续流动。
随着服务器中下行数据包队列的引入,其内存 占用在默认配置下增加了数兆字节。 在低内存环境中使用(例如在你的 DSL 路由器上运行)时,你可以 在 user.h 中减少 USERS 并取消定义 OUTPACKETQ_LEN,而不会产生任何不良 后果,前提是任意时刻最多只有一个客户端连接。仍然建议设置较小的 DNSCACHE_LEN,最好为 2 或更高,不过你也可以取消定义它以再节省几 KB。
一个 iodine 服务器可以处理多个域。在同一域上设置不同的 NS 记录,
全部指向同一主机,并在 topdomain 参数的开头使用通配符
(例如 *.mydomain.com)。iodine
将接受所有匹配该模式的域的隧道流量。通配符
必须位于 topdomain 参数的开头,并且后面跟一个点。
本节以表格形式列出了一些性能测量结果。要正确查看,请使用 等宽字体,如 Courier。
测量是在协议 00000502 的 lazy 模式下进行的;上行编码
始终为 Base128;iodine -M255;iodined -m1130。网络条件并非
极其有利;结果不是基准测试,而是对类似情况下可预期的
真实世界性能的现实指示。
上行/下行吞吐量通过 scp 传输一个先前从 /dev/urandom 读取的文件(即不可压缩)来测量,并在单独的非隧道连接上使用 ls -l ; sleep 30 ; ls -l 测量文件大小。鉴于 scp 的块大小较大,为 16 kB,这给出了 4.3 kbit/s 的分辨率,这解释了为什么某些值完全相等。
Ping 往返时间使用 ping -c100 测量,呈现的是平均 rtt 和平均偏差(表示围绕平均值的散布),单位为毫秒。
Laptop -> Wifi AP -> Home server -> DSL provider -> Datacenter iodine DNS "relay" bind9 DNS cache iodined
downstr. upstream downstr. ping-up ping-down
fragsize kbit/s kbit/s avg +/-mdev avg +/-mdev
-----------------------------------------------------------------------------
iodine -> Wifi AP :53
-Tnull (= -Oraw) 982 43.6 131.0 28.0 4.6 26.8 3.4
iodine -> Home server :53
-Tnull (= -Oraw) 1174 48.0 305.8 26.6 5.0 26.9 8.4