Skip to content

Commit 308a472

Browse files
committed
add custom netcat tutorial
1 parent 4bb2639 commit 308a472

File tree

3 files changed

+324
-0
lines changed

3 files changed

+324
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ This is a repository of all the tutorials of [The Python Code](https://www.thepy
7171
- [How to Check Password Strength with Python](https://thepythoncode.com/article/test-password-strength-with-python). ([code](ethical-hacking/checking-password-strength))
7272
- [How to Perform Reverse DNS Lookups Using Python](https://thepythoncode.com/article/reverse-dns-lookup-with-python). ([code](ethical-hacking/reverse-dns-lookup))
7373
- [How to Make a Clickjacking Vulnerability Scanner in Python](https://thepythoncode.com/article/make-a-clickjacking-vulnerability-scanner-with-python). ([code](ethical-hacking/clickjacking-scanner))
74+
- [How to Build a Custom NetCat with Python](https://thepythoncode.com/article/create-a-custom-netcat-in-python). ([code](ethical-hacking/custom-netcat/))
7475

7576
- ### [Machine Learning](https://www.thepythoncode.com/topic/machine-learning)
7677
- ### [Natural Language Processing](https://www.thepythoncode.com/topic/nlp)
+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# [How to Build a Custom NetCat with Python](https://thepythoncode.com/article/create-a-custom-netcat-in-python)
+322
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,322 @@
1+
import sys, socket, getopt, threading, subprocess, signal, time
2+
3+
4+
class NetCat:
5+
def __init__(self, target, port):
6+
self.listen = False
7+
self.command = False
8+
self.upload = False
9+
self.execute = ""
10+
self.target = target
11+
self.upload_destination = ""
12+
self.port = port
13+
self.running = True
14+
self.threads = []
15+
16+
def signal_handler(self, signum, frame):
17+
print('\n[*] User requested an interrupt. Exiting gracefully.')
18+
self.running = False
19+
time.sleep(0.5)
20+
sys.exit(0)
21+
22+
def run_command(self, cmd):
23+
cmd = cmd.rstrip()
24+
try:
25+
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT, shell=True)
26+
except subprocess.CalledProcessError as e:
27+
output = e.output
28+
except Exception as e:
29+
output = str(e).encode()
30+
return output
31+
32+
def handle_client(self, client_socket):
33+
try:
34+
if len(self.upload_destination):
35+
file_buffer = ""
36+
while self.running:
37+
try:
38+
data = client_socket.recv(1024)
39+
if not data:
40+
break
41+
else:
42+
file_buffer += data.decode('utf-8')
43+
except (ConnectionResetError, BrokenPipeError) as e:
44+
print(f"[!] Connection error during upload: {str(e)}")
45+
break
46+
except Exception as e:
47+
print(f"[!] Error receiving data: {str(e)}")
48+
break
49+
50+
try:
51+
with open(self.upload_destination, "wb") as file_descriptor:
52+
file_descriptor.write(file_buffer.encode('utf-8'))
53+
try:
54+
client_socket.send(
55+
f"Successfully saved file to {self.upload_destination}\r\n".encode('utf-8'))
56+
except (BrokenPipeError, ConnectionResetError):
57+
print("[!] Couldn't send success message - connection lost")
58+
except OSError as e:
59+
print(f"[!] File operation failed: {str(e)}")
60+
try:
61+
client_socket.send(
62+
f"Failed to save file to {self.upload_destination}\r\n".encode('utf-8'))
63+
except (BrokenPipeError, ConnectionResetError):
64+
print("[!] Couldn't send error message - connection lost")
65+
66+
if len(self.execute) and self.running:
67+
try:
68+
output = self.run_command(self.execute)
69+
client_socket.send(output)
70+
except (BrokenPipeError, ConnectionResetError):
71+
print("[!] Couldn't send command output - connection lost")
72+
except Exception as e:
73+
print(f"[!] Error executing command: {str(e)}")
74+
75+
if self.command:
76+
while self.running:
77+
try:
78+
# Send prompt
79+
client_socket.send(b"<Target:#> ")
80+
81+
# Receive command
82+
cmd_buffer = b''
83+
while b"\n" not in cmd_buffer and self.running:
84+
try:
85+
data = client_socket.recv(1024)
86+
if not data:
87+
raise ConnectionResetError("No data received")
88+
cmd_buffer += data
89+
except socket.timeout:
90+
continue
91+
except (ConnectionResetError, BrokenPipeError):
92+
raise
93+
94+
if not self.running:
95+
break
96+
97+
# Execute command and send response
98+
try:
99+
cmd = cmd_buffer.decode().strip()
100+
if cmd.lower() in ['exit', 'quit']:
101+
print("[*] User requested exit")
102+
break
103+
104+
output = self.run_command(cmd)
105+
if output:
106+
client_socket.send(output + b"\n")
107+
else:
108+
client_socket.send(b"Command completed without output\n")
109+
110+
except (BrokenPipeError, ConnectionResetError):
111+
print("[!] Connection lost while sending response")
112+
break
113+
except Exception as e:
114+
error_msg = f"Error executing command: {str(e)}\n"
115+
try:
116+
client_socket.send(error_msg.encode())
117+
except:
118+
break
119+
120+
except ConnectionResetError:
121+
print("[!] Connection reset by peer")
122+
break
123+
except BrokenPipeError:
124+
print("[!] Broken pipe - connection lost")
125+
break
126+
except Exception as e:
127+
print(f"[!] Error in command loop: {str(e)}")
128+
break
129+
130+
except Exception as e:
131+
print(f"[!] Exception in handle_client: {str(e)}")
132+
finally:
133+
try:
134+
client_socket.close()
135+
print("[*] Client connection closed")
136+
except:
137+
pass
138+
139+
def server_loop(self):
140+
server = None
141+
try:
142+
if not len(self.target):
143+
self.target = "0.0.0.0"
144+
145+
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
146+
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
147+
server.bind((self.target, self.port))
148+
server.listen(5)
149+
150+
print(f"[*] Listening on {self.target}:{self.port}")
151+
152+
server.settimeout(1.0)
153+
154+
while self.running:
155+
try:
156+
client_socket, addr = server.accept()
157+
print(f"[*] Accepted connection from {addr[0]}:{addr[1]}")
158+
159+
client_thread = threading.Thread(
160+
target=self.handle_client,
161+
args=(client_socket,)
162+
)
163+
client_thread.daemon = True
164+
self.threads.append(client_thread)
165+
client_thread.start()
166+
167+
except socket.timeout:
168+
continue
169+
except Exception as e:
170+
if self.running:
171+
print(f"[!] Exception in server_loop: {str(e)}")
172+
break
173+
174+
except Exception as e:
175+
print(f"[!] Failed to create server: {str(e)}")
176+
finally:
177+
if server:
178+
try:
179+
server.close()
180+
print("[*] Server socket closed")
181+
except:
182+
pass
183+
184+
for thread in self.threads:
185+
try:
186+
thread.join(timeout=1.0)
187+
except threading.ThreadError:
188+
pass
189+
190+
def client_sender(self, buffer):
191+
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
192+
193+
try:
194+
print(f"[*] Connecting to {self.target}:{self.port}")
195+
client.connect((self.target, self.port))
196+
197+
if len(buffer):
198+
try:
199+
client.send(buffer.encode('utf-8'))
200+
except (BrokenPipeError, ConnectionResetError):
201+
print("[!] Failed to send initial buffer - connection lost")
202+
return
203+
204+
while self.running:
205+
try:
206+
# Receive response from server
207+
recv_len = 1
208+
response = b''
209+
210+
while recv_len:
211+
data = client.recv(4096)
212+
recv_len = len(data)
213+
response += data
214+
215+
if recv_len < 4096:
216+
break
217+
218+
if response:
219+
print(response.decode('utf-8'), end='')
220+
221+
# Get next command
222+
buffer = input()
223+
if not self.running:
224+
break
225+
226+
if buffer.lower() in ['exit', 'quit']:
227+
break
228+
229+
buffer += "\n"
230+
try:
231+
client.send(buffer.encode('utf-8'))
232+
except (BrokenPipeError, ConnectionResetError):
233+
print("\n[!] Failed to send data - connection lost")
234+
break
235+
236+
except ConnectionResetError:
237+
print("\n[!] Connection reset by peer")
238+
break
239+
except BrokenPipeError:
240+
print("\n[!] Broken pipe - connection lost")
241+
break
242+
except EOFError:
243+
print("\n[!] EOF detected - exiting")
244+
break
245+
except Exception as e:
246+
print(f"\n[!] Exception in client loop: {str(e)}")
247+
break
248+
249+
except socket.error as exc:
250+
print("\n[!] Exception! Exiting.")
251+
print(f"[!] Caught exception socket.error: {exc}")
252+
finally:
253+
print("[*] Closing connection")
254+
try:
255+
client.close()
256+
except:
257+
pass
258+
259+
def main():
260+
if len(sys.argv[1:]) == 0:
261+
print("Custom Netcat")
262+
print("\nSYNOPSIS")
263+
print(" netcat.py [OPTIONS...]\n")
264+
print("OPTIONS")
265+
print(" -l, --listen Start server in listening mode on specified host:port")
266+
print(" -e, --execute=<file> Execute specified file upon connection establishment")
267+
print(" -c, --command Initialize an interactive command shell session")
268+
print(" -u, --upload=<path> Upload file to specified destination path on connection")
269+
print(" -t, --target=<host> Specify target hostname or IP address")
270+
print(" -p, --port=<port> Specify target port number")
271+
print()
272+
sys.exit(0)
273+
274+
try:
275+
opts, args = getopt.getopt(sys.argv[1:], "hle:t:p:cu:",
276+
["help", "listen", "execute", "target",
277+
"port", "command", "upload"])
278+
279+
for o, a in opts:
280+
if o in ("-h", "--help"):
281+
main()
282+
elif o in ("-l", "--listen"):
283+
toolkit.listen = True
284+
elif o in ("-e", "--execute"):
285+
toolkit.execute = a
286+
elif o in ("-c", "--command"):
287+
toolkit.command = True
288+
elif o in ("-u", "--upload"):
289+
toolkit.upload_destination = a
290+
elif o in ("-t", "--target"):
291+
toolkit.target = a
292+
elif o in ("-p", "--port"):
293+
toolkit.port = int(a)
294+
else:
295+
assert False, "Unhandled Option"
296+
297+
except getopt.GetoptError as err:
298+
print(str(err))
299+
main()
300+
301+
signal.signal(signal.SIGINT, toolkit.signal_handler)
302+
signal.signal(signal.SIGTERM, toolkit.signal_handler)
303+
304+
try:
305+
if not toolkit.listen and len(toolkit.target) and toolkit.port > 0:
306+
buffer = sys.stdin.read()
307+
toolkit.client_sender(buffer)
308+
309+
if toolkit.listen:
310+
toolkit.server_loop()
311+
except KeyboardInterrupt:
312+
print("\n[*] User requested shutdown")
313+
except Exception as e:
314+
print(f"\n[!] Unexpected error: {str(e)}")
315+
finally:
316+
toolkit.running = False
317+
print("[*] Shutdown complete")
318+
sys.exit(0)
319+
320+
if __name__ == "__main__":
321+
toolkit = NetCat("", 0)
322+
main()

0 commit comments

Comments
 (0)