풀이 날짜: 2026-09-15 (사용자 확정). 익스플로잇 성공은 2026-09-16 사용자 확인에 따른다. 아래 실행 결과는 보존된 원본 기록이며 이번 감사에서 새로 실행한 결과가 아니다.

검사기가 허용한 ELF와 로더가 실행한 ELF

NX 3의 서비스는 입력 ELF에서 실행 가능한 section/segment와 일부 동적 태그를 검사한 뒤 실행한다. 핵심은 검사기와 로더가 같은 의미의 데이터를 검사·사용하는지다. 후속 solver-notes는 정수 overflow나 검사기 OOB를 필수 원인으로 삼지 않고, PT_DYNAMIC의 파일 위치와 실행 시 메모리 위치를 해석하는 차이를 분석한다.

check_elf의 실제 검사

아래는 original/server.c의 동적 태그 검사 루프다. 앞선 범위·정렬 검사는 생략했다.

for (uint64_t off = p_offset; off < p_offset + p_filesz; off += 16) {
    uint64_t tag = u64(d + off);
    if (tag == DT_NULL)
        break;
    if (unsafe_dynamic_tag(tag))
        return "Unsupported interpreter metadata";
}

검사기의 입력은 파일 버퍼 d이며, 위치는 p_offset, 범위는 p_filesz다. 반면 후속 분석에서 관찰한 glibc 동적 로더는 매핑된 주소의 dynamic table을 사용한다. 파일 범위를 올바르게 검사한 것과 로더가 사용할 의미상 대상을 완전히 검사한 것은 다르다.

항목 검사기 관점 실행 관점
dynamic metadata 파일 내 바이트 범위 매핑된 가상 주소의 데이터
실행 가능성 입력 segment의 PF_X 인터프리터 자체의 처리도 포함
진입점 ELF 헤더의 값 동적 로더의 초기 처리가 먼저 수행될 수 있음

구체적 payload layout이나 relocation 조합 없이도 이 표가 검사 계약의 틈을 설명한다.

분석과 실패 가설

초기 README는 정적 분석 범위를 기록하고, solver-notes는 실제 x86 게스트에서의 실행 관찰을 보완한다. 비실행 매핑의 정상 진입은 실패했지만 동적 로더 단계에서 다른 결과가 관찰되었다. 따라서 '실행 segment가 없으니 로더까지 포함해 어떤 코드도 실행될 수 없다'는 가정을 유지할 수 없다.

solver가 구분한 결과도 중요하다. 입력 디코딩 거부, ELF metadata 거부, 자식 timeout, 조기 EOF를 동일한 실패로 합치지 않는다. ELF가 검사기를 통과했다는 사실만으로 목표 동작이 실행되었다고 판정하지 않는다.

결과와 환경 제한

사용자는 익스플로잇 성공을 확인했다. 기존 기록에는 NX가 활성화된 x86 Linux 게스트 및 로컬 컨테이너에서의 성공과 원본 서비스 경로를 거친 clean boot 세 번의 결과가 있다. 반환값은 로컬 예시이며 공식 원격 성공 근거는 없다.

원 Docker base digest를 사용하지 못해 보유한 libc 환경으로 검증한 차이를 유지한다. 이 글의 로더 해석과 결과는 그 관찰 범위에 한정된다. 이번에는 ELF, solver, 검증기를 실행하지 않았다.

근거 자료

  • sources/nx_3/original/server.c
  • sources/nx_3/analysis/README.md
  • sources/nx_3/analysis/solver-notes.md

원본 문서·solver·검증 로그는 전달 staging에 보존되어 있다. 이 경로들은 provenance이며 CMS의 다운로드 첨부 링크가 아니다.