풀이 날짜: 2026-09-14 (사용자 확정). 익스플로잇 성공은 2026-09-16 사용자 확인에 따른다. 아래 실행 결과는 보존된 원본 기록이며 이번 감사에서 새로 실행한 결과가 아니다.
생성 중인 Array가 GC에서 사라지는 이유
문제의 핵심은 Frame::Run()이 배열 리터럴을 만드는 동안 사용하는 arr_def와 GC가 보는 참조 그래프가 다르다는 점이다. ARR_DEF에서 새 Array를 만들고 ARR_END에서 완성된 Array를 operand stack에 넣는다. 원소 표현식을 평가하는 중에도 함수 호출과 SafePoint가 가능하므로, 생성과 root 등록 사이에 GC가 실행될 수 있다.
객체 관계와 소스 근거
Frame::GetReferences()는 callee, caller, global, local, builtin, stack, codes를 반환한다. Frame::Run()의 지역 변수인 arr_def는 이 집합에 없다. GC는 C++ stack 전체를 보수적으로 검사하지 않는다.
| 시점 | 실행기가 가진 참조 | GC 관점 |
|---|---|---|
ARR_DEF 이후 |
C++ 지역 포인터 arr_def |
새 Array가 root graph에서 누락될 수 있음 |
| 원소 평가 중 | 이후 다시 쓸 생성 중 객체 | SafePoint에서 회수 가능 |
ARR_END |
operand stack에 Array 추가 | 정상 경로라면 이때부터 추적 가능 |
이는 exploit 실행 순서가 아니라 소스의 수명 관계다. SOLUTION은 배포 바이너리에서 생성된 Array 주소가 GC::SweepYoung에 의해 회수되고 이후 다시 사용되는 것을 확인했다고 기록한다. 따라서 단순 crash 가능성 추측과 구분된다.
길이 검사 오류의 별도 역할
Array::SetElem(long long, Object*)의 핵심 부분은 다음과 같다.
if(length->GetNum() <= idx) {
elems.resize(idx+1, &NONE());
length->SetNum(idx);
}
elems[idx] = obj;
확장 후 저장 공간의 크기는 idx+1인데 논리 길이는 idx다. 정상 객체에서 이것만으로 바로 임의 메모리 쓰기가 되는 것은 아니다. 최종 분석은 GC 수명 오류와 논리 길이·실제 저장 크기의 불일치가 함께 고려되어야 한다고 정리한다. root 누락, off-by-one 의미 오류, 그 조합의 메모리 손상 영향을 구분해야 과장 없는 설명이 된다.
가설과 후속 정정
| 가설 | 최종 판단 |
|---|---|
| H1: 혼합 타입 equality의 unchecked cast | 확인된 타입 처리 문제지만 독립적인 최종 경로로 채택하지 않음 |
| H2: 생성 중 Array의 GC root 누락 | 배포 바이너리에서 확인한 최종 root cause |
| H3: 논리 길이와 vector 크기 불일치 | 단독 의미 오류와 H2 결합 시 영향을 구분 |
| H4: worker의 GC 동기화 | 교착·가용성 문제를 별도 분석; 독립 race exploit이 필수라는 결론 아님 |
초기 ASTRA_STATIC의 source/binary 문자열 차이 주장은 후속 SOLUTION에서 정정되었다. 원본 source와 바이너리 모두 같은 오류 문자열을 포함하며, 조사한 핵심 handler에서 중요한 차이를 확인하지 못했다는 최신 결론을 따른다.
결과
사용자는 익스플로잇 성공을 확인했고, SOLUTION에는 배포 deploy/er 자체를 대상으로 한 로컬 flag 읽기 성공이 있다. 당시 aarch64 호스트에서 QEMU user mode와 Ubuntu 24.04를 사용했다. 원래 배포 이미지 digest 및 libc patch level의 완전한 일치는 주장하지 않는다. 공식 원격은 실행하지 않은 기록이며 이번 감사에서도 어떤 코드를 실행하지 않았다.
근거 자료
sources/er/analysis/source/src/frame.ccsources/er/analysis/source/src/array.ccsources/er/SOLUTION.mdsources/er/ASTRA_STATIC.md
원본 문서·solver·검증 로그는 전달 staging에 보존되어 있다. 이 경로들은 provenance이며 CMS의 다운로드 첨부 링크가 아니다.