From 4418d3c65d07c91732e01675c20a4800a1ccc759 Mon Sep 17 00:00:00 2001 From: anoduck <11767-anoduck@users.noreply.gitgud.io> Date: Thu, 22 Feb 2024 05:05:07 -0500 Subject: [PATCH] feat(Features): :triangular_flag_on_post: Begin development of Hidden Dragon corrected parsing of log level (sort of), beginning work on implementing Hidden Dragon. --- Docs/User_Docs.md | 21 +++++++++++ __version__.py | 1 + changelog.org | 14 ++++++++ crouching_tiger.code-workspace | 6 +++- ctiger/__main__.py | 58 +++++++++++++++++++++++++----- ctiger/hdragon.py | 44 +++++++++++++++++++++++ ctiger/hidden_dragon/__init__.py | 5 +++ ctiger/hidden_dragon/__main__.py | 15 ++++++++ ctiger/hidden_dragon/ap.py | 12 +++++++ ctiger/hidden_dragon/data.py | 44 +++++++++++++++++++++++ ctiger/hidden_dragon/mk_inf.py | 60 ++++++++++++++++++++++++++++++++ ctiger/proclog.py | 42 ++++++++++++---------- 12 files changed, 294 insertions(+), 28 deletions(-) create mode 100644 Docs/User_Docs.md create mode 100644 __version__.py create mode 100644 ctiger/hdragon.py create mode 100644 ctiger/hidden_dragon/__init__.py create mode 100644 ctiger/hidden_dragon/__main__.py create mode 100644 ctiger/hidden_dragon/ap.py create mode 100644 ctiger/hidden_dragon/data.py create mode 100644 ctiger/hidden_dragon/mk_inf.py diff --git a/Docs/User_Docs.md b/Docs/User_Docs.md new file mode 100644 index 0000000..ee830a3 --- /dev/null +++ b/Docs/User_Docs.md @@ -0,0 +1,21 @@ + + +# Crouching Tiger User Documentation + +**Hazaa! You made it to the docs!** + +## Forward + +Out dear little program finally grew to the point where it was felt that more thorough documentation +was a neccessity. Personally, yours truly, has always been frustrated with projects where the +doccumentation is lacking in substance. Thankfully, I am rather long winded, literorically speaking. +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. diff --git a/__version__.py b/__version__.py new file mode 100644 index 0000000..908c0bb --- /dev/null +++ b/__version__.py @@ -0,0 +1 @@ +__version__ = '0.4.3' diff --git a/changelog.org b/changelog.org index 30f14da..63858d1 100644 --- a/changelog.org +++ b/changelog.org @@ -9,6 +9,20 @@ #+EXPORT_EXCLUDE_TAGS: noexport # --------------------------------------- * Changelog +** unreleased +*** 2024.02.22 + - 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. + - Created apData class in hidden_dragon module. + - added scapy-fakeap to project workspace for reference. + - Created TapIf for hidden_dragon + - Created __main__ for hdragon. + - Began to add hdragon parameters to config file. + - Created native banner for ctiger, art no longer needed. + - Created banner for hdragon, art still no longer needed. + - added argparse condiguration for hdragon. + - ctiger passes args to hdragon class and displays cute message. ** 0.4.3 *** 2024.02.13 - Setup own awkward Semver solution, not too pleased diff --git a/crouching_tiger.code-workspace b/crouching_tiger.code-workspace index 4fd2218..feef2b2 100644 --- a/crouching_tiger.code-workspace +++ b/crouching_tiger.code-workspace @@ -2,11 +2,15 @@ "folders": [ { "path": "." + }, + { + "path": "../scapy-fakeap" } ], "settings": { "conventionalCommits.scopes": [ - "Structure" + "Structure", + "Features" ] } } \ No newline at end of file diff --git a/ctiger/__main__.py b/ctiger/__main__.py index a423cad..4735fa0 100644 --- a/ctiger/__main__.py +++ b/ctiger/__main__.py @@ -5,11 +5,11 @@ import os import sys import argparse -from art.art import tprint from configobj import ConfigObj, validate from .proclog import ProcLog from .mac_purge import Purge from .attack import Attack +from .hdragon import Dragon from semver import Version from .__version__ import version as _version @@ -38,12 +38,23 @@ use_daemon = boolean(default=False) # ----------------------------------------------------------- -# Mac Purge Setings +# Mac Purge Settings # ----------------- [MAC_PURGE] if_type = option('create', 'switch', default='switch') valid_results = string(default='ct_valid.csv') 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') +dragon_results = string(default='dragon_results.csv') +# ----------------------------------------------------------- """ @@ -60,7 +71,7 @@ class ProcArgs: None """ alog = ProcLog() - log = alog.get_log(args.log_file) + log = alog.get_log(args.log_file, args.log_level) log.info('Started crouching tiger') log.info('Started logger...') match args.module: @@ -73,8 +84,6 @@ class ProcArgs: log=log) case "mac": log.info('Beginning Mac Purge') - #mon_dev, mon_type, valid_file, channels - #mon_dev, mon_type, valid_file, channels log.debug('args: {0}'.format(args)) global valid_file valid_file = args.valid_file @@ -84,6 +93,15 @@ class ProcArgs: valid_file=args.valid_file, channels=args.channels, log=log) + case "dragon": + 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, + results=args.results, + log=log) case _: ap.print_help() @@ -106,7 +124,11 @@ class ProcArgs: - None """ - tprint('Crouching Tiger', font='tarty3') + print(""" +░█▀▀█ █▀▀█ █▀▀█ █──█ █▀▀ █──█ ─▀─ █▀▀▄ █▀▀▀ ▀▀█▀▀ ─▀─ █▀▀▀ █▀▀ █▀▀█ +░█─── █▄▄▀ █──█ █──█ █── █▀▀█ ▀█▀ █──█ █─▀█ ─░█── ▀█▀ █─▀█ █▀▀ █▄▄▀ +░█▄▄█ ▀─▀▀ ▀▀▀▀ ─▀▀▀ ▀▀▀ ▀──▀ ▀▀▀ ▀──▀ ▀▀▀▀ ─░█── ▀▀▀ ▀▀▀▀ ▀▀▀ ▀─▀▀ +""") # This script must be run as root! if not os.geteuid() == 0: @@ -171,11 +193,14 @@ class ProcArgs: '\n' 'There are three types of actions that can be performed:\n' '\n' - '2. ATTACK [att] = Will run a scan in the background looking for aps in target list.\n' + '1. ATTACK [att] = Will run a scan in the background looking for aps in target list.\n' ' If found will begin capturing a pcap file and deauth attack.\n' '\n' - '3. Mac_Purge [mac] = Experimental: Scans for wireless devices and acquires their MAC\n' ' addresses. Then transmits a Clear to Send Frame. If the device responds with \n' + '2. Mac_Purge [mac] = Experimental: Scans for wireless devices and acquires their MAC\n' ' addresses. Then transmits a Clear to Send Frame. If the device responds with \n' ' data frame, then information on the device will be stored and written to file.\n' + '\n' + '3. Hidden_Dragon [dragon] = Experimental: Creates a "evil twin" access point\n' + 'from which various attack vectors can be leverages.\n' '\n') # options parser ap.add_argument('-v', '--version', action='version', @@ -213,6 +238,7 @@ class ProcArgs: # help='Run in daemon mode.') att_parse.set_defaults(fun=Attack.attack) + # mac parse subcommands mac_parse = subparse.add_parser('mac', help='Grab Valid addresses') mac_parse.add_argument('-t', '--type', dest='mon_type', choices=['create', 'switch'], @@ -226,6 +252,22 @@ class ProcArgs: help='A single or comma seperated list of channels.') mac_parse.set_defaults(fun=Purge.mac_revealer) + # 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'], + help='Database file of APs') + hd_parse.add_argument('-r', '--results', dest='results', + default=config['DRAGON']['dragon_results'], + help='File to write results too.') + ################## # parse the args # ################## diff --git a/ctiger/hdragon.py b/ctiger/hdragon.py new file mode 100644 index 0000000..4af3d7a --- /dev/null +++ b/ctiger/hdragon.py @@ -0,0 +1,44 @@ +# Copyright (c) 2024 Anoduck +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT +# from .hidden_dragon import __main__ + + +class Dragon: + + def __init__(self): + print(""" + ______ __ __ ______ ______ ______ +/\ ___\ /\ "-.\ \ /\__ _\ /\ ___\ /\ == \ +\ \ __\ \ \ \-. \ \/_/\ \/ \ \ __\ \ \ __< + \ \_____\ \ \_\\"\_\ \ \_\ \ \_____\ \ \_\ \_\ + \/_____/ \/_/ \/_/ \/_/ \/_____/ \/_/ /_/ + + ______ __ __ ______ +/\__ _\ /\ \_\ \ /\ ___\ +\/_/\ \/ \ \ __ \ \ \ __\ + \ \_\ \ \_\ \_\ \ \_____\ + \/_/ \/_/\/_/ \/_____/ + + ) ( + ( /( ( ( )\ ) + )\()) ( )\ ) )\ ) ( (()/( ( ) ( ( +((_)\ )\ (()/( (()/( ))\ ( /(_)) )( ( /( )\))( ( ( + _((_)((_) ((_)) ((_))/((_) )\ ) (_))_ (()\ )(_))((_))\ )\ )\ ) +| || | (_) _| | _| |(_)) _(_/( | \ ((_)((_)_ (()(_)((_) _(_/( +| __ | | |/ _` |/ _` |/ -_)| ' \)) | |) || '_|/ _` |/ _` |/ _ \| ' \)) +|_||_| |_|\__,_|\__,_|\___||_||_| |___/ |_| \__,_|\__, |\___/|_||_| + |___/ + """) + + def hidden_dragon(self, mon_type, ap_iface, ap_db, results, log): + log.info('Starting Hidden Dragon') + print(""" + You have configured Hidden Dragon to run with these options: + mon_type: {0} + ap_iface: {1} + ap_db: {2} + results: {3} + Have a good day, and thanks for all the fish! + """.format(mon_type, ap_iface, ap_db, results)) diff --git a/ctiger/hidden_dragon/__init__.py b/ctiger/hidden_dragon/__init__.py new file mode 100644 index 0000000..b671717 --- /dev/null +++ b/ctiger/hidden_dragon/__init__.py @@ -0,0 +1,5 @@ +# Copyright (c) 2024 Anoduck +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + diff --git a/ctiger/hidden_dragon/__main__.py b/ctiger/hidden_dragon/__main__.py new file mode 100644 index 0000000..042e2e9 --- /dev/null +++ b/ctiger/hidden_dragon/__main__.py @@ -0,0 +1,15 @@ +# Copyright (c) 2024 Anoduck +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT +from .data import apData +from .mk_inf import TunIf + + +def main(ap, log): + iface = TunIf(ap=ap, apData=apData, log=log) + pass + + +if __name__ == '__main__': + main(ap, log) diff --git a/ctiger/hidden_dragon/ap.py b/ctiger/hidden_dragon/ap.py new file mode 100644 index 0000000..1833c77 --- /dev/null +++ b/ctiger/hidden_dragon/ap.py @@ -0,0 +1,12 @@ +# Copyright (c) 2024 Anoduck +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +import subprocess +from scapy.all import sniff +from time import time, sleep +from scapy.layers.dot11 import RadioTap, conf as scapyconf +from scapy.layers.inet import TCP + + diff --git a/ctiger/hidden_dragon/data.py b/ctiger/hidden_dragon/data.py new file mode 100644 index 0000000..51d6684 --- /dev/null +++ b/ctiger/hidden_dragon/data.py @@ -0,0 +1,44 @@ +# Copyright (c) 2024 Anoduck +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT + +from dataclasses import dataclass +from random import choice + + +@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) + 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 + AP_WLAN_TYPE_WPA: int = 1 + AP_WLAN_TYPE_WPA2: int = 2 + AP_WLAN_TYPE_WPA_WPA2: int = 3 + AP_AUTH_TYPE_OPEN: int = 0 + AP_AUTH_TYPE_SHARED: int = 1 + AP_RATES: str = "\x0c\x12\x18\x24\x30\x48\x60\x6c" + + DOT11_MTU: int = 4096 + + DOT11_TYPE_MANAGEMENT: int = 0 + DOT11_TYPE_CONTROL: int = 1 + DOT11_TYPE_DATA: int = 2 + + DOT11_SUBTYPE_DATA: int = 0x00 + DOT11_SUBTYPE_PROBE_REQ: int = 0x04 + DOT11_SUBTYPE_AUTH_REQ: int = 0x0B + DOT11_SUBTYPE_ASSOC_REQ: int = 0x00 + DOT11_SUBTYPE_REASSOC_REQ: int = 0x02 + DOT11_SUBTYPE_QOS_DATA: int = 0x28 + + IFNAMSIZ: int = 16 + IFF_TUN: int = 0x0001 + 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 diff --git a/ctiger/hidden_dragon/mk_inf.py b/ctiger/hidden_dragon/mk_inf.py new file mode 100644 index 0000000..138b791 --- /dev/null +++ b/ctiger/hidden_dragon/mk_inf.py @@ -0,0 +1,60 @@ +# Copyright (c) 2024 Anoduck +# +# This software is released under the MIT License. +# https://opensource.org/licenses/MIT +import fcntl +import struct +import os +import threading +import subprocess +from scapy.layers.inet import IP +from .data import apData +from ctiger import NetDev + + +class TunIf(threading.Thread): + def __init__(self, ap, apData, name="fakeap", log): + threading.Thread.__init__(self) + + self.apData = apData + self.log = log + + if len(name) > apData.IFNAMSIZ: + raise Exception( + "Tun interface name cannot be larger than " + str(apData.IFNAMSIZ)) + + self.name = name + self.setDaemon(True) + self.ap = ap + + # Virtual interface + self.fd = open('/dev/net/tun', 'r+b') + ifr_flags = apData.IFF_TUN | apData.IFF_NO_PI # Tun device without packet information + ifreq = struct.pack('16sH', name, ifr_flags) + fcntl.ioctl(self.fd, apData.TUNSETIFF, ifreq) # Syscall to create interface + + # 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) + + print("Created TUN interface %s at %s. Bind it to your services as needed." % ( + name, self.ap.ip)) + + def write(self, pkt): + os.write(self.fd.fileno(), str(pkt[IP])) # Strip layer 2 + + def read(self): + raw_packet = os.read(self.fd.fileno(), apData.DOT11_MTU) + return raw_packet + + def close(self): + os.close(self.fd.fileno()) + + def run(self): + while True: + raw_packet = self.read() + self.ap.callbacks.cb_tint_read(raw_packet) diff --git a/ctiger/proclog.py b/ctiger/proclog.py index 71a03c8..67b43d2 100644 --- a/ctiger/proclog.py +++ b/ctiger/proclog.py @@ -4,34 +4,38 @@ # https://opensource.org/licenses/MIT import logging +from logging import handlers +from warnings import warn import os class ProcLog: - def get_log(self, log_file,): + def get_log(self, log_file, lev): self.log_file = log_file - lev = 'debug' - rot = True + self.lev = 'debug' if not os.path.exists(log_file): open(log_file, 'a').close() - if rot: - log_size = os.path.getsize(log_file) - smart_size = log_size % 1024 - if smart_size >= 1024: - os.remove(log_file) - log = logging.getLogger(__name__) + log = logging.getLogger(__name__) if log.hasHandlers(): log.handlers.clear() - if lev == 'info': - log.setLevel(logging.INFO) - elif lev == 'debug': + log_levels = {'info': logging.INFO, + 'debug': logging.DEBUG, + 'error': logging.ERROR} + if lev in log_levels.keys(): + set_level = log_levels[lev] + log.setLevel(set_level) + else: + warn('Invalid level. Defaulting to debug') log.setLevel(logging.DEBUG) - handler = logging.FileHandler(log_file, mode='a', encoding='utf-8') - formatter = logging.Formatter( - '%(asctime)s - %(levelname)s - %(message)s') - handler.setFormatter(formatter) - log.addHandler(handler) - log.info('started motion detection') - log.info('Acquired Logger') + handler = handlers.RotatingFileHandler(filename=log_file, + mode='a', maxBytes=1024, + backupCount=2, encoding='utf-8', + delay=False) + formatter = logging.Formatter( + '%(asctime)s - %(levelname)s - %(message)s') + handler.setFormatter(formatter) + log.addHandler(handler) + log.info('started motion detection') + log.info('Acquired Logger') return log