FlawOpen

flawopen.com/Incidents/citrix-bleed-session-token-leak-october-2023

● Citrix · CVE-2023-4966 · October 2023 · CVSS 9.4 · 심각
보안 연구 · FlawOpen

Citrix Bleed: 세션 토큰 유출과 MFA 우회

CVE-2023-4966은 세션 토큰을 포함한 NetScaler 메모리를 노출해 MFA를 우회한 세션 탈취를 가능하게 했습니다. 악용은 2023년 8월 말 시작됐습니다.[1], [2]

💡 알기 쉬운 설명 (ELI5)

비서에게 메시지를 작은 카드에 옮겨 적고 길이를 알려 달라고 부탁했다고 해 봅시다. 카드에는 50자밖에 들어가지 않아 50자만 적었지만, 길이를 묻자 원문이 그랬으니 정직하게 "800자"라고 답합니다. 당신은 그 카드를 다른 사람에게 주며 800자를 읽으라고 합니다. 그 사람은 당신의 50자를 다 읽고도 종이의 나머지에 적힌 내용을 계속 읽어 내려갑니다.[2]

핵심 개념 및 용어

snprintf
고정 크기 버퍼에 문자열을 서식화해 넣는 C 함수입니다. 들어가지 않으면 잘라 내지만, 반환값은 전체 문자열이 가졌을 길이입니다.[3]
NetScaler ADC / Gateway
네트워크 경계에서 원격 접속과 부하 분산을 제공하는 Citrix 장비입니다. Gateway 또는 AAA 가상 서버로 구성된 장비만 영향을 받았습니다.[1]
버퍼 범위 밖 읽기(CWE-125)
버퍼 끝을 넘어 읽는 것입니다. 덮어쓰는 것은 없지만, 인접한 메모리에 있던 내용이 노출됩니다. 이는 구체적인 범위 밖 읽기 메커니즘(CWE-125)을 설명합니다. Citrix는 CVE-2023-4966을 더 넓은 CWE-119로 분류합니다.[1], [3]
세션 토큰
사용자가 이미 로그인했음을 증명하는 값입니다. 이를 제시하는 사람은 비밀번호나 MFA 확인 없이 그 사용자로 취급됩니다.[2]

프레임워크 매핑

사고 타임라인

2023-08

이후 사고 대응 보고서에 따르면, 이 결함의 제로데이 악용이 시작됩니다.[2]

2023-10-10

Citrix가 수정 빌드 14.1-8.50, 13.1-49.15, 13.0-92.19를 담은 보안 공지 CTX579459를 발표합니다.[1]

2023-10-18

CISA가 CVE-2023-4966을 알려진 악용 취약점 목록에 추가합니다.[4]

단계별 공격 실행 흐름

단계 1

지나치게 긴 헤더

매우 긴 Host 헤더를 가진 인증 없는 요청이 장비에서 OpenID Connect 디스커버리 응답을 만드는 코드에 도달합니다.[2], [3]

단계 2

길이를 잘못 읽는다

snprintf는 버퍼에 맞게 문자열을 자르지만 원래 길이를 반환하고, 코드는 이를 실제로 쓴 바이트 수로 취급합니다.[3], [2]

단계 3

버퍼 너머의 메모리가 전송된다

응답이 그 큰 길이로 전송되어, 장비는 잘린 JSON과 함께 인접한 메모리까지 돌려줍니다.[2]

단계 4

세션이 탈취된다

유출된 메모리에는 유효한 세션 토큰이 들어 있을 수 있습니다. 이를 재사용하면 공격자는 비밀번호와 MFA 없이 사용자의 로그인 세션을 얻습니다.[2], [1]

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

설명용 C 코드이며 NetScaler 소스 코드가 아닙니다. 수정 코드는 전체 출력의 예상 길이를 나타내는 snprintf 반환값을 확인합니다. 값이 음수이거나 버퍼 크기 이상이면 형식화가 실패했거나 출력이 잘린 것이므로 함수가 아무것도 보내지 않습니다. 따라서 전체 응답이 buf에 들어가는 경우에만 해당 길이를 send_response에 전달합니다.[3], [2]

취약한 구현
#include <stdio.h>

int send_response(const char *data, int len);

int send_oidc_config(const char *host)
{
    char buf[256];
    /* n은 실제로 기록될 바이트 수이며 sizeof(buf)보다 클 수 있습니다 */
    int n = snprintf(buf, sizeof(buf), "{\"issuer\":\"https://%s\"}", host);
    return send_response(buf, n);   /* n바이트를 보내기 위해 buf 범위 밖까지 읽습니다 */
}
보안 강화 패치
#include <stdio.h>

int send_response(const char *data, int len);

int send_oidc_config(const char *host)
{
    char buf[256];
    int n = snprintf(buf, sizeof(buf), "{\"issuer\":\"https://%s\"}", host);
    if (n < 0 || (size_t)n >= sizeof(buf))
        return -1;                  /* 잘린 출력을 거부하고 버퍼 범위를 넘겨 보내지 않습니다 */
    return send_response(buf, n);
}

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

출처