分享

记一次代码审计中发现的几个漏洞

 zZ华 2023-07-15 发布于广东

扫码领资料

获黑客教程

免费&进群

图片

0调试环境配置

该项目由springboot开发,我一开始是想在源码中debug,但是有些maven依赖的jar包在maven的中央仓库还没上传,导致不能用源码运行,后边用他们在docker中部署的环境来运行,这里介绍一下调试docker中jar文件的方式,因为我自己之前不会,就记录一下
1.在docker desktop中找到jar文件位置,保存到本地(有源码这一步就不用了)

图片

2.Inspect中找到jar的路径

图片

3.新建个java项目将保存的jar添加到依赖中(选中jar文件,右键Add as Library)
4.解压jar包放到项目根目录,然后右键BOOT-INF下的lib目录选择Add as Library将这些jar全部添加到依赖中

图片


5.将要调试的class文件夹添加到依赖关系中,这里就是BOOT-INF目录下的所有文件

图片

6.在idea配置调试环境,添加一个“Remote JVM Debug”,配置端口,选择对应jdk版本,添加项目类文件

图片

7.复制要调试的jar包的docker启动命令

图片

8.在复制好的docker启动参数末尾追加如下参数:-jar后面的jar包路径就是第一步中得到的docker中jar包的路径,
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 -jar /opt/apps/backend-1.18.4.jar

9.在复制好的docker启动参数-p 8091:8081后追加如下参数:通过5005端口进行调试通信
-p 127.0.0.1:5005:5005

10.使用追加了参数的docker命令启动docker镜像

图片

11.最后点击debug即可正常下断点调试jar代码

图片

12.如果有源码,也可以像第三步中一样,直接在idea设置debug后,也可以正常debug

1查看通用配置

1、审计前先看下配置文件、过滤器、拦截器、shiro这些通用配置。
系统用了一个过滤器,过滤器通过实例化SqlFilter类实现,使用了/*让所有请求经过过滤器处理

图片

2、过滤器的主要代码放在doFilter方法,看看这个过滤器doFilter方法做了什么,先将ServletRequest转为HttpServletRequest,再传入XssAndSqlHttpServletRequestWrapper类的构造器进行实例化,再通过checkXSSAndSql校验请求参数

图片

3、如果直接就在过滤器里把Request对象的参数拿处理校验,那么后续的Controller将得到一个“失效”的Request对象,后面的业务逻辑就会全线垮掉……,所以需要将Request对象进行包装,这个XssAndSqlHttpServletRequestWrapper类继承了HttpServletRequestWrapper,通过HttpServletRequestWrapper先将Request对象包装,包装后在XssAndSqlHttpServletRequestWrapper中对参数进行读写校验,而不是直接操作Request对象的参数

图片

4、checkXSSAndSql方法用于校验请求参数是否包含xss和SQL注入的有关字符串,可见过滤器中配置了xss和sql注入的校验防护,但这种黑名单的方案说不好就会被绕过

图片

漏洞1-上传功能

5、现在来看Controller层的代码,经过过滤器处理,请求放行后就来到Controller层的具体业务逻辑中,看看上传文件的Controller

图片

6、进入Service层看下upload方法具体怎么处理文件,发现并没有校验文件合法性

图片

7、尝试去上传一个html去执行xss

图片

漏洞2-不安全的直接对象引用

8、查看删除应用的Controller,接口没有使用AOP,也没有对用户身份做校验,属于不安全的直接对象引用,任何用户都有权限删除

图片

8、直接根据路径变量中传入的appTemplateId去删除应用

图片

图片

9、使用普通用户登录,调用/delete/{appTemplateId}传入应用id删除应用

图片

10、使用管理员登录,发现管理员之前创建的应用已被普通用户删除

图片

漏洞3-越权

1、查看更新应用信息的Controller(系统的应用只有管理员可以编辑,普通用户看不到这个功能,如果任意用户都可以编辑,系统就会变得没有规范,乱套了),接口配置了AOP注解,通过AOP来鉴权

图片

2、AOP (Aspect Orient Programming):面向切面编程,AOP的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。AOP可以拦截指定的方法,并且对方法增强,比如:事务、日志、权限、性能监测等增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离

3、这里使用的是注解方式,即在需要使用切面管理的方法上配置AOP注解(这里就是@DePermissions注解),AOP注解方法的定义需要声明@Retention、@Target,表示这是一个AOP注解

图片

4、然后在切面类上方使用@Aspect注解声明这个是切面类,用于切面编程,为系统中添加了@DePermission注解的业务方法添加通用功能

图片

5、在切面类中就可以定义切面方法,最常用的就是环绕通知,用于在目标方法(添加了AOP注解的方法)执行前、执行过程中、执行后为其添加增强方法。@Around声明这个方法是环绕通知,他的value属性的值就是上方声明的AOP注解的全限定名

图片

6、来看看这个aop方法做了什么,首先调用AuthUtils.getUser().getIsAdmin()判断当前用户是不是管理员,进入看看是怎么判断的

图片

7、从USER_INFO这个常量中用get方法获取CurrentUserDto对象,

图片

8、CurrentUserDto继承SysUserEntity,是用户实体类,包含了用户的完整身份信息

图片

图片

9、查看这个USER_INFO常量,ThreadLocal,他是一个线程局部变量的值,当用户发送一个请求过来,这个请求就是一个线程,线程局部变量中存在了CurrentUserDto对象,就可以从请求中拿到CurrentUserDto对象,再通过CurrentUserDto对象的getIsAdmin方法返回一个Boolean值判断是不是管理员

图片

图片

10、如果是管理员,说明权限是够的,就会调用point.proceed()执行原始目标方法

图片

10、如果不是管理员,就要继续鉴权,这里我把代码意思写在注释上,方便看

图片

11、判断AOP注解的属性logical的值是不是AND,指一个逻辑条件,只有当两个AOP注解都满足条件才能访问原始目标方法

图片

12、我们回到原始目标方法的AOP注解,他的AOP注解的属性logical是AND,也就是这里 @DePermissions 中的两个 @DePermission 都要满足条件才能访问update方法

图片

13、回去看AOP的切面代码,会调用access方法依次处理这两个 @DePermission

图片


13、最后会根据access方法的返回结果来判断有没有权限调用原始目标方法(update方法)

图片

14、access方法的返回类型是Boolean,他接收了三个参数:被AOP注解的方法的参数列表、AOP注解、0 。access方法中,先获取了AOP注解的type、value、requireLevel这几个属性的值

图片

15、然后调用AuthUtils.permissionByType(type),这个方法中,返回了当前用户可以操作的type对应的数据

图片

15、再用stream流去过滤返回的数据中,level大于等于@DePermission上配置的所要求的level属性值的数据

图片

16、回到原始目标方法update上配置的注解,可以看到@DePermissions中的第一个@DePermission注解没有声明level属性值,

图片

17、如果没有声明,level的默认值是1

图片

18、普通用户的level值是满足这个level的,但是这里要同时校验两个DePermission,另一个DePermission配置的level属性值是5,普通用户的level小于这个level,也就是说,普通用户是没有权限调用这个update方法的

图片

19、再看access方法的代码,如果传入的Object为空,则直接返回true

图片

20、于是使用level为1的普通用户去调用更新的接口,只传入id指定应用,不传入pid,pid对应的注解就会为空,即可绕过权限校验去更新应用

图片

漏洞4-权限绕过

1、来看一个查看用户信息的接口,同样是配置了AOP来鉴权,要求的level级别是3

图片

2、用普通用户去调用会提示权限不够

图片

3、看看他的AOP切面代码中是如何做的鉴权,AuthUtils.permissionByType(type)方法前的代码逻辑在第三个漏洞中已经分析过,在permissionByType方法中,获取这个用户对应的level级别可以查看的用户信息

图片

4、普通用户的level级别为1,调用不了level 3 的接口,所以调用api/user/userGrid/1提示未授权

图片

5、但是在最后返回的时候自动增加了一个level为3,值为0的数据集

图片

6、这个值为0的数据集level为3,可以调用到这个原始目标方法

图片

7、此时requireLevel是3,会提取出level大于等于3的数据集,判断请求过来的/userGrid/{datasetId} 的datasetId在不在level大于等于3的数据集中,而值为0这个datasetId刚好能满足条件

图片

8、于是通过/api/user/userGrid/0可以实现权限绕过获取到所有用户信息

图片

9、后边研发也反馈要增加一个level为3值为0的数据集是有其他的业务意义,这个接口之后也被删除

漏洞5-sql注入

1、由于使用的是mybatis框架来支持curd,可以直接搜索xml文件中的${,${}拼接的sql,如果sql参数可控,可能就存在注入风险,

图片

2、但这样做可能有遗漏,因为有的sql语句使用的是sql注解直接把sql语句写在了代理接口方法上方,审计的时候两种情况都看看

图片

3、找到order by的字段sort是拼接

图片

4、往上找到代码接口searchOne被调用的地方

图片

5、找到servie层中实现了searchOne接口后被调用的位置

图片

图片

6、找到Controller中对Service方法getWithPermission的调用

图片

7、发现只有一个路径变量参数id,sort不可控,只能换一个了

图片

8、按照上述方式找其他的调用链中sort可控的Controller,找到一个PanelTemplateRequest入参的Controller

图片

9、其中sort参数可控

图片

10、确定sort可控后,尝试注入,因为一开始分析过滤器代码时,发现有对sql关键字的黑名单检验

图片

11、尝试找到黑名单意外的函数去执行,比如这个GTID_SUBSET就没在黑名单中

图片

原文地址:https://xz.aliyun.com/t/12681

声明:⽂中所涉及的技术、思路和⼯具仅供以安全为⽬的的学习交流使⽤,任何⼈不得将其⽤于⾮法⽤途以及盈利等⽬的,否则后果⾃⾏承担。所有渗透都需获取授权

@
学习更多渗透技能!体验靶场实战练习

hack视频资料及工具

图片

(部分展示)

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多