WordPress Unauthorized Password Reset Vulnerability (CVE-2017-8259)

This content is 2 years old. Technology changes with time. Keep that in mind as you read this article.

WordPress has a password reset feature that contains a vulnerability which might in some cases allow attackers to get hold of the password reset link without previous authentication.

Such attack could lead to an attacker gaining unauthorized access to a victim’s WordPress account.  This affects all versions of WordPress, including the current version, 4.7.4.


The vulnerability stems from WordPress using untrusted data by default when creating a password reset e-mail that is supposed to be delivered only to the e-mail associated with the owner’s account.

This can be observed in the following code snippet that creates a From email header before calling a PHP mail() function:


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;

3 separate example scenarios (both the ones that require victim interaction and those that do not) include:

  1. Attacker can perform a prior DoS attack on the victim’s email account/server (e.g by sending multiple large files to exceed user’s disk quota, attacking the DNS server etc) in order to prevent the password reset email from reaching the victim’s account and bounce back to the malicious sender address that is pointed at the attacker (no user interaction required)
  2. Some autoresponders might attach a copy of the email sent in the body of the auto-replied message (no user interaction required)
  3. Sending multiple password reset emails to force the user to reply to the message to inquiry explanation for endless password reset emails. The reply containing the password link would then be sent to attacker. (user interaction required)


  1. If you are using Apache, you can turn on UseCanonicalName (see: https://httpd.apache.org/docs/2.4/mod/core.html#usecanonicalname)
  2. I created a simple plugin that you can install in your WordPress installation. It will disable the last password functionality.
    Disable Password Reset

From Linux Install to WordPress

This content is 2 years old. Technology changes with time. Keep that in mind as you read this article.

A typical installation of Debian 8.x (Jessie) precedes this where I only select base system and ssh server options during operating system installation.  After installation, this is a typical configuration to get me up and running. These are my notes.

Debian Customization

These are customizations to suit my taste.

apt-get update && apt-get upgrade
dpkg-reconfigure dash
echo UseDNS no >>/etc/ssh/sshd_config && /etc/init.d/ssh restart
apt-get install fail2ban vim-nox unzip

Webserver Installation: nginx

wget http://nginx.org/keys/nginx_signing.key
apt-key add nginx_signing.key
echo 'deb http://nginx.org/packages/debian/ jessie nginx' >> /etc/apt/sources.list
echo 'deb-src http://nginx.org/packages/debian/ jessie nginx' >> /etc/apt/sources.list
apt-get update && apt-get install nginx

nginx Configuration

There are a few customizations I make and I have scripted most of this since it’s repetitive.

In the first line below, worker_processes 2; is derived from grep 'cpu cores' /proc/cpuinfo | head -1
sed -i 's/user[ ]*nginx/user www-data/g; s/worker_processes[ ]*1/worker_processes 2/g' /etc/nginx/nginx.conf
sed -i 's/access_log.*;/access_log off;/g' /etc/nginx/nginx.conf
sed -i '/access_log off;/a client_max_body_size 12m;' /etc/nginx/nginx.conf
/etc/init.d/nginx restart

With basic configuration changes made to nginx.conf, I now focus on creating the site configuration.

rm /etc/nginx/conf.d/*

cat <<EOF >>/etc/nginx/conf.d/`hostname`.conf
server {
        listen 80;
        root /var/www;
        index index.php index.html index.htm;

        server_name techish.net www.techish.net;

        location / {
                try_files $uri $uri/ /index.php;

        location ~ .php$ {
                try_files $uri =404;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name
                include fastcgi_params;
                fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;

    location ~ /.well-known {
                allow all;

That concludes nginx installation and configuration to THIS point. I’ll revisit toward the end when I implement SSL.

PHP 7 Installation

I use dotdeb to install PHP 7.0 via apt-get.

echo 'deb http://packages.dotdeb.org jessie all' >> /etc/apt/sources.list
echo 'deb-src http://packages.dotdeb.org jessie all' >> /etc/apt/sources.list
wget https://www.dotdeb.org/dotdeb.gpg
apt-key add dotdeb.gpg
apt-get update
apt-get install php7.0-fpm php7.0-mysql php7.0-gd php7.0-mcrypt

PHP 7 Configuration

With PHP7 installation completed, I make a few changes.

sed -i 's/^upload_max_filesize.*/upload_max_filesize = 10m/g; s/^allow_url_fopen.*/allow_url_fopen = Off/g; s/^post_max_size.*/post_max_size = 12m/g' /etc/php/7.0/fpm/php.ini

MySQL (MariaDB) Installation

Installing MariaDB is pretty straight forward with only a minor tweak in the configuration at the end.

apt-get install mariadb-server

MariaDB Configuration

sed -i 's/^bind-address/#bind-address/g; /^#bind-address/a skip-networking' /etc/mysql/my.cnf

Restart Services & Test

Restart the services and test out things to make sure everything works.

systemctl restart nginx.service
systemctl restart php7-fpm.service
systemctl restart mysql.service

WordPress Installation

WordPress installation is straight forward.

Database Preparation

Make sure to substitute wordpress, wpuser and ... below to reflect your database, database username and database user password.
cd /var/www
mysql -uroot -p -e create database wordpress; grant all on wordpress.* to 'wpuser'@'%' identified by '...'; flush privileges

WordPress Download & Extract

wget https://wordpress.org/latest.zip
unzip latest.zip
mv wordpress/* .
rm -rf wordpress/; rm latest.zip
chown www-data.www-data -R .

WordPress Configuration

At this point, the database is ready to go and I just visit my website to finish the WordPress installation via Web interface.

Let’s Encrypt SSL Certificate


openssl dhparam -out /etc/ssl/certs/dhparam.pem 2048

Add Jessie backports repo and install.

echo 'deb http://ftp.debian.org/debian jessie-backports main' >>/etc/apt/sources.list
apt-get update
apt-get install certbot -t jessie-backports

Certificate Generation

I generate a certificate for my top level domain and subdomain.

certbot certonly --webroot -w /var/www -d techish.net
certbot certonly --webroot -w /var/www -d www.techish.net

nginx SSL Configuration

Create a directory in /etc/nginx to store a few snippets of nginx configuration.

mkdir /etc/nginx/ssl

Create SSL parameters configuration file, ssl-params.conf, that we’ll call in our site configuration file.

cat <<EOF >>/etc/nginx/ssl/ssl-params.conf
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ecdh_curve secp384r1;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver valid=300s;
    resolver_timeout 5s;
    add_header Strict-Transport-Security max-age=63072000; includeSubdomains; preload;
    add_header Strict-Transport-Security max-age=63072000; includeSubdomains;
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    ssl_dhparam /etc/ssl/certs/dhparam.pem;

Create a configuration file, ssl-kreider.conf, that will reference where my top level domain SSL certificate is stored. I call this file from main nginx site configuration file later.

cat <<EOF >>/etc/nginx/ssl/ssl-kreider.conf
ssl_certificate /etc/letsencrypt/live/techish.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/techish.net/privkey.pem;

Create a configuration file, ssl-www-kreider.conf, that will reference where my www subdomain SSL certificate is stored. I call this file from main nginx site configuration file later.

cat <<EOF >>/etc/nginx/ssl/ssl-www-kreider.conf
ssl_certificate /etc/letsencrypt/live/www.techish.net/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/www.techish.net/privkey.pem;

I update my main site’s nginx configuration.

Note that `hostname` will expand the system hostname automatically. Replace `hostname`.conf (including backticks) with your configuration file name as applicable.
vim /etc/nginx/conf.d/`hostname`.conf

My finalized site configuration file.

server {
        listen 80;
        server_name techish.net www.techish.net;
        return 307 https://techish.net$request_uri;

server {
        listen 443 ssl;
        include ssl/ssl-kreider.conf;
        include ssl/ssl-params.conf;
        root /var/www;
        index index.php index.html index.htm;

        server_name techish.net;

        location / {
                try_files $uri $uri/ /index.php;

        location ~ .php$ {
                try_files $uri =404;
                fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name                                                                                                                               ;
                include fastcgi_params;
                fastcgi_pass unix:/var/run/php/php7.0-fpm.sock;

Change WordPress Password using MySQL

This content is 2 years old. Technology changes with time. Keep that in mind as you read this article.

If the Forgot password option doesn’t work for you (which it really should), then you can change the password of your self-hosted WordPress login.

Check the contents of wp-config.php for the database login. Use that login information for MySQL login.

define('DB_NAME', 'wpdatabase');
define('DB_USER', 'wpdbuser');
define('DB_PASSWORD', 'wpdbpassword');

Replacing DB_USER and DB_NAME from that command with the values from wp-config.php, enter the following command to connect to the database on your server.

mysql -uDB_USER -p DB_NAME

Get a list of users in the WordPress database.

select user_login from wp_users;

This might return multiple entries, but it was just one in my case: admin

To change the password, issue the following. Be sure to change ‘admin’ to the value of your user_login.

update wp_users set user_pass=md5('NEW_PASSWORD_HERE') where user_login='admin';

All done. Now you can log into your WordPress administration panel with the new password you specified.

Part 1: Analysis of a WordPress Malware

This content is 2 years old. Technology changes with time. Keep that in mind as you read this article.

I had some time at lunch to kill, so I decided to see how Malware techniques were improving in the land of WordPress and free premium theme download sites.

Enter the Darknet.

A simple Google search got me a theme ZIP file pretty quickly.  Now, it was time to see what malicious happenings this thing would cause.

Unpacked, here’s the structure of the ZIP file.

├── functions.php
├── home.php
├── images
│   ├── arrow.png
│   ├── bg-pattern.png
│   ├── bg.png
│   ├── blockquote.png
│   ├── blue
│   │   ├── gradient.png
│   │   ├── logo.png
│   │   ├── logo-texture.png
│   │   ├── logo-vert-left.png
│   │   └── logo-vert-right.png
│   ├── favicon.ico
│   ├── footer-twitter.png
│   ├── footer-widgets.png
│   ├── gradient.png
│   ├── green
│   │   ├── gradient.png
│   │   ├── logo.png
│   │   ├── logo-texture.png
│   │   ├── logo-vert-left.png
│   │   └── logo-vert-right.png
│   ├── icon-dot.png
│   ├── list-after-post.png
│   ├── list.png
│   ├── logo.png
│   ├── logo-texture.png
│   ├── logo-vert-left.png
│   ├── logo-vert-right.png
│   ├── red
│   │   ├── gradient.png
│   │   ├── logo.png
│   │   ├── logo-texture.png
│   │   ├── logo-vert-left.png
│   │   └── logo-vert-right.png
│   ├── rss.png
│   ├── social-icons.png
│   └── twitter-nav.png
├── page_landing.php
├── page_landing2.php
├── README.txt
├── screenshot.png
└── style.css

Right off the bat, page_landing2.php sticks out to me. Let’s take a look.

Oh. Would you look at that fun. Time to see what this is doing.

First, I don’t like trying to read the garbled code, so I “prettify” it.

Ok, so let’s decode the above to make it readable.

There are a few interesting pieces here.

These interest me because they are making a call to a website to get additional payload/scripts. Let’s see what they are. =)

The first one, pastebin link, shows me this garbled shit. What I really care about is the compressed base64 at the end.

So, now I look to deobfuscating the compressed/base64 garbage… Here’s part of the file, my screencapture died when my computer automatically locked; [FIXME]

NOTE: Click on the image for a higher resolution. It’s like 62k pixels tall, lol.


What I’m interested in is the top of this file.

So again, uncompressing the base64 encoding of that gives me the following file.

Going back for a minute the the previous garbled shit $plsym variable which contains the compressed/base64 is decompressed and unencoded and saved as a perl file.

At this point, I have everything I need to begin to follow this even deeper into the dark underworld. There are a few domains (which I didn’t highlight in this article, but you can find them in the screenshots) and some passwords.

Stay tuned… in the next update, I show you what happens when I infiltrate their command servers. Much fun!