
CVE-2026-86476에 대한 개념 증명: unixfd 역직렬화를 통한 GStreamer GstVideoMeta 힙 범위 초과 읽기/쓰기로, ASan 재현 및 RCE 체인을 포함합니다.
GStreamer의 libgstvideo(gst-plugins-base)에 존재하는 힙 버퍼 오버플로입니다. GstVideoMeta는 비디오 프레임의 픽셀이 메모리에 어떻게 배치되는지를 설명합니다. 이 설명이 신뢰할 수 없는 unixfd 피어로부터 역직렬화될 때, 설명이 주장하는 기하 구조가 이를 뒷받침하는 버퍼 내부에 들어맞는지 확인하는 검사가 전혀 없습니다. 피어는 과도하게 큰 평면 스트라이드를 설정할 수 있으며, 소비자는 할당 범위를 넘어 읽거나 씁니다.
| 필드 | 값 |
|---|---|
| CVE | CVE-2026-86476 |
| 분류 | CWE-125(범위 밖 읽기), CWE-787(범위 밖 쓰기), CWE-502(신뢰할 수 없는 데이터의 역직렬화)를 통해 도달 |
| 구성 요소 | libgstvideo: gstvideometa.c, video-frame.c |
| 영향 | 범위 밖 읽기(확인됨), 코드 실행으로 이어지는 범위 밖 쓰기(시연됨) |
| 트리거 | 조작된 평면 스트라이드를 가진 GstVideoMeta가 unixfd 피어로부터 역직렬화됨 |
| 결함 지점 | video_orc_convert_I420_BGRA, videoconvert ORC 커널 |
이 버그는 서로 맞물려 증폭되는 세 가지 누락된 검사입니다.
프레임 맵의 메타 분기에 크기 검사가 없음. video-frame.c의 gst_video_frame_map_id()에는 두 개의 분기가 있습니다. 메타가 없는 분기는 if (frame->map[0].size < info->size) goto invalid_size;로 끝납니다. GstVideoMeta가 존재할 때마다 선택되는 메타 분기는 info->width <= meta->width와 info->height <= meta->height만을 강제합니다. 이는 하한일 뿐입니다. 평면 기하 구조를 실제 메모리의 크기와 비교하는 것은 아무것도 없습니다.
default_map()은 평면당 1바이트만 검사함. gstvideometa.c에서 평면 메모리는 gst_buffer_find_memory(buffer, offset, 1, ...)로 확인됩니다. 크기 인자는 1입니다. 코드는 평면의 시작 오프셋에 1바이트가 존재함을 확인한 다음, 스트라이드를 그대로 복사합니다. 평면의 실제 범위인 stride * height는 할당과 비교되지 않습니다. 검사 바로 위에는 FIXME use plane size 주석이 있습니다.
역직렬화기는 아무것도 범위 검사하지 않음. video_meta_deserialize()는 바이트 리더가 입력을 다 소진하지 않았는지, n_planes <= GST_VIDEO_MAX_PLANES인지, version != 0인지만 검증합니다. 오프셋과 스트라이드는 스트림에서 그대로 복사됩니다. 각 스트라이드는 부호 있는 gint32로 읽히므로 음수 스트라이드도 허용됩니다.
수신 측에서 gst_unix_fd_src_create()는 빈 버퍼에 대해 메타를 역직렬화하고, 그 후에 피어의 메모리를 연결합니다. 메타가 지닌 스트라이드와 버퍼를 뒷받침하는 메모리의 크기는 결코 비교되지 않습니다.
악의적인 피어는 모든 평면 스트라이드가 1000000으로 설정된 320x240 I420 프레임에 대한 GstVideoMeta를 직렬화합니다. 소비자는 범위 검사 없이 이를 역직렬화하고, 작은 실제 메모리를 연결한 뒤 프레임을 매핑합니다. 각 평면의 첫 바이트가 존재하기 때문에 default_map()은 통과합니다. 그런 다음 videoconvert는 각 입력 평면을 stride * height 바이트만큼 읽으며 할당 범위를 넘어 나아갑니다.
이것이 읽기입니다. 쓰기는 그 거울상입니다. gst_video_frame_copy_plane()은 stride * height 바이트를 memcpy하므로, 거짓 메타를 지닌 작은 버퍼가 복사의 대상이 되면 memcpy는 그 뒷받침 메모리를 넘어 씁니다.
unixfd 전송은 FD가 아닌 버퍼를 memfd로 복사하므로, 기본 파이프라인은 읽기를 mmap된 매핑 경계에 부딪히게 합니다(크래시 및 정보 유출). 동일한 역직렬화기를 동일한 신뢰할 수 없는 바이트로 힙 기반 대상에 대해 구동하면 쓰기 원시 동작을 실행하게 됩니다. 전송 방식은 오버플로가 매핑 경계에 부딪히는지 힙에 부딪히는지를 결정합니다. 버그가 존재하는지 여부를 결정하지는 않습니다.
힙 쓰기의 경우, 개념 증명은 인접한 GstMiniObject의 헤더에 있는 free 함수 포인터를 덮어쓰고, 명령 문자열을 첫 번째 인자 레지스터에 들어갈 위치에 배치한 뒤, unref를 트리거하여 위조된 포인터가 system()으로 호출되게 합니다.
unixfd를 통해 완전히 신뢰하지 않는 프로세스로부터 비디오 버퍼를 받아들이는 모든 파이프라인. 샌드박스 처리된 미디어 디코더, 포털 스타일의 분리된 파이프라인, 그리고 권한 또는 신뢰 경계를 넘어 프레임을 수신하기 위해 unixfdsrc를 사용하는 모든 것.
확인된 사항:
libgstreamer-1.0.so.0.2804.0 (1.28.4-2)에 대한 쓰기 측.유닉스 소켓을 통한 두 프로세스, 표준 엘리먼트만 사용.
fakesrc ! capsfilter ! unixfdsink, 실제 스트라이드가 320/160/160이지만 평면 스트라이드가 1000000인 GstVideoMeta를 가진 유효한 320x240 I420 버퍼를 푸시.unixfdsrc ! videoconvert ! capsfilter ! fakesink, 여기서 capsfilter가 I420을 BGRA로 강제하므로 videoconvert는 메타 스트라이드로 프레임을 읽어야 함.| 실행 | 메타 스트라이드 | 결과 |
|---|---|---|
| 1 | 1000000 / 1000000 / 1000000 | video_orc_convert_I420_BGRA에서 ASan 하에 범위를 벗어난 읽기 |
| 2 | 320 / 160 / 160 | 정상 완료, 버퍼가 fakesink에 도달 |
읽기 측 증명을 위해서는 -Db_sanitize=address로 GStreamer를 빌드하십시오. RCE 개념 증명은 표준 빌드에서 실행되는데, ASan 빌드는 첫 번째 오버플로우 memcpy에서 중단되기 때문입니다.
default_map()에서 1바이트 gst_buffer_find_memory(..., 1, ...)를 stride * height 평면 범위에 대한 검사로 교체하십시오.gst_video_frame_map_id()의 메타 분기에 메타가 없는 분기와 동일한 invalid_size 검사를 부여하십시오.video_meta_deserialize()에서 버퍼에 대한 오프셋과 스트라이드의 범위를 검사하고, 음수 스트라이드를 거부하십시오.이 중 어느 하나라도 체인을 끊습니다. 세 가지 모두가 신뢰 경계를 넘는 구조체에 대한 올바른 수정입니다.