'Pentest' tag logo

wwwolf’s PHP webshell user’s guide

Web shells are backdoors relying on server-side scripting languages to be executed by the targeted server and usually accessed through a browser. While focused on wwwolf’s PHP webshell features, some part of this post are general and can be applied to other other webshells as well.

While some web shells attempt to provide the most complete post-exploitation frameworkas possible, and are therefore heavy and prone to bugs and incompatibilities, wwwolf’s PHP webshell considers the web shell as a transitional step in taking over a server.

wwwolf’s PHP webshell focuses on the functionalities necessary to do:

  • Local enumeration to discover the target’s environment and choose your next step.
  • Payloads and toolkits files transfer and execution, to proceed with your next step.

It tries its best to:

  • Be unobtrusive, with a simple yet efficient interface.
  • Be reliable, being as tolerant as possible regarding the target’s environment and the execution method.
  • Provide feedbacks, so the attacker’s knows what’s going on server-side and stays in control.

Why another web shell?

I often encountered issues when using other web shells:

  • They use new PHP syntax and features not compatible with the old PHP version running on some targets.
  • They make wrong assumptions on the remote URL, breaking PHP code injection or GET parameters (un)expected by the server.
  • They often only display the standard output content, loosing stderr output.
  • They poorly handle special characters in output display (such as <).
  • They do not allow file upload, or offer a method unsupported/blocked by the target’s settings.
  • They require manual modifications depending whether the target is running a UNIX-like or a Windows system.

wwwolf PHP webshell attempts to address all these issues and runs everywhere, without requiring any modification or setting change:

  • From the antique PHP 4.0 (2000):

    PHP 4.0.6 on Linux 2.4.8 (Mandrake 8.1 "vitamin", the browser is Konqueror 2.2.1)

  • To the latest PHP 7 (2017):

    PHP 7.0.19, running on Linux 4.9.0 (Debian 9.2 "stretch", the browser is Firefox ESR 52.5.0)

  • Through Windows servers:

    PHP 7.1.11, running on Windows 2016 (the browser is Internet Explorer 11)

Preparation (optional)

Set a password

You have the possibility to set a password to restrict the access to the webshell, this is an optional step and by default no password is used.

Don’t do misplaced trust in this feature, as its main purpose is to protect the innocent who may stumble on the webshell by accident. In particular:

  • To remain compatible with the widest range of targets, a relatively “weak” algorithm (SHA-256) is used to obfuscate your password.

    Even if unlikely when a complex password has being used, it remains safer to assume that the adversary knows your password as soon as you send the webshell containing your hash so don’t reuse it for any other purpose.

  • If the target doesn’t have the required PHP modules enabled, then this password feature will voluntary fail open with an appropriate warning message.

    This password feature is indeed designed as an optional and additional convenience and must not get in your way of taking over your target.

To set a password to your webshell:

  1. Use the provided passhash.sh script to generate your password hash:

    wwwolf@attacker:~$ sh passhash.sh
    WhiteWinterWolf's PHP webshell password hash generator
    Input the new password:   (hidden input)
    Type the new password again:   (hidden input)
    Update 'webshell.php' with the following values:
    $passprompt = "WhiteWinterWolf's PHP webshell: ";
    $passhash = "59a3d7ec4e90384fc98251927f1b02ff91f92e478521ecaabbe0c586ff711338";

    passhash.sh offers several options, allowing to use it in an automated way or to change the value of the webshell’s password prompt. Use -h to list all available parameters.

  2. Update the content of webshell.php as instructed in passhash.sh output:

    * Optional password settings.
    * Use the 'passhash.sh' script to generate the hash.
    * NOTE: the prompt value is tied to the hash!
    $passprompt = "WhiteWinterWolf's PHP webshell: ";
    $passhash = "59a3d7ec4e90384fc98251927f1b02ff91f92e478521ecaabbe0c586ff711338";


    As indicated in the source code quoted above, the prompt value is tied to the hash (it is used as a salt), so altering the prompt will invalidate the hash and make accessing the webshell impossible unless you used the -p option in passhash.sh to generate the matching hash value.

Accessing the webshell now requires to input a password before accessing main features:

Password input page

Embed the webshell in an picture file

A lot of web applications limit their upload feature to images files (profile picture, embedded image in a post, etc.), often checking and altering the file (not necessarily for security purposes, often just to ensure that the image is properly sized and compressed).

The most documented way to bypass such measure is to include the webshell in the image’s Exif metadata. Being the most documented, it is also the most widely checked: a lot of websites now systematically remove Exif metadata both for security and privacy1 reasons.

Being the result of a common effort between several media file formats (Exif is not limited to static images, it can also be used in sound or video files the same way), Exif is the most widely known but several file formats also offer their own metadata system in addition to Exif metadata.

The wrjpgcom command shown below integrates the webshell as a JPEG comment block in some random and innocuous JPEG image file:

wrjpgcom -comment "$(cat webshell.php)" innocuous.jpg >malicious.jpg

The webshell stored this way in malicious.jpg:

  • Won’t be detected by checking the file MIME type or the file content validity.
  • Won’t be detected nor removed by Exif tools and libraries.
  • Won’t be affected by image manipulation (resize, resolution change, crop, etc.).
  • Will remain a fully functional PHP script file.

Execute the web shell on the target

Obviously a properly secured target won’t let you complete this step. However, there are a number of potential entry points, from coding error in the web application to configuration issues in either the web server or PHP, and you need only one single exploitable vulnerability to be successful.

Remote file inclusion (RFI)

Consider the following vulnerable download.php file:

include $_GET['file'];

In the PHP settings, if both allow_url_fopen (enabled by default) and allow_url_include (added in PHP 5.2.0, disabled by default) are enabled, it becomes possible to exploit this code to run remotely hosted PHP files:



Notice the .txt extension at the end of the file name to avoid the web shell PHP code to be interpreted on the attacker’s web server instead of the targeted one.

Of course, this is the best scenario, but as stated above PHP default settings will now prevent this so most chances are that you will first have to somehow upload the webshell onto the target in order to execute it.

Upload the webshell file to the target

Upload forms

The easiest way to upload a file to the target system remains by using file upload forms provided by the targeted web application itself.

Especially if you managed to get a valid account, there is a high amount of chances that you have access to one or several upload forms fields, such as:

  • Adding images or attachments to a post, gallery, private messages, calendar entries, support tickets or any other kind of object the webapp handles.
  • Adding a profile picture.
  • Changing theme and customization features.

If you are very lucky and that both:

  • The uploaded file type is not checked.
  • Uploaded files are stored in a directory where PHP scripts execution is allowed.

Then you can simply upload the plain webshell.php file as-is and access its resulting URL directly to execute it.

Bypass file type checking

Image upload fields

A lot of web applications enforce some checks on uploaded files. This however does not mean that the party is over, as such check may be weakly implemented (relying on a blacklist instead of a whitelist, using weak regular expressions, etc.) and still offer various ways to bypass them.

The most most widespread upload feature being image files upload, the most common check that you will face will therefore be a verification that the file you uploaded is indeed a valid image. Sometimes, this check is not designed as a security feature per-see but is just a side-effect of how the image is being processed server-side. Image manipulation libraries raising an error on invalid image files, the upload process will naturally fail.

See above to get more information on how to properly embed the webshell in an image file so that the file will remain a valid image and the webshell code will persist through most image manipulations.

Fancy names and extensions

Various issues exists, either bugs in the web application and/or weak server settings, which may allow you to upload, and potentially execute, the webshell when its file is named a certain way:

  • Replacing the .php extension with an equivalent yet less common extension: webshell.phtml, webshell.pht, webshell.php7, webshell.php5, webshell.php4, webshell.php3. All of them equally designate PHP script files, but they may be missing on a list of blacklisted extensions (a sane web application would only rely on a whitelist of allowed extensions precisely to avoid such issues).

  • Prefixing the .php extension with a known and allowed extension: webshell.jpg.php. Some buggy webapp checks may start from the first dot to check the extension and wrongly allow such file.

  • Inserting a null byte between the real an fake extension (webshell.php\0.txt) so the check goes against the fake extension while the file will be saved with the malicious one.

  • With Apache targets: appending an unrecognized extension: webshell.php.123. Poorly configured Apache’s will make the MIME module to treat this as a double-extension and still invoke PHP to parse this file.


    This devastating issue came from an unfortunate recommendation in the PHP documentation and was the default setting implemented by major distributions when setting-up PHP with Apache 2 for years (this documentation was first published in 2004 and fixed in 2008).

  • With Windows targets:

    • You may try to use upper-case letters in the extension to bypass blacklisting (webshell.pHp).

    • Exploit special characters replacement rules when PHP is run by a IIS server (‘>’, ‘<’ and ‘”’ (double-quote) are respectively replaced by ‘?’, ‘*’ and ‘.’).

    • On old IIS (6 and below): append the fake extension using a semicolon as separator (webshell.php;.jpg).

These are just a few examples, you can find other ideas on OWASP and Acunetix websites.

Use other services

Even if the targeted web application itself doesn’t allow file uploads, maybe there are other webapps or other services running on the same server (FTP, SMB, etc.) which may do.

If not directly vulnerable, these services may still be exploitable due to password reuse or password guessing. Various tools, see here for nice descriptions of some of them, allow to build custom wordlists notably starting from the content of the targeted webapp.

If, for some reasons, these wordlist creation tools do not work with your target here are a few shell commands which may help you:

# Create a local mirror of the website.
# Here I exclude .exe, .iso and .msi files (large and not useful here).
wget -mpkEH -D -R "*.exe,*.iso,*.msi" ""

# Create a sorted list of words of more that four chars, lowercase and
# without duplicates.
grep -IRoh -E '\w{4,}' | tr '[A-Z]' '[a-z]' | sort -u >grep.out

# Generate derivatives from the collected words.
john --wordlist=./grep.out --rules=Single --stdout >john.out

Execute the uploaded file

Access the file’s URL

As long as the uploaded files are located in directories where PHP scripts execution is allowed (which, of course, is a very bad idea), several of the upload method described above allow to execute the PHP file by simply accessing its URL.

Rename the file

You may have the possibility to rename the file once uploaded. If the check on the new name is not as thorough as the check on the original uploaded file name, you may be able to:

  1. Upload the webshell embedded in a picture as webshell.jpg.
  2. Rename the uploaded file to webshell.php.
  3. Access the webshell URL to execute it.
Exploit PATH_INFO virtual directories

PATH_INFO are virtual directory appended to a script name, consider the following URL:


The path /2017/12/01 doesn’t physically exist on the server, but this string will be passed as parameter to the script /archives.php.

Vulnerable servers will execute the webshell upon reception of a URI such as /images/webshell.jpg/anything.php, where /images/webshell.jpg is the path to the webshell file:

  • The URL ending with the .php extension selects the use of the PHP parser.

  • Since the file anything.php doesn’t exists, the server will consider this to be a parameter to pass to the /images/webshell.jpg script.


Nginx servers were the most affected by this issue notably because of weak configuration spread through blogs. Always check that a PHP file requested by a client physically exists on the server before passing the URL to the PHP parser.

Local file injection (LFI)

If the web application suffers from an PHP file injection vulnerability as we saw in the RFI section but its exploitation is not possible (prevented by PHP settings, hardcoded string beginning, etc.), you should still be able to use it to trigger a local file injection (LFI).

It works exactly as a RFI, instead that you provide a local path to a previously uploaded webshell instead of a remote URL:


Moreover this method doesn’t require the upload directory to allow PHP script execution, however PHP script must be allowed to read uploaded files (but they usually do).


Sometimes it may be hard to precisely determine the exact location of the uploaded file server-side. In such situation:

  • Don’t hesitate to download and install the same web application as your target on a test system (an evaluation version for commercial software or a slightly different version than the target’s one should also be fine).

  • Check the product documentation, users forums and other support resources to gather information on typical directory tree layouts.


And what if the extension of the included file is hardcoded? For instance:

include $_GET['file'] . '.inc.php';

In PHP version up to 5.3.3 included (2011) it was possible to append a null byte at the end of the injected string to truncate it:


Some blogs and forums now mention the possibility to still get the string truncated by making the final string exceed some maximum length, I’m a bit doubtful about this method and didn’t managed to get it working in practice, but your mileage may vary2.

Upload a .htaccess file

With Apache servers, if you have the possibility to upload a malicious .htaccess file you can set JPG files to be considered as PHP script as follow:

AddType application/x-httpd-php .jpg

In fact, in such situation the whole backdoor could be bundled in the .htaccess, see Wireghoul’s htshells project for more information.


On Windows environments, it might be possible to overwrite a .htaccess file located in the upload directory by exploiting Windows’ 8.3 names and uploading the new file as htacce~1.

However be careful on how uploaded files are handled by the target service, in particular if the destination destination file is opened and rewritten or if the uploaded file is moved to its destination (the most common behavior with PHP), as in the latter case this method will delete the existing .htaccess and replace it with a new file named htacce~1. This may result in a denial-of-service of the web application.

Main interface features

wwwolf’s PHP webshell main interface is composed of several areas. Not all fields may be available depending on the target’s settings: in this case a warning message will tell you which feature has been disabled and why.

wwwolf's PHP webshell interface

  1. Fetch: these fields allows you to make the target fetch a file from a remote server, usually a machine under your control.

    If allow_url_fopen is enabled (the default but often disabled for security reasons, wonder why…), the webshell will use fopen() to fetch the file. Otherwise, it will attempt to use lower-level function allowing to bypass this limitation. The fetch method used in the latte case is very basic (redirection for instance are ignored) but effective.

    • host: the address or name of the server the target must connect to. By default this field is populated with your address as seen by the remote server.

      If OpenSSL is enabled in the target’s PHP, you can use TLS to protect the communication by prefixing the host name or address with the tls:// prefix3.

    • port: the port to connect to.

    • path: the path of the file to fetch, it should usually start with a leading /.

  2. CWD: The current working directory.

    It defaults to the PHP script’s current working directory. This default value will most likely directly tell you if you are facing a Unix-like or a Windows target (compare /var/www/html with C:\Inetpub\wwwroot for instance).

    Any modification to this value will be persistent, allowing to upload several files and execute various commands in the same directory without having to re-type the path each time.

    wwwolf’s PHP webshell attempts to clear the open_basedir setting (this setting should be overwritable since PHP version 5.3.0 included). If the operation fails a warning will tell you its value.

  3. Upload: Upload a file, if the file_uploads setting allows it.

  4. Cmd: A shell command to execute on the remote server.

    You should be able to type the command the same way as you would on a traditional console, wwolf’s PHP webshell taking care of passing it to the target’s shell (/bin/sh on Unix-like and cmd.exe on Windows) and redirecting the error output to ensure that you get a complete and accurate feedback.

    The value is field is persistent. To easily browse the target’s filesystem as part of the reconnaissance task, simply leave this field to ls -Al (Unix-like targets) or dir /a /q /-c (Windows targets) while changing the value the CWD field.

    You can execute several commands at once, use ; to separate them when facing a Unix-like target and & to separate them when facing a Windows target.


    Some Windows commands use control characters to format their output, screwing it up as-far-as PHP is concerned. Hopefully such commands propose an option to disable this feature, for instance the dir command offers the /-c parameter.

  5. Clear Cmd: This link is JavaScript powered and allows to easily clear and set the focus on the Cmd field.

    This is a convenience-only feature as having to manually select and delete the content of the Cmd field each time you want to execute a different command may quickly become tedious.

    If you don’t use JavaScript this feature will not work but this won’t impact the webshell functionalities or behavior.

  6. Execute: Click on this button or press Enter to submit the form.

    Server-side, the form content will be processed in this order:

    1. Change to the directory CWD.
    2. Fetch and save a file from the Fetch URL.
    3. Save a file sent through the Upload field.
    4. Execute the Cmd command.

    The main idea here is that the current directory is changed as the first step, and that the command is executed as the last step. This allows to go in a directory, transfer a file and process it (extract the tools archive, trigger a payload, etc.) in one single step.

  7. This is the output area, containing the executed commands output as well as warning and information messages from wwwolf’s PHP webshell.

All form data is sent through POST requests. A lot of webshells rely on GET , but this presents several disadvantages:

  • If the webshell overwrites a value used to invoke the webshell, this will fail. For instance, if the webshell tries to set a value to the path CGI parameter but you happen to execute the webshell using a LFI on the path CGI parameter, with most other webshells you will have to manually modify the webshell to use another variable, loosing time because of a tool limitation.

  • The same way the addition of some CGI parameters may change the server’s execution flow, being interpreted as an attempt to access another page or webapp functionality, again breaking your work and requiring you to alter the webshell code.

  • The values submitted through GET are appended to the requested URL and, therefore, most likely logged on the target-side.

Using POST requests instead avoids these issues.

What to do next?

Your next move will depend on a lot of factors, including what you want to achieve, the nature of your target, your access level, etc. There is no unique answer to “What to do next?”.

You may want to answer questions such as:

  • What software and which versions is the target running?
  • Under which account are you executing your commands?
  • What have you access to? In particular:
    • Are there any commands or scripting environment which may help you?
    • Have you a write access to a directory which would allow you to store transfered files?

Once you know your target a little better, you may want to upload a payload, a rootkit or a toolbox of some sort on the target to proceed with privilege escalation or persistence tasks.

Good luck! And as always: stay ethical!

  1. Devices and software used to store a lot of potentially privacy sensitive information as Exif metadata: not only the name, model and version of the device used but also often its unique ID number potentially allowing to link a file with an phyical individual, GPS coordinates, a timestamp and a thumbnail which may allow to retrieve hidden parts of anonymized photographs. 

  2. If you have a practical scenario allowing to make this technique actually work, don’t hesitate to let me know and I will update this post with your contribution. 

  3. When fopen() is used, a side-effect is that you have access to more protocol prefixes. For now this is just presented as a low-level “trick” in using this webshell, to keep things simple. Feel free to send me feedbacks if you need some more proper handling of fopen() abilities. 

PHP 4.0.6 on Linux 2.4.8 (Mandrake 8.1 "vitamin", the browser is Konqueror 2.2.1)

PHP 4.0.6 on Linux 2.4.8 (Mandrake 8.1 "vitamin", the browser is Konqueror 2.2.1)

PHP 7.0.19, running on Linux 4.9.0 (Debian 9.2 "stretch", the browser is Firefox ESR 52.5.0)

PHP 7.0.19, running on Linux 4.9.0 (Debian 9.2 "stretch", the browser is Firefox ESR 52.5.0)

PHP 7.1.11, running on Windows 2016 (the browser is Internet Explorer 11)

PHP 7.1.11, running on Windows 2016 (the browser is Internet Explorer 11)

Password input page

Password input page

wwwolf's PHP webshell interface

wwwolf's PHP webshell interface

Popular tags see all