before big cleaning of new method

This commit is contained in:
anoduck 2023-10-24 16:42:27 -04:00
parent 34d3fbb350
commit 1d485bc890
3 changed files with 293 additions and 24 deletions

View file

@ -117,6 +117,8 @@ sudo $(which poetry) run python3 ctiger.py -h
* Website: http://anoduck.github.io
* Github: [@anoduck](https://github.com/anoduck)
[![built with Codeium](https://codeium.com/badges/main)](https://codeium.com)
## Show your support
Give a ⭐️ if this project helped you!

204
ctiger.py
View file

@ -9,7 +9,6 @@ Created on Fri Jul 21 17:05:07 2023
import os
import sys
import fcntl
import argparse
from scapy.sendrecv import sniff
from scapy.sendrecv import AsyncSniffer
@ -24,7 +23,6 @@ from scapy.layers.dot11 import Dot11FCS
from scapy.config import Conf as scapyconfig
from scapy.layers.eap import EAPOL
from scapy.utils import PcapWriter
from getmac import get_mac_address
# Import Faker.
from faker import Faker
# Import the WifiESSID class from Faker Wi-Fi ESSID.
@ -33,15 +31,14 @@ from faker import Faker
# from scapy_ex import Dot11Elt
from art.art import tprint
from dataclasses import dataclass
import struct
import multiprocessing as mp
import asyncio
import threading
from random import choice, randint
from threading import Thread
from random import choice
from configobj import ConfigObj, validate
from collections import Counter
import pandas as pd
import socket
import signal
import logging
from time import sleep
@ -214,24 +211,36 @@ def PRN2(pkt):
# 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=65535)
ID=durid)
log.debug('Sending RTS frame to {0} with type 11'.format(bssid))
res = srp1(new_pkt, timeout=3, verbose=0, retry=0)
if res:
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')
# -------------------------------------------------------------------
@ -312,7 +321,8 @@ class NetDev:
mon_crtd = self.interface + 'mon'
self.mon_crtd = mon_crtd
if self.mon_type == 'create':
self.create_if()
self.create_if(interface=self.interface,
macaddr=self.macaddr, mon_crtd=mon_crtd)
mon_if = self.mon_crtd
return mon_if
elif self.mon_type == 'switch':
@ -325,15 +335,6 @@ class NetDev:
sys.exit(1)
def signal_handler(signal, frame) -> None:
print('You pressed Ctrl+C!')
log.info('Shutting down')
scan_df.to_csv('ct_purge.csv')
log.info('Saved results to: {0}'.format('ct_purge.csv'))
log.info('Going Down!!')
sys.exit(0)
# ██████╗ ██╗ ██╗██████╗ ██████╗ ███████╗
# ██╔══██╗██║ ██║██╔══██╗██╔════╝ ██╔════╝
# ██████╔╝██║ ██║██████╔╝██║ ███╗█████╗
@ -349,6 +350,9 @@ class Purge(object):
self.valid_file = kwargs.get('valid_file')
self.channels = kwargs.get('channels')
def __getitem__(self, pkt):
return pkt
def channel_runner(self, mon_if, channels) -> None:
self.mon_if = mon_if
self.channels = channels
@ -385,6 +389,7 @@ class Purge(object):
# log.info('We will be writing captured macs to ', str(self.valid_file))
chop = asyncio.to_thread(self.channel_runner,
self.mon_if, self.channels)
global chopper
chopper = asyncio.create_task(chop)
log.info('Channel runner started.')
await asyncio.sleep(0)
@ -397,13 +402,152 @@ class Purge(object):
log.info('asniffer started')
forever_wait = threading.Event()
forever_wait.wait()
def send_pkt(self, bssid) -> None:
self.bssid = bssid
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))
sendp(new_pkt, timeout=3, verbose=0, retry=0, threaded=True)
async def get_interface(self, interface, mon_type) -> str:
self.interface = interface
self.mon_type = mon_type
ndev = NetDev(interface=self.interface, mon_type=self.mon_type)
mon_if = ndev.start_monitor(interface=self.interface,
mon_type=self.mon_type)
return mon_if
async def random_chan(self, channels):
self.channels = channels
chlist = self.channels.split(',')
chans = [int(chan) for chan in chlist]
ichan = choice(chans)
return ichan
async def change_chan(self, mon_if, ichan):
self.mon_if = mon_if
self.ichan = ichan
log.debug('Monitor interface: {0}'.format(self.mon_if))
log.debug('New Channel: {0}'.format(self.ichan))
# iw [options] dev <devname> set channel <channel>
change_it = os.system(
'iw dev ' + self.mon_if + ' set channel ' + self.ichan
)
if change_it:
current_channel = self.ichan
return current_channel
def probe_proc(self, probe_pkts):
ptup_list = []
for ppkt in probe_pkts:
dbm_signal = ppkt.dBm_AntSignal
self.dbm_signal = dbm_signal
bssid = ppkt[Dot11FCS].addr2
self.bssid = bssid
ptuple = (bssid, dbm_signal)
ptup_list.append(ptuple)
punified = list(set(ptup_list))
return punified
def cts_prn(self, pkt):
dbm_signal = pkt.dBm_AntSignal
ichan = extract_channel(pkt[Dot11])
scan_df.loc[self.bssid] = [macaddr, dbm_signal, ichan, 'N/A']
scan_df.to_csv(valid_file, mode='a')
log.info('Results written to {0}'.format(valid_file))
async def chan_timer(self):
await asyncio.sleep(14.7)
log.info('Channel timer done.')
def probe_prn(self, pkt):
bssid = pkt[Dot11FCS].addr2
self.bssid = bssid
log.info('Sending RTS frame to {0}'.format(bssid))
self.send_pkt(bssid)
return
async def mac_revealer(self, interface, mon_type, valid_file, channels):
log.info('mac revealer started')
self.interface = interface
self.mon_type = mon_type
self.valid_file = valid_file
self.channels = channels
log.info('setting up class attributes')
global scan_df
scan_df = get_df()
log.info('acquired Dataframe')
mon_if = await self.get_interface(self.interface, self.mon_type)
log.debug('mon_if: {0}'.format(mon_if))
log.debug('return type: {0}'.format(type(mon_if)))
self.mon_if = mon_if
log.info('interface {0} is up and running.'.format(self.mon_if))
chop = asyncio.to_thread(self.channel_runner,
self.mon_if, self.channels)
global chopper
chopper = asyncio.create_task(chop)
log.info('Channel runner started.')
while True:
probe_sniff = AsyncSniffer(
iface=mon_if, prn=self.probe_prn,
filter="type mgt subtype probe-req",
monitor=True)
probe_sniff.start()
log.info('Probe sniffer started')
# presult = await self.probe_proc(probe_sniff.results)
# log.info('Processing results from probe sniffer.')
# bssid = presult[0]
# dbm_signal = presult[1]
await asyncio.sleep(0)
# await self.send_pkt(bssid)
# log.info('Sending RTS frame to {0}'.format(bssid))
# await asyncio.sleep(0)
cts_sniff = AsyncSniffer(filter='type ctl subtype cts',
iface=mon_if, prn=self.cts_prn,
monitor=True)
cts_sniff.start()
log.info('CTS sniffer started')
await asyncio.sleep(0)
# valid_cts = await self.cts_proc(cts_sniff.results)
# log.info('Validating CTS packet.')
# if valid_cts:
# scan_df.loc[bssid] = [macaddr, dbm_signal, ichan, 'N/A']
# scan_df.to_csv(valid_file, mode='a')
# log.info('Results written to {0}'.format(valid_file))
# else:
# log.info('Unable to validate CTS packet.')
# await asyncio.sleep(0)
# alltasks = asyncio.all_tasks()
# current_task = asyncio.current_task()
# alltasks.remove(asyncio.current_task)
# await asyncio.wait(alltasks)
# ttimer = asyncio.create_task(self.chan_timer())
# await ttimer
# log.info('Beginning channel hopping.')
# ichan = await self.random_chan(self.channels)
# self.ichan = ichan
# await asyncio.create_task(self.change_chan(self.mon_if, self.ichan))
# await asyncio.sleep(0)
# continue
def start_purge(self) -> None:
asyncio.run(self.mac_purge(self.interface,
self.mon_type,
self.valid_file,
self.channels))
signal.signal(signal.SIGINT, signal_handler)
print('Enter Ctrl+C TWICE to fully stop the script.')
# asyncio.run(self.mac_purge(self.interface,
# self.mon_type,
# self.valid_file,
# self.channels))
asyncio.run(self.mac_revealer(self.interface,
self.mon_type,
self.valid_file,
self.channels))
forever_wait = threading.Event()
forever_wait.wait()
# -----------------------------------------------------------------------------
@ -613,11 +757,21 @@ def get_df():
return scan_df
def get_log(log_file):
def signal_handler(signal, frame) -> None:
print('You pressed Ctrl+C!')
log.info('Shutting down')
log.info('Going Down!!')
exit(0)
def get_log(log_file, log_level):
logfile = os.path.abspath(log_file)
log = logging.getLogger('scapy.runtime')
if log.hasHandlers():
log.handlers.clear()
# lvl = str(f'logging.{log_level}')
# lgvl = lvl.strip()
# log.setLevel(lgvl)
log.setLevel(logging.DEBUG)
handler = logging.FileHandler(logfile, mode='a', encoding='utf-8')
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
@ -648,7 +802,7 @@ def process_args(args: argparse.Namespace) -> None:
None
"""
global log
log = get_log(args.log_file)
log = get_log(args.log_file, args.log_level)
log.info('Started crouching tiger')
log.info('Started logger...')
match args.module:
@ -660,6 +814,8 @@ def process_args(args: argparse.Namespace) -> None:
#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
pge = Purge(interface=args.name,
mon_type=args.mon_type,
valid_file=args.valid_file,

View file

@ -1963,6 +1963,117 @@
"DEBUG: Invalid monitor type\n",
"```"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Custom Scapy Sessions\n",
"\n",
"One can configure their own scapy session, "
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# SPDX-License-Identifier: GPL-2.0-only\n",
"# This file is part of Scapy\n",
"# See https://scapy.net/ for more information\n",
"\n",
"\"\"\"\n",
"Sessions: decode flow of packets when sniffing\n",
"\"\"\"\n",
"\n",
"from collections import defaultdict\n",
"import socket\n",
"import struct\n",
"\n",
"from scapy.compat import orb\n",
"from scapy.config import conf\n",
"from scapy.packet import NoPayload, Packet\n",
"from scapy.pton_ntop import inet_pton\n",
"\n",
"# Typing imports\n",
"from typing import (\n",
" Any,\n",
" DefaultDict,\n",
" Dict,\n",
" Iterator,\n",
" List,\n",
" Optional,\n",
" Tuple,\n",
" cast,\n",
" TYPE_CHECKING,\n",
")\n",
"from scapy.compat import Self\n",
"if TYPE_CHECKING:\n",
" from scapy.supersocket import SuperSocket\n",
"\n",
"\n",
"# You will only need to extend the Default Session\n",
"class DefaultSession(object):\n",
" \"\"\"Default session: no stream decoding\"\"\"\n",
"\n",
" def __init__(self, supersession: Optional[Self] = None):\n",
" if supersession and not isinstance(supersession, DefaultSession):\n",
" supersession = supersession()\n",
" self.supersession = supersession\n",
"\n",
" def process(self, pkt: Packet) -> Optional[Packet]:\n",
" \"\"\"\n",
" Called to pre-process the packet\n",
" \"\"\"\n",
" # Optionally handle supersession\n",
" if self.supersession:\n",
" return self.supersession.process(pkt)\n",
" return pkt\n",
"\n",
" def recv(self, sock: 'SuperSocket') -> Iterator[Packet]:\n",
" \"\"\"\n",
" Will be called by sniff() to ask for a packet\n",
" \"\"\"\n",
" pkt = sock.recv()\n",
" if not pkt:\n",
" return\n",
" pkt = self.process(pkt)\n",
" if pkt:\n",
" yield pkt\n",
" \n",
" \n",
"class Dot11Session(DefaultSession):\n",
" \"\"\"Decode Dot11 packets 'on-the-flow'.\n",
"\n",
" Usage:\n",
" >>> sniff(session=Dot11Session)\n",
" \"\"\"\n",
"\n",
" def __init__(self, *args, **kwargs):\n",
" # type: (*Any, **Any) -> None\n",
" DefaultSession.__init__(self, *args, **kwargs)\n",
" self.keys = {} # type: Dict[Tuple[Any, ...], List[Packet]] # noqa: E501\n",
"\n",
"\n",
"class IPSession(DefaultSession):\n",
" \"\"\"Defragment IP packets 'on-the-flow'.\n",
"\n",
" Usage:\n",
" >>> sniff(session=IPSession)\n",
" \"\"\"\n",
"\n",
" def __init__(self, *args, **kwargs):\n",
" # type: (*Any, **Any) -> None\n",
" DefaultSession.__init__(self, *args, **kwargs)\n",
" self.fragments = defaultdict(list) # type: DefaultDict[Tuple[Any, ...], List[Packet]] # noqa: E501\n",
"\n",
" def process(self, packet: Packet) -> Optional[Packet]:\n",
" from scapy.layers.inet import IP, _defrag_ip_pkt\n",
" if IP not in packet:\n",
" return packet\n",
" return _defrag_ip_pkt(packet, self.fragments)[1] # type: ignore\n"
]
}
],
"metadata": {