From 1a1d7168e59ce50b9f41cbf1d1a3820efa369360 Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Mon, 10 Feb 2025 11:07:51 -0500 Subject: [PATCH 1/8] sanitize user-provided LDFLAG parameters Sanitize user-provided LDFLAG values for custom golang compilation to avoid RCE --- app/service/file_svc.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/app/service/file_svc.py b/app/service/file_svc.py index 01cf2b0ec..783b04520 100644 --- a/app/service/file_svc.py +++ b/app/service/file_svc.py @@ -4,6 +4,7 @@ import copy import json import os +import re import subprocess import sys @@ -19,7 +20,8 @@ from app.utility.payload_encoder import xor_file, xor_bytes FILE_ENCRYPTION_FLAG = '%encrypted%' - +ALLOWED_SERVER_LDFLAG_REGEX = re.compile(r'^[\w\-\.:%+]+$') +ALLOWED_DEFAULT_LDFLAG_REGEX = re.compile(r'^[\w\-\.]+$') class FileSvc(FileServiceInterface, BaseService): @@ -172,6 +174,26 @@ async def compile_go(self, platform, output, src_fle, arch='amd64', ldflags='-s except subprocess.CalledProcessError as e: self.log.warning('Problem building golang executable {}: {} '.format(src_fle, e)) + @staticmethod + def sanitize_ldflag_value(value): + """ + Validate that the specified LDFLAG value only contains safe characters. + Raises a ValueError if disallowed characters are found. + """ + if not ALLOWED_DEFAULT_LDFLAG_REGEX.fullmatch(value): + ValueError('Invalid characters in LDFLAG value: %s' % value) + return value + + @staticmethod + def sanitize_server_ldflag_value(value): + """ + Validate that the specified server LDFLAG value only contains safe characters. + Raises a ValueError if disallowed characters are found. + """ + if not ALLOWED_SERVER_LDFLAG_REGEX.fullmatch(value): + ValueError('Invalid characters in server LDFLAG value: %s' % value) + return value + def get_payload_name_from_uuid(self, payload): for t in ['standard_payloads', 'special_payloads']: for k, v in self.get_config(prop=t, name='payloads').items(): From a3d4ab2589824a6faa68a95279a1e3762c7c512f Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Mon, 10 Feb 2025 11:26:11 -0500 Subject: [PATCH 2/8] allow slashes in server val --- app/service/file_svc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/service/file_svc.py b/app/service/file_svc.py index 783b04520..f59bf0b58 100644 --- a/app/service/file_svc.py +++ b/app/service/file_svc.py @@ -20,7 +20,7 @@ from app.utility.payload_encoder import xor_file, xor_bytes FILE_ENCRYPTION_FLAG = '%encrypted%' -ALLOWED_SERVER_LDFLAG_REGEX = re.compile(r'^[\w\-\.:%+]+$') +ALLOWED_SERVER_LDFLAG_REGEX = re.compile(r'^[\w\-\.:%+/]+$') ALLOWED_DEFAULT_LDFLAG_REGEX = re.compile(r'^[\w\-\.]+$') class FileSvc(FileServiceInterface, BaseService): From 7a59276dc381256e7344c13a32f515f048c379e1 Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Mon, 10 Feb 2025 11:28:40 -0500 Subject: [PATCH 3/8] add sanitization unit tests --- tests/services/test_file_svc.py | 54 +++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/tests/services/test_file_svc.py b/tests/services/test_file_svc.py index bf007269b..0ce2384c4 100644 --- a/tests/services/test_file_svc.py +++ b/tests/services/test_file_svc.py @@ -242,6 +242,60 @@ def test_is_extension_xored_false(self, file_svc): ret = file_svc.is_extension_xored(test_value) assert ret is False + def test_sanitize_ldflag_value(self, file_svc): + safe_values = [ + 'safevalue', + 'SAFE29VALUE', + '_safe_', + 's-a-f-e.s_a_f_e.2', + '1234567890' + ] + for value in safe_values: + assert value == file_svc.sanitize_ldflag_value(value) + + safe_server_values = [ + 'http://localhost', + 'https://localhost:8443', + 'https://127.0.0.1:8443/home.html', + 'https://some.domain.net:8443/home%20test.html', + 'https://_underscore.domain-with-dash.net:8443/home+test.html', + ] + for value in safe_server_values: + assert value == file_svc.sanitize_server_ldflag_value(value) + + unsafe_values = [ + 'unsafe with spaces', + 'unsafe,comma', + 'unsafe;semicolon', + 'unsafe!', + 'unsafe&&test', + 'unsafe||test', + 'unsafe>test', + 'unsafe Date: Mon, 10 Feb 2025 11:42:32 -0500 Subject: [PATCH 4/8] Throw valueerror --- app/service/file_svc.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/service/file_svc.py b/app/service/file_svc.py index f59bf0b58..9e037ff0d 100644 --- a/app/service/file_svc.py +++ b/app/service/file_svc.py @@ -181,7 +181,7 @@ def sanitize_ldflag_value(value): Raises a ValueError if disallowed characters are found. """ if not ALLOWED_DEFAULT_LDFLAG_REGEX.fullmatch(value): - ValueError('Invalid characters in LDFLAG value: %s' % value) + raise ValueError('Invalid characters in LDFLAG value: %s' % value) return value @staticmethod @@ -191,7 +191,7 @@ def sanitize_server_ldflag_value(value): Raises a ValueError if disallowed characters are found. """ if not ALLOWED_SERVER_LDFLAG_REGEX.fullmatch(value): - ValueError('Invalid characters in server LDFLAG value: %s' % value) + raise ValueError('Invalid characters in server LDFLAG value: %s' % value) return value def get_payload_name_from_uuid(self, payload): From 16c9f42dbdd5410d3575b5f1c480d31e80c7af50 Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Mon, 10 Feb 2025 11:43:07 -0500 Subject: [PATCH 5/8] handle stye --- app/service/file_svc.py | 1 + 1 file changed, 1 insertion(+) diff --git a/app/service/file_svc.py b/app/service/file_svc.py index 9e037ff0d..92bcb1fa1 100644 --- a/app/service/file_svc.py +++ b/app/service/file_svc.py @@ -23,6 +23,7 @@ ALLOWED_SERVER_LDFLAG_REGEX = re.compile(r'^[\w\-\.:%+/]+$') ALLOWED_DEFAULT_LDFLAG_REGEX = re.compile(r'^[\w\-\.]+$') + class FileSvc(FileServiceInterface, BaseService): def __init__(self): From 53649b325722ba35c37127bf47a3144c0e27defd Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Tue, 11 Feb 2025 08:40:18 -0500 Subject: [PATCH 6/8] combine sanitization methods --- app/service/file_svc.py | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/app/service/file_svc.py b/app/service/file_svc.py index 92bcb1fa1..0e520178d 100644 --- a/app/service/file_svc.py +++ b/app/service/file_svc.py @@ -20,8 +20,13 @@ from app.utility.payload_encoder import xor_file, xor_bytes FILE_ENCRYPTION_FLAG = '%encrypted%' -ALLOWED_SERVER_LDFLAG_REGEX = re.compile(r'^[\w\-\.:%+/]+$') +URL_SANITIZATION_REGEX = re.compile(r'^[\w\-\.:%+/]+$') ALLOWED_DEFAULT_LDFLAG_REGEX = re.compile(r'^[\w\-\.]+$') +ALLOWED_LDFLAG_REGEXES = { + 'server': URL_SANITIZATION_REGEX, + 'http': URL_SANITIZATION_REGEX, + 'socket': re.compile(r'^[\w\-\.:]+$') +} class FileSvc(FileServiceInterface, BaseService): @@ -176,23 +181,14 @@ async def compile_go(self, platform, output, src_fle, arch='amd64', ldflags='-s self.log.warning('Problem building golang executable {}: {} '.format(src_fle, e)) @staticmethod - def sanitize_ldflag_value(value): + def sanitize_ldflag_value(param, value): """ - Validate that the specified LDFLAG value only contains safe characters. + Validate that the specified LDFLAG value for the given parameter + only contains safe characters. Raises a ValueError if disallowed characters are found. """ - if not ALLOWED_DEFAULT_LDFLAG_REGEX.fullmatch(value): - raise ValueError('Invalid characters in LDFLAG value: %s' % value) - return value - - @staticmethod - def sanitize_server_ldflag_value(value): - """ - Validate that the specified server LDFLAG value only contains safe characters. - Raises a ValueError if disallowed characters are found. - """ - if not ALLOWED_SERVER_LDFLAG_REGEX.fullmatch(value): - raise ValueError('Invalid characters in server LDFLAG value: %s' % value) + if not ALLOWED_LDFLAG_REGEXES.get(param, ALLOWED_DEFAULT_LDFLAG_REGEX).fullmatch(value): + raise ValueError('Invalid characters in %s LDFLAG value: %s' % (param, value)) return value def get_payload_name_from_uuid(self, payload): From e2259b50cf95b7a297bdd5acccc4908f73054004 Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Tue, 11 Feb 2025 08:45:15 -0500 Subject: [PATCH 7/8] update unit test --- tests/services/test_file_svc.py | 45 +++++++++++++++++++++++++++++---- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/tests/services/test_file_svc.py b/tests/services/test_file_svc.py index 0ce2384c4..ded6d6b6e 100644 --- a/tests/services/test_file_svc.py +++ b/tests/services/test_file_svc.py @@ -251,7 +251,9 @@ def test_sanitize_ldflag_value(self, file_svc): '1234567890' ] for value in safe_values: - assert value == file_svc.sanitize_ldflag_value(value) + assert value == file_svc.sanitize_ldflag_value('contact', value) + assert value == file_svc.sanitize_ldflag_value('group', value) + assert value == file_svc.sanitize_ldflag_value('genericparam', value) safe_server_values = [ 'http://localhost', @@ -261,7 +263,17 @@ def test_sanitize_ldflag_value(self, file_svc): 'https://_underscore.domain-with-dash.net:8443/home+test.html', ] for value in safe_server_values: - assert value == file_svc.sanitize_server_ldflag_value(value) + assert value == file_svc.sanitize_ldflag_value('server', value) + assert value == file_svc.sanitize_ldflag_value('http', value) + + safe_socket_values = [ + 'localhost:1234', + '10.10.10.10.:8888', + 'f.q.d.n:443', + 'domain-with-dash.net:443', + ] + for value in safe_socket_values: + assert value == file_svc.sanitize_ldflag_value('socket', value) unsafe_values = [ 'unsafe with spaces', @@ -278,8 +290,8 @@ def test_sanitize_ldflag_value(self, file_svc): ] for value in unsafe_values: with pytest.raises(Exception) as e_info: - file_svc.sanitize_ldflag_value(value) - assert str(e_info.value) == 'Invalid characters in LDFLAG value: {}'.format(value) + file_svc.sanitize_ldflag_value('group', value) + assert str(e_info.value) == 'Invalid characters in group LDFLAG value: {}'.format(value) unsafe_server_values = [ 'http://localhost||test', @@ -293,9 +305,32 @@ def test_sanitize_ldflag_value(self, file_svc): ] for value in unsafe_server_values: with pytest.raises(Exception) as e_info: - file_svc.sanitize_server_ldflag_value(value) + file_svc.sanitize_ldflag_value('server', value) assert str(e_info.value) == 'Invalid characters in server LDFLAG value: {}'.format(value) + with pytest.raises(Exception) as e_info: + file_svc.sanitize_ldflag_value('http', value) + assert str(e_info.value) == 'Invalid characters in http LDFLAG value: {}'.format(value) + + unsafe_socket_values = [ + 'localhost:8888||test', + '127.0.0.1:8443 space', + 'domain.com:8443@', + 'localhost:8443"test', + 'localhost:8443\'test', + '127.0.0.1:8443$(test)', + 'some.domain.net:8443 && test', + 'domain-with-dash.net:8443; test', + ] + for value in unsafe_server_values: + with pytest.raises(Exception) as e_info: + file_svc.sanitize_ldflag_value('server', value) + assert str(e_info.value) == 'Invalid characters in server LDFLAG value: {}'.format(value) + + with pytest.raises(Exception) as e_info: + file_svc.sanitize_ldflag_value('http', value) + assert str(e_info.value) == 'Invalid characters in http LDFLAG value: {}'.format(value) + @staticmethod def _test_download_file_with_encoding(event_loop, file_svc, data_svc, encoding, original_content, encoded_content): filename = 'testencodedpayload.txt' From 4d68fda4935ca83edc0e87f682f838313cc3759e Mon Sep 17 00:00:00 2001 From: Daniel Matthews <58484522+uruwhy@users.noreply.github.com> Date: Tue, 11 Feb 2025 08:52:16 -0500 Subject: [PATCH 8/8] update unit test --- tests/services/test_file_svc.py | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/tests/services/test_file_svc.py b/tests/services/test_file_svc.py index ded6d6b6e..fe7c6c16b 100644 --- a/tests/services/test_file_svc.py +++ b/tests/services/test_file_svc.py @@ -322,14 +322,10 @@ def test_sanitize_ldflag_value(self, file_svc): 'some.domain.net:8443 && test', 'domain-with-dash.net:8443; test', ] - for value in unsafe_server_values: + for value in unsafe_socket_values: with pytest.raises(Exception) as e_info: - file_svc.sanitize_ldflag_value('server', value) - assert str(e_info.value) == 'Invalid characters in server LDFLAG value: {}'.format(value) - - with pytest.raises(Exception) as e_info: - file_svc.sanitize_ldflag_value('http', value) - assert str(e_info.value) == 'Invalid characters in http LDFLAG value: {}'.format(value) + file_svc.sanitize_ldflag_value('socket', value) + assert str(e_info.value) == 'Invalid characters in socket LDFLAG value: {}'.format(value) @staticmethod def _test_download_file_with_encoding(event_loop, file_svc, data_svc, encoding, original_content, encoded_content):