FlawOpen

flawopen.com/Teardowns/cve-2024-33066-qualcomm-wlan-log-redirection

● CVE-2024-33066 · CVSS 9.8 · 심각
보안 연구 · FlawOpen

CVE-2024-33066: Qualcomm WLAN 로그 메모리 손상

CVE-2024-33066은 Qualcomm WLAN Resource Manager가 호출자 지정 경로로 로그를 돌릴 때 메모리를 손상시킬 수 있습니다. 2024년 10월 수정됐습니다.

💡 알기 쉬운 설명 (ELI5)

누구든 전화해서 "앞으로 투숙객 명부 사본은 이 주소로 보내 주세요"라고 말할 수 있는 호텔 프런트를 떠올려 보세요. 직원은 불러 주는 내용을 작은 카드에 받아 적는데, 글씨가 카드 밖으로 넘쳐 책상까지 이어져도 개의치 않습니다. 그리고 적힌 곳으로 사본을 보냅니다. 지배인의 금고라도 말이죠. 해결책은 자료실 서랍 하나를 가리키는 짧은 이름표만 받는 것입니다.

핵심 개념 및 용어

WLAN Resource Manager
Qualcomm의 CVE-2024-33066 공지에 언급된 Wi-Fi 구성 요소입니다. 라우터, 메시 시스템, 액세스 포인트에 쓰이는 Qualcomm 네트워킹 플랫폼에서 동작합니다.
IPQ / QCN 칩셋
Qualcomm의 Wi-Fi 네트워킹 시스템온칩 제품군입니다. 영향 목록에는 IPQ5332, IPQ8074A, IPQ9574, QCN9000, Immersive Home 플랫폼이 포함되며 휴대폰 칩셋은 아닙니다.
CWE-20
부적절한 입력 검증: 코드가 의존하는 속성(최대 길이, 허용된 디렉터리 등)을 입력이 갖췄는지 확인하지 않고 그대로 믿는 것입니다.
디렉터리 기준 열기
openat()으로 디렉터리 서술자를 기준 삼아 '/'가 없는 단순한 이름으로 파일을 여는 방식입니다. 결과가 그 디렉터리 밖으로 나갈 수 없습니다.
CVSS 3.1 벡터
AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H: 네트워크로 도달 가능, 낮은 복잡도, 권한과 사용자 상호작용 불필요, 세 가지 속성 모두에 높은 영향(점수 9.8).

프레임워크 매핑

  • 이 가이드에서 설명하는 약점의 CWE 참조: CWE-20

근본 원인 분석 (Root Cause)

Qualcomm의 공지는 WLAN Resource Manager에서 "while redirecting log file to any file location with any file name" 메모리 손상이 발생했다고만 밝힙니다(CWE-20). 로그 리디렉션 요청과 함께 전달된 대상이 길이도, 가리키는 위치도 확인되지 않은 채 사용되었습니다. 아래 코드는 이 입력 검증 공백을 모델링한 설명용 코드이며 Qualcomm의 소스 코드가 아닙니다.

단계별 공격 실행 흐름

단계 1

1. 로그 리디렉션 요청이 도착한다

WLAN Resource Manager는 로그 출력을 다른 곳으로 보내 달라는 요청을 받아들입니다. CVSS 벡터(AV:N/PR:N/UI:N)는 이 결함을 권한이나 사용자 상호작용 없이 네트워크로 도달 가능한 것으로 평가합니다.

단계 2

2. 어떤 위치와 이름이든 받아들인다

Qualcomm의 설명에 따르면 리디렉션은 어떤 위치로든, 어떤 파일 이름으로든 가능했습니다. 요청된 대상의 길이도, 가리키는 곳도 확인하지 않았습니다.

단계 3

3. 검증되지 않은 입력이 메모리를 손상시킨다

아래 모델에서는 고정 크기 경로 버퍼보다 긴 이름이 버퍼를 넘치게 하고(strcpy), 설정 파일을 포함한 어떤 경로든 쓰기용으로 열 수 있습니다. Qualcomm은 자사 코드에서 어느 경우가 발생했는지 공개하지 않았습니다.

단계 4

4. 영향과 현황

CVSS 3.1은 기밀성, 무결성, 가용성에 대한 영향을 모두 높음으로 평가합니다(9.8). Qualcomm은 2024년 10월 공지에서 기기 제조사에 수정을 제공했으며, CISA 분류에는 알려진 악용이 기록되어 있지 않습니다.

소스 코드 비교: 취약한 구현 vs 보안 패치

이 취약점 유형을 보여 주는 설명용 모델이며 Qualcomm 코드가 아닙니다. 강화된 핸들러는 입력을 쓰기 전에 strnlen으로 길이를 제한하고, 고정된 문자 집합으로 된 짧은 이름만 받으며('/'와 선행 '.' 불가), 시작 시 정한 로그 디렉터리를 기준으로 openat()과 O_NOFOLLOW를 써서 엽니다. 따라서 미리 심어 둔 심볼릭 링크로 쓰기 대상을 돌릴 수 없습니다. 호출자는 파일 이름은 고를 수 있어도 위치는 고를 수 없고, 너무 긴 입력은 복사되지 않고 거부됩니다.

취약한 구현
#include <fcntl.h>
#include <string.h>

#define LOG_PATH_MAX 64

struct rm_log_cfg {
    char path[LOG_PATH_MAX];
    int fd;
};

/* “로그 리디렉션” 요청을 처리하며 `requested`는 요청 본문에서 옵니다. */
int rm_set_log_destination(struct rm_log_cfg *cfg, const char *requested)
{
    strcpy(cfg->path, requested);   /* 길이 검사 없음: 긴 입력이 path[] 범위를 넘어씁니다. */
    cfg->fd = open(cfg->path, O_WRONLY | O_CREAT | O_APPEND, 0644);  /* 어떤 위치든 */
    return cfg->fd < 0 ? -1 : 0;
}
보안 강화 패치
#include <errno.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>

#define LOG_NAME_MAX 32

struct rm_log_cfg {
    int dir_fd;                /* 고정 로그 디렉터리이며 시작 시 한 번만 엽니다. */
    char name[LOG_NAME_MAX];
    int fd;
};

static int valid_log_name(const char *name, size_t len)
{
    if (len == 0 || len >= LOG_NAME_MAX || name[0] == '.')
        return 0;
    for (size_t i = 0; i < len; i++) {
        char c = name[i];
        int ok = (c >= 'a' && c <= 'z') || (c >= '0' && c <= '9') ||
                 c == '-' || c == '_' || c == '.';
        if (!ok)
            return 0;          /* /를 거부하므로 이름이 dir_fd 밖으로 나갈 수 없습니다. */
    }
    return 1;
}

int rm_set_log_destination(struct rm_log_cfg *cfg, const char *requested)
{
    size_t len = strnlen(requested, LOG_NAME_MAX);
    if (!valid_log_name(requested, len))
        return -EINVAL;

    int fd = openat(cfg->dir_fd, requested,
                    O_WRONLY | O_CREAT | O_APPEND | O_NOFOLLOW | O_CLOEXEC, 0600);
    if (fd < 0)
        return -errno;

    memcpy(cfg->name, requested, len + 1);
    if (cfg->fd >= 0)
        close(cfg->fd);
    cfg->fd = fd;
    return 0;
}

엔지니어링 및 시스템 보안 강화 체크리스트

출처