对 Nginx DNS 解析漏洞的应急响应

本文最后更新于:4 个月前

经常有小伙伴问我应急响应平时有哪些事情要做,今天我就分享一次新鲜的应急(CVE-2021-23017)

背景

CVE-2021-23017,第一次见是在绿盟的公众号:

现在的公众号,一般会把比较重要或者有价值的信息放在靠上的位置,所以我当时看到这个推送,先去看了一下 VMware 的洞。本来都不打算看第二条,谁让它是 Nginx 的洞呢,我想还是值得一看的。这里我把漏洞描述搬运过来了(不重要的信息已省略):

5月26日,… Nginx 官方发布安全公告,修复了 nginx 解析器中的一个 DNS 解析程序漏洞(CVE-2021-23017),… 在处理 DNS 响应时存在错误,当 nginx 配置文件中使用了 resolver 指令时,… 攻击者能够伪造来自 DNS 服务器的 UDP 数据包,… 导致 1 字节内存覆盖,从而造成拒绝服务或任意代码执行…

受影响版本:Nginx 0.6.18 - 1.20.0

resolver 这玩意一般是用于反代,橘友们可以看看这篇文章,了解一下具体的作用与场景,我就不赘述了:

https://www.jianshu.com/p/5caa48664da5

然后这种新爆出的,内存覆盖、溢出啥的 CVE,poc 一般都没公开,更不用说 exp 了,所以我看完就关了,懒得去找 poc 了。

所以当天我得出的结论是:

  1. 不用 resolver 配置的 Nginx,不受影响
  2. 由于攻击者要需要伪造 DNS 服务器的响应,所以如果用的是内网的 DNS 那么影响程度就很小了
  3. 由于 resolver 用的还算比较多,所以还是有必要排查具体的使用情况
  4. 按照版本匹配,低于这个版本评估一下,该升级的直接升级,该改配置的改配置。大多数生产网的 Nginx 版本没有那么高,升级的话可能要跨多个版本,还是需要小心一些的
  5. 受影响版本:Nginx 0.6.18 - 1.20.0

以上是背景。

应急

27 号大晚上的,正在 ow 里和队友 “友好交流”,有位老伙计(某互联网金融公司)问我有没有看到这个 CVE 的相关信息,然后问了我这么一个问题:

同时给出了两个版本的 openresty:1.13.6.x1.15.8.x

这个问题勾起了我的好奇心。

openresty,写过 waf 的橘友们应该都接触过。我虽然没用过,但没吃过猪肉我也见过猪跑,在我对 openresty 浅薄的认知里,openresty 就是魔改的 Nginx(基于 Nginx 做的一个 Web 应用服务器,特点是可以用 Lua 脚本语言调用 Nginx 支持的各种 C 以及 Lua 模块,性能超强)。

好了,接下来开始帮忙这位老伙计应个急。噢,我的老伙计,大晚上你怎么在修福报?大家混口饭吃都不容易,还是帮他一把,游戏咱先不打了。

首先,这位老伙计说,他们的 openresty 是基于 Nginx 二次开发的,那么这里其实有个坑,基于 Nginx 二次开发,有可能是魔改了 openresty,也有可能是魔改了 Nginx,也有可能他说的就是官方的 openresty(因为官方就是在 Nginx 上做二次开发),所以这一点一定要问清楚。这里就是第一个小经验,永远不要直接通过研发/运维说的话得出结论,要换一种说法再问一遍,如果对的上那才能得出结论,所以我问了这么一个问题:

得到的回复是:

那么现在就可以得出结论了,他们其实用的就是官方版本的 openresty,无其他魔改操作。

接下来,我先去 openresty 官网(这里是第二个小经验,信息尽量找权威渠道获取)查一下官方的发布记录,看有没有写明是基于哪个 Nginx 版本,果然有:

嗯,按照版本匹配,这位老伙计的 openresty 都是受此漏洞影响的。

排查到这里,结论好像几乎就在嘴边了:升级 openresty 至最新版本?

当我去找了一下最新版的 openresty 1.19.3.1 的发布信息,发现它是基于的是 Nginx 1.19.3

这就比较麻烦了,按照版本匹配,openresty 最新版用的 Nginx 也是受此漏洞影响的。所以单单升级 openresty 至最新版是没有啥用的。

(这里顺便提一个在排查过程中发现的很有意思的现象,openresty 好像是在每个大版本的第一小版本更新 Nginx core,并且版本的命名与 Nginx 是同步的,比如 使用的 Nginx 是 1.19.3 的话,那么 openresty 的版本就是 1.19.3.1)

应急继续。既然没法直接修复漏洞,那就问问是否满足利用条件:

老伙计证实确实有用到这个配置:

并且还给出了配置文件:

1
2
3
4
5
...

resolver 127.0.0.1

...

可以看到,这是不满足我先前的第二个结论的,由于攻击者要需要伪造 DNS 服务器的响应,而这个 DNS 服务器与 openresty 在一台服务器上,走本地回环进行通信,所以暂时可以不用担心被攻击了。

定心丸有了,还是得解决根本问题。现在 openresty 官方还没发布修复,对于这种开源的组件,可以先去 GitHub 看看有无 issue(这又是一个小经验),有,而且很新:

https://github.com/openresty/openresty/issues/738

26 号当天就有人提了,然后 27 号上午就有补丁发布,但是处于 Open 状态。

然后我看了一下这个补丁:

https://github.com/openresty/openresty/pull/739

看起来只需要给 src/core/ngx_resolver.c 加一些代码即可。

然后呢,这里又有个小经验,看到补丁不要着急使用,一定要确认这个补丁是不是官方给的(官方 review 过、merged、核心开发者 push 等),如果不是的话可能有恶意代码,需要仔细看。

那么直接采用这个补丁肯定多少有点安全风险;而判断这个人是不是核心开发者也比较麻烦,毕竟那么多大佬咱也记不住名字;review 代码呢,咱也不一定看得懂。但是没事,代码咱们不敢用,打补丁的思路可以参考。

所以一开始我想直接把 ngx_resolver.c 换成最新的就完事了:

但后来发现有点不对,因为在 openresty 源码中,我只找到了 .patch 文件,并没有找到 Nginx 的源码。于是我 clone 了一下 xiaocang 这位老哥的版本,试了下 make,发现 patch 文件是在 make 阶段就直接打上了:

学习了。

然后 make 的过程中,我发现 ngx_resolver.c 这个文件之前就已经有 3 个补丁了,所以它现在与官方的 ngx_resolver.c 肯定会有很多不一样的地方,我抱着试一试的心态直接对比了一下这位老哥 patch 后的和 Nginx 1.21.0src/core/ngx_resolver.c 代码,看看这位老哥的修复方式与官方有什么区别:

这段是最重要的代码,处理方式好像差不多,至少没看出有安全问题,至于功能上嘛,咱也没读过 Nginx 源码,不知道有没有问题。而其他地方,相比与官方的补丁,这位老哥的补丁少了一些 invalid 的处理,比如:

嗯,所以这个补丁要不要用呢?这个时候,我又去 Nginx 官网看了一眼漏洞信息:

http://mailman.nginx.org/pipermail/nginx-announce/2021/000300.html

嗯?这是啥?

…好家伙,原来就是这个补丁…那没事了…

所以我给老伙计的建议是:

我 27 号晚上看的时候,这个 pull request 还是 Open 的,等待 review 中,28 号早上再看已经 merged 了:

效率真高。既然已经合并到 master 里了,那么直接按照他这个进行 patch 即可,clone 然后重新 make && make install 就好了。

总结

总结一些经验:

  1. 永远不要直接通过研发/运维说的话得出结论
  2. 信息尽量找权威渠道获取
  3. 对于开源的组件,可以先去开源的地方看一下 issue,可能漏洞已经有人通知了他们,甚至都已经在修复中了;如果没有,可以开个 issue 提醒一下。
  4. 看到漏洞补丁不要着急使用,一定要确认这个补丁是不是官方给的

整个应急过程中其实没用到什么复杂的技术,也没花费很多时间,都是基操,更多的还是看经验。这次应急并没有直接与业务/运维沟通,其实出现一个新的漏洞,也不一定就必须升级组件,具体情况还得具体分析,这种工作算是甲方安全应急人员日常工作的一小部分。大佬们随便放出几个 CVE,我们就要哼哧哼哧地去排查、去推进。

CVE 强者,恐怖如斯


《反序列化 PHP 篇》
也差不多写完了
快发了快发了
这次不鸽!


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!