《Python 黑帽子》学习笔记 - proxy - Day 10

作者提到,proxy 可以将数据从一个主机转发给另一个主机,而且可以评估基于网络的软件。作者常常在实际案例中部署简单的 TCP 代理以了解未知的协议,修改发送到应用的数据包,或者为模糊测试创建一个测试环境。

渗透测试的高手,常常用简单的工具来验证奇思妙想。希望自己也能成为高手。

功能

监听本地端口,接收客户端连接,将客户端的网络数据转发到远程主机,并将远程主机的反馈数据转发到客户端。在转发数据之前,可以对数据进行协议分析、模糊测试、检测认证或者数据修改等工作。

实现

  1. 解析参数,确定监听端口,远程主机和端口。
  2. 服务端在主循环监听连接请求,有新的请求到达,创建一个线程,调用 proxy_handler(), 处理客户端和远程主机的数据。
  3. 根据 receive_first, 确定是否先接收远程主机数据。
  4. 进入数据转发循环,获取客户端数据,转发至远程主机,获取远程主机数据,转发至客户端
  5. 每次接收到数据后,首先调用 hexdump(), 转储为 16 进制,查看是否有价值的信息,或进行其他分析。
  6. 发送数据到远程主机之前,通过 response_handler() 进行数据分析。
  7. 发送数据到客户端之前,通过 response_handler() 进行数据分析。

Python3 代码

Python2 的代码见原书,以下为修改的 Python3 代码。

#!/usr/bin/env python3
# -*- code: utf-8 -*-

import sys
import socket
import threading

import logging

logging.basicConfig(level=logging.ERROR,
                    format='%(filename)s[line:%(lineno)d] %(funcName)s %(levelname)s %(message)s')


def hexdump(src, length=16):
    result = []
    digits = 4 if isinstance(src, str) else 2

    for i in range(0, len(src), length):
        s = src[i:i + length]
        hexa = ' '.join([hex(x)[2:].upper().zfill(digits) for x in s])
        text = ''.join([chr(x) if 0x20 <= x < 0x7F else '.' for x in s])
        result.append("{0:04X}".format(i) + ' ' * 3 + hexa.ljust(length * (digits + 1)) + ' ' * 3 + "{0}".format(text))

    # return '\n'.join(result)
    print('\n'.join(result))


def receive_from(connection):
    buffer = b""
    # We set a 2 second time out depending on your
    # target this may need to be adjusted
    connection.settimeout(2)

    try:
        # keep reading into the buffer until there's no more data
        # or we time out
        while True:
            data = connection.recv(4096)
            logging.debug(data)

            if not data:
                break

            buffer += data
    except Exception as err:
        logging.error(err)

    return buffer


# modify any requests destined for the remote host
def request_handler(buffer):
    # perform packet modifications
    return buffer


# modify any responses destined for the local host
def response_handler(buffer):
    # perform packet modifications
    return buffer


def proxy_handler(client_socket, remote_host, remote_port, receive_first):
    # connect to the remote host
    remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    remote_socket.connect((remote_host, remote_port))

    # receive data from the remote end if necessary
    if receive_first:

        remote_buffer = receive_from(remote_socket)
        logging.debug(remote_buffer)
        hexdump(remote_buffer)

        # send it to our response handler
        remote_buffer = response_handler(remote_buffer)

        # if we have data to send to our local client send it
        if len(remote_buffer):
            print("[<==] Sending %d bytes to localhost." % len(remote_buffer))
            client_socket.send(remote_buffer)

    # now let's loop and reading from local, send to remote, send to local
    # rinse wash repeat
    while True:

        # read from local host
        local_buffer = receive_from(client_socket)

        if len(local_buffer):
            print("[==>] Received %d bytes from localhost." % len(local_buffer))
            hexdump(local_buffer)

            # send it to our request handler
            local_buffer = request_handler(local_buffer)

            # send off the data to the remote host
            remote_socket.send(local_buffer)
            print("[==>] Sent to remote.")

        # receive back the response
        remote_buffer = receive_from(remote_socket)

        if len(remote_buffer):
            print("[<==] Received %d bytes from remote." % len(remote_buffer))
            hexdump(remote_buffer)

            # send to our response handler
            remote_buffer = response_handler(remote_buffer)

            # send the response to the local socket
            client_socket.send(remote_buffer)

            print("[<==] Sent to localhost.")

        # if no more data on either side close the connections
        if not len(local_buffer) or not len(remote_buffer):
            client_socket.close()
            remote_socket.close()
            print("[*] No more data. Closing connections.")

            break


def server_loop(local_host, local_port, remote_host, remote_port, receive_first):
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        server.bind((local_host, local_port))
    except Exception as err:
        logging.error(err)
        print("[!!] Failed to listen on %s:%d" % (local_host, local_port))
        print("[!!] Check for other listening sockets or correct permissions.")
        sys.exit(0)

    print("[*] Listening on %s:%d" % (local_host, local_port))

    server.listen(5)

    while True:
        client_socket, addr = server.accept()

        # print out the local connection information
        print("[==>] Received incoming connection from %s:%d" % (addr[0], addr[1]))

        # start a thread to talk to the remote host
        proxy_thread = threading.Thread(target=proxy_handler,
                                        args=(client_socket, remote_host, remote_port, receive_first))
        proxy_thread.start()


def main():
    # no fancy command line parsing here
    if len(sys.argv[1:]) != 5:
        logging.debug(sys.argv[1:])
        print("Usage: ./proxy.py [localhost] [localport] [remotehost] [remoteport] [receive_first]")
        print("Example: ./proxy.py 127.0.0.1 9000 10.12.132.1 9000 True")
        sys.exit(0)

    # setup local listening parameters
    local_host = sys.argv[1]
    local_port = int(sys.argv[2])

    # setup remote target
    remote_host = sys.argv[3]
    remote_port = int(sys.argv[4])

    # this tells our proxy to connect and receive data
    # before sending to the remote host
    receive_first = sys.argv[5]

    if "True" in receive_first:
        receive_first = True
    else:
        receive_first = False

    # now spin up our listening socket
    server_loop(local_host, local_port, remote_host, remote_port, receive_first)

main()

测试结果如图:

proxy-ftp

我在本地假设了一个 ftp 服务器,用脚本监听 8888 端口,并连接本地 ftp 服务的 21 端口。

有个问题是,对 socket 进行了超时设置 connection.settimeout(2), connection.recv(4096) 很快就报 time out 错误,但是又能收到数据,找不出原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值