Last active
August 29, 2015 14:00
Revisions
-
emboss revised this gist
May 1, 2014 . 1 changed file with 2 additions and 2 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -166,5 +166,5 @@ spoof = OpenSSL::PKey::RSA.new(2048) cert = OpenSSL::X509::Certificate.new(File.read("some_certificate.cer")) resigned = cert.sign(spoof, OpenSSL::Digest::SHA1.new) # original certificate, resigned with new RSA key puts "Resigned certificate verifies with the new key? #{resigned.verify(key)}" # true and expected -
emboss revised this gist
May 1, 2014 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -15,7 +15,7 @@ # The following lines are redundant. What this code does is it # creates a copy of the original certificate. # 'ef.issuer_certificate' is what's going to be signed below, # it would be equivalent to simply sign 'certificate' instead. # IMHO it doesn't matter if the certificate is changed or not # in any way for what is to follow. -
emboss revised this gist
May 1, 2014 . 1 changed file with 8 additions and 6 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -75,24 +75,26 @@ # This prints 'true', but this is expected behavior. We resigned the certificate # with the new key, and all that X509Certificate#verify does is to check whether # a certificate's signature is valid given a certain private key. It *does not* # check whether the private key actually matches the public key information which # is part of the certificate itself. # While it certainly is surprising at first that X509Certificate#verify reports # "true" in this case, this is still correct behavior. Since we resigned the # certificate, it faithfully reports that the signature is correct. While the # signature value matches, it is true that the public key referenced in the # certificate and the private key used for issuing the new signature do not. But # this is not a security-critical issue IMO. A certificate resigned this way # cannot be used for anything meaningful unless you tricked somebody into explicitly # trusting it. But such an attack is clearly out of scope concerning the # responsibilities of Ruby OpenSSL as a library. # In fact, OpenSSL itself doesn't check whether keys match for root certificates. # We trust root certificates merely by possession (i.e. they are part of our # preconfigured 'trust stores'). # Everybody could generate a valid self-signed root # certifcate, whose data may even match exactly that of a real CA root certificate. # But they are not included in any trust store (browser, OS, etc.) and thus useless # unless you explicitly/actively *want to* trust such a certificate. # If we spoofed an intermediate or end entity certificate in this way, it wouldn't # help either. Because the signature would no longer be accepted during the PKIX # validation process (where the signature of the spoofed certificate would be checked # against the public key of the supposed issuing certificate and consequently fail). puts root.verify(key) -
emboss revised this gist
May 1, 2014 . 1 changed file with 2 additions and 1 deletion.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -133,7 +133,8 @@ file.syswrite("#{cert.to_der}") printf "Check spoof.cer" # keys.rb: Proof that mixing the original certificate public key with a newly generated RSA # key in one file actually yields only the new RSA key when reloaded. Nothing shady happening. ##################################################################################### require 'openssl' -
emboss revised this gist
May 1, 2014 . 1 changed file with 4 additions and 0 deletions.There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,4 +1,5 @@ # Original castealer.rb with analysis ##################################################################################### require 'rubygems' #redundant require 'openssl' @@ -97,6 +98,7 @@ puts root.verify(key) # Second, simplified PoC with analysis ##################################################################################### require 'openssl' raw = File.read "root.cer" b = OpenSSL::PKey::RSA.new File.read 'root.key' @@ -132,6 +134,7 @@ printf "Check spoof.cer" # keys.rb (proving that reloading the "spoofed" key file loads the original key) ##################################################################################### require 'openssl' @@ -154,6 +157,7 @@ puts "spoof_key == new_key: #{spoof_key.to_der == new_key.to_der}" #true # Simplified castealer.rb - IMHO, this covers all the important aspects of both PoC scripts ##################################################################################### require 'openssl' -
emboss renamed this gist
May 1, 2014 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
emboss created this gist
May 1, 2014 .There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,163 @@ # Original castealer.rb with analysis require 'rubygems' #redundant require 'openssl' require 'digest/md5' #redundant key = OpenSSL::PKey::RSA.new(2048) cipher = OpenSSL::Cipher::AES.new(256, :CBC) #redundant ctx = OpenSSL::SSL::SSLContext.new #redundant puts "Spoof must be in DER format and saved as root.cer" raw = File.read "root.cer" cert = OpenSSL::X509::Certificate.new raw # The following lines are redundant. What this code does is it # creates a copy of the original certificate. # ef.issuer_certificate' is what's going to be signed below, # it would be equivalent to simply sign 'certificate' instead. # IMHO it doesn't matter if the certificate is changed or not # in any way for what is to follow. cert.version = 2 ef = OpenSSL::X509::ExtensionFactory.new ef.issuer_certificate = OpenSSL::X509::Certificate.new raw cert.subject = ef.issuer_certificate.subject ef.subject_certificate = ef.issuer_certificate cert.issuer = ef.issuer_certificate.issuer cert.serial = ef.issuer_certificate.serial ctx.key = ef.issuer_certificate.public_key cert.public_key = ef.issuer_certificate.public_key cert.not_after = ef.issuer_certificate.not_after cert.not_before = ef.issuer_certificate.not_before cert.extensions = ef.issuer_certificate.extensions # Note that two distinct keys are written to the file 'root.key'. # The first one is the original certificate's public key, the # second one is the public/private key pair of the newly created # RSA key. The resulting file looks like this: # #-----BEGIN PUBLIC KEY----- #<original certificate's public key> #-----END PUBLIC KEY----- #-----BEGIN RSA PRIVATE KEY----- #<public private key/pair of new RSA key> #-----END RSA PRIVATE KEY----- # # 'spoof' and 'key' both represent the same key a = File.open("root"".key", "w") a.syswrite("#{cert.public_key}") # 'syswrite' or normal 'write', no difference a.syswrite("#{key.to_pem}") # 'syswrite' or normal 'write', no difference # This simply reloads the new RSA key, cf. 'keys.rb' for proof. # The certificate's original public key is simply ignored. spoof = OpenSSL::PKey::RSA.new File.read 'root.key' printf "Verifying Keys Work: " puts spoof.private? # true (it's the new key, after all) ctx.cert = ef.issuer_certificate #redundant puts "=============================================================" # This effectively resigns the original certificate with the new key root = ef.issuer_certificate.sign(spoof, OpenSSL::Digest::SHA1.new) # Not important, just saving objects involved in files filer = File.open("#{cert.serial}"".key", "w") filer.syswrite("#{spoof.to_pem}") file = File.open("spoof"".cer", "w") file.syswrite("#{cert.to_der}") files = File.open("#{cert.serial}"".pem", "w") files.syswrite("#{cert.to_pem}") files.syswrite("#{spoof.to_pem}") puts "Hijacked Certificate with chainloaded key saved @ #{cert.serial}.pem" printf "Verifying Keys Intergity: " # This prints 'true', but this is expected behavior. We resigned the certificate # with the new key, and all that X509Certificate#verify does is to check whether # a certificate's signature is valid given a certain private key. It *does not* # check whether the private key actually matches the public key which is part of # the certificate. # While it certainly is surprising at first that X509Certificate#verify reports # "true" in this case, this is still correct behavior. Since we resigned the # certificate, it faithfully reports that the signature is correct. While the # signature value matches, it is true that the public and private key do not. But # this is not a security-critical issue IMO. A certificate resigned this way # cannot be used for anything meaningful unless you tricked somebody into explicitly # trusting it. # In fact, OpenSSL itself doesn't check whether keys match for root certificates. # We trust root certificates merely by possession (i.e. they are part of our # preconfigured 'trust stores'). # Everybody could generate a valid self-signed root # certifcate, whose data may even match exactly that of a real CA root certificate. # But they are not included in any trust store (browser, OS, etc.) and thus useless # unless you explicitly/actively want to trust such a certificate. # If we spoofed an intermediate or end entity certificate in this way, it wouldn't # help either because the signature would no longer be accepted during the PKIX # validation process (where the signature of the spoofed certificate would be checked # against the public key of the supposed issuing certificate and consequently fail). puts root.verify(key) # Second, simplified PoC with analysis require 'openssl' raw = File.read "root.cer" b = OpenSSL::PKey::RSA.new File.read 'root.key' cert = OpenSSL::X509::Certificate.new raw # The following lines are redundant regarding the final # outcome issuer_certificate = OpenSSL::X509::Certificate.new raw cert.subject = issuer_certificate.subject subject_certificate = issuer_certificate cert.issuer = issuer_certificate.issuer cert.serial = issuer_certificate.serial cert.public_key = issuer_certificate.public_key cert.not_after = issuer_certificate.not_after cert.not_before = issuer_certificate.not_before cert.extensions = issuer_certificate.extensions # Again, this just reloads 'b', as in the original PoC. # Cf. 'keys.rb' for proof. a = File.open("spoof"".key", "w") a.syswrite("#{cert.public_key}") a.syswrite("#{b.to_pem}") # 'spoof' is the same as 'b' spoof = OpenSSL::PKey::RSA.new File.read 'spoof.key' # The certificate is resigned, so the same considerations # apply as for the original PoC. root = cert.sign(spoof, OpenSSL::Digest::SHA1.new) file = File.open("spoof"".cer", "w") file.syswrite("#{root.to_der}") file.syswrite("#{cert.to_der}") printf "Check spoof.cer" # keys.rb (proving that reloading the "spoofed" key file loads the original key) require 'openssl' raw = File.read 'root.pem' new_key = OpenSSL::PKey::RSA.new(2048) cert = OpenSSL::X509::Certificate.new raw public_key = cert.public_key File.open('spoof.key', 'w') do |f| f.write(cert.public_key) f.write(new_key.to_pem) end spoof_key = OpenSSL::PKey::RSA.new(File.read('spoof.key')) puts "cert.public_key == public_key: #{cert.public_key.to_der == public_key.to_der}" # true puts "spoof_key.public_key == public_key: #{spoof_key.public_key.to_der == public_key.to_der}" # false puts "spoof_key.public_key == new_key.public_key: #{spoof_key.public_key.to_der == new_key.public_key.to_der}" #true puts "spoof_key == new_key: #{spoof_key.to_der == new_key.to_der}" #true # Simplified castealer.rb - IMHO, this covers all the important aspects of both PoC scripts require 'openssl' spoof = OpenSSL::PKey::RSA.new(2048) cert = OpenSSL::X509::Certificate.new(File.read("some_certificate.cer")) root = cert.sign(spoof, OpenSSL::Digest::SHA1.new) # original certificate, resigned with new RSA key puts "New Root certificate verifies with the new key? #{root.verify(key)}" # true and expected