Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Wordpress user login validation for recent versions of WordPress #14882

Merged
merged 2 commits into from
Mar 11, 2021
Merged

Fix Wordpress user login validation for recent versions of WordPress #14882

merged 2 commits into from
Mar 11, 2021

Conversation

thesunRider
Copy link
Contributor

@thesunRider thesunRider commented Mar 11, 2021

This PR fixes #14790

BUG

While trying using

module> auxiliary/scanner/http/wordpress_login_enum

User validation does not occur correctly in wordpress 5.x

Verification

  • Launch Wordpress >= 5.x on localhost@8000
  • Start msfconsole
  • creds add user:test
  • use auxiliary/scanner/http/wordpress_login_enum
  • set options and run
  • Verify wether the user test was recognised

Expected Output

msf6 > use auxiliary/scanner/http/wordpress_login_enum 
msf6 auxiliary(scanner/http/wordpress_login_enum) > show options

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

   Name                 Current Setting  Required  Description
   ----                 ---------------  --------  -----------
   BLANK_PASSWORDS      false            no        Try blank passwords for all users
   BRUTEFORCE           true             yes       Perform brute force authentication
   BRUTEFORCE_SPEED     5                yes       How fast to bruteforce, from 0 to 5
   DB_ALL_CREDS         false            no        Try each user/password couple stored in the current database
   DB_ALL_PASS          false            no        Add all passwords in the current database to the list
   DB_ALL_USERS         false            no        Add all users in the current database to the list
   ENUMERATE_USERNAMES  true             yes       Enumerate usernames
   PASSWORD                              no        A specific password to authenticate with
   PASS_FILE                             no        File containing passwords, one per line
   Proxies                               no        A proxy chain of format type:host:port[,type:host:port][...]
   RANGE_END            10               no        Last user id to enumerate
   RANGE_START          1                no        First user id to enumerate
   RHOSTS                                yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   RPORT                80               yes       The target port (TCP)
   SSL                  false            no        Negotiate SSL/TLS for outgoing connections
   STOP_ON_SUCCESS      false            yes       Stop guessing when a credential works for a host
   TARGETURI            /                yes       The base path to the wordpress application
   THREADS              1                yes       The number of concurrent threads (max one per host)
   USERNAME                              no        A specific username to authenticate as
   USERPASS_FILE                         no        File containing users and passwords separated by space, one pair per line
   USER_AS_PASS         false            no        Try the username as the password for all users
   USER_FILE                             no        File containing usernames, one per line
   VALIDATE_USERS       true             yes       Validate usernames
   VERBOSE              true             yes       Whether to print output for all attempts
   VHOST                                 no        HTTP server virtual host

msf6 auxiliary(scanner/http/wordpress_login_enum) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 auxiliary(scanner/http/wordpress_login_enum) > set RPORT 8080
RPORT => 8080
msf6 auxiliary(scanner/http/wordpress_login_enum) > set DB_ALL_USERS true
DB_ALL_USERS => true
msf6 auxiliary(scanner/http/wordpress_login_enum) > run

[-] The connection was refused by the remote host (127.0.0.1:8080).
[-] / does not seem to be WordPress site
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/wordpress_login_enum) > set RPORT 8000
RPORT => 8000
msf6 auxiliary(scanner/http/wordpress_login_enum) > run

[*] / - WordPress Version 5.6.1 detected
[*] 127.0.0.1:8000 - / - WordPress User-Enumeration - Running User Enumeration
[*] 127.0.0.1:8000 - / - WordPress User-Validation - Running User Validation
[*] / - WordPress User-Validation - Checking Username:'Administrator'
[-] 127.0.0.1:8000 - [1/4] - / - WordPress User-Validation - Invalid Username: 'Administrator'
[*] / - WordPress User-Validation - Checking Username:'administrator'
[-] 127.0.0.1:8000 - [2/4] - / - WordPress User-Validation - Invalid Username: 'administrator'
[*] / - WordPress User-Validation - Checking Username:'test'
[+] / - WordPress User-Validation - Username: 'test' - is VALID
[+] / - WordPress User-Validation - Found 1 valid user
[-] 127.0.0.1:8000 - [4/4] - / - WordPress User-Validation - Invalid Username: 'normal'
[*] 127.0.0.1:8000 - [5/4] - / - WordPress Brute Force - Running Bruteforce
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/wordpress_login_enum) > 

Please remind me if any issue is found with the fix! ;-)

## BUG

While trying using 

`module> auxiliary/scanner/http/wordpress_login_enum`

User validation does not occur correctly in wordpress 5.x



## Verification

- mkdir wordpress
- cd wordpress
- nano docker-compose.yml
- Enter the following into the file

```
version: '3.3'
services:
   db:
     image: mysql:5.7
     volumes:
       - db_data:/var/lib/mysql
     restart: always
     environment:
       MYSQL_ROOT_PASSWORD: somewordpress
       MYSQL_DATABASE: wordpress
       MYSQL_USER: wordpress
       MYSQL_PASSWORD: wordpress

   wordpress:
     depends_on:
       - db
     image: wordpress:latest
     ports:
       - "8000:80"
     restart: always
     environment:
       WORDPRESS_DB_HOST: db:3306
       WORDPRESS_DB_USER: wordpress
       WORDPRESS_DB_PASSWORD: wordpress
       WORDPRESS_DB_NAME: wordpress
volumes:
    db_data: {}
```
- sudo docker-compose up -d

- Follow the setup instructions at 127.0.0.1:8000 and set up a new user in WordPress.
- Start msfconsole
- creds add user:test
- use auxiliary/scanner/http/wordpress_login_enum
- set RHOSTS 127.0.0.1
- set RPORT 8000
- set DB_ALL_USERS true
- run

## Expected Output

```
msf6 > use auxiliary/scanner/http/wordpress_login_enum 
msf6 auxiliary(scanner/http/wordpress_login_enum) > show options

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

   Name                 Current Setting  Required  Description
   ----                 ---------------  --------  -----------
   BLANK_PASSWORDS      false            no        Try blank passwords for all users
   BRUTEFORCE           true             yes       Perform brute force authentication
   BRUTEFORCE_SPEED     5                yes       How fast to bruteforce, from 0 to 5
   DB_ALL_CREDS         false            no        Try each user/password couple stored in the current database
   DB_ALL_PASS          false            no        Add all passwords in the current database to the list
   DB_ALL_USERS         false            no        Add all users in the current database to the list
   ENUMERATE_USERNAMES  true             yes       Enumerate usernames
   PASSWORD                              no        A specific password to authenticate with
   PASS_FILE                             no        File containing passwords, one per line
   Proxies                               no        A proxy chain of format type:host:port[,type:host:port][...]
   RANGE_END            10               no        Last user id to enumerate
   RANGE_START          1                no        First user id to enumerate
   RHOSTS                                yes       The target host(s), range CIDR identifier, or hosts file with syntax 'file:<path>'
   RPORT                80               yes       The target port (TCP)
   SSL                  false            no        Negotiate SSL/TLS for outgoing connections
   STOP_ON_SUCCESS      false            yes       Stop guessing when a credential works for a host
   TARGETURI            /                yes       The base path to the wordpress application
   THREADS              1                yes       The number of concurrent threads (max one per host)
   USERNAME                              no        A specific username to authenticate as
   USERPASS_FILE                         no        File containing users and passwords separated by space, one pair per line
   USER_AS_PASS         false            no        Try the username as the password for all users
   USER_FILE                             no        File containing usernames, one per line
   VALIDATE_USERS       true             yes       Validate usernames
   VERBOSE              true             yes       Whether to print output for all attempts
   VHOST                                 no        HTTP server virtual host

msf6 auxiliary(scanner/http/wordpress_login_enum) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 auxiliary(scanner/http/wordpress_login_enum) > set RPORT 8080
RPORT => 8080
msf6 auxiliary(scanner/http/wordpress_login_enum) > set DB_ALL_USERS true
DB_ALL_USERS => true
msf6 auxiliary(scanner/http/wordpress_login_enum) > run

[-] The connection was refused by the remote host (127.0.0.1:8080).
[-] / does not seem to be WordPress site
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/wordpress_login_enum) > set RPORT 8000
RPORT => 8000
msf6 auxiliary(scanner/http/wordpress_login_enum) > run

[*] / - WordPress Version 5.6.1 detected
[*] 127.0.0.1:8000 - / - WordPress User-Enumeration - Running User Enumeration
[*] 127.0.0.1:8000 - / - WordPress User-Validation - Running User Validation
[*] / - WordPress User-Validation - Checking Username:'Administrator'
[-] 127.0.0.1:8000 - [1/4] - / - WordPress User-Validation - Invalid Username: 'Administrator'
[*] / - WordPress User-Validation - Checking Username:'administrator'
[-] 127.0.0.1:8000 - [2/4] - / - WordPress User-Validation - Invalid Username: 'administrator'
[*] / - WordPress User-Validation - Checking Username:'test'
[+] / - WordPress User-Validation - Username: 'test' - is VALID
[+] / - WordPress User-Validation - Found 1 valid user
[-] 127.0.0.1:8000 - [4/4] - / - WordPress User-Validation - Invalid Username: 'normal'
[*] 127.0.0.1:8000 - [5/4] - / - WordPress Brute Force - Running Bruteforce
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/wordpress_login_enum) > 

```

## Linked Issue

[https://github.com/rapid7/metasploit-framework/issues/14790#issue-81372544](https://github.com/rapid7/metasploit-framework/issues/14790#issue-813725448,"https://github.com/rapid7/metasploit-framework/issues/14790#issue-813725448")

Please remind me if any issue is found with the fix! ;-)
Now the search will be for the exact user error
@thesunRider
Copy link
Contributor Author

Done!

@gwillcox-r7
Copy link
Contributor

Thanks going to verify this now, appreciate the quick turn around!

@gwillcox-r7
Copy link
Contributor

Hmm this doesn't seem to be working for me. Valid username:password combo should be test:test123 but that isn't showing up in the results. There should also be a valid user test22 with an unknown password that should be picked up:

msf6 > creds add user:test password:test123
msf6 > creds add user:test22
msf6 > use auxiliary/scanner/http/wordpress_login_enum 
msf6 auxiliary(scanner/http/wordpress_login_enum) > set DB_ALL_CREDS
DB_ALL_CREDS => false
msf6 auxiliary(scanner/http/wordpress_login_enum) > set DB ALL_USERS
DB => ALL_USERS
msf6 auxiliary(scanner/http/wordpress_login_enum) > set DB ALL_USERS true
DB => ALL_USERS true
msf6 auxiliary(scanner/http/wordpress_login_enum) > set DB_ALL_USERS true
DB_ALL_USERS => true
msf6 auxiliary(scanner/http/wordpress_login_enum) > set DB_ALL_CREDS true
DB_ALL_CREDS => true
msf6 auxiliary(scanner/http/wordpress_login_enum) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 auxiliary(scanner/http/wordpress_login_enum) > set RPORT 80
RPORT => 80
msf6 auxiliary(scanner/http/wordpress_login_enum) > run

[-] / does not seem to be WordPress site
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/wordpress_login_enum) > set RPORT 9993
RPORT => 9993
msf6 auxiliary(scanner/http/wordpress_login_enum) > run

[-] The connection was refused by the remote host (127.0.0.1:9993).
[-] / does not seem to be WordPress site
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/wordpress_login_enum) > set RPORT 8000
RPORT => 8000
msf6 auxiliary(scanner/http/wordpress_login_enum) > run

[*] / - WordPress Version 5.7 detected
[*] 127.0.0.1:8000 - / - WordPress User-Enumeration - Running User Enumeration
[*] 127.0.0.1:8000 - / - WordPress User-Validation - Running User Validation
[*] / - WordPress User-Validation - Checking Username:'wordpress'
[-] 127.0.0.1:8000 - [1/5] - / - WordPress User-Validation - Invalid Username: 'wordpress'
[*] / - WordPress User-Validation - Checking Username:'test'
[-] 127.0.0.1:8000 - [2/5] - / - WordPress User-Validation - Invalid Username: 'test'
[*] / - WordPress User-Validation - Checking Username:'test22'
[-] 127.0.0.1:8000 - [3/5] - / - WordPress User-Validation - Invalid Username: 'test22'
[*] 127.0.0.1:8000 - [4/5] - / - WordPress Brute Force - Running Bruteforce
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/wordpress_login_enum) > creds
Credentials
===========

host  origin  service  public     private    realm  private_type  JtR Format
----  ------  -------  ------     -------    -----  ------------  ----------
                       test                                       
                       wordpress  wordpress         Password      
                       test       test123           Password      
                       test22                                     

msf6 auxiliary(scanner/http/wordpress_login_enum) > 

Looking into this a bit closer things should be working but somehow the fix isn't correct:

[*] / - WordPress User-Validation - Checking Username:'test22'
####################
# Request:
####################
POST /wp-login.php HTTP/1.1
Host: 127.0.0.1:8000
User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)
Content-Type: application/x-www-form-urlencoded
Content-Length: 50

log=test22&pwd=BojuBD&redirect_to=&wp-submit=Login
####################
# Response:
####################
HTTP/1.1 200 OK
Date: Thu, 11 Mar 2021 22:33:00 GMT
Server: Apache/2.4.38 (Debian)
X-Powered-By: PHP/7.4.16
Expires: Wed, 11 Jan 1984 05:00:00 GMT
Cache-Control: no-cache, must-revalidate, max-age=0
Set-Cookie: wordpress_test_cookie=WP%20Cookie%20check; path=/
X-Frame-Options: SAMEORIGIN
Vary: Accept-Encoding
Content-Length: 7601
Content-Type: text/html; charset=UTF-8

<!DOCTYPE html>
	<html lang="en-US">
	<head>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
	<title>Log In &lsaquo; test &#8212; WordPress</title>
	<meta name='robots' content='max-image-preview:large, noindex, noarchive' />
<link rel='dns-prefetch' href='//s.w.org' />
<link rel='stylesheet' id='dashicons-css'  href='http://127.0.0.1:8000/wp-includes/css/dashicons.min.css?ver=5.7' media='all' />
<link rel='stylesheet' id='buttons-css'  href='http://127.0.0.1:8000/wp-includes/css/buttons.min.css?ver=5.7' media='all' />
<link rel='stylesheet' id='forms-css'  href='http://127.0.0.1:8000/wp-admin/css/forms.min.css?ver=5.7' media='all' />
<link rel='stylesheet' id='l10n-css'  href='http://127.0.0.1:8000/wp-admin/css/l10n.min.css?ver=5.7' media='all' />
<link rel='stylesheet' id='login-css'  href='http://127.0.0.1:8000/wp-admin/css/login.min.css?ver=5.7' media='all' />
	<meta name='referrer' content='strict-origin-when-cross-origin' />
		<meta name="viewport" content="width=device-width" />
		</head>
	<body class="login no-js login-action-login wp-core-ui  locale-en-us">
	<script type="text/javascript">
		document.body.className = document.body.className.replace('no-js','js');
	</script>
		<div id="login">
		<h1><a href="https://wordpress.org/">Powered by WordPress</a></h1>
	<div id="login_error">	<strong>Error</strong>: The password you entered for the username <strong>test22</strong> is incorrect. <a href="http://127.0.0.1:8000/wp-login.php?action=lostpassword">Lost your password?</a><br />

@@ -14,7 +14,8 @@ def wordpress_user_exists?(user)

return true if res and res.code == 200 and
(res.body.to_s =~ /Incorrect password/ or
res.body.to_s =~ /document\.getElementById\('user_pass'\)/)
res.body.to_s =~ /document\.getElementById\('user_pass'\)/ or
res.body.to_s =~/<strong>^#{user}<\/strong> is incorrect/)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wait I think there might be a typo here. Looks like an extra ^ slipped in. Let me check this quickly.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There we go that was the issue 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Applied the fix in 94dbe9f before landing. Thanks again!

@gwillcox-r7
Copy link
Contributor

msf6 > creds add user:test
msf6 > creds add user:wordpress
msf6 > creds add user:test password:test123
msf6 > creds add user:test22
msf6 > use auxiliary/scanner/http/wordpress_login_enum 
msf6 auxiliary(scanner/http/wordpress_login_enum) > set DB_ALL_CREDS true
DB_ALL_CREDS => true
msf6 auxiliary(scanner/http/wordpress_login_enum) > set DB_ALL_USERS true
DB_ALL_USERS => true
msf6 auxiliary(scanner/http/wordpress_login_enum) > set RHOSTS 127.0.0.1
RHOSTS => 127.0.0.1
msf6 auxiliary(scanner/http/wordpress_login_enum) > set RPORT 8000
RPORT => 8000
msf6 auxiliary(scanner/http/wordpress_login_enum) > run

[*] / - WordPress Version 5.7 detected
[*] 127.0.0.1:8000 - / - WordPress User-Enumeration - Running User Enumeration
[*] 127.0.0.1:8000 - / - WordPress User-Validation - Running User Validation
[*] / - WordPress User-Validation - Checking Username:'test'
[+] / - WordPress User-Validation - Username: 'test' - is VALID
[*] / - WordPress User-Validation - Checking Username:'wordpress'
[-] 127.0.0.1:8000 - [2/4] - / - WordPress User-Validation - Invalid Username: 'wordpress'
[*] / - WordPress User-Validation - Checking Username:'test22'
[+] / - WordPress User-Validation - Username: 'test22' - is VALID
[+] / - WordPress User-Validation - Found 2 valid users
[*] 127.0.0.1:8000 - [4/4] - / - WordPress Brute Force - Running Bruteforce
[*] 127.0.0.1:8000 - [4/4] - / - WordPress Brute Force - Skipping all but 2 valid users
[*] 127.0.0.1:8000 - [1/4] - / - WordPress Brute Force - Trying username:'test' with password:'test123'
[+] / - WordPress Brute Force - SUCCESSFUL login for 'test' : 'test123'
[*] 127.0.0.1:8000 - [3/4] - / - WordPress Brute Force - Trying username:'test22' with password:''
[-] 127.0.0.1:8000 - [3/4] - / - WordPress Brute Force - Failed to login as 'test22'
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf6 auxiliary(scanner/http/wordpress_login_enum) > 

With typo fixed this works! Thanks for submitting this PR, I know a lot of people were trying to get this issue addressed. Will go ahead and make a ninja edit to fix the typo and then get this landed 👍

@gwillcox-r7 gwillcox-r7 changed the title Fixed wordpress user login validation Fixe Wordpress user login validation for recent versions of WordPress Mar 11, 2021
@gwillcox-r7 gwillcox-r7 changed the title Fixe Wordpress user login validation for recent versions of WordPress Fix Wordpress user login validation for recent versions of WordPress Mar 11, 2021
gwillcox-r7 added a commit that referenced this pull request Mar 11, 2021
@gwillcox-r7 gwillcox-r7 merged commit 37eaf79 into rapid7:master Mar 11, 2021
@gwillcox-r7 gwillcox-r7 added rn-fix release notes fix enhancement rn-enhancement release notes enhancement and removed bug rn-fix release notes fix labels Mar 11, 2021
@gwillcox-r7
Copy link
Contributor

gwillcox-r7 commented Mar 11, 2021

Release Notes

Improved lib/msf/core/exploit/remote/http/wordpress/users.rb to support valid username identification and login identification for newer versions of WordPress up-to-and-including 5.7.

@thesunRider thesunRider deleted the wordpress_login_validation_fix branch October 4, 2021 18:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

auxiliary/scanner/http/wordpress_login_enum does not correctly enumerate logins on WordPress 5.6.1
2 participants