Projects STRLCPY RTSPbrute Commits 872fbb14
🤬
  • Add CLI and support for multiple ports; beautify output

  • Loading...
  • Woolf committed 4 years ago
    872fbb14
    1 parent 46a93eff
  • ■ ■ ■ ■ ■ ■
    README.md
    skipped 5 lines
    6 6   
    7 7  > Inspired by [Cameradar](https://github.com/Ullaakut/cameradar)
    8 8   
    9  - 
    10 9  ## Features
    11 10   
    12  -* **Find accessible RTSP streams** on any target
    13  -* Brute-force **stream routes**
    14  -* Brute-force **credentials**
    15  -* **Make screenshots** on accessible streams
    16  -* Generate **user-friendly report** of the results:
    17  - * `.txt` file with each found stream on new line
    18  - * `.html` file with screenshot of each found stream
     11 +- **Find accessible RTSP streams** on any target
     12 +- Brute-force **stream routes**
     13 +- Brute-force **credentials**
     14 +- **Make screenshots** on accessible streams
     15 +- Generate **user-friendly report** of the results:
     16 + - `.txt` file with each found stream on new line
     17 + - `.html` file with screenshot of each found stream
    19 18   
    20 19  ### Report files
    21 20   
    22  -#### `result.txt`
    23  - 
    24  -* Each target is on a new line
    25  -* Import to VLC: change extension to `.m3u` and open in VLC
    26  - 
    27  -#### `index.html`
    28  - 
    29  -* Responsive
    30  -* Click on the screenshot to copy its link
    31  - 
     21 +- `result.txt`: Each target is on a new line. Import to VLC: change extension to `.m3u` and open in VLC
     22 +- `index.html`: Click on the screenshot to copy its link
    32 23   
    33 24  ## Installation
    34 25   
    35 26  ### Requirements
    36 27   
    37  -* `python` (> `3.7`)
    38  -* `av`
    39  -* `colorama`
    40  -* `Pillow`
     28 +- `python` (> `3.7`)
     29 +- `av`
     30 +- `Pillow`
     31 +- `rich`
    41 32   
    42 33  ### Steps to install
    43 34   
    skipped 1 lines
    45 36  2. `cd RTSPbrute`
    46 37  3. `pip install -r requirements.txt`
    47 38   
     39 +## CLI
    48 40   
    49  -## Configuration
     41 +```
     42 +USAGE
     43 + $ core.py [-h] [-t TARGETS] [-p PORTS [PORTS ...]] [-r ROUTES] [-c CREDENTIALS]
     44 + [-ct N] [-bt N] [-st N] [-T TIMEOUT] [-d]
     45 + 
     46 +ARGUMENTS
     47 + -h, --help show this help message and exit
     48 + -t, --targets TARGETS the targets on which to scan for open RTSP streams
     49 + -p, --ports PORTS [PORTS ...] the ports on which to search for RTSP streams
     50 + -r, --routes ROUTES the path on which to load a custom routes
     51 + -c, --credentials CREDENTIALS the path on which to load a custom credentials
     52 + -ct, --check-threads N the number of threads to brute-force the routes
     53 + -bt, --brute-threads N the number of threads to brute-force the credentials
     54 + -st, --screenshot-threads N the number of threads to screenshot the streams
     55 + -T, --timeout TIMEOUT the timeout to use for sockets
     56 + -d, --debug enable the debug logs
    50 57   
    51  -At the moment it is possible to change only the following variables in `config.py` file:
    52  -* Number of `CHECK`, `BRUTE` and `SCREENSHOT` `_THREADS`
    53  -* `PORT` to check
    54  -* `SOCKET_TIMEOUT`
     58 +EXAMPLES
     59 + $ python core.py
     60 + $ python core.py -t ips.txt -p 554 5554
     61 + $ python core.py -r paths.txt -c combinations.txt
     62 + $ python core.py -st 10 -T 10
     63 +```
    55 64   
    56  -In the future, the CLI will be used for this.
     65 +- **"-t, --targets"** (`hosts.txt`): Set custom path to the input file. The file can contain IPs, IP ranges and CIDRs. Each one of them should be on a separate line, e.g.:
    57 66   
     67 +```
     68 +0.0.0.0
     69 +192.168.100.1-192.168.254.1
     70 +192.17.0.0/16
     71 +```
    58 72   
    59  -## Usage
     73 +- **"-p, --ports"** (`554`): Set custom ports, e.g.: `-p 554 5554 8554`
     74 +- **"-r, --routes"** (`routes.txt`): Set custom path to the file with routes. Each route should start with `/` and be on a separate line, e.g.:
     75 + 
     76 +```
     77 +/1
     78 +/11
     79 +/h264
     80 +```
    60 81   
    61  -1. Get IPs in any format (`1.1.1.1-1.10.10.1`, `192.168.100.1/24`, `8.8.8.8`):
    62  - * Scan manually
    63  - * Use [Shodan](https://www.shodan.io/) or [Censys](https://censys.io/)
    64  -2. Insert them into the `hosts.txt` file so that each IP object (range, cidr or single IP) is on a new line
    65  -3. `python core.py`
     82 +- **"-c, --credentials"** (`credentials.txt`): Set custom path to the file with credentials. Each combination should contain `:` and be on a separate line, e.g.:
    66 83   
     84 +```
     85 +admin:admin
     86 +user:user
     87 +```
     88 + 
     89 +- **"-ct, --check-threads"** (`500`): Set custom number of threads to brute-force the routes
     90 +- **"-bt, --brute-threads"** (`200`): Set custom number of threads to brute-force the credentials
     91 +- **"-st, --screenshot-threads"** (`20`): Set custom number of threads to screenshot the streams. Smaller number leads to more successful screenshots: when there's too much threads PyAV will throw errors and wouldn't connect to target.
     92 +- **"-T, --timeout"** (`2`): Set custom timeout value for socket connections
     93 +- **"-d, --debug"** (`False`): Enable debug logging to `debug.log` file
    67 94   
    68 95  ## TODO
    69 96   
    70  -- [ ] Add support for multiple ports
     97 +- [x] Add support for multiple ports
     98 +- [ ] Optimize for large input
    71 99  - [ ] Add tests
    72  -- [ ] Add CLI
    73  -- [ ] Beautify format of output to terminal
     100 +- [x] Add CLI
     101 +- [x] Beautify format of output to terminal
    74 102  - [ ] Release on PyPI
     103 + 
  • ■ ■ ■ ■ ■ ■
    config.py
    skipped 1 lines
    2 2  from pathlib import Path
    3 3  from typing import List
    4 4   
    5  - 
    6  -# The number of threads that brute-force the routes.
    7  -CHECK_THREADS: int = 500
    8  - 
    9  -# The number of threads that brute-force the credentials.
    10  -BRUTE_THREADS: int = 200
    11  - 
    12  -# The number of threads that screenshot the streams.
    13  -# Note: less SCREENSHOT_THREADS leads to more successful
    14  -# screenshots: when there's too much threads PyAV will
    15  -# throw errors and wouldn't connect to target.
    16  -# On author's machine 20-30 is most effective number.
    17  -SCREENSHOT_THREADS: int = 20
    18  - 
    19  - 
    20  -PORT: int = 554
    21  -SOCKET_TIMEOUT: int = 2
     5 +from modules.cli.output import progress_bar
    22 6   
    23  - 
    24  -CREDENTIALS: List[str] = []
    25  -ROUTES: List[str] = []
    26  -TARGETS: List[str] = []
     7 +ROUTES: List[str]
     8 +CREDENTIALS: List[str]
     9 +PORTS: List[int]
    27 10   
     11 +CHECK_PROGRESS = progress_bar.add_task("[bright_red]Checking...", total=0)
     12 +BRUTE_PROGRESS = progress_bar.add_task("[bright_yellow]Bruting...", total=0)
     13 +SCREENSHOT_PROGRESS = progress_bar.add_task("[bright_green]Screenshoting...", total=0)
    28 14   
    29 15  start_datetime = time.strftime("%Y.%m.%d-%H.%M.%S")
    30 16  DEBUG_LOG_FILE = Path.cwd() / "debug.log"
    skipped 5 lines
  • ■ ■ ■ ■ ■ ■
    core.py
     1 +import collections
    1 2  import logging
    2 3  import threading
    3 4  from queue import Queue
    4  -from typing import List
     5 +from typing import Callable, List
    5 6   
    6 7  import av
    7  -from colorama import init, Fore, Style
     8 +from rich.panel import Panel
    8 9   
    9 10  import config
    10  -from modules import *
    11  - 
    12  -# Logging module set up
    13  -logging.basicConfig(
    14  - level=logging.INFO, format="[%(asctime)s] [%(levelname)s] %(message)s",
    15  -)
    16  -debugger = logging.getLogger("debugger")
    17  -debugger.setLevel(logging.DEBUG)
    18  -file_handler = logging.FileHandler(config.DEBUG_LOG_FILE, "w")
    19  -file_handler.setFormatter(
    20  - logging.Formatter(
    21  - "[%(asctime)s] [%(levelname)s] [%(threadName)s] [%(funcName)s] %(message)s"
    22  - )
    23  -)
    24  -debugger.addHandler(file_handler)
    25  -debugger.propagate = False
    26  - 
    27  -# Redirect PyAV logs only to file
    28  -libav_logger = logging.getLogger("libav")
    29  -libav_logger.setLevel(logging.DEBUG)
    30  -libav_logger.addHandler(file_handler)
    31  -libav_logger.propagate = False
    32  -av_logger = logging.getLogger("av")
    33  -av_logger.setLevel(logging.DEBUG)
    34  -av_logger.addHandler(file_handler)
    35  -av_logger.propagate = False
    36  -# This disables ValueError from av module printing to console, but this also
    37  -# means we won't get any logs from av, if they aren't FATAL or PANIC level.
    38  -av.logging.set_level(av.logging.FATAL)
     11 +from modules import utils, worker
     12 +from modules.cli.input import parser
     13 +from modules.cli.output import console
     14 +from modules.rtsp import RTSPClient
    39 15   
    40 16   
    41  -def start_threads(number, target, *args):
     17 +def start_threads(number: int, target: Callable, *args) -> List[threading.Thread]:
    42 18   debugger.debug(
    43 19   f"Starting {number} threads of {target.__module__}.{target.__name__}"
    44 20   )
    45 21   threads = []
    46 22   for _ in range(number):
    47 23   thread = threading.Thread(target=target, args=args)
     24 + thread.daemon = True
    48 25   threads.append(thread)
    49 26   thread.start()
    50 27   return threads
    skipped 7 lines
    58 35   
    59 36   
    60 37  if __name__ == "__main__":
    61  - init()
     38 + args = parser.parse_args()
    62 39   
    63  - config.CREDENTIALS = utils.load_txt("credentials.txt", "credentials")
    64  - config.ROUTES = utils.load_txt("routes.txt", "routes")
    65  - config.TARGETS = utils.load_txt("hosts.txt", "targets")
     40 + # Logging module set up
     41 + debugger = logging.getLogger("debugger")
     42 + debugger.setLevel(logging.DEBUG)
     43 + if args.debug:
     44 + file_handler = logging.FileHandler(config.DEBUG_LOG_FILE, "w")
     45 + file_handler.setFormatter(
     46 + logging.Formatter(
     47 + "[%(asctime)s] [%(levelname)s] [%(threadName)s] [%(funcName)s] %(message)s"
     48 + )
     49 + )
     50 + debugger.addHandler(file_handler)
     51 + else:
     52 + debugger.addHandler(logging.NullHandler())
     53 + debugger.propagate = False
     54 + 
     55 + # Redirect PyAV logs only to file
     56 + libav_logger = logging.getLogger("libav")
     57 + libav_logger.setLevel(logging.DEBUG)
     58 + if args.debug:
     59 + libav_logger.addHandler(file_handler)
     60 + libav_logger.propagate = False
     61 + av_logger = logging.getLogger("av")
     62 + av_logger.setLevel(logging.DEBUG)
     63 + if args.debug:
     64 + av_logger.addHandler(file_handler)
     65 + av_logger.propagate = False
     66 + # This disables ValueError from av module printing to console, but this also
     67 + # means we won't get any logs from av, if they aren't FATAL or PANIC level.
     68 + av.logging.set_level(av.logging.FATAL)
     69 + 
     70 + targets = collections.deque(set(utils.load_txt(args.targets, "targets")))
     71 + config.ROUTES = utils.load_txt(args.routes, "routes")
     72 + config.CREDENTIALS = utils.load_txt(args.credentials, "credentials")
     73 + 
     74 + config.PORTS = args.ports
    66 75   
    67 76   utils.create_folder(config.PICS_FOLDER)
    68 77   utils.create_file(config.RESULT_FILE)
    skipped 4 lines
    73 82   screenshot_queue = Queue()
    74 83   
    75 84   check_threads = start_threads(
    76  - config.CHECK_THREADS, worker.brute_routes, check_queue, brute_queue
     85 + args.check_threads, worker.brute_routes, check_queue, brute_queue
    77 86   )
    78 87   brute_threads = start_threads(
    79  - config.BRUTE_THREADS, worker.brute_credentials, brute_queue, screenshot_queue
     88 + args.brute_threads, worker.brute_credentials, brute_queue, screenshot_queue
    80 89   )
    81 90   screenshot_threads = start_threads(
    82  - config.SCREENSHOT_THREADS, worker.screenshot_targets, screenshot_queue
     91 + args.screenshot_threads, worker.screenshot_targets, screenshot_queue
    83 92   )
    84 93   
    85  - logging.info(f"{Fore.GREEN}Starting...\n{Style.RESET_ALL}")
     94 + console.print("[green]Starting...\n")
    86 95   
    87  - for ip in config.TARGETS:
    88  - check_queue.put(RTSPClient(ip, port=config.PORT, timeout=config.SOCKET_TIMEOUT))
     96 + config.progress_bar.update(config.CHECK_PROGRESS, total=len(targets))
     97 + config.progress_bar.start()
     98 + while targets:
     99 + check_queue.put(RTSPClient(ip=targets.popleft(), timeout=args.timeout))
    89 100   
    90 101   wait_for(check_queue, check_threads)
    91 102   debugger.debug("Check queue and threads finished")
    skipped 2 lines
    94 105   wait_for(screenshot_queue, screenshot_threads)
    95 106   debugger.debug("Screenshot queue and threads finished")
    96 107   
     108 + config.progress_bar.stop()
     109 + 
    97 110   print()
    98  - file_handler.close()
    99  - config.DEBUG_LOG_FILE.rename(config.REPORT_FOLDER / config.DEBUG_LOG_FILE.name)
     111 + if args.debug:
     112 + file_handler.close()
     113 + config.DEBUG_LOG_FILE.rename(config.REPORT_FOLDER / config.DEBUG_LOG_FILE.name)
    100 114   screenshots = list(config.PICS_FOLDER.iterdir())
    101  - logging.info(f"{Fore.GREEN}Saved {len(screenshots)} screenshots{Style.RESET_ALL}")
    102  - logging.info(
    103  - f"{Fore.GREEN}Report available at {str(config.REPORT_FOLDER)}{Style.RESET_ALL}"
     115 + console.print(f"[green]Saved {len(screenshots)} screenshots")
     116 + console.print(
     117 + Panel(
     118 + f"[bright_green]{str(config.REPORT_FOLDER)}", title="Report", expand=False
     119 + ),
     120 + justify="center",
    104 121   )
    105 122   
    106  - 
  • ■ ■ ■ ■ ■ ■
    modules/__init__.py
    1  -from modules import attack, utils, worker
    2  -from .rtsp import RTSPClient
    3  - 
    4  -__all__ = ['attack', 'utils', 'worker', 'RTSPClient']
    5 1   
  • ■ ■ ■ ■ ■ ■
    modules/attack.py
    skipped 1 lines
    2 2  import sys
    3 3   
    4 4  import av
    5  -from colorama import Fore, Style
    6 5   
     6 +import config
    7 7  from modules import utils
     8 +from modules.cli.output import console
    8 9  from modules.rtsp import RTSPClient, Status
    9 10   
    10 11  sys.path.append("..")
    11  -import config
    12 12   
    13 13  dummy_route = "/0x8b6c42"
    14 14  logger = logging.getLogger("debugger")
    15 15   
    16 16   
    17  -def attack(target: RTSPClient, route=None, credentials=None):
     17 +def attack(target: RTSPClient, port=None, route=None, credentials=None):
     18 + if port is None:
     19 + port = target.port
    18 20   if route is None:
    19 21   route = target.route
    20 22   if credentials is None:
    21 23   credentials = target.credentials
    22 24   
    23 25   # Create socket connection.
    24  - ok = target.connect()
     26 + ok = target.connect(port)
    25 27   if not ok:
    26 28   if target.status is Status.UNIDENTIFIED:
    27 29   logger.debug(
    skipped 3 lines
    31 33   logger.debug(f"Failed to connect {str(target)}: {target.status.name}")
    32 34   return False
    33 35   
    34  - attack_url = RTSPClient.get_rtsp_url(target.ip, target.port, credentials, route)
     36 + attack_url = RTSPClient.get_rtsp_url(target.ip, port, credentials, route)
    35 37   # Try to authorize: create describe packet and send it.
    36  - ok = target.authorize(route, credentials)
     38 + ok = target.authorize(port, route, credentials)
    37 39   request = "\n\t".join(target.packet.split("\r\n")).rstrip()
    38 40   if target.data:
    39 41   response = "\n\t".join(target.data.split("\r\n")).rstrip()
    skipped 15 lines
    55 57   # If the stream responds positively to the dummy route, it means
    56 58   # it doesn't require (or respect the RFC) a route and the attack
    57 59   # can be skipped.
    58  - ok = attack(target, route=dummy_route)
    59  - if ok and any(code in target.data for code in ok_codes):
    60  - target.routes.append("/")
    61  - return target
     60 + for port in config.PORTS:
     61 + ok = attack(target, port=port, route=dummy_route)
     62 + if ok and any(code in target.data for code in ok_codes):
     63 + target.port = port
     64 + target.routes.append("/")
     65 + return target
    62 66   
    63  - # Otherwise, bruteforce the routes.
    64  - for route in config.ROUTES:
    65  - ok = attack(target, route=route)
    66  - if not ok:
    67  - return False
    68  - if any(code in target.data for code in ok_codes):
    69  - target.routes.append(route)
    70  - return target
     67 + # Otherwise, bruteforce the routes.
     68 + for route in config.ROUTES:
     69 + ok = attack(target, port=port, route=route)
     70 + if not ok:
     71 + break
     72 + if any(code in target.data for code in ok_codes):
     73 + target.port = port
     74 + target.routes.append(route)
     75 + return target
    71 76   
    72 77   
    73 78  def attack_credentials(target: RTSPClient):
    74 79   def _log_working_stream():
    75  - logging.info(f"{Style.DIM}Working stream at {str(target)}{Style.RESET_ALL}")
     80 + console.print("Working stream at", target)
    76 81   logger.debug(
    77 82   f"Working stream at {str(target)} with {target.auth_method.name} auth"
    78 83   )
    skipped 24 lines
    103 108   return target
    104 109   
    105 110   
    106  -def get_screenshot(target: RTSPClient, tries=0) -> str:
     111 +def get_screenshot(target: RTSPClient, tries=0):
    107 112   file_name = utils.escape_chars(f"{str(target).lstrip('rtsp://')}.jpg")
    108 113   file_path = config.PICS_FOLDER / file_name
    109 114   
    skipped 22 lines
    132 137   logger.debug(
    133 138   f"Broken video stream or unknown issues with {str(target)}"
    134 139   )
    135  - return ""
     140 + return
    136 141   video.streams.video[0].thread_type = "AUTO"
    137 142   for frame in video.decode(video=0):
    138 143   frame.to_image().save(file_path)
    skipped 4 lines
    143 148   # Try one more time in hope for luck.
    144 149   if tries == 2:
    145 150   tries += 1
    146  - logging.info(
    147  - f"{Fore.YELLOW}Retry to get a screenshot of the {str(target)}{Style.RESET_ALL}"
    148  - )
     151 + console.print("[yellow]Retry to get a screenshot of the", target)
    149 152   return get_screenshot(target, tries)
    150 153   else:
    151  - logging.warning(
    152  - f"{Fore.RED}Missed screenshot of {str(target)}: if you see this message a lot - consider lowering SCREENSHOT_THREADS ({config.SCREENSHOT_THREADS}){Style.RESET_ALL}"
     154 + console.print(
     155 + f"[italic red]Missed screenshot of [underline]{str(target)}[/underline] - if you see this message a lot, consider reducing the number of screenshot threads",
    153 156   )
    154  - return ""
     157 + return
    155 158   except Exception as e:
    156 159   logger.debug(f"get_screenshot failed with {str(target)}: {repr(e)}")
    157  - return ""
     160 + return
    158 161   
    159  - logging.info(
    160  - f"{Style.BRIGHT}Captured screenshot for {str(target)}{Style.RESET_ALL}"
    161  - )
     162 + console.print("[bold]Captured screenshot for", target)
    162 163   logger.debug(f"Captured screenshot for {str(target)}")
    163 164   return file_path
    164 165   
  • ■ ■ ■ ■ ■ ■
    modules/cli/input.py
     1 +import argparse
     2 + 
     3 + 
     4 +class CustomHelpFormatter(argparse.HelpFormatter):
     5 + def __init__(self, prog):
     6 + super().__init__(prog, max_help_position=40, width=99)
     7 + 
     8 + def _format_action_invocation(self, action):
     9 + if not action.option_strings or action.nargs == 0:
     10 + return super()._format_action_invocation(action)
     11 + default = self._get_default_metavar_for_optional(action)
     12 + args_string = self._format_args(action, default)
     13 + return ", ".join(action.option_strings) + " " + args_string
     14 + 
     15 + 
     16 +fmt = lambda prog: CustomHelpFormatter(prog)
     17 +parser = argparse.ArgumentParser(
     18 + description="Tool for RTSP that brute-forces routes and credentials, makes screenshots!",
     19 + formatter_class=fmt,
     20 +)
     21 +parser.add_argument(
     22 + "-t",
     23 + "--targets",
     24 + default="hosts.txt",
     25 + help="the targets on which to scan for open RTSP streams",
     26 +)
     27 +parser.add_argument(
     28 + "-p",
     29 + "--ports",
     30 + nargs="+",
     31 + default=[554],
     32 + type=int,
     33 + help="the ports on which to search for RTSP streams",
     34 +)
     35 +parser.add_argument(
     36 + "-r",
     37 + "--routes",
     38 + default="routes.txt",
     39 + help="the path on which to load a custom routes",
     40 +)
     41 +parser.add_argument(
     42 + "-c",
     43 + "--credentials",
     44 + default="credentials.txt",
     45 + help="the path on which to load a custom credentials",
     46 +)
     47 +parser.add_argument(
     48 + "-ct",
     49 + "--check-threads",
     50 + default=500,
     51 + type=int,
     52 + help="the number of threads to brute-force the routes",
     53 + metavar="N",
     54 +)
     55 +parser.add_argument(
     56 + "-bt",
     57 + "--brute-threads",
     58 + default=200,
     59 + type=int,
     60 + help="the number of threads to brute-force the credentials",
     61 + metavar="N",
     62 +)
     63 +parser.add_argument(
     64 + "-st",
     65 + "--screenshot-threads",
     66 + default=20,
     67 + type=int,
     68 + help="the number of threads to screenshot the streams",
     69 + metavar="N",
     70 +)
     71 +parser.add_argument(
     72 + "-T", "--timeout", default=2, type=int, help="the timeout to use for sockets"
     73 +)
     74 +parser.add_argument("-d", "--debug", action="store_true", help="enable the debug logs")
     75 + 
  • ■ ■ ■ ■ ■ ■
    modules/cli/output.py
     1 +from rich.console import Console
     2 +from rich.progress import BarColumn, Progress, TaskID
     3 + 
     4 + 
     5 +class ProgressBar(Progress):
     6 + def __init__(self, console: Console) -> None:
     7 + super().__init__(
     8 + "[progress.description]{task.description}",
     9 + BarColumn(),
     10 + "{task.completed} of {task.total}",
     11 + console=console,
     12 + )
     13 + 
     14 + def add_total(self, task_id: TaskID, n: int = 1) -> None:
     15 + with self._lock:
     16 + self._tasks[task_id].total += n
     17 + 
     18 + 
     19 +console = Console(highlight=False)
     20 +progress_bar = ProgressBar(console)
     21 + 
  • ■ ■ ■ ■ ■ ■
    modules/rtsp.py
    skipped 50 lines
    51 51   )
    52 52   
    53 53   def __init__(
    54  - self, ip: str, port: int = 554, credentials: str = ":", timeout: int = 2
     54 + self, ip: str, port: int = 554, timeout: int = 2, credentials: str = ":",
    55 55   ) -> None:
    56 56   try:
    57 57   ip_address(ip)
    skipped 9 lines
    67 67   self.routes: List[str] = []
    68 68   self.status: Status = Status.NONE
    69 69   self.auth_method: AuthMethod = AuthMethod.NONE
    70  - self.last_error: Exception = None
    71  - self.realm: str = None
    72  - self.nonce: str = None
     70 + self.last_error: Union[Exception, None] = None
     71 + self.realm: Union[str, None] = None
     72 + self.nonce: Union[str, None] = None
    73 73   self.socket = None
    74 74   self.timeout = timeout
    75 75   self.packet = None
    skipped 15 lines
    91 91   def is_authorized(self):
    92 92   return "200" in self.data
    93 93   
    94  - def connect(self):
     94 + def connect(self, port: int = None):
    95 95   if self.is_connected:
    96 96   return True
     97 + 
     98 + if port is None:
     99 + port = self.port
    97 100   
    98 101   self.packet = None
    99 102   self.cseq = 0
    skipped 1 lines
    101 104   retry = 0
    102 105   while retry < MAX_RETRIES and not self.is_connected:
    103 106   try:
    104  - self.socket = socket.create_connection(
    105  - (self.ip, self.port), self.timeout
    106  - )
     107 + self.socket = socket.create_connection((self.ip, port), self.timeout)
    107 108   except Exception as e:
    108 109   self.status = Status.from_exception(e)
    109 110   self.last_error = e
    skipped 8 lines
    118 119   
    119 120   return False
    120 121   
    121  - def authorize(self, route=None, credentials=None):
     122 + def authorize(self, port=None, route=None, credentials=None):
    122 123   if not self.is_connected:
    123 124   return False
    124 125   
     126 + if port is None:
     127 + port = self.port
    125 128   if route is None:
    126 129   route = self.route
    127 130   if credentials is None:
    skipped 1 lines
    129 132   
    130 133   self.cseq += 1
    131 134   self.packet = describe(
    132  - self.ip, self.port, route, self.cseq, credentials, self.realm, self.nonce
     135 + self.ip, port, route, self.cseq, credentials, self.realm, self.nonce
    133 136   )
    134 137   try:
    135 138   self.socket.sendall(self.packet.encode())
    skipped 33 lines
    169 172   def __str__(self) -> str:
    170 173   return self.get_rtsp_url(self.ip, self.port, self.credentials, self.route)
    171 174   
     175 + def __rich__(self) -> str:
     176 + return f"[underline cyan]{self.__str__()}[/underline cyan]"
     177 + 
  • ■ ■ ■ ■ ■ ■
    modules/utils.py
    skipped 4 lines
    5 5  from pathlib import Path
    6 6  from typing import List
    7 7   
    8  -from colorama import Fore, Style
    9  - 
     8 +from modules.cli.output import console
    10 9  from modules.rtsp import RTSPClient
    11 10   
    12 11  logger = logging.getLogger("debugger")
    skipped 88 lines
    101 100   target for line in get_lines(path) for target in parse_input_line(line)
    102 101   ]
    103 102   except FileNotFoundError as e:
    104  - logging.error(
    105  - f"{Fore.RED}Couldn't read {name} file at {path}: {repr(e)}{Style.RESET_ALL}"
    106  - )
     103 + console.print(f"[red]Couldn't read {name} file at {path}: {repr(e)}")
    107 104   sys.exit()
    108  - logging.info(
    109  - f"{Fore.YELLOW}Loaded {len(result)} {name} from {path}{Style.RESET_ALL}"
    110  - )
     105 + console.print(f"[yellow]Loaded {len(result)} {name} from {path}")
    111 106   return result
    112 107   
    113 108   
    skipped 43 lines
  • ■ ■ ■ ■ ■
    modules/worker.py
    1 1  import sys
    2  -from threading import Lock
    3 2  from queue import Queue
     3 +from threading import Lock
    4 4   
    5  -sys.path.append("..")
    6 5  import config
    7 6   
    8  -from .attack import attack_route, attack_credentials, get_screenshot
     7 +from .attack import attack_credentials, attack_route, get_screenshot
    9 8  from .rtsp import RTSPClient
    10 9  from .utils import append_result
    11 10   
     11 +sys.path.append("..")
     12 + 
     13 + 
    12 14  GLOBAL_LOCK = Lock()
    13 15   
    14 16   
    skipped 5 lines
    20 22   
    21 23   result = attack_route(target)
    22 24   if result:
     25 + config.progress_bar.add_total(config.BRUTE_PROGRESS)
    23 26   output_queue.put(result)
    24 27   
     28 + config.progress_bar.update(config.CHECK_PROGRESS, advance=1)
    25 29   input_queue.task_done()
    26 30   
    27 31   
    skipped 5 lines
    33 37   
    34 38   result = attack_credentials(target)
    35 39   if result:
     40 + config.progress_bar.add_total(config.SCREENSHOT_PROGRESS)
    36 41   output_queue.put(target)
    37 42   
     43 + config.progress_bar.update(config.BRUTE_PROGRESS, advance=1)
    38 44   input_queue.task_done()
    39 45   
    40 46   
    skipped 9 lines
    50 56   GLOBAL_LOCK, config.RESULT_FILE, config.HTML_FILE, image, target
    51 57   )
    52 58   
     59 + config.progress_bar.update(config.SCREENSHOT_PROGRESS, advance=1)
    53 60   input_queue.task_done()
    54 61   
  • ■ ■ ■ ■
    requirements.txt
    1  -av
    2 1  colorama
    3 2  Pillow
     3 +rich
Please wait...
Page is in error, reload to recover