-
-
Notifications
You must be signed in to change notification settings - Fork 444
Plugin development guide
If you plan to write a plugin for phpsploit, please observe the following guidelines and conventions.
๐ธ Respect the PEP8 convention while writing python
It eases code visibility for other users, and maintains coherence with the framework's core (which already observes this coding style).
This way, the plugin maximises its chances to be correctly executed in a single request, even with drastical server limitations. Keep in mind that the python plugin code (plugin.py) is executed from attacker side, while the php payload (*.php) is dynamically loaded in HTTP requests, and executed on target server.
plugins are stored in ./plugins/<CATEGORY>/<NAME>/
.
This file is mandatory. It has 2 purposes:
- send PHP payload to remote server
- process raw results & return them to user
- ๐ plugin help docstring:
Phpsploit generateshelp <PLUGIN>
output from the docstring present at the start of this file
It's commonly named payload.php
, but a plugin can have multiple PHP payloads (like edit
plugin),
and it can also have no payload (like whoami
plugin).
-
โ
!import(<LIB>)
syntax:
non-standard notation, includes contents of./src/api/php-functions/<LIB>.php
at runtime -
๐ฉ be retrocompatible:
phpsploit sould be able to work on old versions of PHP (at least 4.3.0), until latest version. Keep it in mind while using PHP standard functions
The API documentation is available directly from phpsploit, like a standard python library.
To get started, run a python console from inside phpsploit, and browse the api's python docstrings:
phpsploit > # run python console from phpsploit
phpsploit > corectl python-console
Phpsploit corectl: python console interpreter
>>> # browse api docstrings & objects from python console
>>> import api
>>> print(api.plugin.name)
plugin_example
>>> help(api)
...
phpsploit comes with a small script included. It starts a tiny pre-backdoored php server listening on localhost to test plugins:
$ ./utils/start_phpsploit_connected.sh
use the following command to connect phpsploit to server:
----------------------------------------
./dev/phpsploit/phpsploit -t 127.0.0.1:64956 -ie 'set BACKDOOR %%DEFAULT%%; set REQ_HEADER_PAYLOAD %%DEFAULT%%; exploit'
----------------------------------------
PHP 7.3.19-1~deb10u1 Development Server started at Tue Sep 15 22:24:30 2020
Listening on http://127.0.0.1:64956
Document root is /tmp/phpsploit-temp-server
Press Ctrl-C to quit.
After that, just run the command returned by the script in another terminal to have a phpsploit session in connected mode.
After making a change, you can run corectl reload-plugins
to refresh plugins,
so you don't need to restart phpsploit each time you make a change.
It contains multiple tools very useful to write a plug-in.
I heavily recommend to run help corectl
from phpsploit for further info.
In phpsploit, verbose outputs (lines starting with [#]
) are hidden by default.
Therefore, they might be useful to get more informations for dev and debug.
Use set VERBOSITY True
to enable it.
Example:
phpsploit > lrun pwd
/tmp
phpsploit > set VERBOSITY True
[#] CMD('set' 'VERBOSITY' 'True'): Returned 0
phpsploit > lrun pwd
[#] CMD('lrun' 'pwd'): Running...
/tmp
[#] CMD('lrun' 'pwd'): Returned 0
phpsploit >
"""Remove empty directory
SYNOPSIS:
rmdir <REMOTE-DIRECTORY>
DESCRIPTION:
Remove REMOTE-DIRECTORY if it is empty.
AUTHOR:
nil0x42 <http://goo.gl/kb2wf>
"""
import sys
from api import plugin
from api import server
if len(plugin.argv) != 2:
sys.exit(plugin.help)
rel_path = plugin.argv[1]
abs_path = server.path.abspath(rel_path)
payload = server.payload.Payload("payload.php") # prepare a payload request
payload["DIR"] = abs_path # this var will be added to PHP's $PHPSPLOIT variable
payload.send() # send payload
!import(dirAccess) // include content of ./src/api/php-functions/dirAccess.php
$dir = $PHPSPLOIT["DIR"]; // get variable given by plugin.py
// return error() if failed. otherwise, return anything else
if (!@file_exists($dir))
return error("failed to remove '%s': No such file or directory", $dir);
if ((@fileperms($dir) & 0x4000) != 0x4000)
return error("failed to remove '%s': Not a directory", $dir);
if (@rmdir($dir) === FALSE) {
if (dirAccess($dir, 'r'))
return error("failed to remove '%s': Directory not empty", $dir);
return error("failed to remove '%s': Permission denied", $dir);
}