Struts2漏洞利用原理及OGNL机制研究

 

概述

在MVC开发框架中,数据会在MVC各个模块中进行流转。而这种流转,也就会面临一些困境,就是由于数据在不同MVC层次中表现出不同的形式和状态而造成的:

View层—表现为字符串展示

数据在页面上是一个扁平的、不带数据类型的字符串,无论数据结构有多复杂,数据类型有多丰富,到了展示的时候,全都一视同仁的成为字符串在页面上展现出来。数据在传递时,任何数据都都被当作字符串或字符串数组来进行。

Controller层—表现为java对象

在控制层,数据模型遵循java的语法和数据结构,所有的数据载体在Java世界中可以表现为丰富的数据结构和数据类型,你可以自行定义你喜欢的类,在类与类之间进行继承、嵌套。我们通常会把这种模型称之为复杂的对象树。数据在传递时,将以对象的形式进行。

可以看到,数据在不同的MVC层次上,扮演的角色和表现形式不同,这是由于HTTP协议与java的面向对象性之间的不匹配造成的。如果数据在页面和Java世界中互相传递,就会显得不匹配。所以也就引出了几个需要解决的问题:

1.当数据从View层传递到Controller层时,我们应该保证一个扁平而分散在各处的数据集合能以一定的规则设置到Java世界中的对象树中去。同时,能够灵活的进行由字符串类型到Java中各个类型的转化。

2.当数据从Controller层传递到View层时,我们应该保证在View层能够以某些简易的规则对对象树进行访问。同时,在一定程度上控制对象树中的数据的显示格式。

我们稍微深入思考这个问题就会发现,解决数据由于表现形式的不同而发生流转不匹配的问题对我们来说其实并不陌生。同样的问题会发生在Java世界与数据库世界中,面对这种对象与关系模型的不匹配,我们采用的解决方法是使用如hibernate,iBatis等框架来处理java对象与关系数据库的匹配。

现在在Web层同样也发生了不匹配,所以我们也需要使用一些工具来帮助我们解决问题。为了解决数据从View层传递到Controller层时的不匹配性,Struts2采纳了XWork的一套完美方案。并且在此的基础上,构建了一个完美的机制,从而比较完美的解决了数据流转中的不匹配性。就是表达式引擎OGNL。它的作用就是帮助我们完成这种规则化的表达式与java对象的互相转化(以上内容参考自Struts2 技术内幕)。

关于OGNL

OGNL(Object Graph Navigation Language)即对象图形导航语言,是一个开源的表达式引擎。使用OGNL,你可以通过某种表达式语法,存取Java对象树中的任意属性、调用Java对象树的方法、同时能够自动实现必要的类型转化。如果我们把表达式看做是一个带有语义的字符串,那么OGNL无疑成为了这个语义字符串与Java对象之间沟通的桥梁。我们可以轻松解决在数据流转过程中所遇到的各种问题。
OGNL进行对象存取操作的API在Ognl.java文件中,分别是getValue、setValue两个方法。getValue通过传入的OGNL表达式,在给定的上下文环境中,从root对象里取值:

setValue通过传入的OGNL表达式,在给定的上下文环境中,往root对象里写值:

OGNL同时编写了许多其它的方法来实现相同的功能,详细可参考Ognl.java代码。OGNL的API很简单,无论何种复杂的功能,OGNL会将其最终映射到OGNL的三要素中通过调用底层引擎完成计算,OGNL的三要素即上述方法的三个参数expression、root、context。

OGNL三要素

Expression(表达式):

Expression规定OGNL要做什么,其本质是一个带有语法含义的字符串,这个字符串将规定操作的类型和操作的内容。OGNL支持的语法非常强大,从对象属性、方法的访问到简单计算,甚至支持复杂的lambda表达式。

Root(根对象):

OGNL的root对象可以理解为OGNL要操作的对象,表达式规定OGNL要干什么,root则指定对谁进行操作。OGNL的root对象实际上是一个java对象,是所有OGNL操作的实际载体。

Context(上下文):

有了表达式和根对象,已经可以使用OGNL的基本功能了。例如,根据表达式对root对象进行getvalue、setvalue操作。
不过事实上在OGNL内部,所有的操作都会在一个特定的数据环境中运行,这个数据环境就是OGNL的上下文。简单说就是上下文将规定OGNL的操作在哪里进行。
OGNL的上下文环境是一个MAP结构,定义为OgnlContext,root对象也会被添加到上下文环境中,作为一个特殊的变量进行处理。

OGNL基本操作

对root对象的访问:

针对OGNL的root对象的对象树的访问是通过使用‘点号’将对象的引用串联起来实现的。通过这种方式,OGNL实际上将一个树形的对象结构转化为一个链式结构的字符串来表达语义,如下:
//获取root对象中的name属性值
name
//获取root对象department属性中的name属性值
department.name
//获取root对象department属性中的manager属性中的name属性值
department.manager.name

对上下文环境的访问:

Context上下文是一个map结构,访问上下文参数时需要通过#符号加上链式表达式来进行,从而表示与访问root对象的区别,如下:
//获取上下文环境中名为introduction的对象的值
introduction
//获取上下文环境中名为parameters的对象中的user对象中名为name属性的值
#parameters.user.name

对静态变量、方法的访问:

在OGNL中,对于静态变量、方法的访问通过@[class]@[field/method]访问,这里的类名要带着包名。如下
//访问com.example.core.Resource类中名为img的属性值
@com.example.core.Resource@img
//调用com.example.core.Resource类中名为get的方法
@com.example.core.Resource@get()

访问调用:

//调用root对象中的group属性中users的size()方法
group.users.size()
//调用root对象中group中的containsUser方法,并传递上下文环境中名为user值作为参数
group.containsUser(#user)

构造对象

OGNL支持直接通过表达式来构造对象,构造的方式主要包括三种:

1.构造List:使用{},中间使用逗号隔开元素的方式表达列表

2.构造map:使用#{},中间使用逗号隔开键值对,并使用冒号隔开key和value

3.构造对象:直接使用已知的对象构造函数来构造对象

可以看到OGNL在语法层面上所表现出来的强大之处,不仅能够直接对容器对象构造提供语法层面支持,还能够对任意java对象提供支持。然而正因为OGNL过于强大,它也造成了诸多安全问题。恶意攻击者通过构造特定数据带入OGNL表达式解析并执行,而OGNL可以用来获取和设置Java对象的属性,同时也可以对服务端对象进行修改,所以只要绕过沙盒恶意攻击者甚至可以执行系统命令进行系统攻击。

深入OGNL实现类

在OGNL三要素中,context上下文环境为OGNL提供计算运行的环境,这个运行环境在OGNL内部由OgnlContext类实现,它是一个map结构。在OGNL.java中可以看到OgnlContext会在OGNL计算时动态创建,同时传入map结构的context,并根据传入的参数设定OGNL计算的一些默认行为以及设置root对象。

这里看到MemberAccess属性,在struts2漏洞利用中经常看到。转到OgnlContext类中查看。

MemberAccess指定OGNL的对象属性、方法访问策略,这里初始默认情况下是flase,即默认不允许访问。在struts2中SecurityMemberAccess类封装了DefaultMeberAccess类并进行了扩展,指定是否支持访问静态方法以及通过指定正则表达式来规定某些属性是否能够被访问。其中allowStaticMethodAccess默认为flase,即默认禁止访问静态方法。

另外在OGNL实现了MethodAccessor及PropertyAccessor类规定OGNL在访问方法和属性时的实现方式,详细可参考代码文件。

在struts2中,XWorkMethodAccessor类对MethodAccessor进行了封装,其中在callStaticMethod方法中可以看到,程序首先从上下文获取到DENY_METHOD_EXECUTION值并转换为布尔型,如果为false则可以执行静态方法,如果为true则不执行静态发放并返回null。

Struts2漏洞利用原理

上文已经详细介绍了OGNL引擎,因为OGNL过于强大,它也造成了诸多安全问题。恶意攻击者通过构造特定数据带入OGNL表达式即可能被解析并执行,而OGNL可以用来获取和设置Java对象的属性,同时也可以对服务端对象进行修改,所以只要绕过Struts2的一些安全策略,恶意攻击者甚至可以执行系统命令进行系统攻击。如struts2远程代码执行漏洞s2-005。

虽然Struts2出于安全考虑,在SecurityMemberAccess类中通过设置禁止静态方法访问及默认禁止执行静态方法来阻止代码执行。即上面提及的denyMethodExecution为true,MemberAccess为false。但这两个属性都可以被修改从而绕过安全限制执行任意命令。s2-005
POC如下:

http://mydomain/MyStruts.action?('\u0023_memberAccess[\'allowStaticMethodAccess\']')(meh)=true&(aaa)(('\u0023context[\'xwork.MethodAccessor.denyMethodExecution\']\u003d\u0023foo')(\u0023foo\u003dnew%20java.lang.Boolean("false")))&(asdf)(('\u0023rt.exec(‘ipconfig’)')(\u0023rt\[email]u003d@java.lang.Runt[/email]ime@getRuntime()))=1

在POC中\u0023是因为S2-003对#号进行了过滤,但没有考虑到unicode编码情况,而通过#_memberAccess[\'allowStaticMethodAccess\']')(meh)=true,可以设置允许访问静态方法,因为getRuntime方法是静态的,通过context[\'xwork.MethodAccessor.denyMethodExecution\']=#foo')(#foo=new%20java.lang.Boolean("false")设置拒绝执行方法为false,也就是允许执行静态方法。最后调用rt.exec(‘ipconfig’)执行任意命令。

 

Struts2漏洞利用原理及OGNL机制的更多相关文章

  1. Struts2漏洞利用实例

    Struts2漏洞利用实例 如果存在struts2漏洞的站,administrator权限,但是无法加管理组,内网,shell访问500. 1.struts2 漏洞原理:struts2是一个框架,他在 ...

  2. Struts2漏洞利用工具下载(更新2017-V1.8版增加S2-045/S2-046)

    Struts2漏洞利用工具下载(已更新V1.8版) 2017-03-21:增加S2-046,官方发布S2-046和S2-045漏洞引发原因一样,只是利用漏洞的位置发生了变化,S2-046方式可能绕过部 ...

  3. 20155306 白皎 0day漏洞——漏洞利用原理之GS

    20155306 白皎 0day漏洞--漏洞利用原理之GS 一.GS安全编译选项的保护原理 1.1 GS的提出 在第二篇博客(栈溢出利用)中,我们可以通过覆盖函数的返回地址来进行攻击,面对这个重灾区, ...

  4. 20155306 白皎 0day漏洞——漏洞利用原理之DEP

    20155306 白皎 0day漏洞--漏洞利用原理之DEP 一.DEP机制的保护原理 1.为什么出现DEP? 溢出攻击的根源在于现代计算机对数据和代码没有明确区分这一先天缺陷,就目前来看重新去设计计 ...

  5. 20155306 白皎 0day漏洞——漏洞利用原理之栈溢出利用

    20155306 白皎 0day漏洞--漏洞利用原理之栈溢出利用 一.系统栈的工作原理 1.1内存的用途 根据不同的操作系统,一个进程可能被分配到不同的内存区域去执行.但是不管什么样的操作系统.什么样 ...

  6. Struts2漏洞利用

    Struts漏洞合集 Struts-S2-013漏洞利用 受影响版本 Struts 2.0.0 - Struts 2.3.14.1 漏洞利用 任意命令执行POC: ${(#_memberAccess[ ...

  7. Windows漏洞利用技术概述

    Windows漏洞利用技术总结 1. 前言 本文是我对漏洞利用技术的学习总结,也是自己践行QAD (Questions.Answer.Discussions)的一次实践.本文通过阅读几位大牛的文章.演 ...

  8. Metaploit-永恒之蓝漏洞利用

    目录 Metaploit介绍 实验环境 漏洞利用过程 Metaploit介绍 本次测试主要是利用永恒之蓝漏洞对windows7进行控制利用,掌握Metaploit工具的使用,知道永恒之蓝的漏洞利用原理 ...

  9. PHP文件包含漏洞(利用phpinfo)复现

    0x01 简介 PHP文件包含漏洞中,如果找不到可以包含的文件,我们可以通过包含临时文件的方法来getshell.因为临时文件名是随机的,如果目标网站上存在phpinfo,则可以通过phpinfo来获 ...

随机推荐

  1. Linux下 sleep函数的注意事项

    1. 休眠sleep(unsigned int)为线程内操作  所以如果不同线程,信号量SIGALRM是不能中断sleep():  编写程序进行测试 //timercreate_demo.cpp #i ...

  2. 公众号的TOKEN配置PHP代码

    1.在后台添加好URL和TOKEN和生成43位随机码  注意域名URL需要备案 2.上传到服务器 3.公众号后台要点提交即可 error_reporting(0); $signature=$_REQU ...

  3. CentOS中安装JDK与Intellij idea

    卸载CentOS中自带openjdk CentOS自带openjdk,可以先用java –version检测是否存在jdk版本.如果存在,最好在安装oracle的jdk之前最好卸载,可以使用如下指令 ...

  4. SpringBoot接口服务处理Whitelabel Error Page

    转载请注明来源:http://blog.csdn.net/loongshawn/article/details/50915979 <SpringBoot接口服务处理Whitelabel Erro ...

  5. zookeeper是如何选取主leader的?

    以一个简单的例子来说明整个选举的过程.假设有五台服务器组成的zookeeper集群,它们的id从1-5,同时它们都是最新启动的,也就是没有历史数据,在存放数据量这一点上,都是一样的.假设这些服务器依序 ...

  6. activiti链接

    工作流学习——重要概念扫盲篇一步曲 http://blog.csdn.NET/zwk626542417/article/details/46592471

  7. strerror和perror函数详解

    /*#include <string.h> char *strerror(int errnum); 它返回errnum的值所对应的错误提示信息,例如errnum等于12的话,它就会返回&q ...

  8. Android——Activity生命周期(转)

    Activity生命周期   子曰:溫故而知新,可以為師矣.<論語> 学习技术也一样,对于技术文档或者经典的技术书籍来说,指望看一遍就完全掌握,那基本不大可能,所以我们需要经常回过头再仔细 ...

  9. Hive及HBase数据迁移

    一. Hive数据迁移 场景:两个Hadoop平台集群之间Hive表迁移. 基本思路:Hive表元数据和文件数据export到HDFS文件,通过Distcp将HDFS迁移到另一个集群的HDFS文件,再 ...

  10. jquery post 同步异步总结

    最近在测试,发现有些效果不对,最后发现是post的执行顺序问题,所以研究了下,写了以下总结 1.post被请求多次,解决方法: 连接加入随机数 rand=""+Math.rando ...