From bd75d16412e17e6816505c0f188ffb4336473c01 Mon Sep 17 00:00:00 2001 From: Tejas <47889755+0xtejas@users.noreply.github.com> Date: Mon, 8 Apr 2024 14:24:59 +0530 Subject: [PATCH 1/7] [FIX] security: OS Command Injection vulnerability (x2) #1219 --- web/reNgine/common_func.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/reNgine/common_func.py b/web/reNgine/common_func.py index 2c7bdbd8a..69008eb76 100644 --- a/web/reNgine/common_func.py +++ b/web/reNgine/common_func.py @@ -12,6 +12,7 @@ import requests import tldextract import xmltodict +import subprocess from bs4 import BeautifulSoup from urllib.parse import urlparse @@ -510,8 +511,8 @@ def get_cms_details(url): """ # this function will fetch cms details using cms_detector response = {} - cms_detector_command = f'python3 /usr/src/github/CMSeeK/cmseek.py --random-agent --batch --follow-redirect -u {url}' - os.system(cms_detector_command) + cms_detector_command = ['python3', '/usr/src/github/CMSeeK/cmseek.py', '--random-agent', '--batch', '--follow-redirect', '-u', f'{url}'] + subprocess.run(cms_detector_command) response['status'] = False response['message'] = 'Could not detect CMS!' From 825dad08c8aa5428861678b8fbb2d9e0591d0d5a Mon Sep 17 00:00:00 2001 From: Tejas <47889755+0xtejas@users.noreply.github.com> Date: Tue, 16 Apr 2024 16:22:21 +0530 Subject: [PATCH 2/7] Update web/reNgine/common_func.py Removing F-String format. Co-authored-by: Filipe Pina <636320+fopina@users.noreply.github.com> --- web/reNgine/common_func.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/reNgine/common_func.py b/web/reNgine/common_func.py index 69008eb76..553a51fa4 100644 --- a/web/reNgine/common_func.py +++ b/web/reNgine/common_func.py @@ -511,7 +511,7 @@ def get_cms_details(url): """ # this function will fetch cms details using cms_detector response = {} - cms_detector_command = ['python3', '/usr/src/github/CMSeeK/cmseek.py', '--random-agent', '--batch', '--follow-redirect', '-u', f'{url}'] + cms_detector_command = ['python3', '/usr/src/github/CMSeeK/cmseek.py', '--random-agent', '--batch', '--follow-redirect', '-u', url] subprocess.run(cms_detector_command) response['status'] = False From 632d44aaf800dec376ade2006b226dfa48844de9 Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Wed, 17 Apr 2024 18:57:20 +0530 Subject: [PATCH 3/7] Fix #1219 use inbuilt run_command instead of os.system causing RCE --- web/api/views.py | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/web/api/views.py b/web/api/views.py index 8bcf46a84..90997f6c9 100644 --- a/web/api/views.py +++ b/web/api/views.py @@ -1067,10 +1067,50 @@ def get(self, request): #save_db = True if 'save_db' in req.query_params else False response = {'status': False} try: - response = get_cms_details(url) + # response = get_cms_details(url) + response = {} + cms_detector_command = f'python3 /usr/src/github/CMSeeK/cmseek.py' + cms_detector_command += ' --random-agent --batch --follow-redirect' + cms_detector_command += f' -u {url}' + + _, output = run_command(cms_detector_command, remove_ansi_sequence=True) + + response['message'] = 'Could not detect CMS!' + + parsed_url = urlparse(url) + + domain_name = parsed_url.hostname + port = parsed_url.port + + find_dir = domain_name + + if port: + find_dir += '_{}'.format(port) + # cms_dir_path = '/usr/src/github/CMSeeK/Result/{}'.format(find_dir) + # cms_json_path = cms_dir_path + '/cms.json' + # look for result path in output + path_regex = r"Result: (\/usr\/src[^\"\s]*)" + match = re.search(path_regex, output) + if match: + cms_json_path = match.group(1) + print(cms_json_path) + if os.path.isfile(cms_json_path): + cms_file_content = json.loads(open(cms_json_path, 'r').read()) + if not cms_file_content.get('cms_id'): + return response + response = {} + response = cms_file_content + response['status'] = True + try: + # remove results + shutil.rmtree(cms_dir_path) + except Exception as e: + logger.error(e) + return Response(response) + return Response(response) except Exception as e: response = {'status': False, 'message': str(e)} - return Response(response) + return Response(response) class IPToDomain(APIView): From 8fb5c41dbe98aec4d71f9bdfd36953ae4159ad0e Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Wed, 17 Apr 2024 18:57:44 +0530 Subject: [PATCH 4/7] Add function to remove ansi color coding for regex --- web/reNgine/common_func.py | 57 ++++++-------------------------------- 1 file changed, 8 insertions(+), 49 deletions(-) diff --git a/web/reNgine/common_func.py b/web/reNgine/common_func.py index 553a51fa4..c989f269c 100644 --- a/web/reNgine/common_func.py +++ b/web/reNgine/common_func.py @@ -4,7 +4,6 @@ import random import shutil import traceback -import uuid from time import sleep import humanize @@ -12,7 +11,6 @@ import requests import tldextract import xmltodict -import subprocess from bs4 import BeautifulSoup from urllib.parse import urlparse @@ -28,6 +26,7 @@ from startScan.models import * from targetApp.models import * + logger = get_task_logger(__name__) DISCORD_WEBHOOKS_CACHE = redis.Redis.from_url(CELERY_BROKER_URL) @@ -499,53 +498,13 @@ def get_random_proxy(): # os.environ['HTTPS_PROXY'] = proxy_name return proxy_name - -def get_cms_details(url): - """Get CMS details using cmseek.py. - - Args: - url (str): HTTP URL. - - Returns: - dict: Response. - """ - # this function will fetch cms details using cms_detector - response = {} - cms_detector_command = ['python3', '/usr/src/github/CMSeeK/cmseek.py', '--random-agent', '--batch', '--follow-redirect', '-u', url] - subprocess.run(cms_detector_command) - - response['status'] = False - response['message'] = 'Could not detect CMS!' - - parsed_url = urlparse(url) - - domain_name = parsed_url.hostname - port = parsed_url.port - - find_dir = domain_name - - if port: - find_dir += '_{}'.format(port) - - # subdomain may also have port number, and is stored in dir as _port - - cms_dir_path = '/usr/src/github/CMSeeK/Result/{}'.format(find_dir) - cms_json_path = cms_dir_path + '/cms.json' - - if os.path.isfile(cms_json_path): - cms_file_content = json.loads(open(cms_json_path, 'r').read()) - if not cms_file_content.get('cms_id'): - return response - response = {} - response = cms_file_content - response['status'] = True - # remove cms dir path - try: - shutil.rmtree(cms_dir_path) - except Exception as e: - print(e) - - return response +def remove_ansi_escape_sequences(text): + # Regular expression to match ANSI escape sequences + ansi_escape_pattern = r'\x1b\[.*?m' + + # Use re.sub() to replace the ANSI escape sequences with an empty string + plain_text = re.sub(ansi_escape_pattern, '', text) + return plain_text #--------------------# From e003a91537a0f47b831abd8fb4a5875a70214028 Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Wed, 17 Apr 2024 18:58:14 +0530 Subject: [PATCH 5/7] added remove ansi sequence param in run command to remove ansi chars such as color coding --- web/reNgine/tasks.py | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/web/reNgine/tasks.py b/web/reNgine/tasks.py index 616fe5594..27132fd50 100644 --- a/web/reNgine/tasks.py +++ b/web/reNgine/tasks.py @@ -4090,7 +4090,15 @@ def remove_duplicate_endpoints( logger.warning(msg) @app.task(name='run_command', bind=False, queue='run_command_queue') -def run_command(cmd, cwd=None, shell=False, history_file=None, scan_id=None, activity_id=None): +def run_command( + cmd, + cwd=None, + shell=False, + history_file=None, + scan_id=None, + activity_id=None, + remove_ansi_sequence=False + ): """Run a given command using subprocess module. Args: @@ -4099,7 +4107,7 @@ def run_command(cmd, cwd=None, shell=False, history_file=None, scan_id=None, act echo (bool): Log command. shell (bool): Run within separate shell if True. history_file (str): Write command + output to history file. - + remove_ansi_sequence (bool): Used to remove ANSI escape sequences from output such as color coding Returns: tuple: Tuple with return_code, output. """ @@ -4138,6 +4146,8 @@ def run_command(cmd, cwd=None, shell=False, history_file=None, scan_id=None, act mode = 'w' with open(history_file, mode) as f: f.write(f'\n{cmd}\n{return_code}\n{output}\n------------------\n') + if remove_ansi_sequence: + output = remove_ansi_escape_sequences(output) return return_code, output From 81c9264d1498f6f3a0872934a97b0eea0e17de6d Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Wed, 17 Apr 2024 19:00:25 +0530 Subject: [PATCH 6/7] remove results directory after parsing --- web/api/views.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/web/api/views.py b/web/api/views.py index 90997f6c9..7c2c2ab52 100644 --- a/web/api/views.py +++ b/web/api/views.py @@ -1086,8 +1086,6 @@ def get(self, request): if port: find_dir += '_{}'.format(port) - # cms_dir_path = '/usr/src/github/CMSeeK/Result/{}'.format(find_dir) - # cms_json_path = cms_dir_path + '/cms.json' # look for result path in output path_regex = r"Result: (\/usr\/src[^\"\s]*)" match = re.search(path_regex, output) @@ -1103,6 +1101,7 @@ def get(self, request): response['status'] = True try: # remove results + cms_dir_path = os.path.dirname(cms_json_path) shutil.rmtree(cms_dir_path) except Exception as e: logger.error(e) From 10c5630e7abe43b77014ee35e55a6659cc61c2f9 Mon Sep 17 00:00:00 2001 From: Yogesh Ojha Date: Wed, 17 Apr 2024 19:00:42 +0530 Subject: [PATCH 7/7] remove debug print --- web/api/views.py | 1 - 1 file changed, 1 deletion(-) diff --git a/web/api/views.py b/web/api/views.py index 7c2c2ab52..5aa778692 100644 --- a/web/api/views.py +++ b/web/api/views.py @@ -1091,7 +1091,6 @@ def get(self, request): match = re.search(path_regex, output) if match: cms_json_path = match.group(1) - print(cms_json_path) if os.path.isfile(cms_json_path): cms_file_content = json.loads(open(cms_json_path, 'r').read()) if not cms_file_content.get('cms_id'):