{"id":37168695,"url":"https://github.com/happycakefriends/certainly","last_synced_at":"2026-01-14T19:56:04.307Z","repository":{"id":252291253,"uuid":"791351834","full_name":"happycakefriends/certainly","owner":"happycakefriends","description":"Certainly is a offensive security toolkit to capture large amounts of traffic in various network protocols in bitflip and typosquat scenarios.","archived":false,"fork":false,"pushed_at":"2024-08-14T11:09:45.000Z","size":72,"stargazers_count":65,"open_issues_count":0,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-08-14T12:37:55.437Z","etag":null,"topics":["bitflip","dns-server","http-server","imap-server","smtp-server","typosquatting"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/happycakefriends.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-04-24T15:03:59.000Z","updated_at":"2024-08-14T11:09:49.000Z","dependencies_parsed_at":"2024-08-14T12:48:52.640Z","dependency_job_id":null,"html_url":"https://github.com/happycakefriends/certainly","commit_stats":null,"previous_names":["happycakefriends/certainly"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/happycakefriends/certainly","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/happycakefriends%2Fcertainly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/happycakefriends%2Fcertainly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/happycakefriends%2Fcertainly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/happycakefriends%2Fcertainly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/happycakefriends","download_url":"https://codeload.github.com/happycakefriends/certainly/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/happycakefriends%2Fcertainly/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28433616,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-14T18:57:19.464Z","status":"ssl_error","status_checked_at":"2026-01-14T18:52:48.501Z","response_time":107,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bitflip","dns-server","http-server","imap-server","smtp-server","typosquatting"],"created_at":"2026-01-14T19:56:03.619Z","updated_at":"2026-01-14T19:56:04.291Z","avatar_url":"https://github.com/happycakefriends.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/user-attachments/assets/a5f90a3f-f0cf-4200-a671-d35a23abbc6e\" width=\"200\" /\u003e\n\u003c/p\u003e\n\n# certainly\n\nCertainly 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](https://www.blackhat.com/us-24/briefings/schedule/index.html#flipping-bits-your-credentials-are-certainly-mine-40040). \n\n## How it works\n\n**Built-in protocols**\n - DNS\n - HTTP(S)\n - IMAP(S)\n - SMTP(S)\n\n### DNS\nThe 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.\n\n### HTTP\nWhen 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.\n\nIf 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.\n\n### HTTPS\nHTTPS 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.\n\n### IMAP(S)\nCertainly 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.\n\n### SMTP(S)\nCertainly will kindly accept all email sent towards it and proceed delivering it to the log files.\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/user-attachments/assets/d4466506-a0ba-48e2-b0ec-4aceb4422f8a\" width=\"200\" /\u003e\n\u003c/p\u003e\n\n## Core features\n\n### DNS\n- Full authoritative DNS server support. Just point the nameserver addresses at your domain registrar of choice towards Certainly instance.\n- 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.\n- DNS based ACME challenge solver to support wildcard TLS certificate generation.\n- Custom DNS records to present\n- Configurable protocol(s) to listen; udp, tcp or both\n\n### HTTPS\n- Holding the TLS handshake in ClientHello phase while fetching the certificate to present in the background. This typically takes under 5 seconds.\n- 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.\n- 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.\n- 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.\n- A custom route for `/callback/*` that will just simply answer with `204 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.\n\n### Output\n - Default output format of JSONLines to feed in to your data analysis platform; ELK, Splunk, mad grep oneliners; whatever your prefer.\n - Extensible notification framework for sending automated notifications. Currently only supports Slack.\n\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/user-attachments/assets/9600991b-644e-4bc3-a23f-2812d3f44dfe\" width=\"200\" /\u003e\n\u003c/p\u003e\n\n\n## Installation on Linux\nFor this documentation we're using `/path/to/install/certainly` as the example installation directory.\n\n### 1) Create a user and installation path for Certainly\nCreate the application and the injection template directories\n```\nsudo mkdir -p /path/to/install/certainly/templates\n```\nSet up a system user and group to run Certainly\n```\nsudo useradd --system --user-group certainly\n```\nSet the ownership and permissions for the user to access the filesystem path.\n```\nsudo chown -R certainly:certainly /path/to/install/certainly\nsudo chmod -R ug+w /path/to/install/certainly\n```\n\n### 2) Fetch the application and the example configuration\nCertainly can be fetched by either `go install` or downloading and unarchiving a pre-built binary to your file system location of choice.\n\n#### Using go install\n```\ngo install github.com/happycakefriends/certainly@latest\n```\n\nMove 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/`\n\nWhen using this method, you will also need to download the latest `config.cfg` manually:\n\n```\ncurl -L https://raw.githubusercontent.com/happycakefriends/certainly/main/config.cfg -o /path/to/install/certainly/config.cfg\n```\n#### Downloading a pre-built binary\nHead over to [releases page](https://github.com/happycakefriends/certainly/releases/latest) to download the latest pre-built release for your platform of choice.\n\nUnarchive the contents of the release archive to `/path/to/install/certainly`\n\n### 3) Edit the certainly configuration file\nOpen `/path/to/install/certainly/config.cfg` in your favorite text editor and change the configuration values according to your needs.\n\n### 4) Set capabilities to allow binding to privileged ports\nAllow certainly to bind to necessary ports\n```\nsudo setcap 'cap_net_bind_service=+ep' /path/to/install/certainly/certainly\n```\n\n### 5) Create a systemd service\n```\nsudo touch /etc/systemd/system/certainly.service\nsudo chmod 644 /etc/systemd/system/certainly.service\n```\n\nOpen the newly created systemd service definition file with your favorite text editor and change the paths accordingly.\n```\n[Unit]\nDescription=Certainly\nStartLimitIntervalSec=30\nStartLimitBurst=5\nAfter=syslog.target network.target\n\n[Service]\nType=simple\nWorkingDirectory=/path/to/install/certainly\nUser=certainly\nExecStart=/path/to/install/certainly/certainly\nTimeoutStartSec=30\nRestart=on-failure\n\n[Install]\nWantedBy=multi-user.target\n```\nReload the service configurations\n```\nsudo systemctl daemon-reload\n```\n\n### 6) Start the systemd service\nStart the service\n```\nsudo systemctl start certainly\n```\n(Optionally) set it up to automatically start when OS is booted\n```\nsudo systemctl enable certainly\n```\n\n### 7) Setting Up DNS Configuration with Certainly\n\n1. **Choose and Configure a master domains for DNS Management**\n    \n    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 point `ns1.grandma.tld` and `ns2.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.\n    \n2. **Select a Primary Domain for Your Setup**\n    \n    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 it `mom.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.\n    \n    Update your configuration file with the following:\n    \n    - Add the IP address of your VPS. to the config using `ip=1.2.3.4`\n    - Set the **Mother Domain** (`example mom.tld`) as the `default_domain=`\n    - 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?)\n    - 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.\n      \n3. **Point Domains to Your Nameservers**\n    \n    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` and `ns2.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.\n\nHere are some guides from popular domains providers as a inspiration how to perform this step:\n\nhttps://docs.gandi.net/en/domain_names/common_operations/changing_nameservers.html#nameservers\n\nhttps://www.godaddy.com/help/edit-my-domain-nameservers-664\n    \n4. **Configure Rewrites and TLS Settings**\n    \n    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:\n    \n    - **Add Rewrite Rules**: Specify domain matching pairs.\n    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.\n        \n    - **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.\n\n    - **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:\n```\ntls_filters = [\n'www|ns1|ns2|hostmaster|mail|owa|ssl|webmail|smtp'\n]\n```\nResulting 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\"}`\n\n##Enable Stealth Mode (Optional)\nNote: 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.\n\n```\nClient performs a host lookup:\nHost mx.coogle.com\n\nCertainly performs:\nHost mx.google.com and receives \"not found: 3 (NXDOMAIN)\"\nSince the upstream subdomain doesn't exist, Certainly will drop the session.\n```\n\nExample of `tls_upstream_check = true` feature with a Valid Upstream Domain:\n```\nClient performs a lookup for:\nHost mail.coogle.com\n\nCertainly performs:\nHost mail.google.com and receives \"mail.google.com has address 142.250.74.101\"\n\nSince the upstream domain exists, a response will be delivered to the client using Certainly's IP.\nIf 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.\n```\n\nTo 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.\n\n### Example Configuration \n\n```\n[General]\nip = \"1.2.3.4\"\ntls_upstream_check = false\ntls_filters = [\n  'ns1|ns2|hostmaster|mail|owa|ssl|webmail|smtp',\n  # 'www\\.',\n  \"third_regex\"\n]\n\n[ns]\nport = \"53\"\nprotocol = \"both4\"\ndefault_domain = \"mom.tld\"\ndomains = [\n  \"mom.tld\",\n  \"c  oogle.com\",\n  \"woogle.com\",\n  \"amazkn.com\",\n  \"gordpress.com\",\n]\nnsname = \"ns.mom.tld\"\nnsadmin = \"admin.mom.tld\"\nrecords = [\n  # Any static records go here in zone file format\n  \"ns.mom.tld. A 1.2.3.4\",\n  \"ns.mom.tld. NS ns.mom.tld.\",\n]\n\n[rewrites]\n  \"coogle.com\" = \"google.com\"\n  \"woogle.com\" = \"google.com\"\n  \"amazkn.com\" = \"amazon.com\"\n  \"gordpress.com\" = \"wordpress.com\"\n\n```\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/user-attachments/assets/2cfe181c-602f-4fb5-b99b-971ac0b07500\" width=\"200\" /\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhappycakefriends%2Fcertainly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhappycakefriends%2Fcertainly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhappycakefriends%2Fcertainly/lists"}