Scanning and Reporting on SSL Cert Expiry Dates – an SSL Certificate Scanner using bash, php and jQuery

[(5) Comments]

A while ago I cooked up a bash script to scan relevant internal subnets for ssl certs, save/parse a copy of the x509 data and list all the discovered info in a delimited text file for analysis in a spreadsheet.

This works well by itself, but for the convenience of quick lookups without involving excel or libreoffice, a web page can be useful. PHP provides a simple method for converting a delimited file into a table (fgetcsv() ), and jQuery has a great plugin called tablesorter which allows you to do some quick sorting and filtering right there in the browser. It didnt take long to mash these together into a one script web page to display the sortable certificate data at a glance.

Sample screenshot:

Screenshot sample of scancerts

The sample only shows the three dummy values I’ve included in the demo, but I’ve used this in production with 600+ scanned certs and it works well.

Scancerts has two main components:

  1. Bash script which eats a text file containing a list of networks to scan, uses openssl, sed, awk, grep, cut, etc to generate another text file containing a delimited list of discovered certs.
  2. PHP script which turns the delimited text file into a HTML table, and augments it with some jQuery so your browser can sort and filter the HTML table on the fly.

Installation Steps

  1. Create a web-accessible folder on your linux box
  2. Unpack the files in the provided archive to the web folder
  3. Make sure file/folder permissions are set correctly (and you can run PHP!)
  4. Add the subnets you want to scan into ‘subnets.txt’
  5. Make ‘scancerts’ executable
  6. Run scancerts and optionally add it to cron
  7. View a nice sortable html list of discovered certs

Download: scancerts_v0.1.tar.gz

Simple BASH script to mass query reverse DNS PTR records for a subnet

[No Comments]

Wrote this up the other day to do a quick reverse PTR query of some internal subnets. Possibly handy if you don’t have quick access to another tool like dnsrecon.

#!/bin/bash

#Populate subnets.txt with the first three sections of the target subnet IP, one subnet per line eg
#192.168.1
#192.168.20
#etc

subnets=$(cat './subnets.txt');
for subnet in $subnets
    do
        for ip in $subnet.{1..255}
        do
            dig -x $ip | sed -n '/ANSWER SECTION:/,/Query time:/p' | sed '/ANSWER SECTION/d' | sed '/Query time:/d' | sed '/./!d'
#echo $ip

    done
done

I’ve since found the useful python based dnsrecon which seems to work really well for this also:

./dnsrecon.py -t rvl -r 192.168.0.1/24

Checking SSL certificate expiry date and issuer: an openssl wrapper in BASH

[(1) Comments]

I manage SSL certificate requests (and renewals) at work and the number of certs in use seems to be growing every year. There’s a nice cozy 3 year expiry on most of them which means to me just enough time for them to be potentially forgotten and cause a mad scurry come renewal time (or worse, have them expire and have a service outage as a result).

Our CA has a recently unveiled service which will scan our public IPS and report on detected certificates close to expiry which is handy, but we have a lot of servers on non-routable and / or firewalled addresses, so an internal scan is the only way to cover them all.

There seem to be at least a couple of other published approaches in the google including ssl certificate expiration check and the neat ssl-cert-check script at prefetch.net which will take a list of servers and express the expiry date. I wanted something slightly different (more minimalist): as we’re using the excellent zabbix for general system monitoring which especially likes system commands or scripts which take a single parameter and spit out a single return value.

In this case that parameter is a server name or IP address,  returning  either the number of days until SSL cert expiry, or the certificate issuer, depending on which version of the script is called. As I said, we’re using this with zabbix but this is just a command line script usable for quick on the spot checks or could easily be incorporated into another monitoring / alerting system.

A nice simple method I would probably use if I didn’t have zabbix would be to incorporate this into a quick and dirty loop script which periodically queries a list of ips or subnets and fires off an email if the ‘days to expiry’  is below a certain value (exactly what zabbix does now). It could be made a bit more elegant perhaps by using a bit of nmap to build a fast list of responding servers to query, but depends what you want, how much time you have, and how much of a stickler you are for efficiency =)

(Command line for all scripts is simply:

./script-name server.name.or.ip.address

)

Script: sslcheck-expiry

#!/bin/bash
# Simple SSL cert days-till-expiry check script
# by Glen Scott, www.glenscott.net

openssl_output=$(echo "
GET / HTTP/1.0
EOT"
\
 | openssl s_client -connect $1:443 2>&1);

if [[ "$openssl_output" = *"-----BEGIN CERTIFICATE-----"* ]]; then

        cert_expiry_date=$(echo "$openssl_output" \
         | sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' \
         | openssl x509 -enddate \
         | awk -F= ' /notAfter/ { printf("%s\n",$NF); } ');

        seconds_until_expiry=$(echo "$(date --date="$cert_expiry_date" +%s) - $(date +%s)" |bc);
        days_until_expiry=$(echo "$seconds_until_expiry/(60*60*24)" |bc);

        if [[ $days_until_expiry -ge 0 ]]; then

                echo "$days_until_expiry";
                exit 0

        else

                echo "EXPIRED ($days_until_expiry days)";

                exit 0
        fi

else
    echo "NOT_FOUND";
exit 1

Checking the issuer as well as the expiry

Because we have a bunch of servers setup for dev, test or other purposes with self signed “snake oil” certs and I dont really care about the certs on those, I wanted a method to determine the issuer. (Zabbix then has some logic which only bothers to email us if a cert is about to expire AND is from a real CA.)

Script: sslcheck-issuer-o

Returns the “O” (Organisation) value from the issuer string.

#!/bin/bash
# Simple SSL cert get-issuer-O
# by Glen Scott, www.glenscott.net

openssl_output=$(echo "
GET / HTTP/1.0
EOT"
\
 | openssl s_client -connect $1:443 2>&1);

if [[ "$openssl_output" = *"-----BEGIN CERTIFICATE-----"* ]]; then

        cert_issuer=$(echo "$openssl_output" \
         | sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' \
         | openssl x509 -noout -issuer -nameopt sname \
         | tr '/' '\n' | grep O= | cut -c3- );

                echo "$cert_issuer";
                exit 0
else
    echo "NOT_FOUND";
exit 1
fi

Script: sslcheck-issuer-cn

If for some reason you want the CN value from the issuer string, use this instead.

#!/bin/bash
# Simple SSL cert get-issuer-CN
# by Glen Scott, www.glenscott.net

openssl_output=$(echo "
GET / HTTP/1.0
EOT"
\
 | openssl s_client -connect $1:443 2>&1);

if [[ "$openssl_output" = *"-----BEGIN CERTIFICATE-----"* ]]; then

        cert_issuer=$(echo "$openssl_output" \
         | sed -n '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/p' \
         | openssl x509 -noout -issuer -nameopt sname \
         | tr '/' '\n' | grep CN= | cut -c4- );

                echo "$cert_issuer";
                exit 0

else
    echo "NOT_FOUND";
exit 1
fi

Additional notes:

All scripts use the openssl s_client function to connect to the first string (assuming IP or server name) on default port 443. It echoes some HTTP GET requests and an EOT, otherwise openssl will sit there until timeout waiting for something to happen. If the remote connection sends a certificate down the pipe (identified by the presence of “”—–BEGIN CERTIFICATE—–“) it will process it, otherwise it returns the value ‘NOT FOUND’, which is a catch all for no certificate, a malformed certificate, a network timeout and so on.

A quick glance at these will reveal a lot of duplication: indeed the last two only differ by one line. This is on purpose; I actually started out building a do-everything script with a switch to determine behavior (like this script I subsequently found). This would be a lot more efficient in terms of network requests, you could retrieve the cert once and analyse it in multiple ways. Unfortunately it turns out zabbix really only likes dealing with the one parameter – a minor issue for which I will forgive it – so I’ve broken this out into three smaller scripts.

OpenSSL will only return the date in a format like “Sat Jan 1 17:15:00 WAST 2010”. I was starting to figure out how to chop it up into a usable format like DDMMYYYY using sed, awk, cut and the rest but discovered  to my surprise that the unix date utility understands the string just fine as is. The date is converted to seconds since epoch and the ‘bc’ utility does some math on it, returning a rounded value in days.

Instead of awk in the last two I’ve used a handy-dandy utility called, simply, ‘text replace’ (tr). I actually don’t use awk, sed and regular expressions much and am correspondingly unfamiliar with the syntax, thus was happy to discover and use this nifty shortcut. It’s not as powerful as sed/awk but is great for a simple character replace like this, especially when the process of trying to manipulate both kinds of slashes in the issuer string and replace them with newlines

'\n'

is frying my brain.

As always, your mileage may vary, particularly regarding any differences in the basic syntax of the various utilities across platforms (I’m using RHEL). ‘date’ for example is very forgiving here, this might not be the same everywhere. I’ve also had issues in the past with the syntax of SED on OSX.

Hope someone finds this helpful, and I will write up a brief post on using zabbix to manage SSL cert expiry at some point as well.

Except where specified otherwise, the content of this site is licensed under a
Creative Commons Attribution-Noncommercial-No Derivative Works 3.0 License.