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

声明

出品|且听安全(ID: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的配置文件,通过分析`http-d.conf`,了解Apache引用了`/cfs/web/apache/httpd.conf`配置:

Define userportal_listen_port 65004
Define webconsole_https_port 65003
Define 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/webconsole
ProxyPass /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/perl
use 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 mode 151 的`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或者其他格式的数据处理上的差异实现变量覆盖,完成漏洞利用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值