Summary

Product Singtel WI-FI 6 ROUTER RT5703W
Vendor Singtel/Askey
Severity High - Adversaries may exploit software vulnerabilities to execute arbitrary commands on the underlying OS with root privileges.
Affected Versions V1.6.4-5194 (latest version as of writing)
Tested Versions V1.6.4-5194 (latest version as of writing)
Internal Identifier STAR-2023-0098
CVE Identifier TBD
CVE Description OS command injection vulnerability in net.cgi in Singtel WI-FI 6 ROUTER RT5703W V1.6.4-5194 allows an authenticated attacker on LAN to execute arbitrary OS commands via the /cgi-bin/login endpoint.
CWE Classification(s) CWE-78: Improper Neutralization of Special Elements used in an OS Command (‘OS Command Injection’)
CAPEC Classification(s) CAPEC-88: OS Command Injection

CVSS3.1 Scoring System

Base Score: 8.8 (High) Vector String: CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

Metric Value
Attack Vector (AV) Local
Attack Complexity (AC) Low
Privileges Required (PR) Low
User Interaction (UI) None
Scope (S) Changed
Confidentiality (C) High
Integrity (I) High
Availability (A) High

Product Overview

Singtel WI-FI 6 ROUTER RT5703W is a router provided by Singtel to their customers, manufactured by Askey Computer Corp.

Vulnerability Summary

There is an post-authenticated OS Command Injection vulnerability in Singtel WI-FI 6 ROUTER RT5703W at /cgi-bin/SetLoginPwd, allowing an adversary to execute arbitrary OS commands on the underlying OS with root privileges.

Vulnerability Details

It is discovered that in the password changing process of the admin panel via https://cap.singtel/1.6.4/cgi-bin/SetLoginPwd, the user-supplied new password value is inserted into a string that is later executed as a system command. Below is the code snippet related to the vulnerability within the change_pwd function, located at address 11aaac.

undefined4 change_pwd(char *username, char *encrypted_password, undefined4 *param_3)
{
  ...
  do {
    ...
    if (...) {
      if (...) {
        ...
        des_decrypt_password(encrypted_password, decrypted_password, 0x80);    // [1]
        memset(cmd, 0, 0x400);
        sprintf(cmd, "smbpasswd %s %s", username, decrypted_password);    // [2]
        do_cmd(cmd);    // [3] popen wrapper

At [1], the function argument is decrypted with DES. Then, at [2], the decrypted_password value is appended to the end of a command string, which is passed to do_cmd at [3], which is a wrapper function that calls popen. Therefore, this function is potentially vulnerable to command injection, depending on how the argument is handled by its callers.

The function change_pwd is called by SetLoginPwd (located at fcae8), which can be accessed via the /SetLoginPwd endpoint. Below is the code snippet related to the vulnerability.

int setloginpwd(undefined4 param_1,undefined4 param_2,undefined4 *param_3,undefined4 param_4)
{
  ...
  request_json = json_tokener_parse(request_data);
  ...
  if (...) {}
  else {
    ..
    json_object_object_get_ex(request_json, "NewPwd", &newpwd_obj);
    if (newpwdobj == 0) { ... }
    else {
      newpwd = (char *)0x0;
      newpwd_decrypted = (char *)0x0;
      newpwd_enc_str = (char *)json_object_get_string(newpwd_obj);
      newpwd_enc_len = strlen(newpwd_enc_str);
      if (newpwd_enc_len < 0x81) {
        newpwd_dec = (char *)malloc(local_24 * 4 + 2);
        res = dataDecryption(newpwd, newpwd_dec);
        if (res == 0) {
          strcpy(newpwd, newpwd_dec);
          urldecode(newpwd, newpwd_dec);
          newpwd_dec_len = strlen(newpwd_dec);
          if ((newpwd_dec_len < 0x11) && (newpwd_dec_len = strlen(newpwd_dec), newpwd_dec_len > 3)) {
            res2 = spec_char_check(newpwd_decrypted,"\"'");    // [4]
            if (res2 == 0) {
              des_encrypt_pwd(newpwd_dec, newpwd_encrypted, 0x100);    //[5]
              free(newpwd_decrypted);
              res3 = change_pwd(login_user, newpwd_encrypted, &res);    // [6]
              ...
}

At various parts in this function, the user-supplied password is decrypted then encrypted. We focus our attention on [5], where the password is encrypted with DES, right before passing it to change_pwd at [6], which is shown earlier to be potentially vulnerable to command injection.

At [4], spec_char_check is called to check if the user-supplied new password contains quotes ("'), so that change_pwd is only called if no quotes are present in the string. However, this is not sufficient to prevent command injection shown in [2] earlier. A new password string that contains shell metacharacters such as &, ;, or | allows an attacker to execute arbitrary OS commands.

Exploit Conditions

This vulnerability can be exploited when the attacker is connected to the router’s LAN, and has valid credentials for the admin panel.

Proof-of-Concept

We have tried our best to make the PoC as portable as possible. This report includes a functional exploit written in Python3 that exploits the OS command injection vulnerability to start a telnet service at port 31337, running as the console user with uid=0.

Requirements

  1. Python3
  2. Python requests library
    • To install, run pip3 install requests
  3. Node.js
  4. telnet
    • To install, run sudo apt install -y telnet.

Reproduction Steps

  1. Open poc.py and change the PASSWORD constant to the router’s admin password, e.g. PASSWORD = "adminpassword".
  2. Run python3 poc.py. The exploit will take a few seconds to complete.
  3. Run telnet cap.singtel 31338 to get a shell.

Exploit Details

The command injection can be exploited by setting the POST request field NewPwd to &<ANY COMMAND HERE> in its encrypted form.

Firstly, the DES encryption performed by the web application needs to be replicated locally. It is implemented in des.js attached along with this report. Next, the DES key and token needs to be retrieved from the server, as they are required for crafting requests such as /SetLoginPwd. These are done in steps 2 and 3 in the poc.py script.

Overall, the attached poc.py script performs the following steps:

  1. Login with valid credentials via https://cap.singtel:443/1.6.4/cgi-bin/Login.
  2. Obtain the DES key via https://cap.singtel:443/1.6.4/cgi-bin/GetInitInfo.
  3. Obtain the token via https://cap.singtel:443/1.6.4/cgi-bin/GetToken.
  4. Exploit the vulnerable endpoint at https://cap.singtel:443/1.6.4/cgi-bin/SetLoginPWD.

As the exploit requires modifying the admin password, after completing the exploit, the script will reset the password back to its original value.

In this script, the command cmd_exec telnetd -l sh -p 31338 is executed to start a telnet service listening on port 31338. cmd_exec starts the service in the context of the console user (uid=0).

Suggested Mitigations

Ensure that commands are executed as an argument list using functions like execve, instead of concatenating user input together and executing the command string.

Specifically, the following change should be made.

-         sprintf(cmd, "smbpasswd %s %s", username, password);
-         do_cmd(cmd);
+         execve("/usr/sbin/smbpasswd", (char*[]){ "smbpasswd", username, password, 0 }, 0);

Detection Guidance

It is possible to detect the exploitation of this vulnerability by checking the server’s access logs for all POST requests made to /cgi-bin/SetLoginPwd and manually verifying if the NewPwd field contains suspicious records (e.g. fuzzing, newline characters, OS commands). Note that the NewPwd field is encrypted so it will be less straightforward to check its contents.

Credits

Daniel Lim Wee Soong (@daniellimws) of STAR Labs SG Pte. Ltd. (@starlabs_sg)