前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >springboot代码审计学习-newbeemall审计

springboot代码审计学习-newbeemall审计

作者头像
pankas
发布2022-11-20 15:59:52
4.8K0
发布2022-11-20 15:59:52
举报

前言

参考 @s31k3 师傅的 java SpringBoot框架代码审计 ,本文仅复现这位师傅的教程,用于学习springboot代码审计,特此笔记,原文请关注 @s31k3

环境搭建

审计的项目是github上 9.5k start的开源项目 newbee-mall。由于最新版的项目已修复多个漏洞,本文使用的是 Oct 17, 2019 的版本,项目地址 https://github.com/newbee-ltd/newbee-mall/tree/36807c87d13ee9ca08aff75197063b8836d8711d。

基础配置

使用IntelliJ IDEA打开项目文件夹,配置好SDK后IDEA会以maven项目打开并自动下载依赖包。

Spring属性文件路径:/src/main/resources/application.properties,其中可修改端口和mysql数据库地址

代码语言:javascript
复制
server.port=8089
...
spring.datasource.url=jdbc:mysql://localhost:3306/newbee_mall_db?...

配置文件路径:/src/main/java/ltd/newbee/mall/config/NeeBeeMallWebMvcConfigurer.java,其中配置了图片路径

代码语言:javascript
复制
public void addResourceHandlers(ResourceHandlerRegistry registry) {
	registry.addResourceHandler("/upload/**").addResourceLocations("file:" + Constants.FILE_UPLOAD_DIC);
	registry.addResourceHandler("/goods-img/**").addResourceLocations("file:" + Constants.FILE_UPLOAD_DIC);
}

此时是没有测试数据的,需要将/src/main/resources/upload.zip压缩包中的测试商品数据解压出来,放到任意的目录中。此处作为学习测试使用,可以直接解压在当前路径下,正式系统中一定要存放在非项目路径下。

在文件src/main/java/ltd/newbee/mall/common/Constants.java中,变量FILE_UPLOAD_DIC为当前上传图片路径,将其更改为我们解压upload.zip绝对路径

代码语言:javascript
复制
public class Constants {
	//上传文件的默认url前缀,根据部署设置自行修改
	public final static String FILE_UPLOAD_DIC = "/some_path/src/main/resources/upload/";
	//public final static String FILE_UPLOAD_DIC = "D:\\upload\\";//上传文件的默认url前缀,根据部署设置自行修改
}

数据库配置

/src/main/resources 目录下有数据库文件 newbee_mall_schema.sql 。利用该文件创建数据库。

笔者的数据库是用直接docker pull下来的。

代码语言:javascript
复制
docker cp ./newbee_mall_schema.sql container_id:/root

(container_id为mysql容器名)

进入MYSQL容器创建数据库

代码语言:javascript
复制
root:/# mysql -u root -p
mysql> create database newbee_mall_db;
mysql> exit

执行sql文件

代码语言:javascript
复制
root:/# mysql -u root -p newbee_mall_db</root/newbee_mall_schema.sql

此时数据库成功配置完毕

启动项目

如上文配置完毕后即可使用IDEA启动项目

image-20221118211239599
image-20221118211239599

springboot项目结构

参考 https://s31k31.github.io/2020/04/26/JavaSpringBootCodeAudit-2-SpringBoot/

request-path
request-path
request-example
request-example

SQL注入

该项目GitHub中的第一个issue就是有关 SQL 注入的漏洞的 https://github.com/newbee-ltd/newbee-mall/issues/1

项目的搜索框中输入 1' 发现报错

image-20221118212438097
image-20221118212438097

回到IDEA中查看报错信息

代码语言:javascript
复制
### The error may involve ltd.newbee.mall.dao.NewBeeMallGoodsMapper.findNewBeeMallGoodsListBySearch-Inline
### The error occurred while setting parameters
### SQL: select               goods_id, goods_name, goods_intro,goods_category_id, goods_cover_img, goods_carousel, original_price,     selling_price, stock_num, tag, goods_sell_status, create_user, create_time, update_user,      update_time             from tb_newbee_mall_goods_info          WHERE  (goods_name like CONCAT('%','1'','%') or goods_intro like CONCAT('%','1'','%'))                                  limit ?,?
### Cause: java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1'','%')) 
         
         
            limit 0,10' at line 8
; bad SQL grammar []; nested exception is java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1'','%')) 
         
         
            limit 0,10' at line 8] with root cause

java.sql.SQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '1'','%')) 
         
         
            limit 0,10' at line 8

根据报错信息可以得到这样的payload

代码语言:javascript
复制
/search?keyword=1')) OR 1%23

可以发现回显了所有的商品

image-20221118212832929
image-20221118212832929

使用sqlmap也能轻松检测出来

image-20221118213033306
image-20221118213033306

导致sql注入的原因

在Mybatis的配置 NewBeeMallGoodsMapper.xml 中发现

image-20221118214431414
image-20221118214431414

这里获取到用户传入的 keyword 后使用了 {keyword} 来接收用户传来的参数。而{} 仅仅是纯粹的 string 替换,在动态 SQL 解析阶段将会进行变量替换,类似于直接替换字符串,会导致SQL注入产生。

MyBatis官方文档中有如下叙述:

#{}告诉 MyBatis 创建一个预编译语句(PreparedStatement)参数,在 JDBC 中,这样的一个参数在 SQL 中会由一个“?”来标识,并被传递到一个新的预处理语句中,就像这样:

代码语言:javascript
复制
// 近似的 JDBC 代码,非 MyBatis 代码...
String selectPerson = "SELECT * FROM PERSON WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectPerson);
ps.setInt(1,id);

同时官方文档也对 ${} 给出警示:

提示 用这种方式接受用户的输入,并用作语句参数是不安全的,会导致潜在的 SQL 注入攻击。因此,要么不允许用户输入这些字段,要么自行转义并检验这些参数。

有关更多 Mybatis 的sql注入参考 https://s31k31.github.io/2020/05/01/JavaSpringBootCodeAudit-3-SQL-Injection/#SQL%E6%B3%A8%E5%85%A5%E4%BB%A3%E7%A0%81%E5%AE%A1%E8%AE%A1

XSS

第一处

首先黑盒测试下

image-20221119141854294
image-20221119141854294

搜索框这里并没有XSS,到源码中去看看

image-20221119142110556
image-20221119142110556

可以看到后端并没有对传入的 keyword 做任何处理。但确实是没有XSS。同时审计其他地方也未发现有任何的过滤或替换。但这里没有XSS成功,原因是项目使用了 thymeleaf 模板来渲染,模板自带有字符转义的功能。

这里对搜索的字符串显示使用了 th:text

image-20221119142602873
image-20221119142602873

输出的是转义后的字符串。

想要得到未经过模板转义后的字符串通过 th:utext 来实现,全局搜索下

找到了这两处

image-20221119142840555
image-20221119142840555

第一处在 detail.html 中。是显示商品的介绍。

这里只能通过admin面板的商品介绍处修改

image-20221119143709264
image-20221119143709264

抓包修改标签

image-20221119143830321
image-20221119143830321

前台该商品的介绍成功触发XSS

image-20221119143928286
image-20221119143928286

第二处

在 newbee_mall_goods_edit.html 中也有一处 thymeleaf 的 th:utext 的标签,是编辑商品介绍这里

这一处和上面是一样的,都是商品介绍 这部分的XSS(都需要管理员操作才行,危害不大)

第三处

继续黑盒测试,尝试修改个人订单中的收货地址并提交订单

image-20221119151806853
image-20221119151806853

当前页面没有效果,到管理员面板的订单管理页面发现

1111
1111

可以获取到管理员的cookie。危害较大。

具体查看源码可以发现这部分收货地址的显示是收到可一份json数据,然后前端将数据写入到浏览器html页面,导致xss。后端未对用户传入的收货地址信息进行合法性校验。

image-20221119153956250
image-20221119153956250

本项目还有其他基础XSS,就不依次列举了

越权

该项目是使用 interceptor(拦截器)根据URL路径做访问控制的 具体参考 https://s31k31.github.io/2020/05/04/JavaSpringBootCodeAudit-5-IDOR/ 笔者只是参考这位师傅复现。

springMVC的工作流程

img
img

首先看他的拦截器是如何处理的

Interceptor配置在/src/main/java/ltd/newbee/mall/config/NeeBeeMallWebMvcConfigurer.java中,针对url路径设置了不同的interceptor。

addPathPatterns表示其中的路径会经过设置的拦截器,excludePathPatterns则不过该拦截器。其中两个星**表示匹配任意字符,如果出现一个*则表示匹配单个路径

image-20221119160109414
image-20221119160109414

Spring中拦截器的分配是由DispatcherServlet来分配的,也就是根据ServletPath来分配路径,和getRequestURI无关,也就是不管怎么使用../来做路径穿越,最终得到的还是ServletPath。如果使用类似%00空字符进行截断路径,会使得DispatcherServlet无法将请求分配到正确的Controller,导致请求无效。

所以后面对请求的路由进行判断是应该使用 getServletPath() 来获取最后真正分发到路由地方的 path

getRequestURI() 只是获取了请求的 URI

而该项目对 admin 的拦截判断是这样处理的

image-20221119160438878
image-20221119160438878

使用了 getRequestURI() 来获取URI来判断URI是否以 /admin 开头,如果是 /admin 开头则校验 session,不是则不用校验。

这里存在很明显的越权漏洞,参考 https://joychou.org/web/security-of-getRequestURI.html

可以使用 //admin/index/..;/admin 直接越权成为管理员。

image-20221119160924071
image-20221119160924071
image-20221119160939718
image-20221119160939718

ps:目前较新版的spring和tomcat已经修改了对 / ; 等特殊字符的匹配规则,该漏洞在新版中可能无法利用。

简单调试下看看他是怎么个回事。

image-20221119161326175
image-20221119161326175

可以看到访问过去取到的 URI 是 //admin ,不满足以 /admin 开头的条件,直接返回 true 了。上面也提到过spring根据ServletPath来分配路径,tomcat 服务器在解析 ServletPath 时会对 / ; 等特殊字符进行处理。

image-20221119162702611
image-20221119162702611

像上面的 //admin 经过处理后其ServletPath/admin,那么直接就去找 /admin 的路由了,同时拦截器也给出的时 true ,从而造成越权。实际上这个越权漏洞就是开发者使用的函数不当,造成和服务器解析差,从而产生问题。

水平越权

审计代码,在修改用户信息这里

ltd/newbee/mall/controller/mall/PersonalController.java:114

image-20221119170701015
image-20221119170701015

查看下 updateUserInfo() 的实现,

ltd/newbee/mall/service/impl/NewBeeMallUserServiceImpl.java:72

image-20221119171227788
image-20221119171227788

发现这里从数据库查找用户并未用到 session ,而是直接以传递过来的 userId 为参数来查找并修改数据的,所以这里存在水平越权漏洞,修改 userId 便可修改其他用户的信息。

目标用户 userId 为 11

image-20221119172033334
image-20221119172033334

我们通过另一个用户来修改 userId为 11的用户信息

image-20221119172240231
image-20221119172240231
image-20221119172315856
image-20221119172315856

可以看到成功修改了 userId 为11的用户的信息。

另一处在查询订单这部分。

ltd/newbee/mall/controller/mall/OrderController.java:36

image-20221119173329462
image-20221119173329462

这里我们跟进看下 getOrderDetailByOrderNo() 的实现

ltd/newbee/mall/service/impl/NewBeeMallOrderServiceImpl.java:252

image-20221119173641451
image-20221119173641451

可以发现这里数据库查询所使用的 订单号 并不是上面通过session获得的订单号,而用的是用户传入的 orderNo。

这里用户 userId 为11 的用户下的单

image-20221119173943212
image-20221119173943212

其他用户也可以直接访问

image-20221119174129604
image-20221119174129604

CSRF 和 逻辑漏洞

笔者已复现,懒得写了。反正也是抄来的

参考

https://s31k31.github.io/2020/05/05/JavaSpringBootCodeAudit-6-CSRF/

https://s31k31.github.io/2020/05/06/JavaSpringBootCodeAudit-7-Logical-Vulnerability/

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2022-11-18,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 环境搭建
    • 基础配置
      • 数据库配置
        • 启动项目
        • springboot项目结构
        • SQL注入
        • XSS
          • 第一处
            • 第二处
              • 第三处
              • 越权
                • 水平越权
                • CSRF 和 逻辑漏洞
                相关产品与服务
                云数据库 MySQL
                腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档