getPlus insufficient domain name validation vulnerability


Yorick Koster, April 2009

See also


APSB10-08 Security update available for Adobe Download Manager
CVE-2010-0189
02.23.10 Multiple Vendor NOS Microsystems getPlus Downloader Input Validation Vulnerability
Aviv Raff On .NET: Skeletons in Adobe's security closet

Tested version


These vulnerabilities were tested on Adobe's version of getPlus version 1.5.2.35 on Windows XP SP3.

Fix


A new version of Adobe Download Manager (version 1.6.2.63) was released that resolves this issue.

Introduction


getPlus from NOS Microsystems is a download, upload, and installation manager. It contains functions such as an advanced download basket, checksum verification, automatic proxy and firewall verification, stop/pause/restart controls, a customizable GUI, statistic gathering tools, and incremental downloading.

getPlus comes with an ActiveX control for Internet Explorer. Adobe uses getPlus to distribute Adobe Reader. Users that have installed Adobe Reader from the Adobe website are likely to have this control installed on their machine. The following example demonstrates how getPlus is started from Internet Explorer:

<object id="GetActiveX" classid="clsid:CF40ACC5-E1BB-4aff-AC72-04C2F616BCA7" codebase="http://wwwimages.adobe.com/www.adobe.com/products/acrobat/nos/gp.cab#Version=1,5,2,35" type="application/x-oleobject" width="1" height="1">
<param name="Service-URL" value="http://get.adobe.com/reader/webservices/dlm/" />
<param name="itemid" value="860;941" />
<param name="language" value="" />
<param name="os" value="" />
</object>


The Service-URL parameter specifies the URL from which additional configuration parameters are obtained, including the URL from which the executable can be obtained. The other parameters are appended to this URL and are used to supply additional information about the product that has to be downloaded. The language and os parameters are automatically set by the ActiveX control if they are not provided. The parameter itemid is used to specify which product is to be downloaded. Multiple products (multiple downloads) can be supplied using semi colon characters between identifiers.

getPlus creates a URL based on the previously supplied parameters and it will use this URL to download additional configuration parameters. For example http://get.adobe.com/reader/webservices/dlm/?itemid=941&os=XPSP2&langid=en:

execute=1
showdest=0
openfolder=0
prodname=Smart Checker
diskspace=75376
checksum=8f79795f330f1cadcbe0a55400715da3
object_url=http://ardownload.adobe.com/pub/adobe/acrobat/win/all/sgc15.exe
signoff_url=
dependson=942
required=1
visible=0
params=
ask_for_destination=0
show_finish_dialog=0
decompression_folder_shortcut=0


The parameter object_url contains a link to the file that has to be downloaded. The checksum parameter is an MD5 hash of the file content. The checksum is used to verify the file's integrity. With the params parameter, it is possible to supply extra command line parameters to the executable.

The executable files shipped with getPlus are compressed using NOS' NOSSO technology. When executed, the compressed data is loaded with in a memory segment called .nos.

Insufficient domain name validation


Once the ActiveX control is installed, attackers might utilize this control to install malicious software. To prevent this from happening, getPlus verifies the URL before downloading and installing software from this URL. In case of Adobe's version of getPlus, getPlus validates if the domain name in the URL ends with .adobe.com. If this is not the case getPlus shows a warning message and will refuse to download the file from the supplied URL.


Figure 1: invalid domain name provided in URL

The code responsible for the validation of the domain name is located (after unpacking) in the .nos segment. A fragment of this assembly code is listed below:

.nos:033B9B71 ; =============== S U B R O U T I N E =======================================
.nos:033B9B71
.nos:033B9B71
.nos:033B9B71 isDomainValid proc near
.nos:033B9B71
.nos:033B9B71 var_4 = dword ptr -4
.nos:033B9B71 arg_0 = dword ptr 4
.nos:033B9B71 arg_4 = dword ptr 8
.nos:033B9B71 arg_8 = byte ptr 0Ch
.nos:033B9B71
.nos:033B9B71 push ecx
.nos:033B9B72 push esi
.nos:033B9B73 push edi
.nos:033B9B74 push [esp+0Ch+arg_0]
.nos:033B9B78 call sub_33C12CC ; strlen
.nos:033B9B7D inc eax
.nos:033B9B7E push eax
.nos:033B9B7F call dword ptr loc_33B22FB+1 ; malloc
.nos:033B9B85 mov esi, eax
.nos:033B9B87 pop ecx
.nos:033B9B88 test esi, esi
.nos:033B9B8A pop ecx
.nos:033B9B8B jz short loc_33B9BFA
.nos:033B9B8D push [esp+0Ch+arg_0]
.nos:033B9B91 mov [esp+10h+var_4], esi
.nos:033B9B95 push (offset loc_33B606B+1)
.nos:033B9B9A push esi
.nos:033B9B9B call dword ptr loc_33B2418+4 ; wsprintfA
   
[...]


The assembly code for this function roughly translates to the following C code:

01: int isDomainValid(char *szUrl, char *szDomain, int bRemovePort)
02: {
03:    size_t cbLen;
04:    char *psBuf;
05:    char *psTmp;
06:    char *psTmp2;
07:    
08:    cbLen = strlen(szUrl);
09:    cbLen++;               // wrap possible, not exploitable in real world
10:    psBuf = malloc(cbLen);
11:    if(psBuf == NULL)
12:    {
13:       return 0;
14:    }
15:    
16:    wsprintfA(psBuf, "%s", szUrl);
17:    
18:    if(strlen(psBuf))
19:    {
20:       psTmp = psBuf;
21:       while(*psTmp)
22:       {
23:          if(*psTmp == '\\')
24:          {
25:             *psTmp = '/';
26:          }
27:          psTmp++;
28:       }
29:    }
30:    
31:    /* strip protocol from URL */
32:    if((psTmp = _mbsstr(psBuf, "://")) == NULL)
33:    {
34:       if((psTmp = _mbsstr(psBuf, ":\\\\")) == NULL)
35:       {
36:          free(psBuf);
37:          return 0;
38:       }
39:    }
40:    psTmp += 3;
41:    
42:    /* strip path from URL */
43:    if((psTmp2 = _mbschr(psTmp, '/')) == NULL)
44:    {
45:       free(psBuf);
46:       return 0;
47:    }
48:    psTmp2[0] = '\0';
49:    
50:    /* remove port number from URL */
51:    if(bRemovePort)
52:    {
53:       psTmp2 = _mbschr(psTmp, ':');
54:       if(psTmp2)
55:       {
56:          psTmp2[0] = '\0';
57:       }
58:    }
59:    
60:    /* validate domain name */
61:    if(_mbschr(szDomain, '.'))
62:    {
63:       cbLen = strlen(szDomain);
64:       psTmp2 = psTmp - cbLen;
65:       cbLen = strlen(psTmp);
66:       psTmp2 += cbLen;
67:       if(_mbsicmp(psTmp2, szDomain) != 0)   // lowercase comparison
68:       {
69:          free(psBuf);
70:          return 0;
71:       }
72:    
73:       if(psTmp != (psTmp2 - 1))
74:       {
75:          if(psTmp2[-1] != '.')
76:          {
77:             free(psBuf);
78:             return 0;
79:          }
80:       }
81:    
82:       /* domain is accepted */
83:       return 1;
84:    }
85:    else
86:    {
87:       /* unreachable in Adobe's version */
88:    }
89:    
90:    return 0;
91: }


The function takes the supplied URL (szUrl) and compares it with a static domain name value (szDomain), in this case adobe.com. It start by extracting the domain name from the URL (lines 8 up to 59). After this, it checks whether the domain name ends with the supplied domain name (adobe.com). If this is the case, the function checks whether the character before this part of the URL is the dot character (line 75). This is done to check that the supplied domain name is a sub domain of the static domain name value.

The function does not take in account that it is possible that the URL does not point to a sub domain. The function will fail if it receives a URL such as http://adobe.com/invalid_url/. Specifically, this error exists on line 73 through 75. The variable psTmp contains a pointer to the full domain name. psTmp2 contains a pointer to the last part of the domain name. Normally, psTmp2 - 1 points to a dot character, but in case of http://adobe.com/invalid_url/ it points to the last slash character of the protocol part of the URL.

The dot check is not performed if both psTmp and psTmp2 - 1 point to the same string value. This is only possible for URLs that have the domain name set to ?adobe.com, where ? can be almost any character. For example, aadobe.com, badobe.com, et cetera. If the URL http://aadobe.com/ is supplied, psTmp will point to aadobe.com, psTmp2 points to adobe.com and psTmp2 - 1 points to aadobe.com, thus psTmp == (psTmp2 - 1). At the time of writing, (at least) the domain xadobe.com is still available:

$ whois xadobe.com
   
Whois Server Version 2.0
   
Domain names in the .com and .net domains can now be registered
with many different competing registrars. Go to http://www.internic.net
for detailed information.
   
No match for "XADOBE.COM".
>>> Last update of whois database: Fri, 10 Apr 2009 10:33:54 EDT <<<
   
[...]


An attacker can register a domain name such as xadobe.com and use this domain to download and execute any arbitrary file using getPlus. For an attack to be successful, a vulnerable user must be lured into viewing a malicious website.