Certainly is a offensive security toolkit to capture large amounts of traffic in various network protocols in bitflip and typosquat scenarios. The tool was built to support research on these topics, originally presented at BlackHat USA 2024.
Built-in protocols
- DNS
- HTTP(S)
- IMAP(S)
- SMTP(S)
The core functionality of certainly revolves around the DNS server. It is designed to act as the authoritative name server for a number of apex domains, answering to any DNS questions around the zones with a response pointing to its own IP address while logging any and all requests coming its way. Certainly is trying to play nice and to not to break anything longer than necessary, because of this reason all the answers have TTL of one second.
When certainly receives a HTTP request, it first checks if the requested resource is something that it should apply an injection template on. If so, certainly will copy the request headers to a new proxied request towards the upstream (legit, non-bitflippped / non-typosquatted) domain, copy the response headers to the response sent to the victim while replacing the response body according to the template rules.
If injection template is not configured for the resource, certainly will instead respond with HTTP 307 (Temporary redirect) to the upstream target with the full request URI intact.
HTTPS works similarly to HTTP, but in case certainly doesn't have a valid TLS certificate to present to the client, it will hold the TCP connection after the ClientHello in TLS handshake while fetching the certificate client requested in the TLS SNI. Certainly will try to priorize getting wildcard certificates for all subdomains in order to not to exhaust CA limits as well as to speed up the transaction when receiving a new connection. This behavior is very important as especially in the bitflips the targeted subdomains are far and between and not doing so will risk losing valuable data.
Certainly will initiate the authentication sequence and log the user credentials as well as potential shared secret in case of CRAM-MD5, after which it will disconnect the user.
Certainly will kindly accept all email sent towards it and proceed delivering it to the log files.
- Full authoritative DNS server support. Just point the nameserver addresses at your domain registrar of choice towards Certainly instance.
- CNAMEs to randomly generated UUID subdomains of the configured "main domain" in order to be able to track client behavior per-requester basis. This is omitted for CNAME requests against the "main domain" subdomains in order to prevent infinite loops. A record answers for these CNAMEs are also appended to the answer to lower the necessary network traffic.
- DNS based ACME challenge solver to support wildcard TLS certificate generation.
- Custom DNS records to present
- Configurable protocol(s) to listen; udp, tcp or both
- Holding the TLS handshake in ClientHello phase while fetching the certificate to present in the background. This typically takes under 5 seconds.
- Optional upstream check for existence of a domain record before answering. If the upstream (sub)domain doesn't exist, certainly will proceed answering with NXDOMAIN as well.
- Injection templating based on request uri regexes. Templates have couple of keyword variables that will be replaced: CERTAINLY_UPSTREAM that will be replaced by the full response body of the upstream request, and CERTAINLY_HASH that will be replaced by a UUID generated for the orignal connection.
- Injection template filtering by a list of regexes. There's a lot of noise in the web today, and we saw a lot of random sweep scans hitting us with predetermined paths that we're better off by just ignoring.
- A custom route for
/callback/*
that will just simply answer with204 No Content
instead of the default behavior of doing a temporary redirect. This is to catch and log potential callbacks from injected JavaScript resources without disturbing the intended behavior of the web application too much.
- Default output format of JSONLines to feed in to your data analysis platform; ELK, Splunk, mad grep oneliners; whatever your prefer.
- Extensible notification framework for sending automated notifications. Currently only supports Slack.
For this documentation we're using /path/to/install/certainly
as the example installation directory.
Create the application and the injection template directories
sudo mkdir -p /path/to/install/certainly/templates
Set up a system user and group to run Certainly
sudo useradd --system --user-group certainly
Set the ownership and permissions for the user to access the filesystem path.
sudo chown -R certainly:certainly /path/to/install/certainly
sudo chmod -R ug+w /path/to/install/certainly
Certainly can be fetched by either go install
or downloading and unarchiving a pre-built binary to your file system location of choice.
go install github.com/happycakefriends/certainly@latest
Move the certainly
binary to the installation path (not necessary, but streamlines this documentation). The installation directory for Go binaries can vary based on your configuration, but the default directory location is $HOME/go/bin/
When using this method, you will also need to download the latest config.cfg
manually:
curl -L https://raw.githubusercontent.com/happycakefriends/certainly/main/config.cfg -o /path/to/install/certainly/config.cfg
Head over to releases page to download the latest pre-built release for your platform of choice.
Unarchive the contents of the release archive to /path/to/install/certainly
Open /path/to/install/certainly/config.cfg
in your favorite text editor and change the configuration values according to your needs.
Allow certainly to bind to necessary ports
sudo setcap 'cap_net_bind_service=+ep' /path/to/install/certainly/certainly
sudo touch /etc/systemd/system/certainly.service
sudo chmod 644 /etc/systemd/system/certainly.service
Open the newly created systemd service definition file with your favorite text editor and change the paths accordingly.
[Unit]
Description=Certainly
StartLimitIntervalSec=30
StartLimitBurst=5
After=syslog.target network.target
[Service]
Type=simple
WorkingDirectory=/path/to/install/certainly
User=certainly
ExecStart=/path/to/install/certainly/certainly
TimeoutStartSec=30
Restart=on-failure
[Install]
WantedBy=multi-user.target
Reload the service configurations
sudo systemctl daemon-reload
Start the service
sudo systemctl start certainly
(Optionally) set it up to automatically start when OS is booted
sudo systemctl enable certainly
-
Choose and Configure a master domains for DNS Management
Begin by selecting a master domain, which we'll refer to as
grandma.tld
, and configure it to be hosted by your DNS provider. Create and pointns1.grandma.tld
andns2.grandma.tld
to the IP address of your VPS (Virtual Private Server) that is going to host Certainly. This setup allows you to easily manage DNS records—if the IP address changes, you only need to update these two records. -
Select a Primary Domain for Your Setup
Choose a
default_domain
for your configuration, referred to here as the Mother Domain. This domain will serve as the default domain for all DNS responses. So it should be a domain you own and can control. We'll call itmom.tld
. Since this is the domain that will be used with a uuid for any CNAME lookup performed on any of the bitflip/typo domains, it might be a good idea to have a domain name thats similar to you targets domain. or if your runnings loots of different target domains on the same box, use one that has no connection to the targets, being sneaky is a win here.Update your configuration file with the following:
- Add the IP address of your VPS. to the config using
ip=1.2.3.4
- Set the Mother Domain (
example mom.tld
) as thedefault_domain=
- Include any custom DNS entries. (Certainly in a fully functional Nameserver so add whatever you like here, maybe a custom txt record to indicate that you are a good person?)
- Specify the domains you intend to point to the vps to capture and respond to in the
[ns] domains
section. As seen in the example below.
- Add the IP address of your VPS. to the config using
-
Point Domains to Your Nameservers
After updating the configuration file and ensuring your VPS is operational, change the nameserver of the domains you want to manage to
ns1.grandma.tld
andns2.grandma.tld
using your DNS provider. This action ensures that all DNS requests for these domains are captured and managed by the custom DNS server in your certainly setup.
Here are some guides from popular domains providers as a inspiration how to perform this step:
https://docs.gandi.net/en/domain_names/common_operations/changing_nameservers.html#nameservers
https://www.godaddy.com/help/edit-my-domain-nameservers-664
-
Configure Rewrites and TLS Settings
To keep stealth, provide a seamless experience for any connecting clients and to utilize Certainly's injection capabilities, you should configure the
[rewrites]
section of your configuration file. Here’s what you need to do:-
Add Rewrite Rules: Specify domain matching pairs. Example
"coogle.com" = "google.com"
as seen in this example, any connection to the hosted flip domain (coogle.com
) will be redirected using a 307 to the real (google.com
). Since we are delivering the client to the real domain there isnt any Denial of Service event happening and the connecting client will happily continue its flow. -
Enable Stealth Mode (Optional): If you prefer to operate in stealth mode, set
tls_upstream_check = true
. This setting forces Certainly to before issuing any certificates or responding to any DNS request to verify the existence of the matching target subdomain before responding to requests. -
Set Up TLS Filters: Use the
tls_filters
setting to drop any incoming TLS requests that match any predefined regex patterns. This is particularly useful for filtering out noise and whilefocusing on specific subdomains. For example: maybe you only want to catch some sweet api requests and dont care about common fuzz domains or www, then a simple filter like this might be in handy:
-
tls_filters = [
'www|ns1|ns2|hostmaster|mail|owa|ssl|webmail|smtp'
]
Resulting in a dropped client connection and a logged entry "msg":"http: TLS handshake error from 1.2.3.4: certificate is not allowed for server name blahwww.subdomain.target.tld: decision func: not allowed due to tls filter configuration"}
##Enable Stealth Mode (Optional)
Note: Enabling the tls_upstream_check = true
feature may cause you to miss some subdomain flips or typo hits. For example login.target.tld
might exists upstream, while lkgin.target.tld
most likely does not.
Client performs a host lookup:
Host mx.coogle.com
Certainly performs:
Host mx.google.com and receives "not found: 3 (NXDOMAIN)"
Since the upstream subdomain doesn't exist, Certainly will drop the session.
Example of tls_upstream_check = true
feature with a Valid Upstream Domain:
Client performs a lookup for:
Host mail.coogle.com
Certainly performs:
Host mail.google.com and receives "mail.google.com has address 142.250.74.101"
Since the upstream domain exists, a response will be delivered to the client using Certainly's IP.
If an HTTP/S TCP connection is made, Certainly will hold the session, generate a wildcard certificate for `*.coogle.com`, and release the session once the certificate is in place.
To sum the features and getting started settings, here is a example of a config file, where we used real world domains for the ease of understanding, and has nothing to do with the real targets used.
[General]
ip = "1.2.3.4"
tls_upstream_check = false
tls_filters = [
'ns1|ns2|hostmaster|mail|owa|ssl|webmail|smtp',
# 'www\.',
"third_regex"
]
[ns]
port = "53"
protocol = "both4"
default_domain = "mom.tld"
domains = [
"mom.tld",
"c oogle.com",
"woogle.com",
"amazkn.com",
"gordpress.com",
]
nsname = "ns.mom.tld"
nsadmin = "admin.mom.tld"
records = [
# Any static records go here in zone file format
"ns.mom.tld. A 1.2.3.4",
"ns.mom.tld. NS ns.mom.tld.",
]
[rewrites]
"coogle.com" = "google.com"
"woogle.com" = "google.com"
"amazkn.com" = "amazon.com"
"gordpress.com" = "wordpress.com"