Fixing eviltwin. Lots of changes.
This commit is contained in:
@@ -21,13 +21,13 @@ class AirmonIface(object):
|
||||
self.chipset = chipset
|
||||
self.mac_address = Ifconfig.get_mac(interface)
|
||||
|
||||
# Max length of fields.
|
||||
# Used for printing a table of interfaces.
|
||||
# Max length of fields. Used for printing a table of interfaces.
|
||||
INTERFACE_LEN = 12
|
||||
PHY_LEN = 6
|
||||
DRIVER_LEN = 20
|
||||
CHIPSET_LEN = 30
|
||||
|
||||
|
||||
def __str__(self):
|
||||
''' Colored string representation of interface '''
|
||||
s = ''
|
||||
@@ -37,6 +37,7 @@ class AirmonIface(object):
|
||||
s += Color.s('{W}%s' % self.chipset.ljust(self.CHIPSET_LEN))
|
||||
return s
|
||||
|
||||
|
||||
@staticmethod
|
||||
def menu_header():
|
||||
''' Colored header row for interfaces '''
|
||||
@@ -52,12 +53,13 @@ class AirmonIface(object):
|
||||
|
||||
class Airmon(Dependency):
|
||||
''' Wrapper around the 'airmon-ng' program '''
|
||||
|
||||
dependency_required = True
|
||||
dependency_name = 'airmon-ng'
|
||||
dependency_url = 'https://www.aircrack-ng.org/install.html'
|
||||
|
||||
base_interface = None
|
||||
killed_network_manager = False
|
||||
base_interface = None # Interface *before* it was put into monitor mode.
|
||||
killed_network_manager = False # If we killed network-manager
|
||||
|
||||
# Drivers that need to be manually put into monitor mode
|
||||
BAD_DRIVERS = ['rtl8821au']
|
||||
@@ -66,18 +68,16 @@ class Airmon(Dependency):
|
||||
ARPHRD_IEEE80211_RADIOTAP = 803 #monitor
|
||||
|
||||
def __init__(self):
|
||||
self.refresh()
|
||||
|
||||
def refresh(self):
|
||||
''' Get airmon-recognized interfaces '''
|
||||
self.interfaces = Airmon.get_interfaces()
|
||||
|
||||
|
||||
def print_menu(self):
|
||||
''' Prints menu '''
|
||||
print(AirmonIface.menu_header())
|
||||
for idx, iface in enumerate(self.interfaces, start=1):
|
||||
Color.pl(" {G}%d{W}. %s" % (idx, iface))
|
||||
|
||||
|
||||
def get(self, index):
|
||||
''' Gets interface at index (starts at 1) '''
|
||||
if type(index) is str:
|
||||
@@ -105,6 +105,7 @@ class Airmon(Dependency):
|
||||
|
||||
return interfaces
|
||||
|
||||
|
||||
@staticmethod
|
||||
def start_bad_driver(iface):
|
||||
'''
|
||||
@@ -124,6 +125,7 @@ class Airmon(Dependency):
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@staticmethod
|
||||
def stop_bad_driver(iface):
|
||||
'''
|
||||
@@ -143,6 +145,7 @@ class Airmon(Dependency):
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@staticmethod
|
||||
def start(iface):
|
||||
'''
|
||||
@@ -197,18 +200,26 @@ class Airmon(Dependency):
|
||||
|
||||
return enabled_iface
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _parse_airmon_start(airmon_output):
|
||||
'''Find the interface put into monitor mode (if any)'''
|
||||
'''Returns the interface name that was put into monitor mode (if any)'''
|
||||
|
||||
# airmon-ng output: (mac80211 monitor mode vif enabled for [phy10]wlan0 on [phy10]wlan0mon)
|
||||
enabled_re = re.compile(r'\s*\(mac80211 monitor mode (?:vif )?enabled for [^ ]+ on (?:\[\w+\])?(\w+)\)\s*')
|
||||
|
||||
# airmon-ng output from https://www.aircrack-ng.org/doku.php?id=iwlagn
|
||||
enabled_re2 = re.compile(r'\s*\(monitor mode enabled on (\w+)\)')
|
||||
|
||||
for line in airmon_output.split('\n'):
|
||||
matches = enabled_re.match(line)
|
||||
if matches:
|
||||
return matches.group(1)
|
||||
|
||||
matches = enabled_re2.match(line)
|
||||
if matches:
|
||||
return matches.group(1)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -275,15 +286,16 @@ class Airmon(Dependency):
|
||||
|
||||
Airmon.terminate_conflicting_processes()
|
||||
|
||||
Color.pl('\n{+} looking for {C}wireless interfaces{W}')
|
||||
Color.p('\n{+} looking for {C}wireless interfaces{W}... ')
|
||||
monitor_interfaces = Iwconfig.get_interfaces(mode='Monitor')
|
||||
if len(monitor_interfaces) == 1:
|
||||
# Assume we're using the device already in montior mode
|
||||
iface = monitor_interfaces[0]
|
||||
Color.pl(' using interface {G}%s{W} (already in monitor mode)' % iface);
|
||||
Color.pl(' you can specify the wireless interface using {C}-i wlan0{W}')
|
||||
Color.pl('using interface {G}%s{W} (already in monitor mode)' % iface);
|
||||
#Color.pl(' you can specify the wireless interface using {C}-i wlan0{W}')
|
||||
Airmon.base_interface = None
|
||||
return iface
|
||||
Color.pl('')
|
||||
|
||||
a = Airmon()
|
||||
count = len(a.interfaces)
|
||||
@@ -364,6 +376,7 @@ class Airmon(Dependency):
|
||||
Ifconfig.up(iface)
|
||||
Color.pl(" {G}done{W}")
|
||||
|
||||
|
||||
@staticmethod
|
||||
def start_network_manager():
|
||||
Color.p("{!} {O}restarting {R}NetworkManager{O}...")
|
||||
@@ -404,3 +417,4 @@ if __name__ == '__main__':
|
||||
(disabled_iface, enabled_iface) = Airmon.stop(iface)
|
||||
print("Disabled:", disabled_iface)
|
||||
print("Enabled:", enabled_iface)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ from ..config import Configuration
|
||||
|
||||
class Dnsmasq(Dependency):
|
||||
'''Wrapper for dnsmasq program.'''
|
||||
|
||||
dependency_required = False
|
||||
dependency_name = 'dnsmasq'
|
||||
dependency_url = 'apt-get install dnsmasq'
|
||||
@@ -16,14 +17,15 @@ class Dnsmasq(Dependency):
|
||||
def __init__(self, interface):
|
||||
self.interface = interface
|
||||
self.pid = None
|
||||
self.config_file = None
|
||||
|
||||
|
||||
def create_config_file(self):
|
||||
config_file = os.path.join(Configuration.temp(), 'dnsmasq.conf')
|
||||
if os.path.exists(config_file):
|
||||
os.remove(config_file)
|
||||
self.config_file = os.path.join(Configuration.temp(), 'dnsmasq.conf')
|
||||
if os.path.exists(self.config_file):
|
||||
os.remove(self.config_file)
|
||||
|
||||
with open(config_file, 'w') as config:
|
||||
with open(self.config_file, 'w') as config:
|
||||
config.write('interface={}\n'.format(self.interface))
|
||||
config.write('dhcp-range=10.0.0.10,10.0.0.100,8h\n')
|
||||
config.write('dhcp-option=3,10.0.0.1\n')
|
||||
@@ -31,11 +33,10 @@ class Dnsmasq(Dependency):
|
||||
config.write('server=8.8.8.8\n')
|
||||
config.write('log-queries\n')
|
||||
config.write('log-dhcp\n')
|
||||
return config_file
|
||||
|
||||
|
||||
def start(self):
|
||||
config_file = self.create_config_file()
|
||||
self.create_config_file()
|
||||
|
||||
# Stop already-running dnsmasq process
|
||||
self.killall()
|
||||
@@ -43,25 +44,28 @@ class Dnsmasq(Dependency):
|
||||
# Start new dnsmasq process
|
||||
self.pid = Process([
|
||||
'dnsmasq',
|
||||
'-C', config_file
|
||||
'-C', self.config_file
|
||||
])
|
||||
|
||||
|
||||
def stop(self):
|
||||
# Kill dnsmasq process
|
||||
if self.pid:
|
||||
if self.pid and self.pid.poll() is not None:
|
||||
self.pid.interrupt()
|
||||
|
||||
self.killall()
|
||||
# TODO: Wait until dnsmasq is completely stopped.
|
||||
|
||||
|
||||
def check(self):
|
||||
# TODO: Check if dnsmasq is still running, if there's any errors in the logs, etc.
|
||||
if self.pid.poll() is not None:
|
||||
# Process stopped
|
||||
pass
|
||||
if self.config_file and os.path.exists(self.config_file):
|
||||
os.remove(self.config_file)
|
||||
|
||||
|
||||
def killall(self):
|
||||
Process(['killall', 'dnsmasq']).wait()
|
||||
# TODO: Wait until dnsmasq is completely stopped.
|
||||
|
||||
|
||||
def check(self):
|
||||
if self.pid.poll() is not None:
|
||||
raise Exception('dnsmasq stopped running, exit code: %d, output: %s' % (self.pid.poll(), self.pid.stdout()))
|
||||
# TODO: Check logs/output for problems
|
||||
|
||||
|
||||
@@ -2,42 +2,71 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
||||
from threading import Thread
|
||||
|
||||
class EviltwinServer(HTTPServer):
|
||||
class EviltwinServer(HTTPServer, object):
|
||||
|
||||
def __init__(self, success_callback, port=80):
|
||||
super(EviltwinServer, self).__init__('', port, EviltwinRequestHandler)
|
||||
def __init__(self, success_callback, error_callback, port=80):
|
||||
self.thread = None
|
||||
# Store state in server
|
||||
self.success_callback = success_callback
|
||||
self.error_callback = error_callback
|
||||
self.request_count = 0
|
||||
self.router_pages_served = 0
|
||||
# Initialize with our request handler
|
||||
super(EviltwinServer, self).__init__(('', port), EviltwinRequestHandler)
|
||||
|
||||
def start(self):
|
||||
self.thread = Thread(target=self.serve_forever)
|
||||
self.thread.start()
|
||||
|
||||
def serve_forever(self):
|
||||
super(EviltwinServer, self).serve_forever()
|
||||
def stop(self):
|
||||
# From https://stackoverflow.com/a/268686
|
||||
self.shutdown()
|
||||
self.socket.close()
|
||||
|
||||
if self.thread:
|
||||
self.thread.join()
|
||||
|
||||
def request_count(self):
|
||||
return self.request_count
|
||||
|
||||
def router_pages_served(self):
|
||||
return self.router_pages_served
|
||||
|
||||
|
||||
class EviltwinRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def __init__(self, success_callback):
|
||||
self.success_callback = success_callback
|
||||
|
||||
def do_GET(self):
|
||||
self.server.request_count += 1
|
||||
request_path = self.path
|
||||
|
||||
# TODO: URL mappings to load specific pages.
|
||||
# TODO: URL mappings to load specific pages. E.g. Apple/Android "pings"
|
||||
|
||||
print("\n----- Request Start ----->\n")
|
||||
print('\n----- Request Start ----->\n')
|
||||
print(request_path)
|
||||
print(self.headers)
|
||||
print("<----- Request End -----\n")
|
||||
print('<----- Request End -----\n')
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header("Set-Cookie", "foo=bar")
|
||||
self.send_header('Content-type', 'text/html')
|
||||
self.end_headers()
|
||||
self.wfile.write('<html><head><title>Title goes here.</title></head>')
|
||||
self.wfile.write('<body><p>This is a test.</p>')
|
||||
# If someone went to 'http://something.somewhere.net/foo/bar/',
|
||||
# then s.path equals '/foo/bar/'.
|
||||
self.wfile.write('<p>You accessed path: %s</p>' % self.path)
|
||||
self.wfile.write('</body></html>')
|
||||
|
||||
|
||||
def do_POST(self):
|
||||
self.server.request_count += 1
|
||||
request_path = self.path
|
||||
|
||||
# TODO: If path includes router password, call self.success_callback
|
||||
# TODO: If path includes router password, call self.server.success_callback
|
||||
# TODO: Verify router passwords via separate interface?
|
||||
|
||||
print("\n----- Request Start ----->\n")
|
||||
print('\n----- Request Start ----->\n')
|
||||
print(request_path)
|
||||
|
||||
request_headers = self.headers
|
||||
@@ -46,19 +75,10 @@ class EviltwinRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
print(request_headers)
|
||||
print(self.rfile.read(length))
|
||||
print("<----- Request End -----\n")
|
||||
print('<----- Request End -----\n')
|
||||
|
||||
self.send_response(200)
|
||||
|
||||
do_PUT = do_POST
|
||||
do_DELETE = do_GET
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
parser = OptionParser()
|
||||
parser.usage = ("Creates an http-server that will echo out any GET or POST parameters\n"
|
||||
"Run:\n\n"
|
||||
" reflect")
|
||||
(options, args) = parser.parse_args()
|
||||
|
||||
main()
|
||||
|
||||
@@ -6,6 +6,7 @@ import os
|
||||
|
||||
from .dependency import Dependency
|
||||
from ..config import Configuration
|
||||
from ..util.process import Process
|
||||
|
||||
class Hostapd(Dependency):
|
||||
process_name = 'hostapd'
|
||||
@@ -14,6 +15,7 @@ class Hostapd(Dependency):
|
||||
dependency_name = process_name
|
||||
dependency_url = 'apt-get install hostapd'
|
||||
|
||||
|
||||
@classmethod
|
||||
def exists(cls):
|
||||
return Process.exists(cls.process_name)
|
||||
@@ -23,52 +25,71 @@ class Hostapd(Dependency):
|
||||
self.target = target
|
||||
self.interface = interface
|
||||
self.pid = None
|
||||
# Save hostapd state?
|
||||
self.config_file = None
|
||||
self.output_file = None
|
||||
self.output_write = None
|
||||
self.state = 'Initializing'
|
||||
|
||||
|
||||
def create_config_file(self):
|
||||
if not self.target.essid_known:
|
||||
self.state = 'Error: Target ESSID is not known'
|
||||
raise Exception('Cannot start hostapd if target has unknown SSID')
|
||||
|
||||
config_file = os.path.abspath(os.path.join(Configuration.temp(), 'hostapd.conf'))
|
||||
self.config_file = os.path.abspath(os.path.join(Configuration.temp(), 'hostapd.conf'))
|
||||
|
||||
with open(config_file, 'w') as config:
|
||||
with open(self.config_file, 'w') as config:
|
||||
config.write('driver=nl80211\n')
|
||||
config.write('ssid={}\n'.format(self.target.essid))
|
||||
config.write('hw_mode=g\n') # TODO: support 5ghz
|
||||
# TODO: support 5ghz
|
||||
config.write('hw_mode=g\n')
|
||||
config.write('channel={}\n'.format(self.target.channel))
|
||||
config.write('logger_syslog=-1\n')
|
||||
config.write('logger_syslog_level=2\n')
|
||||
|
||||
return config_file
|
||||
|
||||
|
||||
def start(self):
|
||||
config_file = self.create_config_file()
|
||||
self.create_config_file()
|
||||
|
||||
self.killall()
|
||||
|
||||
temp = Configuration.temp()
|
||||
self.output_file = os.path.abspath(os.path.join(temp, 'hostapd.out'))
|
||||
self.output_write = open(self.output_file, 'a')
|
||||
|
||||
self.pid = Process([
|
||||
self.process_name,
|
||||
'-C', config_file,
|
||||
'-i', self.interface
|
||||
])
|
||||
self.process_name,
|
||||
'-C', self.config_file,
|
||||
'-i', self.interface
|
||||
],
|
||||
stdout=self.output_write,
|
||||
cwd=temp
|
||||
)
|
||||
|
||||
|
||||
def stop(self):
|
||||
if self.pid:
|
||||
if self.pid and self.pid.poll() is not None:
|
||||
self.pid.interrupt()
|
||||
|
||||
self.killall()
|
||||
# TODO: Wait until hostapd is completely stopped.
|
||||
|
||||
if self.output_write:
|
||||
self.output_write.close()
|
||||
|
||||
def check(self):
|
||||
# TODO: Check if hostapd is still running, if there's any errors in the logs, etc.
|
||||
if self.pid.poll() is not None:
|
||||
# Process stopped
|
||||
pass
|
||||
if self.config_file and os.path.exists(self.config_file):
|
||||
os.remove(self.config_file)
|
||||
|
||||
if self.output_file and os.path.exists(self.output_file):
|
||||
os.remove(self.output_file)
|
||||
|
||||
|
||||
def killall(self):
|
||||
Process(['killall', self.process_name]).wait()
|
||||
|
||||
|
||||
def check(self):
|
||||
if self.pid.poll() is not None:
|
||||
raise Exception('hostapd stopped running, exit code: %d, output: %s' % (self.pid.poll(), self.pid.stdout()))
|
||||
# TODO: Check hostapd logs / output for any problems.
|
||||
|
||||
|
||||
@@ -4,14 +4,17 @@
|
||||
import re
|
||||
|
||||
from .dependency import Dependency
|
||||
from ..util.process import Process
|
||||
|
||||
class Iptables(Dependency):
|
||||
|
||||
process_name = 'iptables'
|
||||
|
||||
dependency_required = False
|
||||
dependency_name = process_name
|
||||
dependency_url = 'apt-get install iptables'
|
||||
|
||||
|
||||
@classmethod
|
||||
def exists(cls):
|
||||
return Process.exists(cls.process_name)
|
||||
@@ -19,6 +22,8 @@ class Iptables(Dependency):
|
||||
|
||||
@classmethod
|
||||
def __exec(cls, args, expect_return_code=0):
|
||||
# Helper method for executing iptables commands.
|
||||
|
||||
if type(args) is str:
|
||||
args = args.split(' ')
|
||||
|
||||
@@ -30,12 +35,13 @@ class Iptables(Dependency):
|
||||
raise Exception('Error executing %s:\n%s\n%s' % (' '.join(command), pid.stdout(), pid.stderr()))
|
||||
|
||||
|
||||
# -N, --new-chain <chain>
|
||||
@classmethod
|
||||
def new_chain(cls, chain_name, table):
|
||||
command = ['-N', name, '-t', table]
|
||||
cls.__exec(command)
|
||||
|
||||
args = ['-N', chain_name, '-t', table]
|
||||
cls.__exec(args)
|
||||
|
||||
# -A, --append <chain> <rule-specification>
|
||||
@classmethod
|
||||
def append(cls, chain, table=None, rules=[]):
|
||||
args = []
|
||||
@@ -45,3 +51,23 @@ class Iptables(Dependency):
|
||||
args.extend(rules)
|
||||
cls.__exec(args)
|
||||
|
||||
|
||||
# -F, --flush <chain>
|
||||
@classmethod
|
||||
def flush(cls, table=None):
|
||||
args = []
|
||||
if table is not None:
|
||||
args.extend(['-t', table])
|
||||
args.append('-F')
|
||||
cls.__exec(args)
|
||||
|
||||
|
||||
# -X, --delete-chain <chain>
|
||||
@classmethod
|
||||
def delete_chain(cls, table=None):
|
||||
args = []
|
||||
if table is not None:
|
||||
args.extend(['-t', table])
|
||||
args.append('-X')
|
||||
cls.__exec(args)
|
||||
|
||||
|
||||
@@ -41,7 +41,7 @@ class Iwconfig(Dependency):
|
||||
if mode is None:
|
||||
interfaces.add(iface)
|
||||
|
||||
if mode is not None and 'Mode:{}'.format(mode) in line:
|
||||
if mode is not None and 'mode:{}'.format(mode.lower()) in line.lower():
|
||||
interfaces.add(iface)
|
||||
|
||||
return list(interfaces)
|
||||
|
||||
Reference in New Issue
Block a user