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 ("\n SYNOPSIS" )
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