Rails Active Storage 임의 파일 읽기 취약점


요약
CVE-2026-66066은 Rails Active Storage가 비신뢰 이미지를 libvips로 분석하거나 변환하는 과정에서 발생하는 임의 파일 읽기 취약점이다. 이미지 업로드와 Active Storage 분석 또는 variant 생성 경로가 외부에 노출된 애플리케이션에서는 pre-auth 공격 경로가 될 수 있다. 공개 분석은 실행 환경과 delegate 구성에 따라 RCE 가능성도 제시하지만, 모든 취약 환경에서 RCE가 성립하는 것은 아니다.
Rails는 2026-07-29 security advisory를 공개했다. Patch는 libvips가 unfuzzed 또는 untrusted로 표시한 loader와 saver를 프로세스 수준에서 차단한다. 이 차단 기능을 지원하지 않는 libvips 또는 ruby-vips가 설치된 경우에는 애플리케이션 부팅을 중단한다.
| 항목 | 내용 |
|---|---|
| 식별자 | CVE-2026-66066, GHSA-xr9x-r78c-5hrm |
| 공개일 | 2026-07-29 |
| 영향 컴포넌트 | Rails Active Storage, ruby-vips, libvips |
| 공격 조건 | 공격자가 이미지를 제공하고 metadata 분석 또는 variant 생성을 유도할 수 있어야 함 |
| 인증 조건 | 애플리케이션의 업로드 정책에 따라 pre-auth 악용 가능 |
| Exploit primitive | libvips의 비신뢰 loader·saver를 통한 로컬 파일 읽기 |
| 추가 영향 | delegate와 설치된 도구에 따라 RCE 가능 |
| Patch 핵심 | Vips.block_untrusted(true) 호출과 최소 의존성 버전 강제 |
파일 읽기 이후의 영향
Rails secret, 세션 서명 키, 데이터베이스 자격 증명, 클라우드 토큰이 읽히면 세션 위조와 외부 시스템 접근으로 이어질 수 있다. RCE 성립 여부와 별개로 비밀정보 유출을 기준으로 사고 범위를 산정해야 한다.
취약 지점
Active Storage의 Vips analyzer와 transformer는 업로드된 이미지의 크기를 분석하거나 variant를 만들 때 ruby-vips를 통해 libvips 작업을 호출한다. Patch 전에는 외부 입력 처리 경로에서도 libvips가 비신뢰 콘텐츠에 안전하지 않다고 표시한 loader와 saver를 사용할 수 있었다.
libvips는 충분히 fuzzing되지 않았거나 비신뢰 입력에 적합하지 않은 작업을 unfuzzed 또는 untrusted로 표시한다. 일부 loader는 파일이나 외부 리소스 참조를 해석할 수 있으며, 일부 출력 형식은 ImageMagick 같은 외부 도구에 처리를 위임한다. CVE-2026-66066의 직접적인 exploit primitive는 이 처리 경계를 통한 서버 로컬 파일 읽기다.
공격 경로는 애플리케이션별 attachment 구현에 따라 달라진다.
| 단계 | 처리 흐름 | 보안상 의미 |
|---|---|---|
| 1 | 조작된 이미지 attachment 업로드 | 외부 입력이 Active Storage에 저장됨 |
| 2 | metadata 분석 또는 variant 생성 | 입력이 Vips analyzer나 transformer에 전달됨 |
| 3 | 비신뢰 loader·saver 선택 | 로컬 파일 접근 primitive가 발생할 수 있음 |
| 4 | 분석 결과 또는 생성된 이미지 반환 | 파일 내용이 metadata나 출력 이미지에 반영될 수 있음 |
| 5 | 노출된 secret 악용 | 세션 위조나 외부 서비스 접근으로 확대 가능 |
Rails가 모든 애플리케이션에 공통으로 제공하는 단일 취약 HTTP 엔드포인트나 파라미터는 없다. 실제 진입점은 attachment 업로드 컨트롤러, direct upload 구성, analyzer 실행 시점, variant URL 노출 방식에 따라 달라진다.
Patch 분석
Patch는 Vips 초기화 코드를 active_storage/vips로 모으고 analyzer와 transformer가 이 파일을 먼저 불러오도록 변경한다.
ActiveStorage::Analyzer::ImageAnalyzer::Vips에서는 직접 수행하던 ruby-vips 로딩을 다음 코드로 교체했다.
require "active_storage/vips"
ActiveStorage::Transformers::Vips도 클래스가 로드되기 전에 공통 초기화와 image_processing/vips를 불러온다.
require "active_storage/vips"
require "image_processing/vips"
공통 초기화 경로는 ruby-vips가 사용 가능한 경우 다음 차단을 프로세스 전체에 적용한다.
Vips.block_untrusted(true)
image_processing/vips 2.0도 로드될 때 같은 차단을 수행한다. Patch는 이를 애플리케이션 initializer보다 먼저 로드해 초기화 순서를 고정한다. 그렇지 않으면 initializer가 필요한 loader를 다시 활성화한 뒤 transformer의 지연 로딩이 차단 상태를 다시 덮어쓸 수 있다.
차단 API가 없으면 다음 조건으로 부팅을 중단한다.
unless Vips.respond_to?(:block_untrusted)
raise <<~ERROR.squish
libvips's unfuzzed operations are not safe to use with untrusted content, and
Active Storage cannot disable them. Disabling them requires libvips 8.13 or
later and ruby-vips 2.2.1 or later.
ERROR
end
| 의존성 | 최소 지원 버전 |
|---|---|
| libvips | 8.13 |
| ruby-vips | 2.2.1 |
두 버전은 block_untrusted 기능을 사용할 수 있는 최소 조합이다. ruby-vips가 설치돼 있고 이 기능을 사용할 수 없으면 Active Storage는 RuntimeError를 발생시키고 보호되지 않은 상태로 실행하지 않는다.
호환성 변화
비신뢰 loader와 saver 차단은 일부 이미지 처리 동작을 중단시킨다.
| 형식 또는 처리 | Patch 이후 동작 |
|---|---|
| BMP, ICO, PSD variant 변환 | Vips::Error 발생 가능 |
| BMP, ICO, PSD 분석 | width, height가 기록되지 않을 수 있음 |
| SVG, JPEG XL, JPEG 2000, Netpbm 분석 | width, height가 기록되지 않을 수 있음 |
| FITS, JXL 출력 | Vips::Error 발생 가능 |
| ImageMagick에 위임되는 출력 | Vips::Error 발생 가능 |
| Attachment 저장과 원본 다운로드 | 변경 없음 |
:mini_magick을 variant processor로 사용하는 애플리케이션의 이미지 처리 방식은 바뀌지 않는다. 다만 ruby-vips가 함께 설치돼 있으면 loader·saver 차단과 버전 검사는 프로세스 전체에 적용된다.
영향 범위 식별
Active Storage와 Vips 관련 설정 및 의존성을 확인한다.
rg -n "active_storage|variant_processor|ruby-vips|image_processing" `
Gemfile Gemfile.lock config app
다음 조건이 겹치면 우선 점검 대상이다.
- Active Storage variant processor가
:vips로 설정돼 있다. - 외부 사용자가 이미지 attachment를 업로드할 수 있다.
- 업로드 후 metadata 분석이나 variant 생성이 자동 실행된다.
- 요청 처리 중 variant를 동기 생성한다.
- BMP, ICO, PSD, SVG, JXL, JPEG 2000, Netpbm 같은 형식을 처리한다.
- Rails 프로세스가 secret이나 클라우드 자격 증명을 읽을 수 있다.
- ImageMagick 등 외부 delegate가 같은 환경에 설치돼 있다.
:mini_magick만 사용하면서 ruby-vips가 불필요하게 설치된 경우에는 해당 의존성을 제거하면 프로세스 수준 차단과 버전 검사에 따른 영향을 피할 수 있다.
대응
Rails security advisory가 지정한 수정 release로 업데이트한다. 배포 환경에는 libvips 8.13 이상과 ruby-vips 2.2.1 이상이 포함돼야 한다.
Commit을 선별 적용할 때는 다음 변경이 모두 필요하다.
- analyzer와 transformer가
active_storage/vips를 먼저 로드하도록 변경 image_processing/vips의 초기 로딩Vips.block_untrusted(true)호출Vips.respond_to?(:block_untrusted)를 이용한 부팅 시 검사- 지원하지 않는 의존성 조합에서
RuntimeError발생
Patch 적용 후 특정 형식에서 Vips::Error가 발생하면 해당 MIME type을 variable_content_types에서 제거할 수 있다.
Rails.application.config.active_storage.variable_content_types -=
%w[
image/bmp
image/vnd.microsoft.icon
image/vnd.adobe.photoshop
]
이 설정이 적용된 attachment는 variable image로 처리되지 않으며 variant도 생성되지 않는다. 비신뢰 loader나 saver를 initializer에서 다시 활성화하면 patch의 보호 경계가 해제된다. 해당 형식이 반드시 필요하면 Rails secret과 자격 증명에 접근할 수 없는 별도 프로세스에서 처리한다.
파일 읽기가 의심되는 환경에서는 Rails secret, 세션 서명 키, 데이터베이스 자격 증명, 배포 토큰, 클라우드 자격 증명을 교체하고 기존 세션을 무효화한다.
탐지 포인트
네트워크
- 이미지 업로드 직후 이어지는 Active Storage 분석 또는 variant 요청
- 이미지 MIME type과 실제 파일 signature가 일치하지 않는 업로드
- 일반적인 이미지와 비교해 크기나 응답 시간이 비정상적인 variant 요청
- 생성된 variant에 예상하지 않은 텍스트 또는 서버 설정값이 포함된 응답
엔드포인트
- BMP, ICO, PSD, SVG, JXL, JPEG 2000, Netpbm attachment 처리 기록
- 동일 attachment에 대해 반복되는 analyzer 또는 variant 요청
- Patch 적용 후 증가한
Vips::Error - 동기식 variant 생성 요청에서 발생한 5xx 응답
- Active Storage blob 생성 직후 비정상적인 형식의 representation 요청
서버
- 이미지 처리 시점과 겹치는 secret, 환경 설정 파일 또는 자격 증명 파일 접근
- Rails 프로세스에서 ImageMagick 등 외부 delegate가 실행된 기록
- 부팅 중
block_untrusted지원 실패로 발생한RuntimeError - ruby-vips 또는 libvips 버전이 최소 요구사항을 충족하지 않는 배포
- 취약 기간 이후 세션 서명 실패, 비정상 로그인 또는 외부 서비스 토큰 사용 기록
참고 자료
- Rapid7 KindaRails2Shell 분석
- Ruby on Rails Security Advisory GHSA-xr9x-r78c-5hrm
- Ruby on Rails 수정 Commit Diff