首页 > 解决方案 > 为什么输出保持空白?

问题描述

我正在关注“Black Hat Python”一书,作者创建了一个 TCP 代理,但我似乎无法使其工作。

这是我正在使用的代码:

import sys
import socket
import threading


# this is a pretty hex dumping function directly taken from
# http://code.activestate.com/recipes/142812-hex-dumper/

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 = b' '.join([b"%0*X" % (digits, ord(x)) for x in s])
        text = b''.join([x if 0x20 <= ord(x) < 0x7F else b'.' for x in s])
        result.append(
            b"%04X   %-*s   %s" % (i, length * (digits + 1), hexa, text))

    print(b'\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)
            if not data:
                break
            buffer += data

    except TimeoutError:
        pass

    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)
        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 read 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 socket.error as exc:
        print("[!!] Failed to listen on %s:%d" % (local_host,
                                                  local_port))
        print("[!!] Check for other listening sockets or correct "
              "permissions.")
        print(f"[!!] Caught exception error: {exc}")
        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:
        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()

使用以下命令:

sudo ./proxy.py 127.0.0.1 21 ftp.target.ca 21 True

该程序应给出如下输出:

[*] Listening on 127.0.0.1:21
[==>] Received incoming connection from 127.0.0.1:59218
0000 32 32 30 20 50 72 6F 46 54 50 44 20 31 2E 33 2E 220 ProFTPD 1.3.
0010 33 61 20 53 65 72 76 65 72 20 28 44 65 62 69 61 3a Server (Debia
0020 6E 29 20 5B 3A 3A 66 66 66 66 3A 35 30 2E 35 37 n) [::ffff:22.22
0030 2E 31 36 38 2E 39 33 5D 0D 0A .22.22]..
[<==] Sending 58 bytes to localhost.
[==>] Received 12 bytes from localhost.
0000 55 53 45 52 20 74 65 73 74 79 0D 0A USER testy..
[==>] Sent to remote.
[<==] Received 33 bytes from remote.
0000 33 33 31 20 50 61 73 73 77 6F 72 64 20 72 65 71 331 Password req
0010 75 69 72 65 64 20 66 6F 72 20 74 65 73 74 79 0D uired for testy.
0020 0A .
[<==] Sent to localhost.
[==>] Received 13 bytes from localhost.
0000 50 41 53 53 20 74 65 73 74 65 72 0D 0A PASS tester..
[==>] Sent to remote.
[*] No more data. Closing connections.

我尝试了不同的选项,比如使用不同的 ftp 服务器(例如 ftp.dlptest.com),甚至在 Windows 7 机器(相同子网)上设置了我自己的 ftp 服务器,但我仍然无法连接。

(在不同的终端上,我可以使用命令轻松连接到指定的 ftp 服务器

ftp ftp.dlptest.com

)

我错过了什么?

谢谢

标签: pythonpython-3.xproxy

解决方案


如果你运行它127.0.0.1 21

sudo ./proxy.py 127.0.0.1 21 ftp.dlptest.com 21 True

那么您必须使用

ftp 127.0.0.1 21

然后它开始工作。


但是有一个不同的问题:它似乎hexdump是为 Python 2 创建的,它将bytesstr视为相同类型的数据 - 它用于ord()将 char 转换为其代码/数字。在 Python 3 中不需要它。

您还可以直接将其代码转换为字符串chr(x)并连接到要显示的字符串。

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

    #print('[DEBUG] src:', type(src))

    for i in range(0, len(src), length):
        s = src[i:i+length]

        data = ['%0*X' % (digits, x) for x in s]     
        #print('[DEBUG] data:', data)
        hexa = ' '.join(data)
        #print('[DEBUG] hexa:', hexa)
        
        data = [chr(x) if 0x20 <= x < 0x7F else '.' for x in s]
        #print('[DEBUG] data:', data)
        text = ''.join(data)
        #print('[DEBUG] text:', text)
        
        result.append(
            "%04X   %-*s   %s" % (i, length * (digits+1), hexa, text))

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

其他问题:它需要socket.timeout而不是Timeout捕获这个异常。


其他问题:当代码等待本地数据时,它可能需要更大的价值,connection.settimeout(2)因为人类可能需要时间来放置一些数据 - 即。FTP 连接中的登录名和密码。我对不同的连接使用不同的值。默认情况下,它使用 2 秒

def receive_from(connection, timeout=2):

    connection.settimeout(timeout)

但对于本地数据,它使用 10 秒

local_buffer = receive_from(client_socket, 10)

顺便提一句:

我也抓住KeyboardInterrupt了,所以当我停止使用Ctrl+C.

我也加

server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

解决[Errno 98] Address already in use再次启动服务器时出现错误的问题。


代码:

import sys
import socket
import threading


# this is a pretty hex dumping function directly taken from
# http://code.activestate.com/recipes/142812-hex-dumper/

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

    #print('[DEBUG] src:', type(src))

    for i in range(0, len(src), length):
        s = src[i:i+length]

        data = ['%0*X' % (digits, x) for x in s]     
        #print('[DEBUG] data:', data)
        hexa = ' '.join(data)
        #print('[DEBUG] hexa:', hexa)
        
        data = [chr(x) if 0x20 <= x < 0x7F else '.' for x in s]
        #print('[DEBUG] data:', data)
        text = ''.join(data)
        #print('[DEBUG] text:', text)
        
        result.append(
            "%04X   %-*s   %s" % (i, length * (digits+1), hexa, text))

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


def receive_from(connection, timeout=2):
    buffer = b''

    # We set a 2 second time-out. Depending on your target this may need
    # to be adjusted
    connection.settimeout(timeout)

    try:

        # keep reading into the buffer until there's no more data or we
        # time-out
        while True:
            data = connection.recv(4096)
            if not data:
                break
            buffer += data

    except socket.timeout as ex:
        #print('TimeoutError:', ex)
        pass

    print('[DEBUG] buffer:')
    print(buffer.decode())
    
    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):
    print(client_socket)
    print(remote_host, remote_port)
    print(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:
        print('receive first')
        
        remote_buffer = receive_from(remote_socket)
        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 read from local, send to remote, send to local
    # rinse wash repeat
    while True:
        # read from local host
        local_buffer = receive_from(client_socket, 10) # longer time for user response

        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)
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

    try:
        server.bind((local_host, local_port))
    except socket.error as exc:
        print("[!!] Failed to listen on %s:%d" % (local_host,
                                                  local_port))
        print("[!!] Check for other listening sockets or correct "
              "permissions.")
        print(f"[!!] Caught exception error: {exc}")
        sys.exit(0)

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

    server.listen(5)

    try:
        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()
    except KeyboardInterrupt:
        print('KeyboardInterrupt')
        server.close()

def main():
    # no fancy command line parsing here
    if len(sys.argv[1:]) != 5:
        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]

    receive_first = ("True" in receive_first)

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

main()

当我在一个控制台中运行时

 sudo python3.7 proxy.py 127.0.0.1 5005 ftp.dlptest.com 21 True

并在第二

 ftp 127.0.0.1 5005

然后我得到

$ sudo python3.7 proxy.py 127.0.0.1 5005 ftp.dlptest.com 21 True

[*] Listening on 127.0.0.1:5005
[==>] Received incoming connection from 127.0.0.1:50124
<socket.socket fd=4, family=AddressFamily.AF_INET, type=SocketKind.SOCK_STREAM, proto=0, laddr=('127.0.0.1', 5005), raddr=('127.0.0.1', 50124)>
ftp.dlptest.com 21
True
receive first
[DEBUG] buffer:
220-#########################################################
220-Please upload your web files to the public_html directory.
220-Note that letters are case sensitive.
220-#########################################################
220 This is a private system - No anonymous login

0000   32 32 30 2D 23 23 23 23 23 23 23 23 23 23 23 23    220-############
0010   23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23    ################
0020   23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23    ################
0030   23 23 23 23 23 23 23 23 23 23 23 23 23 0D 0A 32    #############..2
0040   32 30 2D 50 6C 65 61 73 65 20 75 70 6C 6F 61 64    20-Please upload
0050   20 79 6F 75 72 20 77 65 62 20 66 69 6C 65 73 20     your web files 
0060   74 6F 20 74 68 65 20 70 75 62 6C 69 63 5F 68 74    to the public_ht
0070   6D 6C 20 64 69 72 65 63 74 6F 72 79 2E 0D 0A 32    ml directory...2
0080   32 30 2D 4E 6F 74 65 20 74 68 61 74 20 6C 65 74    20-Note that let
0090   74 65 72 73 20 61 72 65 20 63 61 73 65 20 73 65    ters are case se
00A0   6E 73 69 74 69 76 65 2E 0D 0A 32 32 30 2D 23 23    nsitive...220-##
00B0   23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23    ################
00C0   23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23    ################
00D0   23 23 23 23 23 23 23 23 23 23 23 23 23 23 23 23    ################
00E0   23 23 23 23 23 23 23 0D 0A 32 32 30 20 54 68 69    #######..220 Thi
00F0   73 20 69 73 20 61 20 70 72 69 76 61 74 65 20 73    s is a private s
0100   79 73 74 65 6D 20 2D 20 4E 6F 20 61 6E 6F 6E 79    ystem - No anony
0110   6D 6F 75 73 20 6C 6F 67 69 6E 0D 0A                mous login..
[<==] Sending 284 bytes to localhost.
[DEBUG] buffer:
USER furas

[==>] Received 12 bytes from localhost.
0000   55 53 45 52 20 66 75 72 61 73 0D 0A                USER furas..
[==>] Sent to remote.
[DEBUG] buffer:
331 User furas OK. Password required

[<==] Received 38 bytes from remote.
0000   33 33 31 20 55 73 65 72 20 66 75 72 61 73 20 4F    331 User furas O
0010   4B 2E 20 50 61 73 73 77 6F 72 64 20 72 65 71 75    K. Password requ
0020   69 72 65 64 0D 0A                                  ired..
[<==] Sent to localhost.
[DEBUG] buffer:
PASS adasdasda

[==>] Received 16 bytes from localhost.
0000   50 41 53 53 20 61 64 61 73 64 61 73 64 61 0D 0A    PASS adasdasda..
[==>] Sent to remote.
[DEBUG] buffer:

[*] No more data. Closing connections.
^CKeyboardInterrupt

$ ftp 127.0.0.1 5005

Connected to 127.0.0.1.
220-#########################################################
220-Please upload your web files to the public_html directory.
220-Note that letters are case sensitive.
220-#########################################################
220 This is a private system - No anonymous login
Name (127.0.0.1:furas): 
331 User furas OK. Password required
Password:
421 Service not available, remote server has closed connection
Login failed.
No control connection for command: Success
ftp> quit

顺便说一句:如果你使用更大的端口1024(就像5005我的回答一样),那么它应该在没有sudo.


推荐阅读