Detecting the Lets Encrypt CAA Bug_Part 2
This is a continuation and the last part my research on the Lets CAA Bug. You can read Part 1 here.
Why am doing this?
- The best way to learn is to research. At some point in my career, my focus will be on the research of threat actors and infosec issues, i figure i need to start from somewhere, right? Kevin Beaumont inspired a huge chunk of this.
- In my part of the world, most system admins are left to clean up the ITSecurity mess. They are expected to be aware of the mess and how to clean it. These mess are usually caused by a mixture of hired fix-it guys, contractors, vendors who’s mandate is done once the project is closed or paid for. Some organizations arent aware that the’re on LE certificates. Most of these admins lack adequate skills to do an effective job. By sharing my work as others before me have done, those we expect to make the net a better place have a chance and a place to learn from.
- I wanted to identify the domains affected in my country, share it with the respective owners or regulatory body for actioning. Check my GDrive for the Kenyan data-set.
So the CAA Bug…
I had two issues to solve:
- Get a list of (Affected) Domains.
You dont need a list of affected domains. I needed the list to validate my script.
- Figure out How to detect whether a domain is affected or not.
Getting the list of Affected Domains
After the bug was publicly published, i was scratching my head on how i could scan the whole internet to identify any services affected by this(almost missed by bus). I had not done this before to a global scale so i had to learn and remind myself a few things.
The first options that came to mind were Shodan then followed by Masscan. Shodan is a web based service that scans all things connected to the internet. Masscan is a an internet-scale port scanner that scans the entire Internet in under 6 minutes, transmitting 10 million packets per second, from a single machine.
I can do basic Shodan searches but i had never tried to use any queries related to TLS or SSL. Apart from the learning curve, Shodan might miss a few services so it was put on hold. The next go to was masscan. Daniel Miessler has a good tutorial on how to use masscan. Long story short, the two are now on my To-Do list.
While trying to figure this out, i came across:
Basically Scott Helme who by the way has some good projects, had the data i needed. He had gone further to even group the result by ccTLDs. And with that list, my first tasks was complete.
I know Lets Encrypt availed a list of affected certificates that can be used for this. I needed something that wasnt static. Check the work done by Hanno Böck here. for more on this.
Detecting whether a domain is vulnerable
With the data-set available, it was time to script this: and also learn Python 3. I used pycharm as my IDE.
Based on the works by others in the community, i created a script in Python 3 that checks whether any supplied domain via stdin or a text file is affected. The script can be found on my github.
#!/usr/bin/python3import os
import sys
import requests
import rebaseurl='https://unboundtest.com/caaproblem/checkhost'
#postdata={'fqdn':'cpcontacts.eayp.or.ke'}
f = open("scanresult.txt", "w")
#filepath = sys.argv[1]def main():
userop=input("What scan do you want to run?\nEnter 1 if you wish to scan a single domain: \nEnter 2 if you wish"
" to scan multiple domains from a file: ")
if userop == "1":
userdomain=input("Enter the domain to be checked in FQDN format:\n")
postdata = {'fqdn': userdomain.strip()}
checkvuln(postdata,userdomain)
elif userop == "2":
filepath_zz=input("Enter the file path with the list of domain to be checked:\n")
autoscan(filepath_zz)
else:
print("Some error broke this code")
returndef checkloop():
userop=input("Do you wish to run another check? If Yes, Enter 1 else press any other key: ")if userop =="1":
main();
else:
sys.exit()def autoscan (filepath):if not os.path.isfile(filepath):
print("File path {} does not exist. Exiting...".format(filepath))
else:
with open(filepath) as fp:
url=fp.readline()
while url:
postdata={'fqdn':url.strip()}
#print(postdata)
checkvuln(postdata,url)
url=fp.readline()
f.close()
cleanfile()
returndef cleanfile():
with open("scanresult.txt", 'r') as inp, open('cleanedscanresult.txt', 'w') as out:
for line in inp:
if re.search('\S', line):
out.write(line)
print("Check Completed and .txt output files saved in current working directory")
returndef checkvuln(postdata,url):
r = requests.post(baseurl, postdata)
response=r.text
if r.status_code ==200 and "unknown: dial tcp" in response:
print("{} cant be reached. Site might be down or domain cant be resolved".format(url.strip()))
f.write("{} cant be reached. Site might be down Site might be down or domain cant be resolved.\n".format(url.strip()))
elif r.status_code ==200 and "unknown: x509: certificate" in response:
print("{} check is unknown. Might be requiring basic auth".format(url.strip()))
f.write("{} check is unknown. Might be requiring basic auth\n".format(url.strip()))
elif r.status_code ==200 and "needs renewal" in response:
print("{} is Vulnerable".format(url.strip()))
f.write("{} is Vulnerable\n".format(url.strip()))
elif r.status_code ==200 and "It is not one of the certificates affected by the Let's Encrypt CAA rechecking problem" in response:
print("{} is Not vulnerable".format(url.strip()))
f.write("{} is Not Vulnerable\n".format(url.strip()))
else:
print("{} state cant not be defined.Unhandled exception".format(url.strip()))
f.write("{} state cant be defined. Unhandled Exception {}\n".format(url.strip(),response))
# f.close()
return#urlinput('')
if __name__ == '__main__':
main()
checkloop()
I tried to make the script very user friendly for the non technical users.
I am not a professional python developer so the script works but might need improvements. I tried to make it as secure and efficient as possible to the best of my abilities.
The scripts takes a single domain or a list of domains as input. The output is saved to a file.
Below are the stats for the .ke county top level domain. I have uploaded the list on the GDrive for anyone in need of the dataset.
The original data set had around 4674 unique domains and sub-domains. Out of this, 1,414(30%) seem to have fixed the issue.
6519 total
3260 vulnerable.txt
3061 vulnerable_co_ke.txt
77 vulnerable_or_ke.txt
45 vulnerable_ac_ke.txt
29 vulnerable_schools.txt
25 vulnerable_sacco.txt
9 vulnerable_hospital.txt
8 vulnerable_go_ke.txt
5 vulnerable_bank.txt
The following guides offer(ed) valuable insight and guided me with the above script
- NixCraft posted a guide on how to detect here. This was the basis of my initial script. The challenge with this guide was that it couldn't not scale. It could check a single domain but checking thousands of domain wasn't feasible.
- Hanno Böck posted his guide on how to detect here. Its a better improvement as it allows you to check a single domain or a list of domains and didnt require internet access after initial setup. My challenge with this guide was that is needed you to download the list of affected certificates and wasnt very user friendly to a noob.