JAVA代码审计
MVC模型
MVC模式是一种软件框架模式,被广泛应用在JavaEE项目的开发中。
MVC即模型(Model)、视图(View)、控制器(Controller)。
模型(Model)
模型是用于处理数据逻辑的部分。
所谓数据逻辑,也就是数据的映射以及对数据的增删改查,Bean、DAO(dataaccess object,数据访问对象)等都属于模型部分。
视图(View)
视图负责数据与其它信息的显示,也就是给用户看到的页面。
HTML、JSP等页面都可以作为视图。
控制器(controller)
控制器是模型与视图之间的桥梁,控制着数据与用户的交互。
控制器通常负责从视图读取数据,处理用户输入,并向模型发送数据,也可以从模型中读取数据,再发送给视图,由视图显示。
整体结构
首先要了解项目整体结构。大致了解作者编写逻辑,搞清请求流程。
src/main下面有两个目录,分别是java和resources,java目录中主要存放的是java代码,resources目录中主要存放的是资源文件,比如:html、js、css等。
java目录
annotation:放置项目自定义注解;
controller/:存放控制器,接收从前端传来的参数,对访问控制进行转发、各类基本参数校验或者不复用的业务简单处理等;
dao/:数据访问层,与数据库进行交互,负责数据库操作,在Mybaits框架中存放自定义的Mapper接口;
entity/:存放实体类;
interceptor/:拦截器;
service/:存放服务类,负责业务模块逻辑处理。Service层中有两种类,一是Service,用来声明接口;二是ServiceImpl,作为实现类实现接口中的方法;
utils/:存放工具类;
dto/:存放数据传输对象(DataTransfer Object),如请求参数和返回结果;
vo/:视图对象(ViewObject)用于封装客户端请求的数据,防止部分数据泄漏,保证数据安全
constant/:存放常量;
filter/:存放过滤器。
resources目录
mapper/:存放Mybaits的mapper.xml文件;
static/:存放静态资源文件目录(Javascript、CSS、图片等),在这个目录中的所有文件可以被直接访问;
templates/:存放模版文件;
application.properties或application.yml:Spring Boot默认配置文件。
URL请求流程
用户请求URL发送到服务器,服务器解析请求后发送到后端代码处理请求。
在后端代码处,首先经过Filter(过滤器)和Interceptor(拦截器),然后根据请求的URL映射到绑定的Controller,之后调用Service接口类,然后再调用serviceImpl接口实现类,最后调用DAO。
controller:负责简单的逻辑处理和参数校验功能,之后调用Service;
service:接口类,主要负责业务模块逻辑处理;
serviceImpl:接口实现类,实现类实现service接口中的方法;
DAO:如果service涉及数据库操作就会调用DAO。DAO主要处理数据库操作。DAO只做中间传递角色
常见注解和函数
注解
@Controller 注解:标注该类为controller类,可以处理http请求。
@Controller一般要配合模版来使用。现在项目大多是前后端分离,后端处理请求,然后返回JSON格式数
据即可,这样也就不需要模板了。
@ResponseBody 注解:将该注解写在类的外面,表示这个类所有方法的返回的数据直接给浏览器。
@RestController 相当于 @ResponseBody 加上 @Controller
@RequestMapping 注解:配置URL映射 ,可以作用于某个Controller类上,也可以作用于某Controller类下的具体方法中,说白了就是URL中请求路径会直接映射到具体方法中执行代码逻辑。
@RequestParam 注解:将请求参数绑定到你控制器的方法参数上(是springmvc中接
收普通参数的注解),常用于POST请求处理表单。
@PathVariable 注解:接受请求URL路径中占位符的值
如下代码
@Controller
@ResponseBody
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/whoami/{name}/{sex}")
public String hello(@PathVariable("name") String name,@PathVariable("sex") String sex){
return "Hello" + name + sex;
}
}
函数
1、不可信数据入口方法
方法 | 说明 |
---|---|
getParameter | request类获取参数方法 |
getParameterNames | 获取参数名 |
getParameterValues | 获取参数值 |
getParameterMap | 获取参数map类型 |
getQueryString | 获取URL的value值 |
getHeader | 获取http请求头 |
getHeaderNames | 获取请求头名 |
getRequestURI | 获取请求URL |
getCookies | 获取cookie |
getRequestedSessionId | 获取sessionid |
getInputStream | 获取输入数据 |
getReader | 获取请求内容 |
getMethod | 获取请求方法 |
getProtocol | 获取请求协议 |
getServerName | 获取服务名 |
getRemoteUser | 获取当前缓存的用户 |
getUserPrincipal | 获取用户指纹 |
2、不可信文件访问方法
方法 | 说明 |
---|---|
java.io.FileInputStream | 文件输入 |
java.io.FileOutputStream | 文件输出 |
java.io.FileReader | 文件读取 |
java.io.FileWriter | 文件写入 |
审计思路
一、第三方组件漏洞审计
maven看pom.xml,整理出来第三方组件以及版本号,并说明存在漏洞组件
框架相关:S2、shiro、Spring
中间件相关:JBoss、Weblogic、Jenkins
Java库相关:Fastjson、Jackson
第三方编辑器:UEditor、KindEditor、FCKeditor
用脚本PomEye-main
python3 main.py
二、看配置文件
1、看过滤器 filter
这里需要清楚一个概念,过滤器在任何框架都可以使用,而拦截器是Spring MVC独有的。
而过滤器需要配置在web.xml 里面,而拦截器会配置在springmvc.xml文件里面
搜索代码:doFilter
如xss过滤器
(1)web.xml文件 :
<filter>
<filter-name>xssFilter</filter-name>
<filter-class>com.test.filter.xssFiler</filter-class>
</filter>
<!-- 解决xss漏洞 -->
<filter-mapping>
<filter-name>xssFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
(2)Filter代码:
package com.test.filter;
import com.test.utils.XssFilterWrapper;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
/**
* 作用:Xss过滤器
* 作者:Tiddler
* 时间:2018/11/11 10:21
* 类名: XssFilter
**/
public class xssFiler implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
//使用包装器
System.out.println("过滤器执行了");
XssFilterWrapper xssFilterWrapper=new XssFilterWrapper((HttpServletRequest) servletRequest);
filterChain.doFilter(xssFilterWrapper,servletResponse);
}
@Override
public void destroy() {
}
}
(3)XssFilterWrapper代码:
package com.test.utils;
import org.springframework.web.util.HtmlUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
/**
* 作用:防Xss过滤器[包装器]
* 作者:Tiddler
* 时间:2018/11/11 10:20
* 类名: XssFilterWrapper
**/
public class XssFilterWrapper extends HttpServletRequestWrapper {
public XssFilterWrapper(HttpServletRequest request) {
super(request);
}
/**
* 对数组参数进行特殊字符过滤
*/
@Override
public String[] getParameterValues(String name) {
if("content".equals(name)){
//不想过滤的参数,此处content参数是 富文本内容
return super.getParameterValues(name);
}
String[] values = super.getParameterValues(name);
String[] newValues = new String[values.length];
for (int i = 0; i < values.length; i++) {
newValues[i] = HtmlUtils.htmlEscape(values[i]);//spring的HtmlUtils进行转义
}
return newValues;
}
}
2、看配置文件 application.properties、application.yml
(1)SpringBoot Actuator 未授权访问
(2)配置文件中可能会存在数据库或其他组件的连接信息
Druid 登陆暴力破解
3、看config
搜索代码:WebMvcConfigurer
4、看拦截器,一般目录为interceptor
三、正向排查
搜索代码:@(.*?)Mapping\(
从controller接口入手,找到外部可控的参数,并跟踪参数是否传入到危险方法中
四、逆向排查
找危险函数,跟踪参数是否从外部传入,判断是否有做严格的参数校验和过滤
五、功能点审计
六、“工具”+“人工”
七、补丁对比
常见漏洞审计
一、认证和授权
1、三方组件未授权访问
①、Actuator未授权访问
白盒测试
配置文件都在src/main/resources下面,名字通常为application.yml或者
application.properties
pom依赖:spring-boot-starter-actuator
配置文件:management.endpoints
/actuator/heapdump
可用jvisualvm.exe获取数据库密码
select s.value.toString() from java.util.Hashtable$Entry s where /password/.test(s.key.toString())
select s.value.toString() from java.lang.String s where /pass/.test(s.value.toString())
select s from java.lang.String s where /pass/.test(s.value.toString())
获取Shirokey
org.apache.shiro.web.mgt.CookieRememberMeManager
整改方案
①.引入 security 依赖,打开安全限制,或禁用不需要接口
endpoints.env.enabled=false
②.去除可访问文件
②、Swagger未授权访问
白盒测试
pom依赖:springfox-swagger-ui
整改方案
①.加上enable(false)或者将SwaggerConfig给注释掉,弊端就是你自己也访问不了接口文档
.enable(false)
②.添加认证授权机制: 在Swagger配置中添加认证授权机制,确保只有经过授权的用户才能访问Swagger页面。您可以使用基本身份验证、OAuth、API密钥等方式来实现认证。
③.限制访问权限: 通过配置服务器,限制只有特定IP范围或者需要登录后才能访问Swagger页面。这可以减少未经授权的访问。
④.移除生产环境不必要的Swagger文档: 如果您的应用程序是在生产环境部署的,可以考虑在生产环境中禁用Swagger文档或隐藏它以防止未经授权的访问。
③、Druid未授权访问
白盒测试
配置文件:druid
整改方案
https://www.python100.com/html/ZTO76WX5F640.html
2、登录权限绕过
①、过滤器编写不当
在过滤器或者拦截器里获取请求,然后request.getSession(),通过请求携带的sessionID获取对应服务器上的Session,如果没有携带,则Session为null,重新创建一个Session,此时通过session里有无信息就可以判断此用户是否拥有权限
鉴权过后可以重定向到其他页面完成对应的访问或者登陆操作
例子:存在漏洞如下,只要包含路径/admin/login就可以未授权访问
②、拦截器编写不当
例子:漏洞主要发生于第 23 行和第 24 行,下面我们分析下漏洞成因。
首先,关键点是第 23 行,使用了 request.getRequestURI() 方法获取路径。如果使用该方法获取的路径 进行权限判断是极易出现权限绕过漏洞的。
简单来说, getRequestURI 方法返回的路径是未经过服务器端处理的原始路径,可能包含特殊字符或 路径跳转,从而绕过服务器端的安全控制。 可改成getRequestURL方法:getRequestURL()函数可以帮助获取更准确且经过处理的请求 URL,从而减少特殊字符或路径跳转等问题
其次,第 24 行使用了 uri.startsWith(“/admin”) 判断 Uri 路径中是否以 /admin 开头,以及获取并判断 Session 中的 loginUser 属性是否为 null,两个条件 && 在一起结果为 True 的话进入条件代码,提示需要登录并跳转到后台登录页面中。
既然这样,我们知道 a && b 需要两者都为 True 整体则为 True 才会进入条件判断代码中,如果另其中 一个条件为 False 则整体就为 False,就不会进入条件判断中去了。
这两个条件中,Session 部分我们是没办法操纵的。但 uri.startsWith(“/admin”) 这个条件我们可以搞点 小破坏,前面提到了 uri 是使用的 getRequestURI 方法获取的原始路径,那么我们可以找一些特殊字符 绕过路径判断,并且不影响整体接口,比如:分号 ; ,正斜杠 / 等等。
最终,构造结构路径为 /;/admin/test 或 ///admin/test ,这样路径就不是以 /admin 开头了,并且 该路径不会影响结构访问,实现了权限绕过。
③、shiro鉴权
④、Spring Security鉴权
Spring Security 可以通过 http.authorizeRequests() 开启对 web 请求进行授权保护。
url匹配
antMatchers()
使用 antMatchers 方法需要注意配置规则的顺序,配置顺序会影响授权的效果,越是具体的应该放在前面,越是笼统的应该放到后面。
antMatchers(“/cxyxj/**”).hasRole(“admin”):表示访问/cxyxj/路径的必须要有 admin 角色。
antMatchers(“/security/**”).hasRole(“user”):表示访问/security/路径的必须要有 user 角色。
.antMatchers(“/permitAll”).permitAll():表示访问/permitAll 路径不需要认证
.anyRequest().authenticated():表示除了前面定义的url,其余url访问都得认证后才能访问(登录)
and:表示结束当前标签,回到上下文 HttpSecurity,开启新一轮的配置
formLogin:开启表单登陆
@Bean
PasswordEncoder passwordEncoder() {
return NoOpPasswordEncoder.getInstance();
}
@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.inMemoryAuthentication()
.withUser("cxyxj")
.password("123").roles("admin", "user")
.and()
.withUser("security")
.password("security").roles("user");
}
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests() //开启配置
.antMatchers("/cxyxj/**").hasRole("admin") //访问/cxyxj/**下的路径,必须具备admin身份
.antMatchers("/security/**").hasRole("user") //访问/security/**下的路径,必须具备user身份
.antMatchers("/permitAll").