Microsoft Word - WJ01+.doc

Save this PDF as:
 WORD  PNG  TXT  JPG

Size: px
Start display at page:

Download "Microsoft Word - WJ01+.doc"

Transcription

1 1 第章 想象一下, 将你的服务器从 Internet 上隐藏起来, 但仍可以通过 ISP 所提供的优越宽带进行访问 这样, 不需要任何更改, 就可以安全地将该服务器作为文件存储器来使用, 当然也可以另有他用 此外, 你可能还希望授权对命令行的完全访问, 以便可以启动和终止 ( 甚至安装 ) 任何需要使用的服务 具体如何选择取决于只是简单地运行这些服务然后再关闭, 还是持续运行这些服务一段时间并对外可见 在实现过程中可能需要使用一种被称为端口碰撞 (port knocking) 的技术 通过对外部世界关闭所有网络端口, 可以伪装自己的服务器, 从而使其真正不可见 通过使用预先设定的 敲门, 可以随意地为 SSH 服务器或者一些其他服务选择开放单个端口 在本章, 将学习如何创建一台不可见的服务器, 以及在创建过程中需要考虑的一些选项 1.1 背景知识 通过在 Internet 上隐藏一台服务器的存在, 可以秘密地运行一台计算机, 即使是攻击者知道了服务器的存在, 通过限制端口开放 ( 甚至可见 ) 的时间, 也可以减少攻击者可以瞄准的攻击面

2 Linux 服务器安全攻防 探测端口在开始之前, 先让我们近距离了解一下服务器上的网络端口, 以便在头脑中形成一个参照框架 如果你曾用过 Nmap 之类的安全工具, 那么可能会非常熟悉下面的混乱情形 : 某些端口看似被关闭了, 但实际上并没有关闭 Nmap 区分某一端口是关闭还是开放的方法是判断非开放端口后面是否有一个正在进行监听的服务 ( 守护程序 ) Nmap 将关闭的端口定义为虽然后面没有进行监听的守护程序, 但看似开放 ( 或者至少潜在可用 ) 的端口 如果 Nmap 使用了过滤的端口, 则意味着某些类型的防火墙正在阻止对某 IP 地址的访问 ( 可能怀疑该 IP 地址正在扫描系统 ) 这通常是通过 TCP RST 包完成的 此外,Nmap 还会报告其他三种状态 :unfiltered open filtered 以及 closed filtered 如果想要更多地了解这些状态有什么不同, 可以访问 使端口扫描器产生混乱目前你已经知道了端口如何向端口扫描器呈现自己, 那么接下来让我们学习一下如何对返回的响应进行混淆处理, 以便使先进的端口扫描技术产生混乱 其中最常用的工具当属基于 Kernel 的防火墙 Netfilter( 通常被称为 iptables), 这都得益于其强大的功能集 现在介绍该工具的工作原理 针对 TCP 数据包, 我们希望通过使用 iptables 生成一个 REJECT 请求, 从而巧妙地处理对端口探测的响应 而对于其他协议, 则只是简单地丢弃 (DROP) 数据包即可 这样就从 Nmap 获取了一个关闭且未过滤的响应 根据我从网上收集来的大多数意见, 一个关闭的端口是可以期望的最好响应 ( 但这种说法也是有争议的, 也是多变的 ) 这是因为你并没有公开承认使用一个防火墙阻塞任何端口, 但也不会简单地开放端口, 因为在后面运行了一个守护程序 2

3 第 1 章隐身斗篷 接下来的解释更详细一点 在一般情况下, 不可到达的端口通常生成一个 ICMP Port Unreachable 响应 然而, 有时可能并不想生成这些错误, 因为这意味着一个服务器正在该端口上监听, 从而也就暴露了该服务器的存在 此时, 可以生成调整后的 REJECT 响应, 并用以下方式应用 :-reject-with tcp-reset 这样有助于进行相应的响应, 就像该端口未被使用并且关闭, 同时也没有进行过滤 可将下面的代码片段附加到每个 iptables 规则的末尾 : -j REJECT reject-with tcp-reset 通过使用该技术, 可以确保不会泄漏任何与系统相关的不必要信息 请注意, 在后面介绍的端口碰撞示例中, 并不会使用该 iptables 选项 这是因为不会在你的 SSH 服务器上运行其他额外的服务 然而, 上面介绍的背景知识将有助于理解攻击者可能如何接近计算机的端口, 以及如何对其他服务应用 -rejct-with tcp-reset 选项 具体是使用 iptables DROP 还是使用 REJECT 响应, 还存在一些争议 如果你感兴趣, 可以访问 network/drop-vs-reject, 查看一些关于该问题的一些独特见解 1.2 安装 knockd 现在, 你已经掌握了一些有用的背景知识 接下来介绍如何在服务器上安装一个端口碰撞工具 在介绍的过程中, 有时可能需要考虑哪些服务应该以隐藏于 Internet 的方式运行 例如, 当需要在一个不太常见的端口上运行一个 Web 服务器或电子邮件服务器时 软件包接下来, 安装可以为系统提供端口碰撞功能的 knockd 软件包 3

4 Linux 服务器安全攻防 根据系统的不同, 该软件包的安装方法也有所不同 在 Debian 衍生产品中, 以如下方式安装该软件包 : # apt-get install knockd 而在 Red Hat 衍生产品中, 则按照以下方式安装 : # yum install knockd 主配置文件控制了 knockd 所需的大部分配置 在 Debian Jessie 服务器上, 该文件位于 /etc/knockd.conf 中 代码清单 1.1 显示了我的主配置文件, 从中可以了解一下 knockd 的工作方式 代码清单 1.1 主配置文件 端口序列以及 -I INPUT 都进行了修改 ( 相对于默认值 ) [options] UseSyslog [openssh] sequence = 6,1450,8156,22045,23501,24691 seq_timeout = 5 command = /sbin/iptables -I INPUT -s %IP% -p tcp dport 22 -j ACCEPT tcpflags = syn [closessh] sequence = 3011,6145,7298 seq_timeout = 5 command = /sbin/iptables -D INPUT -s %IP% -p tcp dport 22 -j ACCEPT tcpflags = syn 更改默认设置 在代码清单 1.1 的顶部, 可以看到用来设置相关选项的部分, 4

5 第 1 章隐身斗篷 而其他两个部分设置了在 knockd 开放 SSH 访问以及关闭端口访问时所想要执行的操作 此外, 这两个部分还包括了默认的端口碰撞序列, 以便按照 sequence 选项触发这些操作 安装完 knockd 之后, 我立即更改了这些默认的端口值, 从而避免对服务器安全产生影响 开放 SSH 访问的默认序列为端口 和 9000, 而关闭访问的默认序列为端口 和 7000 如你所见, 在本例中, 我添加了一些额外的端口来开放 SSH 访问, 这样就可以减少其他人通过任意端口扫描而偶然发现以上端口组合的可能性 更改完相关设置后, 可在基于 systemd 的操作系统上使用下面的命令重启 knockd: # systemctl restart knockd.service 安装完 knockd 后, 如果想要了解更多关于该软件的背景知识, 可以参考 Debian Jessie 所提供的简要 README 文件 ( 可以访问 /usr/share/doc/knockd/readme 找到该文件 ) 该 README 文件讨论了 knockd 的工作方式以及其他的相关内容 knockd 使用了一个名为 libpcap 的库, 而该库也被其他多个软件包所使用, 比如 tcpdump ngrep 和 iftop( 一款用来捕获数据包并进行检查的软件包 ) 得益于更高明的设计,knockd 甚至不需要为了监视原始流量 (raw traffic) 而与端口相绑定 ( 它会秘密地进行监听 ) 更改文件系统位置诸如连接 断开连接或者错误之类的事件都被直接记录到系统的 syslog 文件中, 此外, 还可以将这些事件记录到 /var/log/messages 或 /var/log/syslog 文件中 如果你不希望将这些重要信息淹没在其他系统日志信息中, 或者想要避免解析那些难处理的日志文件, 那么可以创建自定义日志文件 我个人比较喜欢采用自定义的方法, 以便更清晰地进行调试 通常每天我会使用一款自动化工具或者自定 5

6 Linux 服务器安全攻防 义的 Shell 脚本将相关日志以电子邮件的形式发送给自己, 以便可以对可疑的事件进行监视 因为所有的 knockd 记录都放在一个地方, 所以可以更加容易地使用脚本或者其他工具对信息进行解析 : [options] LogFile = /var/log/portknocking.log 更改日志文件的位置是一种常用的解决方法, 但也可以更改启动 knockd 服务时 Process ID 文件写入的位置 可在配置文件的 [options] 部分更改相关位置, 如下所示 : [options] PidFile = /var/tmp/run/file 1.3 一些配置选项 到目前为止, 我们已经很好地理解了主配置文件的结构, 接下来看一下如何对其进行配置来满足特定的需要 在完成一些任务时, 需要考虑某些选项的超时在创建服务器的过程中所起到的重要作用 启动服务如果你看到了一条错误消息告知 knockd 被禁用了, 请不要恐慌 这只是一种预防措施, 其目的是避免在完成对主配置文件的设置之前 knockd 不会向 iptables 引入一些不受欢迎的更改 在 Debian Jessie 上, 该错误消息会要求我们在 /etc/default/knockd 文件中将下面的参数更改为 1: START_KNOCKD=1 毫无疑问, 只有在仔细检查完配置或者确保带外访问 (out-of-band access) 按照预期那样工作之后才可以完成上述更改操作 6

7 第 1 章隐身斗篷 更改默认的网络接口 一旦配置了首选的端口序列, 接下来可能还需要调整其他参数 在配置文件 (/etc/default/knockd) 中, 可以更改 KNOCKD_OPTS 设置 该文件中的配置示例都被注释掉了, 这意味着可更改 knockd 所监听的网络接口, 如下所示 : KNOCKD_OPTS="-i eth1" 这些选项将被应用到 knockd 服务, 为让所做的更改生效, 需要重启服务, 如下所示 ( 在 systemd 计算机上 ): # systemctl restart knockd 数据包类型和时序 在 /etc/knockd.conf 文件中, 可以通过更改一些设置, 对客户端连接到服务器的方式进行微调 返回到代码清单 1.1, 我们将在 [openssh] 部分添加更多选项 : [openssh] tcpflags = syn seq_timeout = 10 cmd_timeout = 15 其中,tcpflags 选项意味着可以为所发送的 TCP 数据包 ( 即 knockd 所接收到的数据包 ) 指定特定的类型 在上例中,TCP 数据包的类型为 "syn" 可用的 TCP 标记包括 fin syn rst psh ack 以及 urg 如果没有接收到指定类型的 TCP 数据包,knockd 将忽略这些数据包 请注意, 这并不是 knockd 常用的工作方式 通常情况下, 不正确的数据包将会停止整个碰撞序列的工作, 这也就意味着客户端必须再次启动以便进行连接 可以通过使用逗号分隔多种 TCP 数据包类型 此外,knockd 的最新版本 ( 当前的最新版本为 0.5) 还可以使用惊叹号来否定数据包类型, 比如!ack 7

8 Linux 服务器安全攻防 再看一下本例中的其他选项 你可能已经注意到, 默认情况下, seq_timeout 已经出现在代码清单 1.1 中 然而, 由于我们已经在序列设置中增加了端口的数量, 因此将 seq_timeout 值上调至 10, 而不是 5 该调整是很有必要的, 因为在一次缓慢的连接过程中 ( 比如通过智能手机进行连接 ) 可能会发生超时的情况 示例中的最后一个选项是 cmd_timeout 该选项主要应用于 knockd 成功接收到一个碰撞后所发生的事件中 事件序列如下所示 首先, 一旦端口碰撞被确认为有效,knockd 将运行 start_command( 如果需要提示, 请参考代码清单 1.1) 如果设置了该选项, 那么在 knockd 执行完 start_command 选项后, 会等待 cmd_timeout 所指定的时间, 然后执行 stop_command 操作 首选方法是首先开放 SSH 服务器进行访问, 然后在连接建立后马上关闭服务器 此时, 并不会对你的使用产生影响 但如果想要建立新的连接, 则需要再完成一次端口碰撞序列 可将该过程想象为一旦进入房间后即随手关门 这样, 服务器将变得不再可见, 而只有相关联的通信是可见的 1.4 对安装进行测试 由于涉及的是服务器的安全, 因此应该进行一些测试, 以确保 knockd 按照期望的方式工作 比较理想的情况是通过访问另一台客户端计算机来进行测试 在测试过程中, 需要完全确保 knockd 正确地开放和关闭端口, 我个人比较喜欢通过一个完全不同的 IP 地址进行连接, 从而完成相关的测试 如果你无法使用一个不同的 IP 地址访问一个连接, 那么可以周期性地断开 Internet 连接, 以便 ISP 为你分配一个新的动态 IP 地址, 从而完成测试 一些宽带提供商会在计算机重启后分配一个新的 IP 地址, 此外移动提供商也会提供类似功能 8

9 第 1 章隐身斗篷 端口碰撞客户端 为初始化一个连接并开放 SSH 端口, 可使用不同的客户端创建一个碰撞序列 甚至可手动使用诸如 Nmap netcat 或 Telnet 的工具按顺序探测所需的端口 此外, 有关文档还提到可以使用 hping sendip 以及 packit 软件包 ( 前提是它们都可用 ) 接下来让我们看一个 knockd 软件包所附带的 knock 命令的示例 如果用过代码清单 1.1 中所示的 openssh 部分, 就可以使用下面所示的语法创建自己简单的 knock 命令 : # knock [options] <host> <port[:proto]> <port[:proto]> <port[:proto]> 由于前面已经在代码清单 1.1 中配置了 TCP 端口, 因此可以运行下面的 knock 命令, 如下所示 : # knock :tcp 1450:tcp 8156:tcp 22045:tcp 23501:tcp 24691:tcp 在本例中, 目标主机的 IP 地址为 如果愿意, 还可在代码清单 1.1 中组合使用 UDP 和 TCP 端口 ; 此时的客户端碰撞序列应该如下所示 : # knock :tcp 1450:udp 8156:udp 22045:tcp 23501:udp 24691:tcp 如果只想使用 UDP 端口, 那么可以使用一种非常好用的快捷方式, 即在命令的开头添加 -u, 而不需要显式指定这些端口 可运行一个针对 UDP 端口的命令, 如下所示 : # knock -u 接下来, 让我们返回到服务器的配置文件, 看一下如何在有效的碰撞序列中互换 TCP 和 UDP 如果想要混合使用不同协议, 请在 openssh 部分中按下面的代码更改 sequence 行 : 9

10 Linux 服务器安全攻防 [openssh] sequence = 6:tcp 1450:udp 8156:udp 22045:tcp 23501:udp 24691:tcp 1.5 使服务器不可见 一旦确定了安装过程按照期望的方式工作, 那么接下来可以锁定服务器, 以便对攻击者隐藏服务器 可能因为某些原因 ( 比如, 攻击者曾经在托管服务器的 ISP 中工作过 ), 攻击者已经知道了与服务器绑定的 IP 地址, 或者能够查看向该 IP 地址发出的通信以及从该 IP 地址接收到的通信 否则, 服务器对 Internet 用户来说是不可见的 即使可见, 也会碰到防火墙的阻挡 然而, 如果想要获取 Nmap 的关闭端口状态, 可尝试使用下面的方法 测试 iptables 如前所述, 应该使用可信任的 iptables 理想情况下, 在锁定服务器之前应该物理访问服务器, 以免发生错误 如果物理访问失败, 则应该完成某些类型的带外访问, 比如通过一台虚拟计算机的控制台 可以登录的辅助网络接口或者计算机附带的拨号调制解调器进行访问 但要注意, 除非已经在你的开发环境中测试了相关配置, 否则很有可能会出现错误并产生不同的问题 即使之前我用过端口碰撞, 但有时也会被捕获到而被锁在服务器之外 请谨记以上警告 接下来开始学习 iptables 命令 在将相关规则与已经使用的任何规则进行整合时要格外小心 比较容易的方法是在对已有的规则进行备份后直接重写已有的规则 首先, 需要确保服务器可通过本地主机接口与自己对话, 如下所示 : # iptables -A INPUT -s /8 -j ACCEPT 接下来, 必须确保任何现有的连接都被确认和响应, 如下所示 : 10

11 第 1 章隐身斗篷 # iptables -A INPUT -m conntrack ctstate ESTABLISHED,RELATED -j ACCEPT 此时使用 conntrack 来跟踪相关联的连接 一旦这些连接被成功初始化, 就可以继续使用了 在本例中, 假设仅需要为 SSH 服务器开放 TCP 端口 22, 并且没有其他服务 具体的开放过程, 可以参见代码清单 1.1, 请添加下面的命令, 开放 TCP 端口 22: command = /sbin/iptables -I INPUT -s %IP% -p tcp dport 22 -j ACCEPT 请格外注意该代码行 如果在该命令中使用了 -A INPUT( 代表 附加 ), 则会被 iptables 拒之门外 因此必须是 -I( 代表 插入 ), 以便该规则被输入为第一条规则, 并相对于其他规则具有优先权 此时你可能会疑惑 %IP% 变量代表什么 端口碰撞非常聪明, 可以使用 -s 字段中的连接 IP 地址替代 %IP% 值 接下来就必须格外小心了 如果该命令没有按照预期的方式运行, 就没有回头路了 所以, 请确保在虚拟计算机上对相关的规则进行了测试, 或者带外访问服务器, 以防万一 可以使用下面的代码阻塞所有进入服务器的通信 : # iptables -A INPUT -j DROP 如果运行下面的命令检查 iptables 规则, 将看不到 TCP 端口 22 和 SSH 端口 : # iptables -nvl 然而, 一旦成功登录, 将会在 iptables 中看到一条规则 ( 很简单, 只需要为 cmd_timeout 设置一个较低的值即可 ) 如果在该过程中碰到了任何问题, 可以继续往下阅读, 找到解决配置问题以及提高记录级别的方法 如果没有任何问题, 则应该拥有了一台所有端口都报告不存在的服务器, 从而使该服务器不可见, 如图 1.1 所示 11

12 Linux 服务器安全攻防 图 1.1 Nmap 似乎认为该 IP 地址上没有任何计算机 保存 iptables 规则为确保 iptables 规则在计算机重启之后仍然存在, 应该在 Debian 衍生产品中安装一个被称为 iptables-persistent 的软件包, 如下所示 : # apt-get install iptables-persistent 然后使用下面所示的命令保存规则 : # /etc/init.d/iptables-persistent save 或者可以运行下面的命令恢复已保存的配置 : # /etc/init.d/iptables-persistent reload 而在 Red Hat 衍生产品 ( 或者 presystemd 计算机 ) 中, 可以使用下面的命令 : # /sbin/service iptables save 如果想要恢复规则, 则可以运行下面的命令 : # /sbin/service iptables reload 如果想要在 systemd Red Hat 衍生产品中完成上述工作, 则首先需要尝试安装以下软件包 : # yum install iptables-services 1.6 进一步考虑 除上述内容外, 了解一下端口碰撞其他方面的内容也大有裨益 12

13 第 1 章隐身斗篷 接下来我们学习这些内容 智能手机客户端在 Android 智能手机上, 首选的 SSH 应用是 JuiceSSH( juicessh.com) 该应用是一个第三方插件, 可以将一个碰撞序列配置为 SSH 握手的一部分 这也就意味着没有任何理由不去使用端口碰撞, 即使在旅途中身边没有带笔记本电脑 故障排除如果在端口碰撞记录文件上运行命令 tail -f logfile.log, 会看到记录中写入了不同阶段的相关信息 其中包括有效端口是否被碰撞, 更重要的是, 如果是按照正确的顺序被碰撞 通过使用一个调试选项, 可以提高 knock 所产生的记录级别 如果打开 /etc/init.d/knockd 文件并仔细看一下, 会找到 OPTIONS 行, 此时可以将一个大写字母 D(Shift+d) 添加到该行现有的值中, 如下所示 : OPTIONS="-d -D" 一旦诊断完毕并解决了相关问题, 就应该马上关闭额外的记录, 以避免磁盘空间被不必要的信息所填满 在上面的代码中,-d 意味着将 knockd 作为一个守护程序来运行, 以免产生疑惑 通常应该保留该选项, 因为这是一个正常的操作 返回到客户端 如果添加了 -v 选项, 可以向输出添加 碰撞 客户端生成的详细信息 如果结合使用调试选项, 就可以获取来自客户端和服务器端的有用反馈 安全性考虑当涉及与服务器相关联的公共信息时, 应该提醒你的 ISP 不应该公布服务器所使用 IP 地址的 DNS 信息 你的 IP 地址应该显示为 13

14 Linux 服务器安全攻防 未使用且未分配, 以保证服务器不可见 甚至对于诸如 HTTP 之类的公共服务, 也需要记住对正在使用的守护程序的版本信息进行模糊处理 常用的方法是使用世界最流行的 Web 服务器 Apache, 将 "ServerTokens" 更改为 "prod", 同时将 "ServerSignature" 设置为 "Off" 虽然这些配置更改是比较前沿的, 但这样配置后, 当一个新的零日攻击 (zero-day exploit) 被发现时, 该攻击会忽略你的服务器, 因为你的 Apache 版本号不在攻击数据库中 knockd 文档还介绍了其他需要考虑的方面 其中谈到, 如果使用 -l 或 - -lookup 服务启动选项来解决记录条目中主机名的问题, 则可能面临一个安全风险 如果这样做, 则可能将某些信息泄漏给攻击者 而攻击者可以使用这些信息确定序列的第一个端口, 从而可能捕获到来自服务器的 DNS 信息 短暂的序列 是否可以对碰撞序列使用一种不同的方法呢? 当然可以, 可以使用带有预定义的端口序列列表的端口碰撞, 而该序列列表在使用一次后就会失效 返回到代码清单 1.1, 查看一下主配置文件, 可以向 open 和 close 部分添加下面所示的选项, 从而在需要时启用一次性序列 : [openssh] One_Time_Sequences = /usr/local/etc/portknocking_codes 如果从代码清单 1.1 中删除 sequence 行, 并替换为上面所示的代码, 那么 knockd 将会从上述路径指定的文件中获取序列 knockd 处理一次性序列的方法是非常独特的 它首先从文件中读取下一个可用的序列, 然后注释掉该行, 并紧跟着一次有效碰撞所产生的有效连接 knockd 会在代表序列的行的前面添加一个哈希或者 # 字符 相关文档还提过应该在每一行的开头留有空格, 否则当在该行 14

15 第 1 章隐身斗篷 开头添加 # 字符时, 可能会发现该字符已被无意间覆盖了, 也就意味着你被锁定在外了 在序列文件中, 每一行可以添加一个序列 该文件的格式与主配置文件中 sequence 选项所使用的格式是相同的 此外, 文档还指出, 如果想要加入注释, 只需在这些注释之前添加一个 # 字符即可 但如果在编辑序列文件的同时 knockd 已经在运行了, 就会发生糟糕的事情, 比如被锁定在服务器之外 一旦理解了 knockd 的基本功能, 就可以体验更丰富的使用经历了 在测试期间, 可以输入你所记住的电话号码或者任何其他数字序列, 这样就不需要一直寻找一个不安全的序列列表了 例如, 可以使用 5 个电话号码, 并将它们拆分成有效的端口号 1.7 小结 在本章, 除了介绍如何让服务器不可见外, 还介绍了如何在攻击发起之前让服务器在 Internet 上出现 为了充分混淆使用了端口碰撞的服务器, 应该特别留意公共信息, 比如反向 DNS 条目, 这些信息可能会泄露正在使用的 IP 地址 此外, 还可以考虑使用 NAT 来隐藏服务器, 并周期性地动态更改服务器的 IP 地址, 这样在给定的时间内, 只有管理员通过一个秘密的主机名才会知道正在使用的 IP 地址, 而该主机名则由一个特殊的 Domain Name 上的 DNS 秘密发布 还可使用许多其他方法来保护服务器, 但我希望本章可以覆盖足够的知识面, 以便读者可以考虑哪些信息可以泄露给公众, 而哪些信息可能被未来的黑客攻击所利用 此外, 在需要时知道如何对服务器进行隐藏 15