Besides brute-force attacks that try to guess your password by simply using the login screen, bots that try to exploit vulnerabilities in your website PHP code are the most common form of attack targeting WordPress websites. Most of your time securing your site will be spent securing vulnerabilities in your website PHP code. When you upgrade WordPress core to protect against a new kind of attack, you are upgrading to prevent an attack on WordPress’s PHP code. The same applies when you upgrade your themes and plugins to patch a vulnerability.
PHP is the code that runs your WordPress website. Your plugins, themes and any other applications installed on your website like phpmyadmin also include PHP code.
Vulnerabilities in PHP code are usually caused by a mistake that a developer made when writing the original code. It is quite common for a developer to launch a perfectly working PHP application like WordPress, but to not anticipate all the ways that hackers on the Net will try to gain access. As the application is used more and more, the developer will learn from users and their experiences with attacks how the website can be made more secure. Developing a PHP application is an evolutionary process, which is why it is important to keep abreast of security alerts.
You will notice that most of the vulnerabilities below are caused by ‘unsanitized’ user input. That is, input from a user that the web application accepts but contains malicious code or is passed to a part of the application in a way that creates a vulnerability. That is why, when creating PHP applications or analyzing them, it is important to focus on all areas where user input is received and ensure that user input is treated correctly.
Vulnerabilities in PHP are generally grouped into categories based on their type. Below is a list of the most common kinds of vulnerabilities in PHP code and a basic explanation of each. This document will not include example PHP code because it is written for a non-developer audience.
Remote Code Execution (RCE) occurs when an attacker is able to upload code to your website and execute it. A bug in a PHP application may accept user input and evaluate it as PHP code. This could, for example, allow an attacker to tell the website to create a new file containing code that grants the attacker full access to your website. When an attacker sends code to your web application and it is executed, granting the attacker access, they have exploited an RCE vulnerability.
This is a very serious vulnerability because it is usually easy to exploit and grants full access to an attacker immediately after being exploited.
SQL Injection occurs when an attacker is able to send their own instructions to your database and the database executes those instructions. This occurs when a PHP developer has taken input from a website visitor and passed it to the database without checking to see if it contains anything malicious or bothering to clean out any malicious code.
SQL Injection can allow an attacker to access all of your website data. They can also create new data in your database which may include links to malicious or spam websites. An attacker may also be able to use SQL Injection to create a new administrative level user account which they can then use to sign-into your website and gain full access.
SQLi is a serious vulnerability because it is easy to exploit and often grants full access immediately.
Cross-Site Scripting (XSS) occurs when an attacker causes malicious code to load in a website visitor’s browser and execute. This code can then perform malicious actions like stealing user cookies, which can grant administrative level access, or performing functions as the user which can grant additional access.
There are two kinds of XSS: Stored XSS and Reflected XSS. A Stored XSS vulnerability occurs when an attacker is able to get the website to store malicious code which is later served up inside another user’s browser and executed. An example of stored XSS is where an attacker posts a comment on your WordPress website that contains code that steals user cookies and sends them somewhere.
Reflected XSS occurs when an attacker creates a link that contains malicious code. If the link is loaded in a browser, the website then serves up the malicious code as part of the website content. This code then executes in a user’s browser and can steal cookies or perform other malicious tasks. An example of a Reflected XSS is a WordPress search results page where the search query is included in the URL and is not sanitized correctly. The page then serves up the search results along with whatever the original query was. This could be malicious code that executes inside a visitor’s browser. If you wanted to exploit a website using Reflected XSS, you could create a link to a search results page that contains malicious code and send that to the site admin which would then steal his or her cookies.
Cross-Site Request Forgery (CSRF) occurs when an attacker can create a link and get a site administrator or someone with privileged access to click on that link which causes the site to take an action. For example, if an attacker was able to create a link that, when clicked on by a site admin, created a new ‘admin’ user with a known password, that would be a CSRF attack. The attacker would then simply have to find a way to fool the site admin into clicking that link to create a new admin user with a password they know.
WordPress has a clever way to protect against this kind of attack using something called a ‘nonce’. This is a security token (which is simply a number) that the admin is granted every time they sign-in. Every time a site admin on WordPress does something sensitive, their browser includes a ‘nonce’. If an attacker tries to create a link to use in a CSRF attack, they would also have to know the ‘nonce’ to send with that link. The nonce changes daily, which defeats CSRF attacks.
However, occasionally you’ll see a WordPress plugin or theme developer who is not using nonces to verify requests and this introduces CSRF vulnerabilities into their product. To fix this, the developer will simply add code that uses WordPress’s built in nonce capability to verify requests.
Occasionally a PHP developer will think that they are correctly validating that a site visitor has the correct access level before taking an action, but in fact they are checking the wrong thing.
One way this bug creeps into WordPress applications is through a common mistake WordPress developers make where they use a function called ‘is_admin()’ to try to validate that someone is an administrator. This function is actually designed to tell you if someone is viewing an admin page and it does not validate that a site visitor is actually an administrator. If a developer makes this mistake, they can grant non-admin users access to features that only administrators have access to. This is an authentication bypass vulnerability.
There are several other ways to introduce authentication bypass vulnerabilities into applications and generally they are created when a developer does not correctly check if a user has access before executing a function.
This is a more complex attack that occurs when a PHP application takes user input and passes it to a function called ‘unserialize()’ which takes a stored object and turns it into an object in memory. This might sound complicated but the important thing to note here is that once again, it occurs when a developer allows user input to be used in an unsafe way within a PHP application.
Remote File Inclusion or RFI occurs when a PHP application takes user input and passes it to a function that is designed to load a file. If this file is a URL, the function may load PHP code from another website that an attacker specifies which will then be executed on your website. The inclusion of a remote file in a URL is known as Remote File Inclusion or RFI.
If the file an attacker passes is a local file, the application might output the contents of that file to the screen. This is commonly how an attacker gains access to a WordPress website’s wp-config.php file. This is known as Local File Inclusion or LFI.
The functions that can be susceptible to RFI and LFI in PHP are: include, include_once, fopen, file_get_contents, require and require_once.
All of these functions load PHP code or content from a source that the developer specifies. If the PHP installation on the website is configured insecurely, an attacker can load a sensitive file as PHP code or content, or a remote file that lets them gain access to your website.
Most modern PHP installations protect against RFI attacks that load remote URL’s by limiting where files can be included from. However it is quite common for PHP developers to accidentally write code that allows an attacker to gain access to a local file like wp-config.php. For this reason, Remote File Inclusion vulnerabilities are rare these days, while Local File Inclusion vulnerabilities are quite common.
This document has given you an understanding of the most common types of PHP vulnerabilities and how they are created. As a responsible WordPress administrator you will be monitoring security mailing lists and will be alerted to new vulnerabilities as they emerge. Most of the vulnerabilities you will see will be one of the above types. With the new knowledge you have gained, you will better understand the severity of these vulnerabilities, you will be better equipped to ask developers questions about the vulnerability and you will have a good knowledge of how the vulnerability may be exploited on your own website before you patch it.