2020年代码审计总结

1.前言

要熟悉java语法,知道啥是类,啥是方法,啥是接口,啥是常量巴拉巴拉巴拉

2.确定框架

在打开源码时先判断系统框架,例如一个struts2项目中web.xml文件存在Filter-class为:
org.apache.struts2.dispatcher.xxxx

以及resources目录(或src(root)目录下)中存在strtus.xml

如果存在pom那pom.xml中存在struts依赖信息

而springmvc的特征则是在pom.xml中会存在相关依赖

web.xml中存在关于DispatcherServlet的注册配置

3.审计思路

3.1.Struts2

3.1.1.过滤器及映射配置

3.1.1.1.Web.xml

查看web.xml中<filter-mapping><url-pattern>来确定拦截规则,当是.action时所有以.action为结尾的请求都会被struts处理拦截,/test/.action则只有test目录下的请求会被拦截。

3.1.1.2.struts.xml

通过struts.xml文件,查看存在哪些action,以及处理具体请求的java文件路径
例如:

1
<action name="test" method="test" class="novy.action.LoginAction"/>

表示novy.action.LoginAction类中的test方法,处理http://127.0.0.1/test.action的请求。而在另一种的写法中

1
2
3
4
<action name="GoLogin" class="com.action.GoLogin">
<result name="input">/Login.jsp</result>
<result name="success">/Index.jsp</result>
</action>

表示GoLogin类处理http://127.0.0.1/login.jsp和index.jsp请求

还有一种动态方法调用方式请看https://www.cnblogs.com/zhangzhenzhen/p/5959555.html

在审计漏洞之前,我们需要了解一下web各层流程

3.1.2.层次介绍

通常在struts2中
action为业务逻辑处理层,action层接收来自视图层(.jsp(可以理解为前端吧,就是用户看到操作的那层))的请求,并接收请求参数,同时负责调用模型Model层方法来完成业务逻辑的处理,最后控制程序的流程,选择一个合适的视图,将结果显示给用户,一般这个目录下文件的特征表现为xxxxaction.java,比如NovyAction.java;

dao为数据持久层,在这层中通常是用来做数据库请求处理的,增删查改都在这里,一般这个目录下文件的特征表现为xxxxDao.java,比如NovyDao.java。

在web运行处理请求时流程为业务逻辑处理层-数据持久层

3.1.3.实例

Idea打开项目,查看目录结构

从目录得知该框架为struts,web运行处理流程为action->dao,bean是实体处理,db是数据库连接配置,两者不在流程之中。
根据之前**3.1.1.**介绍,我们首先看web.xml文件,查看拦截配置
所有.action的请求会被struts2处理,具体实现为StrustPrepareAndExecuteFilter类

查看struts.xml中映射配置

根据配置我们知道login.jsp请求由GoLogin类处理,所以我们可以根据路径跟进GoLogin类,其路径组成对应为
src(root)/com/action/GoLogin.java

3.1.3.1.代码分析

在GoLogin类中我们就可以看到一些对登陆的处理,如果我们找登陆处的SQL注入的话就看处理登陆参数的相关方法,比如此处new了一个AdminDao类下的checkLogin方法来处理username及Password,再根据判断返回的结果是否为空来显示相应内容

根据3.1.2介绍我们知道AdminDao为数据持久层,那么ChekLogin方法通常就是对登陆做数据库操作的地方,所以我们跟进一下该方法

在此处因为直接拼接请求参数,然后带入数据库去执行查询导致了SQL注入漏洞的产生

3.1.3.2.漏洞验证

根据前面3.1.1.2介绍我们知道其请求路由为login.jsp
根据漏洞位置我们模拟其sql语句为

1
select * from Admin where Admin_Username='username' and Admin_Password='password'

所以登陆时我们可以使用万能用户名来进行登陆绕过
Admin’or”=”or--+

3.2.SpringMVC

3.2.1.配置及依赖

3.2.1.1.Web.xml

通过web.xml中DispatcherServlet配置,来查看springMVC作用范围

通过servlet中contextConfigLocation配置,查看springMVC配置文件所在路径

3.2.1.2.Springmvc.xml

在springMVC配置文件中,component-scan是用来查找Controller类所在位置,org.springframework.web.servlet.view.InternalResourceViewResolver为自定义视图解析器

3.2.1.3.pom.xml

它是Maven项目中的文件,使用XML表示,也可以由此判断该项目是否为maven项目,该配置文件通常用来声明项目信息、环境的配置、引用组件依赖等等

还是老规矩,在审计漏洞之前,我们先看下spring的请求处理流程

3.2.2.层次介绍

通常在springmvc中
controller为控制层(业务逻辑),用来接收客户端的请求,然后调用Service层业务逻辑,获取到数据,传递数据给视图层(客户端)用于视觉呈现,一般请求的url在这里,比如

1
2
@Controller
@RequestMapping(value = "/novy")

则请求url为http://localhost/novy
控制层的文件一般为xxxcontroller.java,比如NovyController.java

Service是业务层,接收Controller层数据,与DAO/Mapper层交互,处理业务逻辑,生成responseDTO数据并返回Controller层 ,该层文件一般为xxxServce.java,比如NovyService.java,此处是接口定义,就是定义一些方法,没有这些方法的实现,但是有时候数据操作会在这里发生(看开发)

Implements是服务实现层(接口实现),用来处理一些方法的实现(这个方法干了啥干了啥),该层文件一般为xxxImpl.java,比如NovyImpl.java,impl 是把mapper和service进行整合的文件,有时候一些sql操作也会发生在这里

Mapper是数据持久层,对数据库进行数据持久化操作,他的方法语句是直接针对数据库操作的,数据持久层文件通常都是xxxMapper.xml,比如NovyMapper.xml

Dao是数据接口层,一些数据请求(接口)会在这里发生(一般用于内部实现)

Entity是实体处理层,用于存放我们的实体类,与数据库中的属性值基本保持一致(定义前端传来的请求参数)

在web运行时处理请求的流程为Controller->Service->impl->mapper

3.2.3.实例

这里以含有漏洞的springboot项目做案例(springboot和springmvc配置不一样,感兴趣的自行百度,但是请求处理流程一样,这里讲的又不是开发,不影响演示),Idea打开项目,等待依赖导入完成

发生报错的就自己下载相关组件导入
查看目录结构

按照3.2.2介绍得知流程为controller->services->mapper,按照3.2.2对pom的介绍,我们先看pom.xml引用了哪些组件,以此来找出包含漏洞版本的组件,然后再看controller及其他,可以在idea中利用file mask来查看所有controller或全局搜索@Controller

3.2.3.1.代码分析

首先查看引用的组件
pom.xml

看到了两个存在漏洞的组件,拿fastjson反序列化来说,全局搜索json.parseObject或JSONObject.parseObject或@RequestBody来查找参数可控的地方

在此处中用@RequestBody注解来获取整个请求体,然后对请求体进行反序列化

按照上面介绍到的搜索并点进一个controller,从3.2.2对controller介绍得知此处请求url为/informlistpaging

在此处我们可以看到informListPaging方法定义了很多参数,拿basekey做例子,在该刚方法中被定义为字符串请求参数,按照3.1.3.1的思路,我们想要找注入就找到调用方法处理该参数的地方

在80行中,nm的sortMyNotice方法对几个参数进行处理,这里需要注意的是,nm并不是一个类,而是一个被定义的接口,所以我们需要注意nm在哪里被定义了

跟进NoticeMapper

此处为接口,为nm提供了sortMyNotice方法,但这里还不是数据库操作的地方,因为controller无法直接调用mapper.xml的方法(select id),所以就需要这个mapper.java来做一个接口中转,所以我们根据3.2.2介绍,转到mapper.xml层
全局搜索sortMyNotice方法

转到notice-mapper.xml

此处的select id即为调用到的方法,往下为sql语句,我们可以看到在like后面直接用%${}%进行模糊查询,导致了漏洞的产生
有人会问service层呢?在这里

imformRelationService的setList方法对mapper处理返回的数据进行封装处理后返回到controller,然后controller返回到视图层,流程结束

3.2.3.2.漏洞验证

3.2.2对controller的介绍中得知,根据controller构造url:
http://localhost/informlistpaging?baseKey=

3.3.ps:其他情况

3.3.1.Sql操作在service层

有时候sql查询会直接发生在service层,比如
某个项目中的某个方法有个查询,定义了一个字符串参数defkey

查看wfservice在哪里被定义

跟进WorkFlowService,在该service层中搜索前面调用到的getHavedonePage方法,在该方法中含有一条没有进行预编译的sql查询,此处直接进行带入到数据库查询导致了漏洞的产生

3.3.2.跟到接口断了

当跟进方法时跟到接口断了怎么办,比如出现这种情况
controller里有一个密码重置

跟进updatePassword方法

到这里之后只看到提供给userService的updatePassword方法,没有看到具体的实现,
不要慌,根据3.2.2对implements的介绍,我们还有个impl没有看,全局搜索implements UserService

就可以看到对接口UserService的updatePassword方法的实现

这时候再继续往下跟就可以了,流程一样

4.小技巧

4.1.命名

无论是struts还是springmvc/boot,按照我的理解,为了方便区分和后续其他开发,除非另类命名(比如3.1.3),在整个请求处理流程中对于类名的前置命名都是一致的,比如
NovyController->NovyService->(NovyServiceImpl->)NovyMapper.xml
而不会出现
NovyController->TestService->(WhyServiceImpl->)OasdMapper.xml
这种情况,所以在审计过程中跟进代码时利用idea的全局搜索能更好的提高审计效率

4.2.方法的跟进

通常调用方法时都是类名.方法名,或者写了一个EntityManager接口,然后再定义一次:
private EntityManager em;
这样em就可以用到EntityManager里的方法
比如某个项目有一个序列化工具类SerializeUtil,在该类里有一个deserialize方法来反序列化接收的request数据

而在controller中定义了一个接口
private SerializeUtil fvlh;
然后在某个@PostMapping注解下的方法进行调用
fvlh.deserialize(request);
如果我们想找反序列化漏洞就在跟进时可以直接ctrl+左键(idea)来跟进deserialize方法查看具体实现,或者先查看哪里定义了fvlh,然后再根据接口去跟进deserialize方法进行漏洞跟踪,最后确定该漏洞是否利用

5.其他

还有tapestry框架,这个我在公司项目中审的,开源没碰见过,所以不好解释,等哪天碰到了再另说。总的来说跟进思路就这样,其他漏洞同理。感谢shxjia对相关专业知识的解答,感谢白帽100少先队的技术分享

文档下载地址(文档比较老,本文章对比文档有改动)


声明:
本文章用于学习交流,严禁用于非法操作,出现后果一切自行承担,阅读此文章表示你已同意本声明。

Disclaimer:
This article is for study and communication. It is strictly forbidden to use it for illegal operations. All consequences shall be borne by yourself. Reading this article means that you have agreed to this statement.