본문 바로가기
Hack/Network

[CVE-2022-3786] OpenSSL punycode Decoding Vulnerability (Stack Buffer over Flow)

by Becoming a Hacker 2022. 11. 3.
반응형

영향 받는 대상

OpenSSL 3.0.x Version (3.0.0 ~ 3.0.6)

 

취약점 전제 조건

출처 : https://securitylabs.datadoghq.com/articles/openssl-november-1-vulnerabilities/

해당 취약점의 경우 CA가 취약한 인증서로 서명되었거나 유효한 인증서가 아님에도 불구하고 통신을 진행할 경우에만 발생 가능한 취약점 입니다. 즉, 일반적인 환경에서 발생할 수 있는 취약점은 아닙니다.

 

위와 같은 이유로 OpenSSL에서도 최초 취약점 등급이었던 Critical을 High로 다운그레이드 하였습니다. 

 

취약점 분석

해당 취약점은 Punycode Domain의 인증서 확인 및 이름 제약 조건 검사 함수인 ossl_a2ulabel에서 발생하는 취약점으로 Stack Buffer over Flow가 가능합니다. 다만, 덮어씌울 수 있는 값은 0x2e(.)으로 한정되어 있습니다.

 

ossl_a2ulabel에서 제약 조건을 검사하는 로직는 다음과 같습니다.

while (1) {
        char *tmpptr = strchr(inptr, '.');
        size_t delta = (tmpptr) ? (size_t)(tmpptr - inptr) : strlen(inptr);

        if (strncmp(inptr, "xn--", 4) != 0) {
            size += delta + 1;

            if (size >= *outlen - 1)
                result = 0;

            if (result > 0) {
                memcpy(outptr, inptr, delta + 1);
                outptr += delta + 1;
            }
        } else {
            unsigned int bufsize = LABEL_BUF_SIZE;
            unsigned int i;

            if (ossl_punycode_decode(inptr + 4, delta - 4, buf, &bufsize) <= 0)
                return -1;

            for (i = 0; i < bufsize; i++) {
                unsigned char seed[6];
                size_t utfsize = codepoint2utf8(seed, buf[i]);
                if (utfsize == 0)
                    return -1;

                size += utfsize;
                if (size >= *outlen - 1)
                    result = 0;

                if (result > 0) {
                    memcpy(outptr, seed, utfsize);
                    outptr += utfsize;
                }
            }

            if (tmpptr != NULL) {
            	// Vulnerable Point
                *outptr = '.';
                outptr++;
                size++;
                if (size >= *outlen - 1)
                    result = 0;
            }
        }

        if (tmpptr == NULL)
            break;

        inptr = tmpptr + 1;
    }

 

해당 로직을 4가지의 로직으로 나눠볼 수 있습니다.

1. delimiter(.)을 기준으로 문자열 나누기

char *tmpptr = strchr(inptr, '.');

2. 분리된 문자열 내 xn-- 존재 여부 확인

if(strncmp(inptr,"xn--",4)!=0) {

3. 로직에 따라 outptr 변수에 문자열 저장

  • xn-- 문자열이 존재하지 않을 경우
size += delta + 1;
if (size >= *outlen - 1)
    result = 0;

if (result > 0) {
    memcpy(outptr, inptr, delta + 1);
    outptr += delta + 1;
}
  • xn-- 문자열이 존재하는 경우
size += utfsize;
if (size >= *outlen - 1)
    result = 0;

if (result > 0) {
    memcpy(outptr, seed, utfsize);
    outptr += utfsize;
}

4. xn-- 문자열이 존재할 경우 outptr 변수에 "." 문자 추가 저장

written_out++;

 

제약 조건 검사 로직에 따르면, 지금까지 복사된 크기를 나타내는 size 값이 outptr의 크기에 해당하는 outlen 값보다 같거나 클 경우 result 값을 0으로 설정하여 이후의 문자열이 복사되지 않도록 하는 로직이 존재하고 있습니다.

 

그러나 xn-- 문자열이 존재할 경우 outptr 변수에 "."을 추가로 저장하는 로직에서는 result의 값과는 상관없이 outptr에 "."을 추가하고 있어 Stack Buffer over Flow가 발생하였습니다.

 

다만, 덮어씌울 수 있는 값이 "."(0x2e)로 한정되어 있어 DoS는 가능하나 RCE의 가능성은 낮다고 판단됩니다.

 

취약점이 패치된 3.0.7 Version에서는 size 값이 outlen 값보다 작을 경우에만 변수에 저장하도록 패치를 진행하였습니다.

int maxsize = *outlen;

#define PUSHC(c)                    \
    do                              \
        if (size++ < maxsize)       \
            *outptr++ = c;          \
        else                        \
            result = 0;             \
    while (0)
    
PUSHC(tmpptr != NULL ? '.' : '\0');

 

대응 방안

CVE-2022-3786 취약점은 OpenSSL Version을 3.0.7로 업그레이드 하는 것으로 피해를 방지할 수 있습니다.

댓글