从某堡垒机FastjsonRCE浅谈Fastjson漏洞利用
For82 发表于 四川 漏洞分析 1939浏览 · 2024-01-31 10:05

路由分析

查看web.xml配置文件,可以发现程序基于spring MVC框架 ,通过DispatcherServlet 会加载指定的配置文件,其中/WEB-INF/spring-servlet.xml中。在Spring框架中,DispatcherServlet扮演前端控制器的角色,负责接收HTTP请求并将其转发到相应的处理器。spring-servlet.xml文件中的配置就是告诉DispatcherServlet如何进行这些操作。

查看spring-servlet.xml配置文件,可以发现其中定义了不少拦截器,程序主要的鉴权逻辑都定义在拦截器中。这里可以发现处理是否登陆的拦截器,拦截所有/3.0路由下的请求,同时对于/3.0/authService/和/3.0/fileService/download/rootCert,/3.0/fileService/downloadPublic/的请求不进行拦截。

也就是这些接口我们是可以未授权访问的,这也是后续利用的一个重要点。

反序列化漏洞点寻找

Fastjson反序列化漏洞成因,简单说就是json字符串在反序列化时,parse触发了set方法,parseObject同时触发了set和get方法,由于存在这种autoType特性。如果@type标识的类中的setter或getter方法存在恶意代码,那么就有可能存在fastjson反序列化漏洞。

寻找漏洞点,就是寻找将 json 反序列化为类的方法。将 json 数据反序列化时常使用的方法为parse()parseObject()parseArray(),这三个方法。其中也有若干重载方法,简单介绍一些常用的。

常见方法

parse()常见的重载方法:

1、parse(String text): 解析JSON字符串,返回一个Object类型,通常是JSONObjectJSONArray

Object object = JSON.parse(jsonString);

2、 parse(String text, Feature... features): 解析JSON字符串,使用指定的反序列化特性。

Object object = JSON.parse(jsonString, Feature.AllowComment, Feature.AllowSingleQuotes);

3、 parse(InputStream is, Charset charset, Feature... features): 从InputStream解析JSON数据,使用指定的字符集和反序列化特性。

Object object = JSON.parse(inputStream, StandardCharsets.UTF_8, Feature.AllowComment);

parseObject 方法常见的重载方法:

4、parseObject(String text, Class<T> clazz): 将JSON字符串解析成指定类的对象。

MyType obj = JSON.parseObject(jsonStr, MyType.class);

5、parseObject(String text, TypeReference<T> typeReference): 用于解析具有泛型信息的对象,如List<mytype>。</mytype>

List<MyType> list = JSON.parseObject(jsonStr, new TypeReference<List<MyType>>(){});

6、parseObject(String text, Type type, Feature... features): 除了指定要解析到的类型,还可以指定解析的特性。

MyType obj = JSON.parseObject(jsonStr, MyType.class, Feature.AllowComment);

7、parseObject(String text, Type type, ParseProcess processor, Feature... features): 在解析的同时,可以通过实现ParseProcess接口来自定义处理解析过程。

MyType obj = JSON.parseObject(jsonStr, MyType.class, myParseProcess, Feature.AllowComment);

8、parseObject(String input, Class<T> clazz, Feature... features): 允许在解析到指定类的对象时使用特定的解析特性。

MyType obj = JSON.parseObject(jsonStr, MyType.class, Feature.AllowSingleQuotes);

9、parseObject(String input, Class<T> clazz, ParseProcess processor, Feature... features, int featureValues): 提供了更加详细的解析控制,可以指定特性、自定义处理器以及特性值。

MyType obj = JSON.parseObject(jsonStr, MyType.class, myParseProcess, new Feature[]{Feature.AllowComment}, Feature.config(Feature.AllowComment, true));

10、 parseObject(String text, Class<T> clazz, ParserConfig config, Feature... features): 允许传入一个ParserConfig配置对象,进一步自定义解析过程。

MyType obj = JSON.parseObject(jsonStr, MyType.class, parserConfig, Feature.AllowComment);

以上方法涵盖了从简单到复杂的各种JSON字符串到Java对象的解析需求。选择合适的方法可以帮助开发者更准确地控制解析行为,并解决特定场景下的反序列化问题。

parseArray 常见的重载方法:

11、parseArray(String text, Class<T> clazz): 将JSON字符串解析成一个指定类型对象的List集合。

List<MyType> list = JSON.parseArray(jsonStr, MyType.class);

12、parseArray(String text, Type[] types): 解析JSON字符串为一个List集合,其中可以包含多种类型的对象。

List<Object> list = JSON.parseArray(jsonStr, new Type[]{String.class, MyType.class});

当我们打不同利用链时,对于服务端使用的反序列化方法,有时会不同的要求

Fastjson版本确定

在白盒审计中,我们可以通过直接查看lib库确定当前程序的fastjson版本,比如当前程序版本为1.2.38,存在漏洞。

但在黑盒中,无法直接接触代码环境,只能通过盲打测试,网上很多文章这里就不多做解释了。

反序列化点寻找

我们可以搜索parse()parseObject()parseArray(),这三个方法快速定位到存在将json字符串反序列化的方法,再结合路由分析,寻找到未授权的存在反序列化方法点。这里我们通过搜索parseObject方法,可以发现5个存在反序列化的利用点。

其中RequestBodyHandlerInterceptor类中的preHandle方法使用,preHandle方法:这是实现自HandlerInterceptor接口的方法。它在处理Http请求之前被调用。会接收三个参数

  • HttpServletRequest request:代表当前的HTTP请求。

  • HttpServletResponse response:代表当前的HTTP响应。

  • Object handler:处理当前请求的处理器。

parseObject方法调用前,先使用GblPubFunc.isEmptyString(body)方法检查请求体是否不为空。body.toString()将请求头转换成string类型,然后parseObjec将一个JSON格式的字符串转换为一个JSONObject对象,然后getJSONObject提取对象中名为"c"键的字段。当parseObjec执行时我们反序列化的目的已经达成是否存在c键,不影响我们反序列化的利用。

同时我们可以发现RequestBodyHandlerInterceptor类是一个拦截器(Interceptor)(拦截器(Interceptor)在Spring框架中是一个独立于具体业务的组件,它可以被配置为在特定的请求被处理之前、之后或完成之后执行。是否需要登录才能触发拦截器取决于拦截器的配置和它所拦截的URL模式。)在Spring框架中,HandlerInterceptor接口定义了三个方法,允许你在请求的不同点进行拦截,而preHandle方法就是在请求处理之前调用。

这里我们只需要寻找哪些请求会先经过RequestBodyHandlerInterceptor拦截器,同时我们需要注意到在Spring XML配置文件中拦截器的触发顺序会与它们在<mvc:interceptors>元素中被声明的顺序相同。在处理请求的时候,拦截器会按照这个顺序执行它们的preHandle方法,如果所有的preHandle方法都返回true,请求才会继续处理,然后在请求完成后,这些拦截器的postHandleafterCompletion方法将会按照相反的顺序被调用。

所以请求需要通过在RequestBodyHandlerInterceptor中之前定义的拦截器。RequestBodyHandlerInterceptor拦截/3.0/authService/**请求

同时我们可以发现在前面的登陆校验以及授权校验,/3.0/authService/**都被排除在外。

所以任意/3.0/authService/**的请求都会触发RequestBodyHandlerInterceptor拦截器同时进行json的反序列化,我们只需要找到利用链便可以达到未授权RCE的目的。

Fastjson这个反序列化漏洞中原生利用链是使用TemplatesImplJdbcRowSetImpl构造恶意代码实现命令执行,其中TemplatesImpl这个类,他的内部使用的是类加载器,去进行new一个对象,这时候定义的恶意代码在静态代码块中,就会被执行。而JdbcRowSetImpl是需要利用到JNDI注入来实现攻击,需要出网。而Fastjson中使用TemplatesImpl链的条件也比较苛刻,因为在Fastjson中需要加入Feature.SupportNonPublicField(只要在启用 Feature.SupportNonPublicField 特性时,Fastjson 将能够访问和处理那些非公开(non-public,即私有 private 或者受保护 protected)的字段。),也就是前面提到的可以指定解析的特性的重载方法,而这种方式并不多见。

但我们也可以利用非原生利用链,也就是调用在classpath下的其他存在反序列化漏洞的利用链。这里我们可以发现存在c3p0,C3P0是一个开源的JDBC连接池,hibernate自带的连接池就是c3p0。

其中C3P0的利用也分为URLClassLoader链,JndiRefForwardingDataSource类和BeanFactory类,WrapperConnectionPoolDataSource类,其中除了WrapperConnectionPoolDataSource类都属于jndi注入,而BeanFactory是通过当Reference对象的classFactoryLocation为null的时候,就不会加载远程字节码,而是直接加载本地字节码的方式达到不出网。

这里我们直接使用WrapperConnectionPoolDataSource类进行利用,成功执行命令。

网上公开的poc之针对config接口,但通过以上分析/3.0/authService/下所有接口都会执行Fastjson反序列化操作。

0 条评论
某人
表情
可输入 255