项目地址:https://github.com/jishenghua/jshERP/releases/tag/2.3

环境搭建:

MySQL 5.7.26,IDEA,Maven 3.9.1,JDK 1.8,

数据库新建jsh_erp数据库,导入sql文件

IDEA 的 JDK 版本切换为1.8

Maven构建

运行 ErpApplication.java 启动程序

目录分析

这个项目的结构更像是 MVC 架构( Mapper/MapperXML; Controller ),又增加了 Service 层 。

  • Maven Assembly 插件,用于帮助打包; assembly.xml 是打包的配置文件
  • bin 中的文件都是 jshERP 的运行脚本
  • resources/mapper_xml: MyBatis 框架的 SQL 映射配置文件, “数据库操作说明书”
  • logback-spring.xml:日志文件输出配置
  • java
    • config
      • PluginBeanConfig.java 插件管理器 Bean
      • PluginConfiguration.java 配置插件系统的运行环境和参数
      • Swagger2Config.java 用于生成 RESTful API 文档 , 提供文档元信息
      • TenantConfig.java 项目数据库访问的统一拦截器和插件配置中心
      • WebConfig.java 指定前端静态文件存放位置, 在 Spring Boot 内置 Web 服务器中生效
    • constants
      • BusinessConstants 业务字典类
      • ExceptionConstants 异常与返回码管理类
    • controller Web 层接口
    • datasource 数据源和数据库访问配置
    • exception 异常处理
    • filter
      • LogCostFilter 自定义 Servlet 过滤器, 控制用户访问权限和登录请求处理
    • service 业务逻辑层
    • utils 工具类
    • ErpApplication.java 入口类,参考SpringBoot-注解 @SpringBootApplication 分析

代码审计

1、LogCostFilter.java

过滤逻辑:

  • 放行:
    • Session 中有 user
    • 访问 /doc.html /register.html /login.html
    • ignoredList 静态资源
    • allowUrls /user/login /user/registerUser /v2/api-docs
  • 其他全部重定向到 /login.html
  • 缺陷:
    • 虽然有白名单,但路径匹配不完整,如果在 url 中构建如:1.css/../index.html doc.html/../index.html 等,就会绕过

漏洞利用:

payload:

注意:

这抓包中有时会抓到这样一行数据: If-Modified-Since: Tue, 05 Jan 2021 22:51:28 GMT

这是浏览器的本地缓存,如果这个资源自这个时间点之后没有修改,就返回304

把这个去掉就可以正常注入了

2、pom.xml - Maven 依赖

2.1 fastjson-1.2.55-反序列化漏洞

入口点:parseObject

**全局搜索 parseObject **,找一个带有可控变量的点

找到 src/main/java/com/jsh/erp/utils/StringUtil.java 中有利用点

分析链:


跟进 JSONObject.parseObject

跟进 parse(String text)

跟进 parse(String text, int features)

跟进 parse(String text, ParserConfig config, int features)

跟进 DefaultJSONParser

lexer(词法分析器)与 parser(语法分析器)

https://blog.csdn.net/buguge/article/details/147525215

**词法分析器(Lexer)和语法分析器(Parser)**是两个核心组件,它们协同工作将原始输入(如JSON字符串、代码文件)转换为结构化数据(如对象、抽象语法树)

返回一步,Object value = parser.parse(); 跟进 parse()

跟进 parseObject

在这个类中找我们需要的处理 @type 的部分,搜索 checkAutoType

跟进 checkAutoType

来到这个类中,按照之前版本的经验,下一步有个 TypeUtils.loadClass ,继续搜索

跟进 TypeUtils.loadClass

下一步执行序列化和反序列化

在执行反序列化的过程中,调用实例化的类,执行后续命令

接下来应该找调用 StringUtil#getInfo 方法的地方

看了参考文章找到 UserComponent

找 getUserList 的调用地方,这时应该找的是控制器里的方法了

几个踩坑的地方:

我们要注意,真正应该调用的是 getUserList(Map<String, String> map),而上面的都不是正确调用

找到真正调用 getUserList(Map<String, String> map) 的地方


小结利用链:

payload

@type:指定 fastjson 要实例化的类, val:这个类的某个字段值

Inet4Address 只能解析 IP/域名,不能触发 LDAP 请求

断点调试:

调试过程基本和“分析链”中一致,

通过dnslog探测fastjson的几种方法(java.net.Inet4Address、Inet6Address、InetSocketAddress,url)

https://blog.csdn.net/Adminxe/article/details/105918000

总结:

2.2 log4j (不存在)

并没有导入 log4j-core 包,单独的 log4j-to-slf4j 是不存在漏洞的

log4j-to-slf4j 是一个 桥接器(bridge),它把 Log4j 2 API 的调用转发到 SLF4J,由 SLF4J 来真正打印

2.3 MyBatis CVE-2020-26945(不存在)

漏洞点分析:

SerializedCache#deserialize()

跟进 delegate.getObject

getObject ctrl+alt+左键 转到声明

跟进到 ScheduledCache

利用条件

  1. 用户启用了二级缓存功能

二级缓存其实就是将查询的结果,放入缓存中,下次查询相同的条件时,直接从缓存中获取结果,降低sql服务器的压力

  1. 攻击者可以修改缓存的内容,替换为恶意反序列化数据
  2. 用户未设置JEP-290过滤,且没有任何防御反序列化攻击的措施

JEP-290 是从 Java 9 开始引入的,在 Java 8 里 不存在全局或类级序列化过滤器 的机制

由于找不到可修改的缓存内容,这部分就作为漏洞学习一遍,本系统不存在此漏洞

接下来的审计按照以下方向进行:

  1. SQL 注入
  2. 文件安全 (在翻找功能点过程中并没有发现有能上传&读取文件的点)
  3. 身份验证&鉴权 (LogCostFilter.java)
  4. 第三方组件&依赖 (已分析)

3、Mybatis SQL 注入

Mybatis框架的sql注入关注${}:

${}用于直接替换SQL语句中的占位符,而#{}用于预编译

  1. like模糊查询
  1. 动态列名 / 表名
  1. Order By
  1. IN

AccoutMapperEx.xml

接下来考虑Controller/Service 中是否传入了 name 参数

跟进 select,

从请求参数 map 中解析搜索条件

寻找调用 getAccountList() 的位置

跟进 select

select(String apiName, Map<String, String> parameterMap) 是整个查询模块的统一入口

继续跟进,

接下来去找对应的功能点,可以发现,在“基本资料”中都是查询接口,而现在我们需要的是“结算账户”的查询接口,其他的接口也应该存在sql注入,之后查看。

对“结算账户”的查询点抓包后,正好对应刚才源码中看到的几个参数“name, serialNo, remark”:

对 name 参数进行sql注入:

从结果来看时间盲注成功了

在Mybatis的日志中, 可以清楚看到 SQL 注入点已经被利用,并且 时间盲注生效:

DepotMapperEx.xml

通过上述AccoutMapperEx.xml的审计,可以确定该系统中有多个类似的sql注入点,他们的流程都是一致的。

再以DepotMapperEx.xml 为例:

小结:

通过全局搜索到like '%${name}%',定位 <select id="selectByConditionXxxxx",之后便在 controller 和 service 中找对应的文件名,在其中找到 selectByConditionXxxxx (基本上都在service中),找到之后会发现 selectByConditionXxxxx 中所需的几个参数,都是来自 select 查询,跟进 select ,找到 getXxxList 方法解析 select(Map<String,String> map) 传入的前端请求参数,跟进 select,发现他是统一接口,通过 apiName 动态调用不同查询。接下来就在前端找对应的功能点,在”基本资料“里可以看到有查询功能,通过抓包或者对应名称来确定需要的接口,例如:GET /account/list?search={"name":"1","serialNo":"2","remark":"3"}&currentPage=1&pageSize=15 HTTP/1.1,name、serialNo、remark 都是对应的 AccoutMapperEx 的参数,那么就在 name 参数注入。

同样的漏洞有:

  • AccoutMapperEx.xml
  • DepotMapperEx.xml
  • LogMapperEx.xml (功能点在“系统管理”-“日志管理”)
    • payload : 111’ OR SLEEP(5) OR ‘1’=‘1
  • MaterialMapperEx.xml (功能点在“商品管理”-“商品信息”)
  • PersonMapperEx.xml (功能点在“基本资料”-“经手人管理”)
  • RoleMapperEx.xml (功能点在“系统管理”-“角色管理”)
  • UnitMapperEx.xml (功能点在“商品管理”-“计量单位”)
  • UserMapperEx.xml (功能点在“系统管理”-“用户管理”)

4、身份验证&鉴权

这一部分依旧是 LogCostFilter.java 发现的漏洞点,在SQL 注入中,我们登录到后台进行的注入,那么结合鉴权漏洞,就可以未登录进行SQL注入。

从图片看到,绕过身份验证也可以进行SQL注入

漏洞复现:

1、存储型XSS

很多功能点都存在此漏洞,以下举三例:

1.1 用户管理

先新增一个用户

修改用户名

出现弹窗

找到源码:

接收前端传入的 info 参数,检查用户数量是否超限,并没有做任何特殊字符过滤,这里就是XSS 的入口点

1.2 商品信息

增加商品:

五个地方都会弹窗

1.3 收入单

参考文章:

通过dnslog探测fastjson的几种方法(java.net.Inet4Address、Inet6Address、InetSocketAddress,url)

https://blog.csdn.net/Adminxe/article/details/105918000

CVE-2020-26945 mybatis二级缓存反序列化的分析与复现

https://www.freebuf.com/vuls/251862.html

MyBatis远程代码执行漏洞CVE-2020-26945

https://www.freebuf.com/articles/web/252542.html

Java 代码审计之华夏 ERP CMS v2.3

https://www.freebuf.com/articles/web/347135.html

【Java代码审计】华夏-ERPv2.3

https://lusensec.github.io/2024/10/20/Code-Audit-%E5%8D%8E%E5%A4%8F-jshERP/index.html