Pastebin launched a little side project called VERYVIRAL.com, check it out ;-) Want more features on Pastebin? Sign Up, it's FREE!
Guest

dLink_router

By: a guest on Oct 14th, 2013  |  syntax: None  |  size: 6.68 KB  |  views: 48  |  expires: Never
download  |  raw  |  embed  |  report abuse  |  print
Text below is selected. Please press Ctrl+C to copy to your clipboard. (⌘+C on Mac)
  1. Reverse Engineering a D-Link Backdoor
  2.   By Craig | October 12, 2013 | Embedded Systems, Security
  3.  
  4. All right. It’s Saturday night, I have no date, a two-liter bottle of Shasta and my all-Rush mix-tape…let’s hack.
  5.  
  6. On a whim I downloaded firmware v1.13 for the DIR-100 revA. Binwalk quickly found and extracted a SquashFS file system, and soon I had the firmware’s web server (/bin/webs) loaded into IDA:
  7. Strings inside /bin/webs
  8.  
  9. Strings inside /bin/webs
  10.  
  11. Based on the above strings listing, the /bin/webs binary is a modified version of thttpd which provides the administrative interface for the router. It appears to have been modified by Alphanetworks (a spin-off of D-Link). They were even thoughtful enough to prepend many of their custom function names with the string “alpha”:
  12. Alphanetworks' custom functions
  13.  
  14. Alphanetworks’ custom functions
  15.  
  16. The alpha_auth_check function sounds interesting!
  17.  
  18. This function is called from a couple different locations, most notably from alpha_httpd_parse_request:
  19. Function call to alpha_auth_check
  20.  
  21. Function call to alpha_auth_check
  22.  
  23. We can see that alpha_auth_check is passed one argument (whatever is stored in register $s2); if alpha_auth_check returns -1 (0xFFFFFFFF), the code jumps to the end of alpha_httpd_parse_request, otherwise it continues processing the request.
  24.  
  25. Some further examination of the use of register $s2 prior to the alpha_auth_check call indicates that it is a pointer to a data structure which contains char* pointers to various pieces of the received HTTP request, such as HTTP headers and the requested URL:
  26. $s2 is a pointer to a data structure
  27.  
  28. $s2 is a pointer to a data structure
  29.  
  30. We can now define a function prototype for alpha_auth_check and begin to enumerate elements of the data structure:
  31.  
  32.     struct http_request_t
  33.     {
  34.         char unknown[0xB8];
  35.         char *url; // At offset 0xB8 into the data structure
  36.     };
  37.  
  38.     int alpha_auth_check(struct http_request_t *request);
  39.  
  40. alpha_auth_check itself is a fairly simple function. It does a few strstr’s and strcmp’s against some pointers in the http_request_t structure, then calls check_login, which actually does the authentication check. If the calls to any of the strstr’s / strcmp’s or check_login succeed, it returns 1; else, it redirects the browser to the login page and returns -1:
  41. alpha_auth_check code snippet
  42.  
  43. alpha_auth_check code snippet
  44.  
  45. Those strstr’s look interesting. They take the requested URL (at offset 0xB8 into the http_request_t data structure, as previously noted) and check to see if it contains the strings “graphic/” or “public/”. These are sub-directories under the device’s web directory, and if the requested URL contains one of those strings, then the request is allowed without authentication.
  46.  
  47. It is the final strcmp however, which proves a bit more compelling:
  48. An interesting string comparison in alpha_auth_check
  49.  
  50. An interesting string comparison in alpha_auth_check
  51.  
  52. This is performing a strcmp between the string pointer at offset 0xD0 inside the http_request_t structure and the string “xmlset_roodkcableoj28840ybtide”; if the strings match, the check_login function call is skipped and alpha_auth_check returns 1 (authentication OK).
  53.  
  54. A quick Google for the “xmlset_roodkcableoj28840ybtide” string turns up only a single Russian forum post from a few years ago, which notes that this is an “interesting line” inside the /bin/webs binary. I’d have to agree.
  55.  
  56. So what is this mystery string getting compared against? If we look back in the call tree, we see that the http_request_t structure pointer is passed around by a few functions:
  57.  
  58. call_graph
  59.  
  60. It turns out that the pointer at offset 0xD0 in the http_request_t structure is populated by the httpd_parse_request function:
  61. Checks for the User-Agent HTTP header
  62.  
  63. Checks for the User-Agent HTTP header
  64. Populates http_request_t + 0xD0 with a pointer to the User-Agent header string
  65.  
  66. Populates http_request_t + 0xD0 with a pointer to the User-Agent header string
  67.  
  68. This code is effectively:
  69.  
  70.     if(strstr(header, "User-Agent:") != NULL)
  71.     {
  72.         http_request_t->0xD0 = header + strlen("User-Agent:") + strspn(header, " \t");
  73.     }
  74.  
  75. Knowing that offset 0xD0 in http_request_t contains a pointer to the User-Agent header, we can now re-construct the alpha_auth_check function:
  76.  
  77.  
  78.     #define AUTH_OK 1
  79.     #define AUTH_FAIL -1
  80.  
  81.     int alpha_auth_check(struct http_request_t *request)
  82.     {
  83.         if(strstr(request->url, "graphic/") ||
  84.            strstr(request->url, "public/") ||
  85.            strcmp(request->user_agent, "xmlset_roodkcableoj28840ybtide") == 0)
  86.         {
  87.             return AUTH_OK;
  88.         }
  89.         else
  90.         {
  91.             // These arguments are probably user/pass or session info
  92.             if(check_login(request->0xC, request->0xE0) != 0)
  93.             {
  94.                 return AUTH_OK;
  95.             }
  96.         }
  97.  
  98.         return AUTH_FAIL;
  99.     }
  100.  
  101. In other words, if your browser’s user agent string is “xmlset_roodkcableoj28840ybtide” (no quotes), you can access the web interface without any authentication and view/change the device settings (a DI-524UP is shown, as I don’t have a DIR-100 and the DI-524UP uses the same firmware):
  102. Accessing the admin page of a DI-524UP
  103.  
  104. Accessing the admin page of a DI-524UP
  105.  
  106. Based on the source code of the HTML pages and some Shodan search results, it can be reasonably concluded that the following D-Link devices are likely affected:
  107.  
  108.     DIR-100
  109.     DI-524
  110.     DI-524UP
  111.     DI-604S
  112.     DI-604UP
  113.     DI-604+
  114.     TM-G5240
  115.  
  116. Additionally, several Planex routers also appear to use the same firmware:
  117.  
  118.     BRL-04UR
  119.     BRL-04CW
  120.  
  121. You stay classy, D-Link.
  122.  
  123. UPDATE:
  124.  
  125. The ever neighborly Travis Goodspeed pointed out that this backdoor is used by the /bin/xmlsetc binary in the D-Link firmware. After some grepping, I found several binaries that appear to use xmlsetc to automatically re-configure the device’s settings (example: dynamic DNS). My guess is that the developers realized that some programs/services needed to be able to change the device’s settings automatically; realizing that the web server already had all the code to change these settings, they decided to just send requests to the web server whenever they needed to change something. The only problem was that the web server required a username and password, which the end user could change. Then, in a eureka moment, Joel jumped up and said, “Don’t worry, for I have a cunning plan!”.
  126.  
  127. Also, several people have reported in the comments that some versions of the DIR-615 are also affected, including those distributed by Virgin Mobile. I have not yet verified this, but it seems quite reasonable.
clone this paste RAW Paste Data