分类 代码审计 下的文章

插桩技术在Java安全中的应用简述

介绍

随着信息技术的发展,软件开发技术呈多样性发展趋势,其中Java在开发领域具有一定代表性。软件效率的提高同时增大了漏洞发现与防御的挑战。在当前WAF与静态代码检测都发展迅速的情况下,WAF在一些特殊情况下可能无法正确拦截,而静态检测的缺点在于误报率高。因此需要进行动态交互式监测,由此可以从底层对于攻击向量进行检测或者验证程序中是否实际存在安全漏洞。

插桩技术是在保证目标程序原有逻辑完整的情况下,在特定的位置插入代码段,从而收集程序运行时的动态上下文信息。

目前基于插桩技术实现Java程序的动态交互安全监测已经有一些实现形式,如RASP,IAST。在Java中插桩通过Instrument以及字节码操作工具(如:ASM,Javassist,Byte Buddy等)实现。接下来会简要介绍该技术以及相关知识内容。

相关知识

Instrument

Java SE 5引入了一个静态Instrument的概念,利用它我们可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在JVM上的程序,可以在程序启动前修改类的定义。这样的特性实际上提供了一种虚拟机级别支持的AOP实现方式,使得开发者无需对应用程序做任何升级和改动,就可以实现某些AOP的功能了。
在应用启动时,通过-javaagent参数来指定一个代理程序。
详细介绍见:https://www.ibm.com/developerworks/cn/java/j-lo-jse61/

Java Instrument工作原理

javaagent
00218c2023a2ba140887543f88a4fd99

  1. 在JVM启动时,通过JVM参数-javaagent,传入agent jar,Instrument Agent被加载,调用其Agent_OnLoad函数;
  2. 在Instrument Agent 初始化时,注册了JVMTI初始化函数eventHandlerVMinit;
  3. 在JVM启动时,会调用初始化函数eventHandlerVMinit,启动了Instrument Agent;
  4. 用sun.instrument.instrumentationImpl类里的方法loadClassAndCallPremain方法去初始化Premain-Class指定类的premain方法。初始化函数eventHandlerVMinit,注册了class解析的ClassFileLoadHook函数;
  5. 调用应用程序的main开始执行,准备解析;
  6. 在解析Class之前,JVM调用JVMTI的ClassFileLoadHook函数,钩子函数调用sun.instrument.instrumentationImpl类里的transform方法,通过TransformerManager的transformer方法最终调用我们自定义的Transformer类的transform方法;
  7. 因为字节码在解析Class之前改的,直接使用修改后的字节码的数据流替代,最后进入Class解析,对整个Class解析无影响;
  8. 重新加载Class依然重新走6-7步骤;

Java字节码操作工具

BCEL
这是Apache Software Fundation的jakarta项目的一部分。BCEL它可以让你深入JVM汇编语言进行类的操作的细节。
ASM
是一个轻量及java字节码操作框架,直接涉及到JVM底层的操作和指令,性能高,功能丰富。
Javassist
是一个开源的分析、编辑和创建java字节码的类库。性能消耗较⼤大,使⽤用容易。
Byte Buddy
是一个字节码生成与操作库,操作起来更简单。

示例

通过插桩获取SpEL执行中表达式的值(使用Byte Buddy)
Agent:

Interceptor:

程序运行时配置

效果:
屏幕快照 2019-03-12 下午12.39.35

应用

RASP

RASP(Runtime application self-protection)运行时应用自我保护,RSAP将自身注入到应用程序中,与应用程序融为一体,实时监测、阻断攻击,使程序自身拥有自保护的能力。并且应用程序无需在编码时进行任何的修改,只需进行简单的配置即可。
7195ace984937ccbddc171ece82e237e
可见百度开源的OpenRASP(https://rasp.baidu.com)
check

IAST

IAST(Interactive Application Security Testing)交互式应用安全测试,是一种灰盒测试技术。结合SAST和DAST的优点,在模拟黑客外部攻击的同时,对内部实时数据进行监视,提高测试精度。
两种模式
1.Active IAST (主动型)
一个组件产生恶意攻击流程,另一个组件在运行时监视应用。由此来达到漏洞定位以及减少误报。即RASP Agent + DAST = IAST
图片3
可以参见Burpsuite的infiltrator(https://portswigger.net/burp/documentation/infiltrator)
屏幕快照 2019-03-11 下午6.05.33

2.Passive IAST (被动型)
在运行时监视应用并分析代码,它不会主动对Web应用程序执行攻击,而是纯粹被动地分析检测代码。这实际上是一个巨大的优势,因为它不会影响同时运行的其他测试活动,并且只需要业务测试(手动或自动)来触发安全测试。
iast-architecture

IAST如何分析
IAST类似于APM,使用安全传感器来检测应用程序和API。安全相关事件由传感器直接在正在运行的应用程序进行监测,并传递给分析引擎,分析引擎汇总这些事件并识别代码执行的易受攻击程度。
IAST传感器创建了一系列与安全相关的事件,并这些事件提供给分析引擎。该引擎可以执行各种规则。
10934490-image1

In effect, IAST establishes guardrails for a program. If the stream of telemetry from the sensors indicates that the behavior of the program has violated one of these guardrails, it is reported as a vulnerability.

总结

插桩技术可以很好的用于交互式应用检测与程序运行时的自我保护,它通过Java探针达到可以在执行代码底层分析上下文环境的能力。随着计算机各个方面性能的整体提高,也已成为当前安全监测中值得研究的一个方向。
10934497-picture3

参考

http://blog.nsfocus.net/rasp-tech/
http://kns.cnki.net/KCMS/detail/detail.aspx?dbcode=CMFD&filename=1014026901.nh
http://ouyblog.com/2019/03/基于Java-Agent与Javassist实现零侵入AOP
https://www.jianshu.com/p/9f4e8dcb3e2f
https://github.com/gyyyy/footprint/blob/f0f811fe2302df8cca9a151660f9d96d4b030784/articles/2018/application-security-testing-cheatsheet.md
http://www.zjtbzx.gov.cn/html/2018/08/31/9744bdb6-cea3-4c51-aa7b-aa76ea647bfa.htm
https://blog.secodis.com/2015/11/26/the-emerge-of-iast/
http://sectooladdict.blogspot.com/2017/05/dast-vs-sast-vs-iast-modern-ssldc-best.html
https://dzone.com/refcardz/introduction-to-iast?chapter=1

由浅入深SpEL表达式注入漏洞

SpEL介绍

认识SpEL

Spring Expression Language(简称SpEL)是一种强大的表达式语言,支持在运行时查询和操作对象图。语言语法类似于Unified EL,但提供了额外的功能,特别是方法调用和基本的字符串模板功能。同时因为SpEL是以API接口的形式创建的,所以允许将其集成到其他应用程序和框架中。
Spring框架的核心功能之一就是通过依赖注入的方式来管理Bean之间的依赖关系,而SpEl可以方便快捷的对ApplicationContext中的Bean进行属性的装配和提取。除此以外SpEL还能做的有很多,从官方文档中我们可以看到,SpEL支持以下功能。

  • Literal expressions
  • Boolean and relational operators
  • Regular expressions
  • Class expressions
  • Accessing properties, arrays, lists, maps
  • Method invocation
  • Relational operators
  • Assignment
  • Calling constructors
  • Bean references
  • Array construction
  • Inline lists
  • Ternary operator
  • Variables
  • User defined functions
  • Collection projection
  • Collection selection
  • Templated expressions

基础用法以及使用场景

上方功能中加粗的几项是我们在其表达式安全中重点学习的地方,我们首先来看SpEL的常见用法,然后会依次介绍其中几项功能的基本用法,以及在部分框架中SpEl的使用位置。

1.SpEL API

这里使用了SpEL API来评估文字字符串表达式“Hello World”。我们通常用该方式来测试或者使用SpEL表达式。
其中接口ExpressionParser负责解析表达式字符串。在这个例子中,表达式字符串是由周围的单引号表示的字符串文字。之后接口Expression负责评估以前定义的表达式字符串。
所以说上述代码含义为首先创建ExpressionParser解析表达式,之后放置表达式,最后通过getValue方法执行表达式,默认容器是spring本身的容器:ApplicationContext

2.SpEL语法

SpEL使用 #{...} 作为定界符,所有在大括号中的字符都将被认为是 SpEL表达式,我们可以在其中使用运算符,变量以及引用bean,属性和方法如:

引用其他对象:#{car}
引用其他对象的属性:#{car.brand}
调用其它方法 , 还可以链式操作:#{car.toString()}

其中属性名称引用还可以用$符号 如:${someProperty}
除此以外在SpEL中,使用T()运算符会调用类作用域的方法和常量。例如,在SpEL中使用Java的Math类,我们可以像下面的示例这样使用T()运算符:

T()运算符的结果会返回一个java.lang.Math类对象。

具体常见表达式用法会在4.功能用法示例中给出。

3.SpEL在bean定义中

  1. XML配置
  2. 基于注解的使用

4.功能用法示例

Class expressions
1.类类型表达式
SpEL中可以使用特定的Java类型,经常用来访问Java类型中的静态属性或静态方法,需要用T()操作符进行声明。括号中需要包含类名的全限定名,也就是包名加上类名。唯一例外的是,SpEL内置了java.lang包下的类声明,也就是说java.lang.String可以通过T(String)访问,而不需要使用全限定名。
因此我们通过 T() 调用一个类的静态方法,它将返回一个 Class Object,然后再调用相应的方法或属性:
如:

成功弹出计算器
2.类实例化
使用new可以直接在SpEL中创建实例,需要创建实例的类要通过全限定名进行访问。
如:

Method invocation
方法使用典型的Java编程语法来调用。
如:

Calling constructors
可以使用new调用构造函数。除了基元类型和字符串(其中可以使用int、float等)之外,所有的类都应该使用完全限定的类名。
如:

Bean references
如果解析上下文已经配置,则可以使用@符号从表达式中查找bean。

Variables
变量定义通过EvaluationContext接口的setVariable(variableName, value)方法定义;在表达式中使用#variableName引用;除了引用自定义变量,SpEL还允许引用根对象及当前上下文对象,使用#root引用根对象,使用#this引用当前上下文对象。
如:

在SpEL中比较常见的用途是针对一个特定的对象实例(称为root object)提供被解析的表达式字符串,当我们把contextroot object设置为一个对象时,我们在取的时候可以省略root对象这个前缀了。如下:
首先定义一个类

设置root object后SpEL执行以及结果如下

这里在执行表达式时,SpEL会在内部使用反射从根对象中获取/设置属性的值。

User defined functions
用户可以在SpEL注册自定义的方法,将该方法注册到StandardEvaluationContext 中的registerFunction(String name, Method m)方法。
如:
我们通过JAVA提供的接口实现字符串反转的方法。

我们可以通过如下代码将方法注册到StandardEvaluationContext并且来使用它。

Templated expressions
表达式模板允许文字文本与一个或多个解析块的混合。 你可以每个解析块分隔前缀和后缀的字符。当然,常见的选择是使用#{}作为分隔符。
如:

该字符串是通过连接文字”random number is”与 计算表达式的#{}定界符获取的结果,在此情况下的结果 中调用一个随机()方法。第二个参数的方法parseExpression() 是类型ParserContext的。在ParserContext接口用于影响如何 表达被解析,以便支持所述表达模板的功能。的TemplateParserContext的定义如下所示。

更多细节可查看官方文档

SpEL导致的任意命令执行

漏洞原因

从上方功能的类类型表达式示例中,我们可以看到成功执行了系统的命令,而这也就是整个SpEL安全中造成RCE漏洞的区域。因为在不指定EvaluationContext的情况下默认采用的是StandardEvaluationContext,而它包含了SpEL的所有功能,在允许用户控制输入的情况下可以成功造成任意命令执行。
屏幕快照 2019-01-17 下午10.06.09

其中容易造成漏洞的两个位置是
1.针对一个特定的对象实例提供被解析的表达式字符串
如之前用法示例中Variables所介绍,可能造成指定属性名被构造成恶意代码
2.双重EL表达式评估
如:

这个很明显通过两次EL表达式执行后,如果可以控制传入的directoryNameForPopup参数为恶意代码就会造成漏洞发生

我们可以再看下SpEL提供的两个EvaluationContext的区别。
(EvaluationContext评估表达式以解析属性,方法或字段并帮助执行类型转换时使用该接口。有两个开箱即用的实现。)

  • SimpleEvaluationContext - 针对不需要SpEL语言语法的全部范围并且应该受到有意限制的表达式类别,公开SpEL语言特性和配置选项的子集。
  • StandardEvaluationContext - 公开全套SpEL语言功能和配置选项。您可以使用它来指定默认的根对象并配置每个可用的评估相关策略。

SimpleEvaluationContext旨在仅支持SpEL语言语法的一个子集。它不包括 Java类型引用,构造函数和bean引用。

所以说指定正确EvaluationContext,是防止SpEl表达式注入漏洞产生的首选,之前出现过相关的SpEL表达式注入漏洞,其修复方式就是使用SimpleEvaluationContext替代StandardEvaluationContext

常用payload

关键字黑名单过滤绕过:
可以参考之前Code-Breaking Puzzles — javacon的这道题目(writeup http://rui0.cn/archives/1015 ),主要通过正则匹配java关键词(如:java.+lang exec.*\(等)来防御,其绕过方式有两种 如下:

  1. 利用反射构造
  2. 利用ScriptEngineManager构造

在Nuxeo RCE中也有个黑名单绕过,因为它过滤了.getClass(
所以采取的姿势是通过SpEL语法的集合选择绕过,payload如下。具体分析可见(http://www.polaris-lab.com/index.php/archives/613

除此以外当执行的系统命令被过滤或者被URL编码掉时我们可以通过String类动态生成字符
如要执行的命令为open /Applications/Calculator.app我们可以采用new java.lang.String(new byte[]{<ascii value>,<ascii value>,...})或者concat(T(java.lang.Character).toString(<ascii value>))嵌套来绕过
两种构造方式的python脚本如下:

加工一下即为

成功执行
屏幕快照 2019-01-17 下午11.41.33

其次如果有输出点需要回显可以使用

[更新-2019.4.14]因为JAVA9新增了JShell,所以我们也可以利用这个功能执行命令 @sagar38 from Twitter

漏洞案例分析

SpringBoot SpEL表达式注入漏洞

影响版本:
1.1.0-1.1.12
1.2.0-1.2.7
1.3.0

首先搭建存在漏洞版本的SpringBoot,创建一个controller并抛出异常
只要在异常信息中包含SpEL表达式即可注入

请求地址

可以看到成功输出
屏幕快照 2019-01-18 下午5.22.29
其造成的原因主要是在ErrorMvcAutoConfiguration.java中的SpelView

该类调用处为

可以知道SpelView主要是为了解析Whitelabel Error Page模板页面去填充其中的相关数据
SpelView中,首先我们可以观察到其使用了StandardEvaluationContext

之后

用于递归解析在${...}中的表达式,也就是这里导致SpEl表达式注入并执行。其中用到SpEl表达式解析执行的目的主要是为了从当前contextrootObject取相关数据 如timestamp(上方功能用法示例中-> Variables 中介绍过)
屏幕快照 2019-01-18 下午7.10.45

大致流程为PropertyPlaceholderHelper类中通过parseStringValue方法递归字符串找到目标去掉 $(),这个方法中调用resolvePlaceholder方法来在context中找到对应的name,并在这里执行了getValue操作。由此造成命令执行。代码如下。

其核心思想就是在递归中从context下的message中取出需要再次递归解析的$(payload),由此来在下一次的解析后去掉$()并把其中payload当作传入的name参数来执行getValue操作。
屏幕快照 2019-01-18 下午7.25.45

其补丁是创建了一个新的NonRecursivePropertyPlaceholderHelper类,来防止递归解析路径中或者名字中含有的表达式。
详见: https://github.com/spring-projects/spring-boot/commit/edb16a13ee33e62b046730a47843cb5dc92054e6

Spring Data Commons远程代码执行漏洞(CVE-2018-1273)

影响版本:
1.13-1.13.10
2.0-2.0.5

漏洞主要因为是在自动解析用户的参数的时候采用了SpEL去解析propertyName
我们直接从补丁看漏洞代码是位于MapPropertyAccessor类的setPropertyValue方法
屏幕快照 2019-01-18 下午8.01.45
可以看到这是很直接的之前错误的使用了StandardEvaluationContext造成的RCE,修复方式也是主要通过替换为SimpleEvaluationContext完成。
漏洞形成的原因就是当用户在开发中利用了Spring-data-commons中的特性对用户的输入参数进行自动匹配时候,会将用户提交的form表单中的参数名作为SpEL执行。
漏洞代码:

开发者使用如下代码:

其流程简单上说就是在获取POST过来的参数时候因为要自动绑定进入实体类,所以首先要通过isWritableProperty中调用的getPropertyPath来判断参数名。如:传来的username参数是否是开发者controller中接收的UserForm实体类里的一个属性名。然后把用户传入的参数key即propertyName进行PARSER.parseExpression(propertyName),最后setValue(context,value)触发了恶意代码。(上方功能用法示例中-> Variables 中介绍过)
细节如果需要了解可以自己调试一下。
payload:

屏幕快照 2019-01-22 上午12.32.05
setValue(context,value)时候会把propertyName内的username作为一个集合,利用了SpEL集合选择的功能,所以就会执行中括号里面的SpEL表达式了。
屏幕快照 2019-01-20 下午12.19.44

防御方式

因为SpEL表达式注入漏洞导致攻击者可以通过表达式执行精心构造的任意代码,导致命令执行。为了防御该类漏洞,Spring官方推出了SimpleEvaluationContext作为安全类来防御该类漏洞。
官方文档:https://docs.spring.io/spring/docs/5.0.6.RELEASE/javadoc-api/org/springframework/expression/spel/support/SimpleEvaluationContext.html
常见用法:

总结

经过常见用法以及几个案例分析,我们可以知道,事实上在一般的开发后台过程中我们基本不会写出这样的漏洞点,一般就是通过注解或者XML用其Bean以及上下文中变量的存取功能。而出现漏洞的位置基本有两种,一是相关框架中在需要用一种通用的方法获取或者设置某对象中指定属性名的属性值的时候,也可以说使用SpEL的地方往往就是需要利用它内部使用反射的这个特点,从而可以省去我们编写的麻烦,来达到一些目的。二是在双重EL表达式评估中发生。发现该漏洞可以通过这些关键触发方法或者类如getValueStandardEvaluationContext等,当然也可以通过find-sec-bug这个插件来寻找。其防御方式是使用SimpleEvaluationContext来禁用其敏感的功能,从而阻止表达式注入执行问题的出现。

参考

https://docs.spring.io/spring/docs/3.0.x/reference/expressions.html
http://www.polaris-lab.com/index.php/archives/613/
https://m.habr.com/company/dsec/blog/433034/
http://blog.nsfocus.net/spel-vulnerability-technical-analysis-and-protection-scheme/
http://deadpool.sh/2017/RCE-Springs/
https://2018.zeronights.ru/wp-content/uploads/materials/10%20ZN2018%20WV%20-%20Spel%20injection%20.pdf
https://www.freebuf.com/vuls/172984.html
http://xxlegend.com/2018/04/12/CVE-2018-1273-%20RCE%20with%20Spring%20Data%20Commons%20分析报告/
https://www.secpulse.com/archives/75930.html

对一款针对微信的木马进行的xjb分析

假期最后一天,收到王老师(同学)发现的一款安卓木马,最后发现该木马实则在去年年底就已爆发过一次。不过伪装手法有趣,值得学习。

木马分析


木马主要来源:酷安

不法分子将木马与各式各样的软件捆绑一起,比如目前我看到的几种(绿色守护/APP Setting/Root_Explorer_v4.0.5)

木马样本:timesync.apk

其他伪装或者捆绑名称:一键卸载大师/wallpapercropper/谷歌应用下载器/软件一键移动到SD卡

为了简单说明该木马用途,这里借用下猎豹移动安全实验室的分析结果:

该微信盗号木马暗藏于鱼龙混杂的各类第三方定制ROM和APP中,伪装为安卓系统服务模块,通过弹出伪造的微信登录和支付的钓鱼界面,获得用户的登录密码以及支付密码后,再通过监控用户短信等手段,远程窃取微信支付绑定的银行卡余额。

经过分析该木马主要功能为如下:

上报用户短信,劫持微信钓鱼获取密码and支付密码,上传微信数据,卸载微信,摧毁系统

具体功能可以看看我截的控制端的图:

2345截图20170212221306

阅读剩余部分 –

LOGO

近期文章

标签

分类目录

文章归档

近期评论

功能

博客统计

  • 17,032 点击次数