漏洞信息#

CVE编号:CVE-2020-0688
登记时间:2019-11-04
漏洞类型:反序列化RCE
NVD漏洞评分:8.8 HIGH
操作系统:Windows Server 2008~2019
漏洞软件:Exchange Server 2010~2019
Shell权限:SYSTEM

漏洞原理#

漏洞的根源是两个,一个是ASP.NET的VIEWSTATE,另一个是Exchange Server安装的默认配置。

ASP.NET VIEWSTATE#

ASP.NET自带的VIEWSTATE功能是为了实现表单填写时,发生错误,页面重新加载后仍能恢复表单内容的功能。VIEWSTATE使用Binary序列化,存储了表单状态等对象信息,通过签名交由客户端存储,减少服务端存储资源消耗的同时,实现了状态的恢复。

.NET、Java和PHP的反序列化是类似的,能够恢复一个上下文中存在的类型实例,并且调用对应的“wakeup”方法。这导致了利用部分启动命令调用的类,比如本次复现中的TextFormattingRunProperties Gadget,执行任意代码。

因此,往往要对任何需要保存至客户端并且传入服务器的序列化内容进行加密和签名,避免反序列化的滥用。

在.NET Framework对应版本安装目录下的Temporary ASP.NET Files目录,会临时存放编译的aspx文件dll。

使用ILSpy可以反编译.NET字节码。发现ecp下的default.aspx文件生成的default_aspx类存放在App_Web_rkzs4hiu.dll文件中。

由此,查看调用至反序列化函数之前的路径。

VIEWSTATE参数触发反序列化的路径如下:

首先,每个ASP.NET的页面进入时,会调用继承的System.Web.UI.Page类的ProcessRequestMain方法:

其中会调用LoadAllState方法:

LoadAllState方法将会调用LoadPageStateFromPersistenceMedium方法:

LoadPageStateFromPersistenceMedium方法将会调用PageStatePersister的get方法获取一个其默认值,然后调用其中的Load方法。

可以看到,当_persister和pageAdapter为null的时候,会返回一个HiddenFieldPageStatePersister。

HiddenFieldPageStatePersister的Load方法最终会通过RequestViewStateString获得__VIEWSTATE Query参数的值,从而调用Util.DeserializeWithAssert进行反序列化。

RequestViewStateString变量的get方法将会对__VIEWSTATE的签名进行认证。

由于Exchange Server的管理面板IIS服务使用SYSTEM账户运行,所以通过反序列化执行命令能够获得SYSTEM权限。

Exchange Server安装的默认配置#

本来VIEWSTATE是安全的,因为进行了签名。但特定版本的Exchange Server在安装时没有随机化生成web.config中的validationKey和validation值为默认一样的值。利用这个Key,通过VIEWSTATE相同的过程生成VIEWSTATE序列化后的值,就能进行RCE。在利用中,使用ysoserial.NET的ViewState插件对该序列化后的值进行生成。

漏洞环境搭建#

使用Windows Server 2019 1809镜像安装系统。

使用Exchange Server 2019 RTM镜像安装漏洞软件。

安装Exchange Server前,需要配置好其先决条件,包括DNS服务器的建立,Active Directory主机的建立。

漏洞复现过程#

漏洞需要默认的validationKey和validation,以及一个低权限Exchange Web控制面板可以登录的账户。用这个账户获得需要的SessionId及generator的值。

首先需要在Active Directory中创建一个低权限账户user,将其加入Exchange Server的用户组View-Only Organization Management:

访问服务器80端口的/ecp目录。先打开浏览器开发人员工具,然后再输入user的用户名和密码登录。

在ecp/的请求Header的Cookie字段能够找到已经登录的ASP.NET_SessionId。

在响应的HTML中,搜索VIEWSTATE,能够找到隐藏的generator值。

使用这四个值,调用ysoserial.NET,生成payload。

其中执行的代码为从本机下载一个反弹shell的PowerShell脚本,然后执行命令反弹Shell。

获得payload后,进行urlencode,然后使用generator和这个payload,构建需要访问的/ecp/default.aspx地址参数。

此时在本机上搭建一个临时的Web服务器(这里我用了php-cli的-S功能开启临时Web服务器)用于提供反弹shell脚本的下载,然后使用ncat监听6666端口,实现反弹shell的交互。

最后访问地址,获得SYSTEM Shell。