feat(Features): 🚩 Begin development of Hidden Dragon

corrected parsing of log level (sort of), beginning work on implementing Hidden Dragon.
This commit is contained in:
anoduck 2024-02-22 05:05:07 -05:00
parent a79cd2e5e9
commit 4418d3c65d
12 changed files with 294 additions and 28 deletions

21
Docs/User_Docs.md Normal file
View file

@ -0,0 +1,21 @@
<!--
Copyright (c) 2024 Anoduck
This software is released under the MIT License.
https://opensource.org/licenses/MIT
-->
# 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.

1
__version__.py Normal file
View file

@ -0,0 +1 @@
__version__ = '0.4.3'

View file

@ -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

View file

@ -2,11 +2,15 @@
"folders": [
{
"path": "."
},
{
"path": "../scapy-fakeap"
}
],
"settings": {
"conventionalCommits.scopes": [
"Structure"
"Structure",
"Features"
]
}
}

View file

@ -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 #
##################

44
ctiger/hdragon.py Normal file
View file

@ -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))

View file

@ -0,0 +1,5 @@
# Copyright (c) 2024 Anoduck
#
# This software is released under the MIT License.
# https://opensource.org/licenses/MIT

View file

@ -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)

View file

@ -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

View file

@ -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"

View file

@ -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)

View file

@ -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