[하루한줄] Broadcom SDK의 Buffer Overflow 취약점

URL

Swimming Upstream: Uncovering Broadcom SDK Vulnerabilities from Bug Reports

Target

  • Broadcom SDK

Explain

네트워크 기기에 UPnP 서비스를 구현하는 Broadcom SDK에서 발견된 스택 및 힙 버퍼 오버플로우 취약점의 세부 정보가 공개되었습니다.

해당 취약점은 약 10년 전 Broadcom SDK를 사용한 몇몇 기기에서 이미 공개되어 패치됐지만, 근본적인 원인인 Broadcom SDK에 대한 조치가 이루어지지 않아 여전히 다른 제품에서도 존재했던 것으로 알려졌습니다. 이러한 문제는 취약점을 찾는 것뿐만 아니라 취약점의 사후 관리와 커뮤니케이션, 공개 권고로 이루어지는 취약점의 인식 또한 중요하다는 것을 시사합니다.

Stack Buffer Overflow

스택 BOF 취약점은 UPnP 검색 프로토콜이 사용하는 HTTP 형식의 UDP 프로토콜인 SSDP(Simple Service Discovery Protocol) 메시지를 처리할 때 발생합니다.

SSDP를 통해 다음 두 가지 종류의 메시지를 보낼 수 있습니다.

  • M-SEARCH : 클라이언트가 네트워크에서 사용 가능한 서비스를 검색할 때 전송합니다.
  • NOTIFY : UPnP 서버가 멀티캐스트 그룹에 서비스 정보의 설정 등을 알리기 위해 전송합니다.

이중 M-SEARCH 메시지는 ssdp_msearch 함수에서 처리됩니다.

int ssdp_msearch(UPNP_CONTEXT *context) {
        char name[128]; 
...
        host = context->HOST; 
        if (!host || strcmp(host, "239.255.255.250:1900") != 0) 
                return -1; 

        man = context->MAN; 
        if (!man || strcmp(man, "\\"ssdp:discover\\"") != 0) 
                return -1; 
 
        st = context->ST; 
...
        else if (memcmp(st, "uuid:", 5) == 0) { // Vuln!
                /* uuid */ 
                type = MSEARCH_UUID; 
                st += 5; 
                strcpy(name, st); 
        } 
}

HTTP 요청의 ST 필드가 “uuid:”로 시작하는 경우 strcpy()name[128]ST 필드의 문자열을 복사하는데, 길이 검사가 존재하지 않아 아래와 같은 요청을 보내면 Stack Buffer Overflow가 발생합니다.

M-SEARCH * HTTP/1.1
HOST:239.255.255.250:1900
ST:uuid:AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...
MX:2
MAN:"ssdp:discover"

Heap Buffer Overflow

UPnP는 SOAP(Simple Object Access Protocol) 요청을 UPnP 제어 서비스에 전송해 port forwarding 규칙을 설정할 수 있습니다. SOAP 요청에는 내부 클라이언트, 내부 포트, 원격 IP, 원격 포트, 프로토콜, 규칙 지속시간이 포함됩니다.

취약점은 SOAP 요청에 따라 새로운 포트를 매핑하는 upnp_portmap_add 함수에서 발생합니다.

/* Add a new port mapping entry */
int
upnp_portmap_add
(
        UPNP_CONTEXT    *context,
        char            *remote_host,
        unsigned short  external_port,
        char            *protocol,
        unsigned short  internal_port,
        char            *internal_client,
        unsigned int    enable,
        char            *description,
        unsigned long   duration
)
{
        UPNP_PORTMAP_CTRL *portmap_ctrl;
        UPNP_PORTMAP *map;

        /* Get control body */
        portmap_ctrl = (UPNP_PORTMAP_CTRL *)(context->focus_ifp->focus_devchain->devctrl);

        /* data validation */
        if (strcasecmp(protocol, "TCP") != 0 &&
                strcasecmp(protocol, "UDP") != 0) {
                upnp_syslog(LOG_ERR, "add_portmap:: Invalid protocol");
                return SOAP_ARGUMENT_VALUE_INVALID;
        }
...
        /* Update database */
        map->external_port  = external_port;
        map->internal_port  = internal_port;
        map->enable         = enable;
        map->duration           = duration;
        map->book_time          = time(0);

        strcpy(map->remote_host,        remote_host);
        strcpy(map->protocol,           protocol);
        strcpy(map->internal_client,    internal_client);
        strcpy(map->description,        description);
...
        return 0;
}

upnp_portmap_add는 수신한 요청에 대한 유효성을 검증하고 기존에 설정된 port map이 없으면 새 port map 구조체를 할당합니다. 새로운 port map 구조체를 설정하는 과정에서 strcpy() 함수를 사용하고 길이 검증을 하지 않아 Heap Buffer Overflow가 발생합니다.