Backdooring the PHP interpreter

Luke Paris
Paradoxis
Published in
7 min readJun 12, 2017

--

Editor’s note: The following post was written in 2017 at the very start of my career in Cyber Security. I was inspired to make a simple proof-of-concept backdoor for the PHP interpreter after reading “Hacking Exposed: Malware and Rootkits” by Michael A. Davis. As such, I named the project the “PHP-Rootkit”.

Ever since I published the post, I’ve received criticism that the project is misleading for being called a “rootkit”. Which in my opinion, is justified. It isn’t a rootkit, it’s a backdoor. So, in an effort to try to retroactively fix this, I’ve renamed all instances of the term “rootkit” to “backdoor”; granted that it makes sense in the context of the sentence.

When reading this post, please keep in mind that it was written by a 20 year old, very inexperienced version of myself, and it no longer reflects the quality of work I strive to deliver today.

Introduction

Before I begin, I want to make it clear that I condemn the act of using backdoors or any other form of malware without clear consent from the individual or organization being targeted. This post aims to educate the reader about the dangers of malicious PHP modules and outline the very real threat that they pose.

In this post I’ll start with a brief introduction to server backdoors, explain the reasoning behind writing one for the PHP interpreter, and end by showing a proof of concept backdoor, including its source code. If that’s all you came here for, scroll down or click here.

The Google results say enough about awareness

A brief history of rootkits

Rootkits have existed since the early 90s (source); traditionally they’ve mainly resided in something software engineers like to refer to as kernel space. Unlike regularuser spaceapplications, like the word processor and web-browser you use on a daily basis, code that runs in the kernel runs with complete authority.

In kernel space you are permitted to do absolutely anything. However, doing so comes with a risk, as any mistake you make will be punished heftily and result in your operating system crashing; which in turn could lead to lots of data loss.

Many rootkits work by finding out where the operating system handles the communication between the kernel and the user, and inject code that will act as a proxy between the two spaces, giving it the ability to read and modify all the data that passes through.

In software development, this process is also referred to as 'hooking' and has many legitimate uses too (like monkey-patching code by other developers as a temporary workaround).

Example of how backdoors can intercept function calls

Why write a backdoor as a PHP module?

So why would anyone write a backdoor for PHP, of all languages? Well, there are quite a few reasons, the most important being:

PHP is one of the most popular programming languages used on the web.

But this still doesn’t explain why you wouldn’t just go and write a backdoor in the form of a kernel rootkit and intercept function calls from there. The reasons can be summed up as follows:

Accessibility

The first and very obvious reason why you would write a backdoor as a PHP module is accessibility. In my experience, learning how to use the Zend Engine (the framework the entire PHP language is built with) is a lot easier than learning how to write kernel modules, simply because the code base itself is smaller, better documented and a lot less complex.

Even without good documentation or tutorials, I managed to learn the basics of writing a PHP module within a day. If I (a novice C developer) can do it, the bad guys definitely can.

Stability

Since traditional rootkits are designed to run in kernel space, the chance of a poorly written rootkit crashing the entire system is very high. PHP interpreter backdoors don’t have this problem. While a poorly written backdoor can certainly cause some damage, it can’t crash the entire underlying system.

In the worst-case scenario, an interpreter backdoor will cause a segmentation fault and just interrupt the current request (note: most web servers report this in their error log, so this could raise suspicion).

Detectability

Let’s be realistic, when is the last time you actually checked the file integrity of your PHP modules? If I were to name my module something deceptive like 'curl.so’, would you be paranoid enough to check if it’s actually the curl extension for PHP?

This, as well as the fact it’s custom code (meaning no antivirus signatures exist), no network-based IDS systems get triggered (since networking is managed by the web server and is expected) and the fact there are many inexperienced developers that only just know how to install PHP, leaves you with the perfect conditions for stealth.

Furthermore, kernel rootkits require you to hook system calls for every process rather than just one, this slows down your machine drastically, which might lead to more suspicion.

Portability

Not only do you get to enjoy the benefits of writing code in the user space, your backdoor also just became a cross-platform backdoor! This is because PHP is (in most cases) platform-independent. Code written for one platform can easily be adapted and compiled to be run on another platform (eg: modules written for Linux can often be compiled for Windows).

Proof of concept

Now comes the exciting part, actually getting to show off how dangerous a malicious extension can be. In my examples, it will be totally obvious to the user that they’re being spied on, but with a few tiny tweaks this becomes essentially invisible to a system administrator.

Hooking cryptography methods

The two most important parts of writing a PHP backdoor are registering the actual module itself and hooking the target functions. The following screenshots are the actual code I used to achieve all of the required functionality, which came in at only 80 lines of code (including comment blocks).

Registering the module in the Zend Engine

Here you can see how a basic PHP extension is registered with the Zend Engine (the underlying framework of PHP). Notice the two strange lines of code in the PHP_MINIT_FUNCTION method?

You’re looking at malicious hooks being injected into the global function table (used to look up what method is located where in memory), in this case I decided to hook the generic hashing method hash and the more popular sha1 method.

The actual hook code works as follows:

  1. It starts out by locating the method you want to hook in the global function table, and storing a reference to it.
  2. If it successfully finds the function, a detour is added by storing the original location of the method in a variable called original and setting the value in the global function table to the address of the hook variable.

If everything goes well, it will result in the hook getting called before the real method, which gives the backdoor author full control of it, meaning they could read out parameters, change return values, skip the function call altogether and do pretty much anything their heart desires.

Injecting ‘invisible’ function hooks

After writing the base hook code, I decided to make the extension log all parameter data that was passed to the sha1 and hash methods. The screenshot below shows what this looks like.

  1. The first command shows that the file ‘/tmp/php-module-rootkit.txt’ doesn’t exist, this will be important later.
  2. The second command is where the backdoor is loaded into the interpreter with the -dextension={module} parameter; this would normally be done by loading it through php.ini.
  3. After that we run our code using the -r '{code}' parameter, any code put in between the quotes will be executed by the PHP interpreter. In this example I make a call to sha1() and echo the results on the screen.
  4. In the last command we see that we read out the newly created ‘/tmp/php-module-rootkit.txt’ file, which contains the password we just hashed!
POC — Hooking the sha1 function to intercept all data

Disclaimer

I felt it was necessary to include this, since I still see people using weak cryptographic algorithms to store passwords: Please never use weak cryptographic algorithms such as md5 or sha1 to store passwords, modern processors and GPU’s make it incredibly easy to crack thousands of passwords per second.

At the time of writing, using bcrypt is an acceptable standard to use. However, outsourcing your authentication via methods like OAuth (Google / Facebook login) is deemed even more secure as you don’t need to store the passwords at all.

Public Source Code

For those who wish to view the code, I’ve published the full source code of the backdoor on GitHub for public reference. To prevent malicious script kiddies from getting their hands on a weaponized PHP module, I’ve removed the compilation instructions alongside the implementation of the method hooks. Furthermore, I will not be releasing pre-compiled binaries.

Paradoxis/PHP-Backdoor on GitHub

Any semi-experienced C developer should be able to find out how to compile PHP modules and implement the the rootkit_hook_function method.

Prevention

It wouldn’t be a security post if I didn’t at least talk about how you could prevent falling victim to malicious PHP modules. The most simple way of detecting whether or not any of your modules are malicious would be to keep a list of the module hashes after installing PHP.

Once you have a list of hashes, add a cron job that tries to hash all files in the extension directory and compares them to the current hash. The following Python script should do the trick:

--

--

Dutch cyber security specialist with a passion for software & penetration testing, my weapons of choice are Python and Linux.