CVE-2022-1040 Sophos Firewall 服务架构与认证绕过漏洞分析之旅


声明

出品|且听安全(ID:WCyber)

以下内容,来自且听安全公众号的WCyber作者原创,由于传播,利用此文所提供的信息而造成的任何直接或间接的后果和损失,均由使用者本人负责,长白山攻防实验室以及文章作者不承担任何责任。

漏洞信息

前段时间 Sophos  Firewall 爆出了一个认证绕过漏洞 CVE-2022-1040 ,最近在深入分析 Sophos 服务架构的同时,完整复现了该漏洞。主要是在 `User Portal` 及 `Webadmin` 两个接口存在认证绕过漏洞,漏洞巧妙利用了 Java 和 Perl 处理解析 JSON 数据的差异性,实现了变量覆盖,从而导致认证绕过及命令执行。漏洞适用范围为 Sophos  Firewall  v18.5 MR3 及以下版本。

环境搭建

首先从官网下载老版本虚拟机。本文研究下载版本为`VI-18.5.2_MR-2.VMW-380`:

按照提示很容易完成安装。Sophos 的 Web 接口主要通过 Java 实现,由启动命令可以看出 Sophos 使用的是 openjdk 环境,如果在 Java 启动参数中直接添加调试参数,将会出现找不到 `libjdwp.so` 动态链接库的错误:

可以通过自行上传完整JDK来解决这个问题:

重启Java服务即可看到监听的端口。

服务架构

Sophos 中的服务架构不是很复杂,主要使用了 Apache 、 Jetty 等 web 服务,第一层语言为 Java 通过网络通信的方式与后端 Perl 服务进行交互:

0x01 Apache 配置

Apache 的启动命令如下:

apache -d /_conf/httpd -DFOREGROUND

进入`/_conf/httpd`目录,存放有Apache的配置文件,通过分析`httpd.conf`,了解Apache引用了`/cfs/web/apache/httpd.conf`配置:

Define userportal_listen_port 65004Define webconsole_https_port 65003Define SSLCertificateFileWithPath "/conf/certificate/ApplianceCertificate.pem"Define SSLCertificateKeyFileWithPath "/conf/certificate/private/ApplianceCertificate.key"Define https_cert_valid true

从配置中可以看出Apache开放了两个主要的端口`userportal 65004`和 `webconsole 65003` :

在Apache配置目录下搜索 `ProxyPass`,找到的代理转发配置如下:

./ssl.conf:53:    ProxyPass /webconsole/images !./ssl.conf:54:    ProxyPass /webconsole/css !./ssl.conf:55:    ProxyPass /webconsole/javascript !./ssl.conf:56:    ProxyPass /webconsole http://localhost:8009/webconsole./ssl.conf:57:    ProxyPassReverse /webconsole http://localhost:8009/webconsole./userportal-static.conf:64:    ProxyPass /userportal/images !./userportal-static.conf:65:    ProxyPass /userportal/CRSSL !./userportal-static.conf:66:    ProxyPass /userportal http://localhost:8009/userportal./userportal-static.conf:67:    ProxyPassReverse /userportal http://localhost:8009/userportal

代理转发策略将`webconsole`和`userportal`端口分别代理到`8009`端口的不同URL :

ProxyPass /webconsole http://localhost:8009/webconsoleProxyPass /userportal http://localhost:8009/userportal

0x02 Jetty 配置

Jetty 配置文件为`/usr/share/jetty/start.ini`,开启了本地服务的`8009`端口:

Jetty 的启动参数在 `/usr/bin/jetty` 脚本中配置,如果要修改可直接修改该文件最后 Java 执行部分。

0x03 CSC 配置

CSC是Sophos的主要服务之一,主要负责启动各个服务进程及提供API接口供其他程序服务调用。其启动命令为:

csc -L 3 -w -c /_conf/cscconf.bin

CSC为标准的ELF 32bit可执行程序,可通过逆向分析其中功能。

`cscconf.bin` 中在CSC程序中有调用解压,猜测是一个加密压缩包。

`/usr/bin/csc`由C语言编写,负责启动加载其他的服务以及加载Perl代码,在虚拟机中CSC启动部分服务如下:

程序中的`extract_conf`函数负责解密`cscconf.bin`并提取压缩包中的内容:

`decrypt_bin`函数主要是通过异或算法将`cscconf.bin`数据解密为`cscconf.tar.gz`压缩包格式:

每次取`0x420`个字节通过`xor_decrypt`函数进行加密块解异或解密,将解密后数据中的`0x400`个字节写入`tar.gz`文件:

`xor_decrypt`核心代码如下,主要通过与`0x80DCB40`地址中实现存放的64 字节逐一进行异或处理:

通过逆向该算法,使用Python编写出加解密算法实现代码。解密得到 `cscconf.tar.gz`压缩包,解开压缩包目录如下:

在压缩包中的其中一个目录名为`service` ,推测CSC通过该目录下的配置文件启动相关服务:

补丁对比

配置Sophos vmware网卡连网后等待一段时间,将虚拟机上的文件与原来的文件进行对比,其中有两个修改的地方,一处为`web.xml`,另一处添加了 `RequestCheckFilter.class`文件:

`web.xml`增加了一段配置,主要给Sophos Java代码添加

`RequestCheckFilter`过滤器,过滤器主要检测request请求包中的JSON参数是否包含不可见字符:

检测规则中,JSON参数的每个字符都必须是 `32~127`之间,如果超出范围则会跳转到登录界面:

那么给我们的启发就是此次漏洞和JSON参数中的不可见字符有着直接的关系,应该是字符编码导致的认证绕过。

JSON 解析差异性分析

漏洞原理可简单理解为 Java 在使用`unicode \u0000` 时,JSON认为`key` 是两个不同的`key` 并没有`0`字节截断,当 Java 把含有unicode编码的`key` 发送给后端的Perl处理时`\u0000` 产生了截断效果,使得带有unicode编码的`key`变为了`mode`, Perl可以处理重复`key`的JSON,如果重复则后面覆盖前面的值:

我们可以通过一个示例来对比不同语言处理JSON重复键的差异性。Java处理带有unicode编码的`key`时可以正常解析:

import org.json.JSONObject;import org.json.JSONException;import java.io.*;class test {    public static void main(String[] args) {        try{          System.out.println(new JSONObject("{ \"name\": \"test\", \"name\\u0000ef\": \"test2\"}"));        }catch (JSONException e){          System.out.println(e);        }    }}

Perl处理Java传递过来的零字节字符串就会产生截断效果,在处理相同`key` 值的JSON时会取最后一个`key`对应的`value` :

#!/usr/bin/perluse JSON;my %rec_hash = ('a' => 1, 'b' => 2, 'c' => 3, 'd' => 4, 'e' => 5, 'a' => 6);my $json = encode_json \%rec_hash;print "$json\n";

其结果为`a` 被覆盖为了最后一个 `a` 的值:

不同语言对JSON解析的差异性是此次认证绕过漏洞的核心原理。Java接收来自用户的以下数据(其中 `\u0000` 后面的`ef`是为了让  JSON中`key`的 hash 排序将`716`排到`151`的后面):

{"mode":151,"mode\u0000ef":716}

登录认证分析

通过登录`webadmin`获取登录数据包:

根据`/webconsole/Controller`及`mode=151`寻找Java代码中的处理逻辑,首先分析`web.xml`中路由对应的Servlet:

主要由`CyberoamCommonServlet`进行处理,该类主要解析`mode`值,并通过`mode`进行分发:

进入`_doPost`函数继续进行分发, `EventBean`从数据库中获取`mode`对应的属性,其中就包括关键属性`Requesttype`:

通过`Requesttype`将请求分为了三种:

 `Requesttype`为`1` ,使用`CyberoamAjaxHelper`处理; `Requesttype`为`2` ,使用`CyberoamCustomHelper`处理; `Requesttype`为其他值时,使用`generateAndSendOpcode`处理。

进入`CyberoamCustomHelper`之后,通过匹配`mode`值进行分发,将会进入`WebAdminAuth`类中进行处理:

`process`函数对请求中的JSON字段进行解析,获取`jsonObject`之后将会给 `cscClient`通过`localhost:299`发送给CSC进程进行处理:

比较有意思的是Sophos通过`cscClient`返回的Status code判断是否登录成功,因此在Java代码中是看不到登录认证的完整过程的,如下图所示如果返回为`200`则会生成合法`sessionBean` :

合法`sessionBean`生成过程如下,将`session`中填充`username` ,`userid` , `csrftoken`等关键信息:

因此我们只需要找到后台返回`200`的函数即可。

CSC Perl API 分析

因为webadmin login mode151的`Requesttype`为`2` ,在`_send`函数最后获取返回值的时候使用`getStatusFromResponse`进行解析:

使用`eventBean`判断`Requesttype` ,因为该`eventBean`在数据包刚开始处理的时候就根据`mode` 值在数据库中进行搜索匹配,中间没有修改的可能性。在如下`else`分支中需要获取 JSON 结果的`status`字段:

因此在寻找可返回`200`的`mode`值时需要考虑的是要能够同时返回`status` 字段,通过搜索数据库找到所有`Requesttype`为`2`的`OPCODE` ,其中有 `716`符合条件。注意Perl代码获取了`request`中的`accessaction`字段,并且需要该字段为`1`才能返回`200` 。

漏洞复现

通过前面的分析,我们很容易构造特殊的JSON数据包实现认证绕过。如果数据包中返回`status`的为`200` ,并且`redirectionURL`路由为`index.jsp`即为认证成功,直接取`Set-Cookie`中的`JSESSIONID`进行使用:

在未登录条件下在浏览器中添加上述认证后返回的`session`,操作如下:

替换浏览器中的`JSESSIONID`,并在url处输入`index.jsp`回车进行跳转:

小结

通过漏洞还能够获取`admin`操作权限,因此可以向固件中添加恶意代码,然后通过上传固件的方式实现命令执行,或者通过修改配置等方式进入底层,方法有很多种不再详细分析。

通过分析复现CVE-2022-1040认证绕过漏洞,学习了一种新的认证绕过思路,通过不同语言对JSON或者其他格式的数据处理上的差异实现变量覆盖,完成漏洞利用。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容