使用常见 Web 服务器对抗近期 CVE-2021-44228 (Log4Shell) 漏洞滥用的有趣方法。
基于 @shipilev 的博文 (https://gist.github.com/shipilev/92e709a868f3d328b6636e1bfc21cf09),我将他的示例移植到了 Apache2。一位同事为 Lighttpd 也实现了类似功能。为了方便,我们决定公开这些示例。
将字符串 "jndi:" 放入请求头、User-Agent 或其他位置的场景少之又少。目前,我知道的唯一理由就是:利用 CVE-2021-44228。 当全世界都在忙于修补受影响的 Log4j 版本时,尽可能拖延攻击者的步伐或许是个合理的选择。那么,当攻击者试图利用你的服务时,给他们发送几个 GB 的无用数据如何?
以下代码片段并不能保护你的设备和服务免受 Log4Shell 0-Day 漏洞攻击!请更新易受攻击的软件,使用 log4j2.formatMsgNoLookups=true 禁用 jndi 查询,或者在补丁可用之前将其下线。仅可在未启用 Log4j 服务的服务器上使用此方案!有关如何缓解 CVE-2021-44228 的更多信息:https://research.hisolutions.com/log4shell
此方案也无法覆盖混淆调用,例如 ${${::-j}${::-n}${::-d}${::-i}:${::-l}${::-d}${::-a}${::-p}://${hostName}.} 或其他试图隐藏 "jndi:" 部分的手段。由于没有简单的解决方案,我暂时不会涉及更高级的检测技术。但我仍然相信,大多数攻击不会使用这些手段,所以我们仍可以烦扰大部分脚本小子 :)
在 Linux 上,创建一个包含随机 HTML 消息的文件。请不要 使用下方示例中的 LOL,因为这会使攻击者更容易实现通用过滤器来规避我们的防御。我们使用 pv 工具来显示文件创建进度,你可能需要通过你喜欢的包管理器安装它,或者直接省略它。
$ awk 'BEGIN { for(c=0;c<10000000;c++) printf "<p>LOL</p>" }' > 100M.html
$ (for I in `seq 1 100`; do cat 100M.html; done) | pv | gzip -9 > 10G.boomgz
$ rm 100M.html
假设你的 Web 服务器以 www-data 用户运行,让我们创建一个由 www-data 拥有的目录,这样我们就不必在 Web 服务器可能服务的每个 Web 根目录下都上传一份副本:
mkdir /bombs
mv 10G.boomgz /bombs
chown -R www-data:www-data /bombs
现在进入有趣的部分...
参见 https://gist.github.com/shipilev/92e709a868f3d328b6636e1bfc21cf09
所有功劳归于 @shipilev
启用所需模块
a2enmod rewrite headers ratelimit
导航到 /etc/apache2/sites-enabled。 用你喜欢的编辑器打开每个主机配置文件,并在包含 的每一行(可能不止一个)之前插入以下代码片段:
RewriteEngine On
RewriteCond %{THE_REQUEST} "^.*(\${jndi|\${\${).*$" [OR]
RewriteCond %{QUERY_STRING} "^.*(\${jndi|\${\${).*$" [OR]
RewriteCond %{REQUEST_URI} "^.*(\${jndi|\${\${).*$" [OR]
RewriteCond %{HTTP_COOKIE} "^.*(\${jndi|\${\${).*$" [OR]
RewriteCond %{HTTP_HOST} "^.*(\${jndi|\${\${).*$" [OR]
RewriteCond %{REMOTE_HOST} "^.*(\${jndi|\${\${).*$" [OR]
RewriteCond %{REMOTE_USER} "^.*(\${jndi|\${\${).*$" [OR]
RewriteCond %{HTTP_USER_AGENT} "^.*(\${jndi|\${\${).*$" [OR]
RewriteCond %{HTTP_REFERER} "^.*(\${jndi|\${\${).*$"
RewriteRule . /bombs/10G_lol.boomgz [L]
<Files ~ "\.boomgz$">
Header Set Expires "Sat, 1 Jan 2000 00:00:00 GMT"
Header Set Content-Encoding "gzip"
Header Set Content-Type "text/html"
SetOutputFilter RATE_LIMIT
SetEnv rate-limit 100
</Files>
<Directory /bombs>
allow from allow
Require all granted
</Directory>
你可能会问,为什么这里比 @shipilev 的原始示例多了这么多变量。我决定尝试尽可能多地捕获各种位置。这可能在极少数情况下会中断服务,所以如果你希望使用原始检查集,只需注释掉或删除 RewriteEngine On 语句后的前 7 行,并移除其余行中的 |\${\${ 部分。
你也可以将代码放入一个新文件,例如 /etc/apache2/conf-available/anti-jndi.conf,并以如下方式包含:
Include /etc/apache2/conf-available/anti-jndi.conf
现在保存文件并重新加载 Apache2 配置:
systemctl reload apache2
如果一切顺利,你将不会收到错误信息。
即将推出
你可以通过 curl 连接到修改后的网站来检查是否成功(记得将 your-hostname 替换为你刚编辑的服务的实际主机名):
curl -s -L your-hostname -A "\${jndi:testing}" | pv > /dev/null
你应该会看到一个进度条,显示大约 100kb/s 的下载速度。如果不取消,它应该会在几分钟后结束,具体取决于你初始 HTML 文件中使用的字符串。对我来说,大约需要 5 分钟。 现在检查客户端发生了什么:
curl -s --compressed -L your-hostname -A "\${jndi:testing}" | pv > /dev/null
看到没有,不再是 100kb/s 了?压缩发挥了作用,客户端侧下载的是几个 GB。所以,在下载了几分钟的无用数据后,假设数据被存储用于分析,攻击者手里就有几个 GB 的垃圾文件了。谁知道呢? :)