Netcat作为一款被广泛使用的计算机网络工具,为了提高个人编程水平和加深对网络的理解,今天我就基于socket来对其进行简单的实现
目录
一 需要使用到的库
import argparse
import socket
import shlex #Split command
import subprocess
import sys
import textwrap
import threading
二 思路
1)作为一个可以连接和被连接的工具,首先应该具备两种模式,即收数据和发数据,在脚本中可以通过指定参数的方式进行控制
2)应该要有一个可以执行命令行指令的功能,同样可以使用参数控制,最终达到的效果应该是可以通过客户端发送指令让服务器端(指在单次连接中有客户端和服务器端的区别)执行指令
3)最后应该有办法从客户端向服务器端(与上同理)传输文件
三 分块实现
为了方便调用应该先考虑建立一个类NetCat来对socket和所要发送的数据进行处理,也就是我们的工具应该能够管理套接字和所要发送的数据。建立一个buffer 缓冲方便后续存储数据
class NetCat:
#use args and buffer to initialize the NetCat
def __init__(self,args,buffer = None):
self.args = args
self.buffer = buffer
self.socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)#create a socket
self.socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,1)
#when a socket come to a close state , it may get into a TIME_WAIT state,SO_REUSEADDR
# can restart the socket immediately
接下来我们应该定义一个处理函数用于处理我们传进来的参数和命令(类似于“192.168.169.12 122 -c 。。。。”)
def handle(self,client_sock):
if self.args.execute:
output = execute(self.args.execute)
client_sock.send(output.encode())
elif self.args.upload:
file_buffer = b''#create a file buffer
while True:
data = client_sock.recv(4096)
if data:
file_buffer +=data
else:
break
with open(self.args.upload,'wb') as f:
f.write(file_buffer)
message = f'Save file{self.args.upload}'
client_sock.send(message.encode())
elif self.args.command:
cmd_buffer = b''
while True:
try:
client_sock.send(b'BHP: #>>')
while '\n' not in cmd_buffer.decode():
cmd_buffer += client_sock.recv(64)
response = execute(cmd_buffer.decode())
if response:
client_sock.send(response.encode())
cmd_buffer = b''
except Exception as e:
print(1)
print(f'Server killed {e}')
self.socket.close()
sys.exit()
(上面的代码中的 execute函数实际上是未定义的)
def execute(cmd):
cmd = cmd.strip() #remove whitespace at the b and e of the string
if not cmd:#if is a an empty string
return
output = subprocess.check_output(shlex.split(cmd),stderr=subprocess.STDOUT)# to run instructions on this machine and assign the returned content to the output
return output.decode()
(上面补上了execute函数)
在定义的处理函数之后,我们接下来定义发送函数send
def listen(self):
self.socket.bind((self.args.target,self.args.port))#bind port and ip to the socket
self.socket.listen(5)#listen 5
while True:
cilent_socket,adds = self.socket.accept()
print(f'[*]Connect from {adds}')
client_thread = threading.Thread(target=self.handle,
args=(cilent_socket,))#create a thread to excute the command
client_thread.start()
和listen函数
def send(self):
self.socket.connect((self.args.target,self.args.port))
if self.buffer:
self.socket.send(self.buffer)#if there has data in the buffer , send it out first
try:
while True:
#When the hot key is not triggered, a large loop continues
recv_len = 1
respose = ''
#When the messages in the current socket have not been received completely, a small loop continues
while recv_len:
data = self.socket.recv(4096)
recv_len = len(data)
respose += data.decode()
if recv_len<4096:
break
if respose:
print(respose)
buffer = input(">>>")
buffer += '\n'
self.socket.send(buffer.encode())
except KeyboardInterrupt:
print("User Quit")
self.socket.close()
sys.exit()
最后定义一个run函数作为调用的入口
def run(self):# to excute te program,as the window between sender and receicer
if self.args.listen:
self.listen()
else:
self.send()
在主函数中利用argparse对脚本进行规范化,其封装的函数能够帮助我们更加简单制作脚本
并在初始化时添加对于参数的解释
if __name__ == '__main__':
parser = argparse.ArgumentParser(
description="A Net Tool from a freshman",
formatter_class=argparse.RawDescriptionHelpFormatter,
epilog= textwrap.dedent('''Example:
netcat.py -t 192.168.1.108 -p 5555 -l -c #command shell
netcat.py -t 192.168.1.108 -p 5555 -l -u=test.txt #upload file
netcat.py -t 192.168.1.108 -p 5555 -l -e=\"cat/etc/passwd\" #execute command
echo 'ABC' | ./netcat.py -t 192.168.1.108 -p 135 #echo text tp server port 135
netcat.py -t 192.168.1.108 -p 5555 #connect to server
'''))
parser.add_argument('-c','--command',action='store_true',help='command shell')
parser.add_argument('-e','--execute',help='execute specified command')
parser.add_argument('-l','--listen',action='store_true',help='listen')
parser.add_argument('-p','--port',type= int,default= 5555,help='specifide port')
parser.add_argument('-t','--target',default= '192.168.1.108',help='specified IP')
parser.add_argument('-u','--upload',help='upload file')
args = parser.parse_args()#Analysis the command and save the results in args
if args.listen:
buffer = ''
else:
buffer = sys.stdin.read()
nc = NetCat(args,buffer.encode())#to define a class NetCat
nc.run()
四 大功告成,现在来试试效果
首先先在虚拟机上打开监听,并且打开命令行的执行权限
在WSL中打开另一个相同的脚本,并将目标指向虚拟机
由于在编写的时候使用STD进行读取故在开始时需要为命令读入一个EOF命令才会继续运行
按下Ctrl +D后 (输入一个EOF命令),可以看见虚拟机接收到了来着WSL的连接,并且在WSL中已经可以远程向虚拟机执行指令了(不过要注意由于没有设计容错所以命令不能打错,不然虚拟机上的程序会直接退出)
尝试执行创建文件指令
成功
重新连接,再测试一下只执行一条指令的功能
成功,在WSL中收到了打开文件的内容
五 写在最后的话
还可以改进的地方:增加对于同一主机其他端口的扫描功能
增加对于输入命令错误时服务器端的容错率
写注释的时候几乎全部是用翻译软件写的,英语大退步(bushi
python很多语法也忘了,不得不说还是要加强对于编程的练习,不过最后看到程序能够正常运行,还是挺开心的