在JAVA
WEB代码审计中,首先要做的就是确定项目的依赖库组织形式,其次就是确定项目所使用的框架。
1.依赖库组织形式确定。组织形式一般有两类,maven与lib文件夹的依赖形式。maven组织形式必然存在pom.xml配置文件,该配置文件中标识了项目中所使用到的各类框架以及工具集,名称与版本。lib组织形式一般在src\main\webapp\WEB-INF\lib下存在大量jar包,其命名方式未库名+版本号形式。
2.项目所使用的框架确定。
如果是maven组织形式,则查看pom.xml内如下关键字:
spring-core/ springframework.core -> spring框架
struts2-core -> struts2框架
springframework.boot -> srping-boot框架
servlet-api -> 原生servlet
如果是lib组织形式,则查看jar包名称。
spring-core/ springframework.core -> spring框架
struts2-core -> struts2框架
spring-boot -> srping-boot框架
servlet-api -> 原生servlet
3.最后就是确定程序入口:
需要确定整个项目路由并接收处理参数的方法,也就是程序入口。如果是servlet项目,则全局搜索doGet()或者doPost()即是入口方法。spring项目,则匹配注解@RequestParam,用法@RequestParam(value=”参数名”,
required=true/false,
defaultValue=””)。struts2使用web.xml匹配路径与类名,寻找action即可
查找项目中的pom.xml,分析xml内的依赖包以及版本信息,对比漏洞库,得出漏洞
读取WEB-INF/lib/下的jar包名称,提取组件名和版本信息,对比漏洞库,得出漏洞
先判断SQL查询是框架还是非框架基于过滤器的。
1.识别框架名称和框架版本,根据框架信息对比漏洞库,查看是否含有该框架的漏洞,如无,则无SQL注入风险,如有,则得出漏洞
2.如果是ibatis或MyBatis框架,查看xml配置文件,是否有${}包裹的查询变量,如果有,则是字符拼接sql语句,可导致SQL注入
整个源码匹配SQL语句,可以匹配关键字,如select,update,insert,delete,order
by等,匹配到关键字之后,定位该SQL语句是在xml配置文件内还是在代码内,如果是在配置文件内,则判断是否是框架查询方式。如果是在JAVA文件内,再提取该关键字的整行信息,分析是否是SQL语句的拼接形式,如果是拼接形式,则得出SQL注入风险。
提取代码中的过滤器方法,使用主动调用方式,调用过滤器方法,传入sql注入
payload,查看该过滤器的返回值,如果返回值与参数相同,则表示该过滤器缺陷,存在sql注入漏洞风险
主要匹配new
Random()生成的随机数对象,匹配到关键字之后,再进行上下文分析,分析使用场景,一般是验证码生成,用户ID,用户会话ID,加密时的key生成等。根据使用场景进行漏洞定级。最后得出漏洞
匹配回显参数是以哪种方式处理。判断标准是
定位过滤器,简单技巧是搜索xss关键字,根据上下文确定过滤器。提取代码中的过滤器方法,使用主动调用方式,调用过滤器方法,传入XSS
payload,查看该过滤器的返回值,如果返回值与参数相同,则表示该过滤器缺陷,存在XSS漏洞风险
1.Html或JS中匹配实体编码的函数,存在的话,不存在XSS漏洞
2.匹配JS中的ajax请求响应,查看响应是否做了编码处理,如果存在编码处理,则不存在XSS漏洞风险。
XSS漏洞风险
通过匹配注释字符,提取注释内容,匹配IP地址,url地址,测试用户,密码等敏感信息。
定位解析请求体的代码部分,跟踪请求体的解析,可分为JSON型请求以及普通型跟踪该解密算法所使用的解密算法,DES为弱加密,编码型隐藏数据为弱加密
分析方式同上,查看该签名验签方式,如果为MD5则为弱签名算法,若匹配不到标准签名算法,则判定为自定义签名算法,分析该签名验签算法。
提取主流web框架的路由方法(注解式路由使用Filter),进行代码匹配,主要匹配是否有错误路径路由,如果无错误路径路由,则会出现前端的组件信息泄露
基于原生servlet,查找web.xml,匹配是否有错误路径路由。如果无错误路径路由,则会出现前端的组件信息泄露
查找servlet方法,请求处理方法,查看该方法是否使用了throws抛出异常,使用则查看回显前端数据过程中是否会出现抛出异常,匹配容易抛出异常的代码,且代码未被try-catch块包裹(要向下跟踪方法调用)。例如非法参数异常,数组越界异常,栈溢出,堆溢出,类加载异常等。
全局匹配Class.forName,查看参数是否使用了变量形式,跟踪变量,查看该变量是否可控,是否由前端参数直接获取
全局匹配loadClass,查看参数是否使用了变量形式,跟踪变量,查看该变量是否可控,是否由前端参数直接获取
匹配invoke方法(是Method类下的方法),查看参数(两个参数)是否使用了变量形式,跟踪变量,查看该变量是否可控,是否由前端参数直接获取
匹配Class.forName()与.loadClass(),查看参数是否可控,如果可控,则可以加载任意的类。
匹配invoke方法(是MethodHandle类下的方法),查看参数是否使用了变量形式,跟踪变量,查看该变量是否可控,是否由前端参数直接获取
new File()参数可控,可以控制任意路径,即可造成任意文件读取。
AsynchronousFileChannel.open(Paths.get(args),READ) 第一个参数为路径。
readFileToString()第一个参数为File对象,同样可以匹配new File()查看参数是否可控。
Runtime.getRuntime().exec(args)参数可控时就可以执行任意命令。
ProcessBuilder的构造函数参数可控,匹配new
ProcessBuilder()即可,参数可控,即存在风险
查看使用的XML解析器,可以直接全代码搜索JAXBContext.newInstance,然后定位该上下文,查找setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES,
false),如果不存在该设置项,则存在XXE漏洞风险。
查看使用的XML解析器,可以直接全代码搜索DocumentBuilderFactory.newInstance(),然后定位该上下文,查找setExpandEntityReferences(false),如果不存在该设置项,则存在XXE漏洞风险。如果newDocumentBuilder()在该设置项之前,同样存在XXE
查看使用的XML解析器,可以直接全代码搜索SchemaFactory.newInstance,然后定位该上下文,查找setProperty(XMLConstants.ACCESS_EXTERNAL_DTD,
“”)
setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, “”)
如果不存在该设置项,则存在XXE漏洞风险
查看使用的XML解析器,可以直接全代码搜索SAXTransformerFactory.newInstance,然后定位该上下文,查找setProperty(XMLConstants.ACCESS_EXTERNAL_DTD,
“”)
setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, “”)
如果不存在该设置项,则存在XXE漏洞风险
查看使用的XML解析器,可以直接全代码搜索SAXParserFactory.newInstance,然后定位该上下文,查找setFeature(“http://apache.org/xml/features/disallow-doctype-decl”,
true)** **此项禁用DTDs,可以防御
以下三项是在不能禁用DTDs时使用,但必须同时存在
setFeature(“http://xml.org/sax/features/external-general-entities”, false)
setFeature(“http://xml.org/sax/features/external-parameter-entities”, false)
setFeature(“http://apache.org/xml/features/nonvalidating/load-external-dtd”,
false)
如果以上两种方式均不存在则存在XXE
查看使用的XML解析器,可以直接全代码搜索new SAXReader(),然后定位该上下文,查找
setFeature(“http://apache.org/xml/features/disallow-doctype-decl”, true)**
**此项禁用DTDs,可以防御
以下三项是在不能禁用DTDs时使用,但必须同时存在
setFeature(“http://xml.org/sax/features/external-general-entities”, false)
setFeature(“http://xml.org/sax/features/external-parameter-entities”, false)
setFeature(“http://apache.org/xml/features/nonvalidating/load-external-dtd”,
false)
如果以上两种方式均不存在则存在XXE
查看使用的XML解析器,可以直接全代码搜索SchemaFactory.newInstance,然后定位该上下文,查找setProperty(XMLConstants.ACCESS_EXTERNAL_DTD,
“”)
setProperty(XMLConstants.ACCESS_EXTERNAL_SCHEMA, “”)
如果不存在该设置项,则存在XXE漏洞风险
查看使用的XML解析器,可以直接全代码搜索XMLReaderFactory.createXMLReader(),然后定位该上下文,查找
setFeature(“http://apache.org/xml/features/disallow-doctype-decl”, true)**
**此项禁用DTDs,可以防御
以下三项是在不能禁用DTDs时使用,但必须同时存在
setFeature(“http://xml.org/sax/features/external-general-entities”, false)
setFeature(“http://xml.org/sax/features/external-parameter-entities”, false)
setFeature(“http://apache.org/xml/features/nonvalidating/load-external-dtd”,
false)
如果以上两种方式均不存在则存在XXE
查看使用的XML解析器,可以直接全代码搜索new SAXBuilder(),然后定位该上下文,查找
setFeature(“http://apache.org/xml/features/disallow-doctype-decl”, true)**
**此项禁用DTDs,可以防御
以下三项是在不能禁用DTDs时使用,但必须同时存在
setFeature(“http://xml.org/sax/features/external-general-entities”, false)
setFeature(“http://xml.org/sax/features/external-parameter-entities”, false)
setFeature(“http://apache.org/xml/features/nonvalidating/load-external-dtd”,
false)
如果以上两种方式均不存在则存在XXE
在接收请求参数的方法内,查找new
URL(),确定该参数是否可控,可控的话,存在则有SSRF漏洞风险。
再查看下文是否含有URLConnection对象,如果未进行HttpURLConnection对象的强制转换,则支持邮件,ftp传输协议,如果进行强制转换,则只支持HTTP协议。
参数为一个远程类路径(http://ip//xx.class),支持任何远程资源加载协议,例如http,ldap,rmi,hessian等,当参数可控时即可产生SSRF,SSRF仅为一种危害,也可以造成远程代码执行。
RPC框架。使用RMI加载远程资源时,需要初始化上下文环境,new
InitialContext,然后使用如下三种方式绑定一个远程资源:
1.** Naming.lookup()**
2.** Naming.bind()**
3. Naming.rebind()
如果以上三个方法参数可控的话,同样可以加载远程的class文件,造成SSRF或者远程代码(命令)执行漏洞。
RPC框架。使用时先初始化对象,new
HessianProxyFactory(),然后使用该对象调用create()函数,该函数传入两个参数,第一个是Class对象,第二个即为远程资源路径,如果第二个参数可控,即造成SSRF或者远程代码(命令)执行。
RPC框架。已逐渐被淘汰。使用时new
BurlapProxyFactory(),使用该对象调用create()方法,触发漏洞逻辑同Hessian。
Spring的RPC框架,需要在xml中配置。在客户端即可看到该配置,关键类名org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean,接下来属性serviceUrl的值即为远程资源路径,如果该路径可控,即可加载任意远程代码并执行,造成SSRF或者远程代码执行。
XML,SOAP,WSDL即为构成webservice的三大技术。客户端中需要使用new
URL()去绑定一个远程的wsdl服务,参数一般形式为ttp://xxx/?wsdl=xxx,参数可控时同样可以加载任意远程资源。达到SSRF或者远程代码执行,该处漏洞主要是由于XML注入形成(其使用的XML解析器为上文中提到的几种),因为其通信报文为XML报文。
这些RPC框架组件一般用作内网中的服务通信,客户端url一般不会可控。但是可以伪造服务端,当进入内网之后,控制一台服务端主机,即可伪造该服务,间接使客户端加载构造的class文件或xml报文。执行任意代码。
以上漏洞也可以叫做JNDI注入,其远程资源加载所使用的协议有ldap,rmi,http等。
在JSP页面获取数据的简单方式(只能获取数据,不能设置数据).JSP2.0引入。使用方式为${xxx},指的是获取xxx对象的值。开发中一般使用这种表达式去获取前端参数并直接回显到页面上。代码审计时要关注JSP页面的${}或HTML或者JS中提交的目的url携带JAVA对象或类信息,如果有,且${}内部分可控,即可造成表达式注入,造成命令执行或代码执行。
表达式样例:(JSP页面的pageContext对象)
pageContext.request.getSession().setAttribute(“a”,pageContext.request.getClass().forName(“java.lang.Runtime”).getMethod(“getRuntime”,null).invoke(null,null).exec(“命令”).getInputStream())
Spring EL ,Spring表达式语言。一般使用new
SpelExpressionParser()初始化对象,使用该对象调用parseExpression()如果参数任意可控,则可以传入任意表达式,以达到远程命令执行或代码执行。
以下是表达式样例:
T(java.lang.Runtime).getRuntime().exec(“calc”)
开发中通常基于struts2中使用OGNL表达式。且是struts2的默认表达式语言。struts2用来处理传入参数以及request中各项参数的值栈OgnlValueStack在进行取值的时候,就会去调用ognl的getValue参数,即执行表达式。使用时需要new
OgnlContext()初始化对象,然后使用Ognl.getValue(args,obj),第一个参数即为Ognl表达式,第二个参数为Ognl的上下文对象。当第一个参数可控时即可造成注入,远程代码执行或命令执行。
使用时如下两种方式
1.MVEL.eval(expression)
MVEL.executeExpression(expr)
以上expression参数为表达式字符串,参数可控时即可执行任意表达式。
反序列化组件主要用于RPC远程通信过程中数据传输的格式化或者网络通信过程中数据的格式化。有字节传输以及可视化数据传输。字节传输过程中使用了java原生的writeObject以及readObject函数,这类传输方式使用场景大多数为RPC通信。可视化数据传输过程中一般有两种,为JSON与XML格式,目前流行的为JSON格式,XML格式主要用于特定的通信过程,比如webservice,RMI等。
JSON反序列化组件主要用于网络通信过程中数据的格式化。
其关键函数为JSON.parseObject(arg1,arg2)参数1为json字符串,参数2为泛型。触发反序列化漏洞的要求有三个
1.参数2为泛型,一般为Object.class
2.接着对该对象进行序列化操作
3.json串可控
关于AutoType问题,其实就是开启了黑名单检查,有些poc绕过黑名单,无论开启与否都会受到影响。
其原理与Jackson大同小异,之前已经分析过其底层原理。(Java序列化反序列化源码—Jackson反序列化漏洞源码分析)
其关键函数为readValue(arg1,arg2)
参数1为json字符串,参数2为泛型。触发反序列化漏洞的要求有两个:
1.参数2为泛型,一般为Object.class
2.接着对该对象进行序列化操作
3.json串可控
Jackson也添加了黑名单限制,其黑名单位置以及挖掘路漏洞的思路,之前的文章也已经分析的很彻底了。(Java序列化反序列化源码—Jackson反序列化漏洞源码分析)
该组件造成的漏洞其实是开发者想达到fastjson或者jackson的效果而造成的。当然也有代码复用的考虑。使用时JSONObject.toBean(arg1,arg2)触发漏洞需要满足如下条件。
1.arg1为可控json串
2.arg2为泛型(Object.class)
3.有序列化操作
该组件漏洞同json-lib造成的原因相同。使用时BeanUtils.populate(arg1,arg2),触发反序列化漏洞需要满足以下条件。
1.arg1为目标反序列化类
2.类属性的Map对象数据可控
3.有序列化操作
该组件核心原理是使用了反射操作,同jackson与fastjson,因此其poc通用,但是开发过程中很少会使用这样的开发方式,一般arg1为不可控参数。
该部分主要为xml反序列化组件造成的漏洞,要区分前文的XML注入漏洞,前文的XML解析器为dom型解析,而要造成XML反序列化其底层原理一般为反射,xml串与实体类的映射。
该组件漏洞造成有两类,一类使用内置的转换器,第二类使用动态代理
该组件解析XML时使用JAVA设计模式中的动态代理时,导致了反序列化漏洞的出现。使用时new
Xstream(),使用该对象调用fromXML(arg)参数为xml字符串或者文件流或远程资源路径。参数可控时会造成该漏洞。
该组件使用内置转换器时同样也可以触发反序列化漏洞。
但该组件漏洞触发均存在于特定版本中。
该组件为集合工具类的库,主要是要通过构造一系列TransFormer对象
主要函数为new InvokerTransformer(arg1,arg2),触发时需要满足以下条件
1.arg1可控,为方法名
2.arg2为Class对象数组,可控
3.调用new
ChainedTransformer(arg)参数为以上transformer数组,且该数组为一些列可控InvokerTransformer(arg1,arg2)对象
TransformedMap.decorate(arg1,arg2,arg3),arg1,arg3为普通map对象,arg2为以上ChainedTransformer对象。
其底层原理就是反射。条件利用较为复杂。
Apache有众多存在反序列化漏洞的组件,也是需要具体版本。详情审计方式可以使用依赖库的版本匹配方式。
代码审计中的逻辑漏洞没有固定的规律可以遵循,只能依靠开发经验去寻找可能出现问题的功能逻辑处,联系上下文判断。此处只列举几个例子。
该部分一般使用原生的权限控制以及第三方组件的权限控制。权限控制花样繁多,自由度高,在审计当中并没有特定的API操作,只举以下session销毁的例子。
使用HttpSessionEvent创建session后,当应用程序点击退出时未调用invalidate(),则session未被销毁,session超时设置在配置文件内
此类主要是验证码的次数限制以及验证码超时设置。首先搜索随机数生成的地方。即new
Random或者new
SecureRandom等java随机数生成函数,确定验证码判断逻辑处,即可联系上下文确定是否存在验证的逻辑问题。
此处一般是数据库连接密码硬编码在配置文件或者代码中。一般可以直接去xml中搜索password关键字或者在代码中搜索getConnection()函数,查看参数3是否是硬编码的密码。此类问题一般是低危问题。