CVE-2022-1040复现笔记
sophos
网络配置
网络适配器配置为NAT
利用虚拟机console配置portA网卡为NAT_ip,这样主机就可以和虚拟机通信了。
开启虚拟机ssh功能,修改ssh配置文件:
1 | ssh admin@NAT_IP |
CSC配置文件解包
逆向分析csc,存在解密代码
1 | num1=[0x7F,0x90,0x0F9,0x0AB,0x0E,0x0CE,0x0B2,0x90,0x0AE,0x2F,0x59,0x0F1,0x0D0,0x0AD,0x0B7,0x11,0x7A,0x6A,0x4B,0x54,0x1A,0x9B,0x4D,0x71,0x0C5,0x8F,0x0ED,0x65,0x0D2,0x19,0x5C,0x0CD,0x0E8,0x1A,0x0EE,0x0D9,0x5D,0x1F,0x0F1,0x90,0x0B7,0x8E,0x5D,0x0D7,0x8,0x0E0,0x0C3,0x98,0x0C9,0x65,0x0ED,0x3B,0x0AC,0x0A0,0x0A1,0x0A3,0x0C2,0x0B3,0x0F,0x0DD,0x0EE,0x0A6,0x9A,0x0E9] |
JSON与PERL差异(粘贴参考文章)
漏洞原理可简单理解为 Java 在使用 unicode \\u0000
时,JSON 认为 key
是两个不同的 key
并没有 0
字节截断,当 Java 把含有 unicode 编码的 key
发送给后端的 Perl处理时 \\u0000
产生了截断效果,使得带有 unicode 编码的 key
变为了 mode
, Perl 可以处理重复 key
的 JSON,如果重复则后面覆盖前面的值
我们可以通过一个示例来对比不同语言处理 JSON 重复键的差异性。Java 处理带有 unicode 编码的 key
时可以正常解析:
1 | import org.json.JSONObject; |
Perl 处理 Java 传递过来的零字节字符串就会产生截断效果,在处理相同 key
值的 JSON 时会取最后一个 key
对应的 value
:
1 | #!/usr/bin/perl |
其结果为 a
被覆盖为了最后一个 a
的值:
不同语言对 JSON 解析的差异性是此次认证绕过漏洞的核心原理。Java 接收来自用户的以下数据(其中 \\u0000
后面的 ef
是为了让 JSON中 key
的 hash 排序将 716
排到 151
的后面):
1 | {"mode":151,"mode\\u0000ef":716} |
2024-4-16修改:最近在公众号上看到又有分析这个漏洞的文章,他提到该漏洞是由于两种不同的json解析器的差异导致。以下内容粘贴自参考文献。
主要起作用的是mode
和json
两个参数,jetty侧会通过org.json-20090211
这个库解析json参数传的json数据,通过cscClient.generateAndSendAjaxEvent
发送到CSC做认证,发送前会将mode
参数put
到解析后的json中,如果这里返回的状态码是200或者201,则认为是登录认证通过。
jetty侧的org.json-20090211
这个库对于重复键会抛出异常
运行结果如下
csc使用的json-c
这个库来解析输入的数据,对于重复键,则取后面一个
运行结果如下
并且当json的key中出现unicode空字符时,json-c
对空字符会做截断,但org.json-json
库会保留
运行结果如下
认证绕过漏洞复现
- 环境:VI-18.5.1_MR-1.VMW-326
对比文件,发现在web.xml中添加一段
并且新增一个requestcheckfilter.class
文件,拖入jadx中分析,发现他会检测json是否存在不可打印字符,如果存在,则会重定向到登录界面。
1 | #####################这段没分析明白################################# |
既然过滤了不可显示字符,那应该就是在编码不可显示字符时存在漏洞。
抓取登陆包,分析是如何处理登录包的
在web.xml中发现,可知,/Controller交由cyberoam.corporate.servlets.CyberoamCommonServlet
处理,分析一下这个类
1 | <servlet> |
根据登陆包中mode=151
,在doPost方法中处理mode时,会交给_doPost
处理。这里的mode是直接从登录包中的post数据mode=中获取。
1 | try {... |
在_doPost中,会根据mode值返回eventObject
对象,eventObject
是从数据库中读出来的。
1 | if{...} else { |
跟进去发现是使用jdbc连接postgresql数据库,数据库配置文件如下
1 | public boolean initialize() throws Exception { |
1 | #ConnectionPool.cfg |
可以看见mode=151对应的requesttype=2,因此进入 CyberoamCustomHelper.process(servletRequest, response, eventObject, transactionBean, sqlReader);
完整代码如下
1 | private void _doPost(HttpServletRequest servletRequest, HttpServletResponse response, int mode, SqlReader sqlReader) throws IOException, JSONException { |
继续跟进发现在WebAdminAuth
中处理
1 | else if (151 == eventBean.getMode()) { |
只要返回状态码为200,就会认证成功,并且返回session
跟进cscClient.generateAndSendAjaxEvent
发现会通过299端口交由csc处理,csc又交由perl处理(这块笔者没有准确分析出是如何交由的,大概知道是由perl c api,写在csc里)
当responsetype不等于1时会读取响应包中json的status参数。
也就是说只要找到会返回status200、requesttype=2,responsetype!=1的mode即可。
发现mode=716满足要求,注意accessaction=1
一下图片所示文件存在于/PATH/cscvalidation/system/logindisclaimeraction.pm
,/PATH/cscconf/system/configure.conf
,由csc解包配置文件获得。
POC
1 | POST /webconsole/Controller HTTP/1.1 |
将返回包返回的session替换掉浏览器的返回包并且输入index.jsp即可绕过认证。
问题
- 在csc中没有找到是如何传递给perl执行的代码段
- 处理json时的perl代码
关于第一个问题,在分析csc时看见execute_opcode_request
函数,该函数中会初始化perl并读取json数据转化为perl,猜测handle_perl
函数是对perl数据的处理。
找到了类似exec opcode的代码
猜测是从csc配置文件中读取opcode代码并执行。