PyShell is new tool made for bug bounty, ethical hacking, penetration testers or red-teamers. This tool helps you to obtain a shell-like interface on a web server to be remotely accessed. Unlike other webshells, the main goal of the tool is to use as little code as possible on the server side, regardless of the language used or the operating system of the server.
The default list with shells that comes with PyShell is below. And as you can see there is also support for WordPress!
So this looks interesting. The wordpress.zip file is a malicious WordPress plugin that places a backdoor into the WordPress installation and makes it possible to communicate to the PyShell shell.
The default wordpress.zip is fairly undetected by most antivirus-solutions at this time of writing. When uploading the wordpress.zip file to VirusTotal that scans the file using 58 different antivirus-software engines the result is as following:
Only one AV-engine detects the backdoor and its Tencent. And if we look closer into the plugins there are two files:
- plugin.php
- shell.php
And they are pretty straight forward and easy to change and make them even more undetectable. For example the plugin name is PyShell and makes it pretty obvious if anyone is doing a forensic investigation.
The content of the files are as following, PHP-code:
You can download PyShell here:
Testing PyShell
First we start by cloning the repo from Github and installing the requirements. Our test is made on Kali Linux 2022.1:
git clone https://github.com/JoelGMSec/PyShell cd PyShell pip install -r requirements.txt python pyshell.py
And if everything works we should see something like this:
The next step is to upload the wordpress.zip plugin file to our WordPress installation. To upload new plugins the user must have the Super Admin role. How to get hold of the Super Admin role or access to a WordPress installation is not in scope for this guide.
So, the first step is to upload the plugin. This is done if you navigate to Plugins -> Add New and then press Upload Plugin in the top of the screen. There is no need to activate the plugin, the backdoor works anyway.
This looks good! The URL to the backdoor now looks like this:
https://scanme.wpsec.com/wp-content/plugins/wordpress/shell.php
Since the file name is wordpress.zip the slug/subfolder will be wordpress. And since shell.php is in the wordpress.zip file the backdoor url will end with shell.php
So, let’s fire up pyshell and test if it is possible to communicate with the backdoor. The command line would be something like this:
python pyshell.py https://scanme.wpsec.com/wp-content/plugins/wordpress/shell.php get
But there is an error: HTTPSConnectionPool(host=’scanme.wpsec.com’, port=443): Max retries exceeded with url: /wp-content/plugins/wordpress/shell.php?code=whoami (Caused by SSLError(SSLCertVerificationError(1, ‘[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: certificate has expired (_ssl.c:1129)’)))
This is due to the SSL/TLS-certificate of scanme.wpsec.com has expired. To fix this problem we created a pull request to the pyshell team and now it works:
Now we can perform commands like id, who, whoami, pwd. Also you can download and upload files using the download and upload command. If we would like to retreive the MariaDB/MySQL password from the wp-config.php file we can run this command:
Of course our communication is very noisy can can be easily detected by looking at the logs. So we recommend to change the get to post, both in the shell.php file and when running the pyshell.py command. POST http-requests are not logged by default in the web server access logs.
Example of webserver logfile entries when using the GET-method for the backdoor:
That’s it for now! In the next blog post we are looking into how to detect the pyshell backdoor. As always, we recommend using our free WordPress vulnerability scanner to prevent the intrusions in the first place.