diff --git a/Docs/User_Docs.md b/Docs/User_Docs.md index ee830a3..9a2bc36 100644 --- a/Docs/User_Docs.md +++ b/Docs/User_Docs.md @@ -18,4 +18,21 @@ So, if my attention span holds up, this should be something worth while. What set aside the original AT&T Unix from other operating systems of the time was Dennis Ritchie's unwavering dedication to thoroughly well-written documentation, and project transparency. As also the creator of the "C" programming language, Ritchie created most of the codebase for modern -computing. We should all keep the importance of documentation in mind in our endeavours. +computing. In honor of this spirit of dedication to documentation, we all should struggle to make +our own documentation worth it. + +Well, that was a load of long winded bullshittery, let get to the good stuff. + +### Acquiring Access Point Information + +I do not claim to be any whiz at anything concerning network security, but what little I have +learned proves that nothing happens like it does in the movies. There are are very intelligent and +adept programmers out there, but it took them years if not a lifetime to obtain this knowledge, and +more often than not, they had a hell of a lot of help to get there. The point of mentioning this is +to stress the amount of effort real network security requires. You really have to invest time and +effort into studying your target, and this will take a lot of time. + +The Hidden Dragon feature takes an approach that has to have been exploited by several hundred chaps +before us. It is to acquire the name of local access points in your target area. You can do this by +performing wardriving on your own, or you can acquire a list of access points from publicly +available sources, such as wigle. diff --git a/README.md b/README.md index 107d052..7062c56 100644 --- a/README.md +++ b/README.md @@ -182,6 +182,13 @@ options: A single or comma seperated list of channels. ``` +## Citations + +```text +Hansen, Y. (2018). Python Scapy Dot11. Createspace Independent Publishing Platform. + +rpp0. (2024, January 23). rpp0/scapy-fakeap. GitHub. https://github.com/rpp0/scapy-fakeap +``` ## Author diff --git a/ap_db-rearrange.awk b/ap_db-rearrange.awk new file mode 100644 index 0000000..9cc6255 --- /dev/null +++ b/ap_db-rearrange.awk @@ -0,0 +1,12 @@ +BEGIN { + FS=OFS=","; + RS="\n"; + outfile = "ap_db.csv"; +} + +NR !=1 { + !$1 and $1 !=0 {print "NULL"} +srand() $0; +} + +print > outfile \ No newline at end of file diff --git a/changelog.org b/changelog.org index 63858d1..d3e291d 100644 --- a/changelog.org +++ b/changelog.org @@ -10,7 +10,30 @@ # --------------------------------------- * Changelog ** unreleased +*** +*** 2024.03.11 + - Using standard csv to rearrange network database + - Created bare knuckle dhcp server + - Added function for dhcp to assign more than one IP address. + - Added Eap file + - Corrected reading of version file from __version__.py + - for some reason still using semver to parse version + - corrected parsing of configuration file when there is none. + - renamed access point database csv to netdb_file + - Renamed DataFrame class to CtigerDataFrame to avoid confusion + - Corrected dual loading of ap network dataframe + - added contents of rpy.py to mkintf +*** 2024.02.24 + - Created awk script to rearrange ap_db file. + - Further flushing out of args to pass to hidden dragon. + - Polished up TapIF Class + - Removed mon_type from list of arguments for Hidden Dragon. +*** 2024.02.23 + - Added two additional dataclasses to the data module. + - Log level selection no longer produces errors + - Added new ascii art to crouching tiger. *** 2024.02.22 +**** feat(Features): 🚩 Begin development of Hidden Dragon - Reconfigured logging to allow dynamic level setting - Setup log to Rotate using logging.handlers.RotateLogging (or something like that.) - Created HDragon class in preparation for creation of hidden_dragon module. diff --git a/ctiger/__init__.py b/ctiger/__init__.py index 08c402e..08e66e3 100644 --- a/ctiger/__init__.py +++ b/ctiger/__init__.py @@ -6,6 +6,6 @@ from ctiger.attack import Attack from ctiger.mac_purge import Purge from ctiger.proclog import ProcLog -from ctiger.dataframe import DataFrame +from ctiger.dataframe import CtigerDataFrame from ctiger.netdev import NetDev from ctiger.__version__ import version diff --git a/ctiger/__main__.py b/ctiger/__main__.py index 4735fa0..cdb226c 100644 --- a/ctiger/__main__.py +++ b/ctiger/__main__.py @@ -50,9 +50,8 @@ channel_list = list(default=list(1, 6, 11)) # Hidden Dragon Settings # ----------------------- [DRAGON] -if_type = option('create', 'switch', default='switch') ap_iface = string(default='mon0') -ap_db = string(default='ap_database.csv') +netdb_file = string(default='net_database.csv') dragon_results = string(default='dragon_results.csv') # ----------------------------------------------------------- """ @@ -97,9 +96,8 @@ class ProcArgs: log.info('Beginning Hidden Dragon') log.debug('args: {0}'.format(args)) dragon = Dragon() - dragon.hidden_dragon(mon_type=args.mon_type, - ap_iface=args.ap_iface, - ap_db=args.ap_db, + dragon.hidden_dragon(ap_iface=args.ap_iface, + netdb_file=args.netdb_file, results=args.results, log=log) case _: @@ -142,41 +140,38 @@ class ProcArgs: if not os.path.isfile(config_file): if not os.path.exists(os.path.dirname(config_file)): os.mkdir(os.path.dirname(config_file)) - config = ConfigObj(config_file, configspec=spec) - config.filename = config_file - validator = validate.Validator() - config.validate(validator, copy=True) - config.write() - print("configuration file written to ", config_path) - sys.exit() - else: config = ConfigObj(config_file, configspec=spec) + config.filename = config_file validator = validate.Validator() - test = config.validate(validator, preserve_errors=True) - if not test: - print("Configuration file is invalid") - sys.exit() - # Versioning - if os.path.exists('VERSION.md'): - with open('VERSION.md', 'r') as f: - raw_vers = f.read() - versionMd = str(raw_vers) - pyver = Version.parse(_version, - optional_minor_and_patch=True) - txtver = Version.parse(versionMd, - optional_minor_and_patch=True) - sem_vers = max(pyver, txtver) - if sem_vers != pyver: - with open('__version__.py', 'w') as wrt: - wrt.write("__version__ = '{}'\n".format(str(sem_vers))) - wrt.close() - f.close() - version = str(sem_vers) - else: - sem_vers = Version.parse(_version, - optional_minor_and_patch=True) - version = str(sem_vers) - print("Crouching Tiger version: ", version) + config.validate(validator, copy=True) + config.write() + print("configuration file written to ", config_path) + sys.exit() + config = ConfigObj(config_file, configspec=spec) + validator = validate.Validator() + test = config.validate(validator, preserve_errors=True) + if not test: + print("Configuration file is invalid") + sys.exit() + # Versioning + global version + if os.path.exists('../VERSION.md'): + with open('../VERSION.md', 'r') as f: + raw_vers = f.read() + versionMd = str(raw_vers) + pyver = Version.parse(_version, optional_minor_and_patch=True) + txtver = Version.parse(versionMd, optional_minor_and_patch=True) + sem_vers = max(pyver, txtver) + if sem_vers != pyver: + with open('__version__.py', 'w') as wrt: + wrt.write("__version__ = '{}'\n".format(str(sem_vers))) + wrt.close() + f.close() + version = str(sem_vers) + else: + sem_vers = Version.parse(_version, optional_minor_and_patch=True) + version = str(sem_vers) + print("Crouching Tiger version: ", version) ################## # ArgParse Setup # @@ -204,7 +199,7 @@ class ProcArgs: '\n') # options parser ap.add_argument('-v', '--version', action='version', - version=f'%(prog)s {version}') + version=f'Crouching Tiger version: {version}') ap.add_argument('-i', '--interface', dest='name', default=config['interface'], help='Interface(s) to scan on') @@ -254,15 +249,11 @@ class ProcArgs: # Dragon Subcommands hd_parse = subparse.add_parser('dragon', help='Perform Dragon') - hd_parse.add_argument('-t', '--type', dest='mon_type', - choices=['create', 'switch'], - default=config['DRAGON']['if_type'], - help='Create new monitor inf or switch mode.') hd_parse.add_argument('-a', '--ap_iface', dest='ap_iface', default=config['DRAGON']['ap_iface'], help='Interface to use for AP') - hd_parse.add_argument('-d', '--ap_db', dest='ap_db', - default=config['DRAGON']['ap_db'], + hd_parse.add_argument('-d', '--netdb_file', dest='netdb_file', + default=config['DRAGON']['netdb_file'], help='Database file of APs') hd_parse.add_argument('-r', '--results', dest='results', default=config['DRAGON']['dragon_results'], diff --git a/ctiger/__version__.py b/ctiger/__version__.py index 3c4d76a..1cc0eed 100644 --- a/ctiger/__version__.py +++ b/ctiger/__version__.py @@ -1 +1 @@ -version = "0.4.2" +version = "0.4.3" diff --git a/ctiger/attack.py b/ctiger/attack.py index 8a2e71a..16e13f1 100644 --- a/ctiger/attack.py +++ b/ctiger/attack.py @@ -11,7 +11,7 @@ from scapy.layers.eap import EAPOL from scapy.utils import PcapWriter from random import choice import pandas as pd -from .dataframe import DataFrame +from .dataframe import CtigerDataFrame from time import sleep import os import threading diff --git a/ctiger/dataframe.py b/ctiger/dataframe.py index a4fbd9a..51a8d1b 100644 --- a/ctiger/dataframe.py +++ b/ctiger/dataframe.py @@ -5,9 +5,38 @@ import os import pandas as pd +import csv +from warnings import warn -class DataFrame: +class CtigerDataFrame: + + def load_apnetworks(self, netdb_file, log) -> pd.DataFrame: + self.netdb_file = netdb_file + self.log = log + log.debug('netdb_file type: {0}'.format(type(netdb_file))) + if os.path.isfile(netdb_file): + log.info('Loading networks from: {}'.format(netdb_file)) + network_df = pd.DataFrame(columns=['SSID', 'Mac', 'Crypto', + 'Channel', 'Last Update', + 'Latitude', 'Longitude']) + network_df.set_index("SSID", inplace=True) + netdict = csv.DictReader(open(netdb_file), delimiter=',') + # SSID,NetID,Encryption,Channel,Last Update,Latitude,Longitude + for entry in netdict: + if entry.get('SSID') == '': + BSSID = entry.get('NetID') + entry['SSID'] = BSSID + network_df.loc[entry['SSID']] = [entry['NetID'], entry['Encryption'], entry['Channel'], entry['Last Update'], entry['Latitude'], entry['Longitude']] + return network_df + else: + warn(message='Could not load file', category=None, stacklevel=1) + exit(1) + + def gen_resdf(self, log): + resdf = pd.DataFrame(columns=['BSSID', 'SSID', 'dBm_Signal', 'Channel', 'Crypto']) + resdf.set_index("BSSID", inplace=True) + return resdf def load_df(self, valid_file, log): self.valid_file = valid_file diff --git a/ctiger/hdragon.py b/ctiger/hdragon.py index 4af3d7a..9422f65 100644 --- a/ctiger/hdragon.py +++ b/ctiger/hdragon.py @@ -2,7 +2,9 @@ # # This software is released under the MIT License. # https://opensource.org/licenses/MIT -# from .hidden_dragon import __main__ +from .hidden_dragon.__main__ import HiddenDragon +from ctiger.dataframe import CtigerDataFrame +import os class Dragon: @@ -20,7 +22,20 @@ class Dragon: \/_/\ \/ \ \ __ \ \ \ __\ \ \_\ \ \_\ \_\ \ \_____\ \/_/ \/_/\/_/ \/_____/ - + ______________ + ,===:'., `-._ + `:.`---.__ `-._ + `:. `--. `. + \. `. `. + (,,(, \. `. ____,-`., + (,' `/ \. ,--.___`.' + , ,' ,--. `, \.;' ` + `{D, { \ : \; + V,,' / / // + j;; / ,' ,-//. ,---. , + \;' / ,' / _ \ / _ \ ,'/ + \ `' / \ `' / \ `.' / + `.___,' `.__,' `.__,' ) ( ( /( ( ( )\ ) )\()) ( )\ ) )\ ) ( (()/( ( ) ( ( @@ -32,13 +47,19 @@ class Dragon: |___/ """) - def hidden_dragon(self, mon_type, ap_iface, ap_db, results, log): + def hidden_dragon(self, ap_iface, netdb_file, results, log): log.info('Starting Hidden Dragon') + netdb_file = os.path.abspath(netdb_file) print(""" You have configured Hidden Dragon to run with these options: - mon_type: {0} - ap_iface: {1} - ap_db: {2} - results: {3} + ap_iface: {0} + ap_db: {1} + results: {2} Have a good day, and thanks for all the fish! - """.format(mon_type, ap_iface, ap_db, results)) + """.format(ap_iface, netdb_file, results)) + dataframe = CtigerDataFrame() + apnetwork_df = dataframe.load_apnetworks(netdb_file, log) + results_df = dataframe.gen_resdf(log) + HD = HiddenDragon() + HD.start(ap_iface=ap_iface, net_db=apnetwork_df, + results=results_df, log=log) diff --git a/ctiger/hidden_dragon/__main__.py b/ctiger/hidden_dragon/__main__.py index 042e2e9..6668b36 100644 --- a/ctiger/hidden_dragon/__main__.py +++ b/ctiger/hidden_dragon/__main__.py @@ -2,14 +2,18 @@ # # This software is released under the MIT License. # https://opensource.org/licenses/MIT +from threading import Thread from .data import apData from .mk_inf import TunIf +from ctiger.dataframe import CtigerDataFrame -def main(ap, log): - iface = TunIf(ap=ap, apData=apData, log=log) - pass +class HiddenDragon(Thread): + def __init__(self): + Thread.__init__(self) - -if __name__ == '__main__': - main(ap, log) + def start(self, ap_iface, net_db, log): + DF = CtigerDataFrame() + resdf = DF.gen_resdf(log) + iface = TunIf(ap=ap_iface, apData=apData, log=log) + pass diff --git a/ctiger/hidden_dragon/ap.py b/ctiger/hidden_dragon/ap.py index 1833c77..8c004ad 100644 --- a/ctiger/hidden_dragon/ap.py +++ b/ctiger/hidden_dragon/ap.py @@ -10,3 +10,7 @@ from scapy.layers.dot11 import RadioTap, conf as scapyconf from scapy.layers.inet import TCP +class wifiAP: + + def pass_proxy(self, ip, port): + pass \ No newline at end of file diff --git a/ctiger/hidden_dragon/arp.py b/ctiger/hidden_dragon/arp.py new file mode 100644 index 0000000..3db56d0 --- /dev/null +++ b/ctiger/hidden_dragon/arp.py @@ -0,0 +1,60 @@ +# Copyright (c) 2024 Anoduck +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +import threading +from scapy.layers.inet import ARP +from scapy.sendrecv import AsyncSniffer +from scapy.sendrecv import sendp +from scapy.utils import PcapWriter +from ctiger.dataframe import CtigerDataFrame +from ctiger.netdev import NetDev +from ctiger.proclog import ProcLog + + +class Arp(NetDev): + def __init__(self, args): + super().__init__(args) + self.arp = ARP() + self.arp.op = 2 + self.arp.hwdst = "ff:ff:ff:ff:ff:ff" + + def send(self, pkt): + sendp(pkt, iface=self.interface, verbose=False) + + def arp_handler(self, pkt): + self.arp_handler.handle_arp(pkt) + + def sniff(self, stop_filter, iface): + AsyncSniffer( + stop_filter=stop_filter, + filter="arp", + iface=iface, + prn=self.arp_handler, + store=0, + timeout=0.01) + + +class ARPHandler(): + def __init__(self): + self.mutex = threading.Lock() + self.arp_table = {} + + def add_entry(self, client_ip, client_mac): + self.mutex.acquire() + if client_ip not in self.arp_table: + self.arp_table[client_ip] = client_mac + self.mutex.release() + + def get_entry(self, client_ip): + self.mutex.acquire() + try: + temp = self.arp_table[client_ip] + except KeyError: + temp = None + printd("Could not find IP %s in ARP table." % + client_ip, Level.WARNING) + self.mutex.release() + + return temp diff --git a/ctiger/hidden_dragon/data.py b/ctiger/hidden_dragon/data.py index 51d6684..b9475c9 100644 --- a/ctiger/hidden_dragon/data.py +++ b/ctiger/hidden_dragon/data.py @@ -3,16 +3,18 @@ # This software is released under the MIT License. # https://opensource.org/licenses/MIT -from dataclasses import dataclass -from random import choice +from dataclasses import dataclass, field +from typing import List + +DNS_SERVERS = ["8.8.8.8", "9.9.9.9", "1.1.1.1", + "208.67.222.222", "208.67.220.220", + "8.26.56.26", "8.20.247.20", + "89.0.142.86"] @dataclass class apData: - dns_servers: list = ["8.8.8.8", "9.9.9.9", "1.1.1.1", - "208.67.222.222", "208.67.220.220", - "8.26.56.26", "8.20.247.20", "89.0.142.86"] - DEFAULT_DNS_SERVER: str = choice(dns_servers) + DNS_SERVERS: List[str] = field(default_factory=lambda: DNS_SERVERS) RSN: str = "\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x04\x01\x00\x00\x0f\xac\x01\x28\x00" AP_WLAN_TYPE_OPEN: int = 0 @@ -41,4 +43,41 @@ class apData: IFF_TAP: int = 0x0002 # Should we want to tunnel layer 2... IFF_NO_PI: int = 0x1000 TUNSETIFF: int = 0x400454ca - IP_ADDRESS: str = "10.0.0.2" \ No newline at end of file + IP_ADDRESS: str = "10.0.2.1" + NETMASK: str = "255.255.255.192" + IP_NETWORK: str = "10.0.2" + NET_BROADCAST: str = "10.0.2.63" + DHCP_MIN: str = "10.0.2.2" + DHCP_MAX: str = "10.0.2.62" + DHCP_EXPIRY: int = 28800 + +@dataclass +class EAPCode: + REQUEST: int = 1 + RESPONSE: int = 2 + SUCCESS: int = 3 + FAILURE: int = 4 + +@dataclass +class EAPType: + IDENTITY: int = 1 + NOTIFICATION: int = 2 + NAK: int = 3 + MD5_CHALLENGE: int = 4 + OTP: int = 5 + GENERIC_TOKEN_CARD: int = 6 + EAP_TLS: int = 13 + EAP_LEAP: int = 17 + EAP_SIM: int = 18 + TTLS: int = 21 + PEAP: int = 25 + MSCHAP_V2: int = 29 + EAP_CISCO_FAST: int = 43 + EAP_MSCHAPV2: int = 44 + EAP_TLV: int = 45 + + @classmethod + def convert_type(cls, type_value): + for key, value in vars(cls).iteritems(): + if value == type_value: + return str(key) diff --git a/ctiger/hidden_dragon/dhcp.py b/ctiger/hidden_dragon/dhcp.py new file mode 100644 index 0000000..a041661 --- /dev/null +++ b/ctiger/hidden_dragon/dhcp.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright (c) 2024 Anoduck +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT +"""# DHCP Server specifically for wifi +1. Craft DHCP Packets: Use Scapy to craft DHCP packets for the various stages of the DHCP process, + including Discover, Offer, Request, Acknowledgement, and more as needed. Scapy provides a + flexible way to build and manipulate network packets, including DHCP packets. + +2. Bind a Socket: Create a raw socket to send and receive UDP packets using Python's socket module. + This allows you to send and receive DHCP messages on the network. +server_mac +3. Listen for DHCP Requests: Listen for DHCP client requests (DHCP Discover) on the network using + the raw socket. When a DHCP Discover packet is received, parse the packet using Scapy to extract + relevant information such as the client's MAC address and requested parameters. + +4. Craft DHCP Offer: Upon receiving a DHCP Discover, craft a DHCP Offer packet using Scapy to + allocate an available IP address and other configuration parameters to the client. Send the DHCP + Offer packet using the raw socket to the client's broadcast address. + +5. Handle DHCP Request: If the client sends a DHCP Request for the offered parameters, process the + request and send a DHCP Acknowledgement (ACK) packet to finalize the configuration. + +6. Error Handling and Lease Management: Implement error handling for invalid requests and manage IP + address lease allocation to ensure that IP addresses are properly assigned and reclaimed. +""" +# from scapy.all import * +from scapy.layers.dhcp import DHCP +from scapy.layers.inet import IP, UDP +from scapy.layers.dot11 import Dot11 +from scapy.layers.dhcp import BOOTP +from scapy.sendrecv import sniff, sendp +import subprocess +from random import choice +import time +import socket +import fcntl +import struct +from ctiger.hidden_dragon import ap +from data import apData + +# Define the DHCP server IP and the network ifname to listen on +server_ip = apData.IP_ADDRESS # "10.0.2.1" +ifname = "wlan0" + + +class apDHCP: + + def __init__(self) -> None: + self.server_ip = apData.IP_ADDRESS + self.netmask = apData.NETMASK + self.broadcast = apData.NET_BROADCAST + self.host_min = apData.DHCP_MIN + self.host_max = apData.DHCP_MAX + self.ip_net = apData.IP_NETWORK + + def get_if_hwaddr(self, ifname): + s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + info = fcntl.ioctl(s.fileno(), 0x8927, struct.pack('256s', ifname[:15])) + return ''.join(['%02x' % ord(char) for char in info[18:24]]) + + # Create a DHCP packet handler + def handle_dhcp_packet(self, packet): + if DHCP in packet: + # Check if the packet is a DHCP Discover + if packet[DHCP].options[0][1] == 1: + # Craft DHCP Offer + min_host = self.host_min.split('.')[3] + max_host = self.host_max.split('.')[3] + cli_int = choice(range(int(min_host), int(max_host))) + client_ip = self.ip_net + '.' + str(cli_int) + dhcp_offer = Dot11(src=self.server_mac, dst="ff:ff:ff:ff:ff:ff") / IP(src=server_ip, dst="self.broadcast") / UDP(sport=67, dport=68) / BOOTP(op=2, yiaddr=client_ip, siaddr=server_ip, chaddr=packet[Dot11].src) / DHCP(options=[("message-type", "offer"), ("subnet_mask", self.netmask), "end"]) + sendp(dhcp_offer, iface=ifname, verbose=0) + print("Sent DHCP Offer to", packet[Dot11].src) + + # Wait for DHCP Request + dhcp_request = sniff(iface=ifname, filter="udp and (port 67 or 68)", count=1) + if DHCP in dhcp_request[0]: + # Craft DHCP Acknowledgement + dhcp_ack = Dot11(src=self.server_mac, dst=dhcp_request[0][Dot11].src) / IP(src=server_ip, dst="self.broadcast") / UDP(sport=67, dport=68) / BOOTP(op=2, yiaddr=client_ip, siaddr=server_ip, chaddr=dhcp_request[0][Dot11].src) / DHCP(options=[("message-type", "ack"), ("subnet_mask", self.netmask), "end"]) + sendp(dhcp_ack, iface=ifname, verbose=0) + print("Sent DHCP Acknowledgement to", dhcp_request[0][Dot11].src) + + def start_dhcp(self): + self.server_mac = self.get_if_hwaddr(ifname) + # Start sniffing DHCP traffic + sniff(iface=ifname, filter="udp and (port 67 or 68)", prn=self.handle_dhcp_packet) \ No newline at end of file diff --git a/ctiger/hidden_dragon/eap.py b/ctiger/hidden_dragon/eap.py new file mode 100644 index 0000000..35a3501 --- /dev/null +++ b/ctiger/hidden_dragon/eap.py @@ -0,0 +1,24 @@ +# Copyright (c) 2024 Anoduck +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT +import threading + + +class EAPHandler(): + def __init__(self): + self.id = 0 + self.mutex = threading.Lock() + + def next_id(self): + self.mutex.acquire() + self.id = (self.id + 1) + temp = self.id + self.mutex.release() + + return temp + + def reset_id(self): + self.mutex.acquire() + self.id = 0 + self.mutex.release() diff --git a/ctiger/hidden_dragon/mk_inf.py b/ctiger/hidden_dragon/mk_inf.py index 138b791..90b736f 100644 --- a/ctiger/hidden_dragon/mk_inf.py +++ b/ctiger/hidden_dragon/mk_inf.py @@ -7,21 +7,24 @@ import struct import os import threading import subprocess +from scapy.arch import str2mac, get_if_raw_hwaddr from scapy.layers.inet import IP from .data import apData -from ctiger import NetDev +from warnings import warn class TunIf(threading.Thread): - def __init__(self, ap, apData, name="fakeap", log): + def __init__(self, ap, apData, log, name="fakeap"): threading.Thread.__init__(self) self.apData = apData self.log = log + self.dev = apData.dev if len(name) > apData.IFNAMSIZ: raise Exception( - "Tun interface name cannot be larger than " + str(apData.IFNAMSIZ)) + "Tun interface name cannot be larger than " + str( + apData.IFNAMSIZ)) self.name = name self.setDaemon(True) @@ -36,13 +39,13 @@ class TunIf(threading.Thread): # Assign IP and bring interface up # set_ip_address(name, self.ap.ip) # def set_ip_address(dev, ip): - if subprocess.call(['ip', 'addr', 'add', apData.ip, 'dev', apData.dev]): - print("Failed to assign IP address {} to {}.".format(apData.ip, apData.dev)) - if subprocess.call(['ip', 'link', 'set', 'dev', apData.dev, 'up']): - print("Failed to bring device %s up." % dev, Level.CRITICAL) + if subprocess.call(['ip', 'addr', 'add', apData.ip, 'dev', self.dev]): + warn("Failed to assign IP address {} to {}.".format(apData.ip, + self.dev)) + if subprocess.call(['ip', 'link', 'set', 'dev', self.dev, 'up']): + warn("Failed to bring device {} up.".format(self.dev)) - print("Created TUN interface %s at %s. Bind it to your services as needed." % ( - name, self.ap.ip)) + log.info("Created TUN interface {} at {}".format(self.name, self.ap.ip)) def write(self, pkt): os.write(self.fd.fileno(), str(pkt[IP])) # Strip layer 2 @@ -58,3 +61,88 @@ class TunIf(threading.Thread): while True: raw_packet = self.read() self.ap.callbacks.cb_tint_read(raw_packet) + + +class RouteTraffic(threading.Thread): + + def __init__(self, apIface, apData, log) -> None: + if subprocess.call(['iptables', '--table', 'nat', '--append', + 'POSTROUTING', '--out-interface', apIface, '-j', + 'MASQUERADE']): + warn("Failed to setup postrouting for {}.".format(apIface)) + if subprocess.call(['iptables', '--append', 'FORWARD', + '--in-interface', apData.dev, '-j', 'ACCEPT']): + warn("Failed to setup forwarding for {}.".format(apData.dev)) + if subprocess.call(['sysctl', '-w', 'net.ipv4.ip_forward=1']): + warn("Failed to enable IP forwarding.") + log.info("Created route traffic for {}".format(apIface)) + pass + + +def set_monitor_mode(wlan_dev, enable=True): + monitor_dev = None + if enable: + result = subprocess.check_output(['airmon-ng', 'start', wlan_dev]) + if not "monitor mode enabled on" in result: + printd(clr(Color.RED, "ERROR: Airmon could not enable monitor mode on device %s. Make sure you are root, and that" + "your wlan card supports monitor mode." % wlan_dev), Level.CRITICAL) + exit(1) + monitor_dev = re.search( + r"monitor mode enabled on (\w+)", result).group(1) + + printd("Airmon set %s to monitor mode on %s" % + (wlan_dev, monitor_dev), Level.INFO) + else: + subprocess.check_output(['airmon-ng', 'stop', wlan_dev]) + + return monitor_dev + + +def set_ip_address(dev, ip): + if subprocess.call(['ip', 'addr', 'add', ip, 'dev', dev]): + printd("Failed to assign IP address %s to %s." % + (ip, dev), Level.CRITICAL) + + if subprocess.call(['ip', 'link', 'set', 'dev', dev, 'up']): + printd("Failed to bring device %s up." % dev, Level.CRITICAL) + + +def clear_ip_tables(): + if subprocess.call(['iptables', '--flush']): + printd("Failed to flush iptables.", Level.CRITICAL) + if subprocess.call(['iptables', '--table', 'nat', '--flush']): + printd("Failed to flush iptables NAT.", Level.CRITICAL) + if subprocess.call(['iptables', '--delete-chain']): + printd("Failed to delete iptables chain.", Level.CRITICAL) + if subprocess.call(['iptables', '--table', 'nat', '--delete-chain']): + printd("Failed to delete iptables NAT chain.", Level.CRITICAL) + + +def hex_offset_to_string(byte_array): + temp = byte_array.replace("\n", "") + temp = temp.replace(" ", "") + return temp.decode("hex") + + +def get_frequency(channel): + if channel == 14: + freq = 2484 + else: + freq = 2407 + (channel * 5) + + freq_string = struct.pack("