通过 DNS 服务器隧道传输 IPv4 数据,以绕过防火墙限制,并为渗透测试提供隐蔽的网络访问。
这是一款软件,可让你通过 DNS 服务器隧道传输 IPv4 数据。在互联网访问被防火墙限制但允许 DNS 查询的各种场景中,它都能派上用场。
编译 iodine 需要 meson。
在 build 目录中运行以下命令进行编译:
meson setup build
cd build
ninja
构建并运行测试需要 check 库。在 build 目录中运行 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 隧道,请在启动 iodined 时加上 -c 选项。
本例中生成的命令行如下:
./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 服务器执行
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. ; 注意末尾的点!
t1ns IN A 10.15.213.99
t1ns IN AAAA 2001:db8::1001:99
可以通过 DNS 隧道路由所有流量。为此,首先 通过有线/无线接口添加一条到 iodine 所用域名服务器的主机路由, 以默认网关作为网关。然后将默认 网关替换为 iodined 服务器在 DNS 隧道内部的 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
在所有这些情况下,回复应该看起来像乱码文本。
在 Mac OS X 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 服务器超时(通常至少
5-10 秒,依据 RFC1035)。一些 DNS 服务器更不耐烦,会在没有隧道数据流量的时段
给出 SERVFAIL 错误(超时)。在這些情况下所有
数据仍应能通过,但 iodine 会将 ping
间隔减少到 1 秒(-I1)以减少错误消息的数量。这对于像 dnsadvantage.com(ultradns)这样非常不耐烦的 DNS 中继可能没有帮助,
它们会在 1 秒甚至更短时间内超时。但数据仍能通过,
你可以忽略 SERVFAIL 错误。
如果你在没有任何中间 DNS 服务器的本地网络上运行,请尝试
-I 50(iodine 和 iodined 在静默 60 秒后关闭连接)。
你唯一会注意到速度变慢的时候,是 DNS 回复数据包丢失时;
iodined 服务器此时必须等待新的 ping 来重新发送数据。你可以
通过产生一些上行流量(按键、ping)来加速。如果这种情况
经常发生,请检查你的网络是否存在瓶颈,和/或使用 -I1 运行。
懒惰模式下的延迟应答会导致一些"运营商级"商业
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)的客户端的数据。 同样,iodine 在 60 秒内未收到下行数据时会退出。在长时间网络中断或 类似情况下,只需重启 iodine(重新登录),可能需要多次,直到你取回 原来的 IP 地址。完成后,只需等待一段时间,你最终会 看到隧道 TCP 流量从中断前的位置继续流动。
随着服务器中下行数据包队列的引入,其内存在默认配置下 增加了数兆字节。 在低内存环境中使用(例如在你的 DSL 路由器上运行),你可以在 user.h 中 减少 USERS 并取消定义 OUTPACKETQ_LEN,不会有任何不良后 果,假设任何时候最多只有一个客户端连接。仍然建议设置较小的 DNSCACHE_LEN,最好为 2 或更高,但你也可以取消定义它以再节省几 KB。
一个 iodine 服务器可以处理多个域名。在同一域名上设置不同的 NS 记录
全部指向同一主机,并在 topdomain 参数开头使用通配符
(例如 *.mydomain.com)。iodine
将接受所有匹配该模式的域名的隧道流量。通配符
必须位于 topdomain 参数的开头,后面跟一个点。
本节列出了一些性能测量数据。要正确查看,请使用 等宽字体,如 Courier。
测量在协议 00000502 的懒惰模式下进行;上行编码
始终为 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