Like "Canary" but more hipster, which means better 😎😎😎
knary is a canary token server that notifies a Slack/Discord/Teams/Lark/Telegram channel (or other webhook) when incoming HTTP(S) or DNS requests match a given domain or any of its subdomains. It also supports functionality useful in offensive engagements including subdomain allow/denylisting, working with Burp Collaborator, and automatic TLS certificate management with Let's Encrypt.
Offensive security teams use canaries to be notified when someone (or something) attempts to interact with a server they control. Canaries help provide visibility over processes that were previously unknown. They can help find areas to probe for RFI or SSRF vulnerabilities, disclose previously unknown servers, provide evidence of an intercepting device, or announce someone interacting with your server.
Defenders also use canaries as tripwires that can alert them of an attacker within their network by having the attacker announce themselves. If you are a defender, https://canarytokens.org might be what you’re looking for.
- Download the applicable 64-bit knary binary OR build knary from source:
Prerequisite: You need Go >=1.21 to build knary.
go install github.com/sudosammy/knary/v3@latest
See here for guidance on which ports to open for knary.
Important: The specifics of how to perform the next two steps will depend on your domain registrar. Google How to set Glue Record on <registrar name>
to get started. Ultimately, you need to configure your knary domain(s) to make use of itself as the nameserver (i.e. ns1.knary.tld
and ns2.knary.tld
) and configure a Glue Record to point these nameservers back to your knary host IP address. You may need to raise a support ticket to have this performed by your registrar.
-
Set your chosen knary domain(s) nameserver(s) to point to a subdomain under itself; such as
ns.knary.tld
. If required, set multiple nameserver records such asns1
andns2
. -
Create a "Glue Record" (sometimes referred to as "Nameserver Registration" or "Nameserver IP address") to point to your knary server. This is what it looks like in
name.com
:
If your registry requires you to have multiple nameservers with different IP addresses, set the second nameserver to an IP address such as 8.8.8.8
or 1.1.1.1
.
-
This will take time to propagate (often several hours), so go setup your webhook(s) while you wait. You can use this tool to check the propagation. If you can't see at least some DNS servers reflecting your knary domain as the nameserver after 12 hours, you've done something wrong.
-
Create a
.env
file in the same directory as the knary binary and configure it as necessary. You can also use environment variables to set these configurations. Environment variables will take precedence over the.env
file. -
Optional For accepting TLS (HTTPS) connections set the
LETS_ENCRYPT=<email address>
variable and knary will automagically manage wildcard certificates for you (see OPSEC note below). Otherwise, you can specify the path to your own certificates withTLS_CRT=<path>
andTLS_KEY=<path>
. -
Run the binary (via the provided Docker container, or in
tmux
/screen
) and hope for output that looks something like this:
In its most common configuration, knary will bind to these ports. You must permit connections from any IP address to these ports on your knary host.
Port | Reason |
---|---|
53 tcp & udp | DNS |
80 tcp | HTTP |
443 tcp | HTTPS |
You will find systems that spam your knary even long after an engagement has ended. You will also find several DNS requests to mundane subdomains hitting your knary every day. To stop these from cluttering your notifications knary has a few features:
- A simple text-based deny and/or allowlist (location specified with
DENYLIST_FILE
and/orALLOWLIST_FILE
). Add the subdomains, IP addresses, or User-Agents separated by a newline (case-insensitive):
knary.tld
www.knary.tld
171.244.140.247
test.dns.knary.tld
sam.knary.tld
If this were a denylist, it would stop knary from alerting on www.knary.tld
but not another.www.knary.tld
.
If this were an allowlist, knary would alert on exact matches (sam.knary.tld
) and subdomain matches (website1.sam.knary.tld
). Use ALLOWLIST_STRICT=true
to prevent this fuzzy matching and only alert on hits to sam.knary.tld
.
You can use both a deny and allowlist simultaneously, note the denylist always has the higher order of precedence. For example, a request to a subdomain that matches the allowlist, would still be denied if the User-Agent matches something in the denylist. Note: wildcards in these files are not supported. An entry of *.knary.tld
will match that string exactly.
- The
DNS_SUBDOMAIN
configuration allows you to specify a subdomain that knary must fuzzy match (i.e.*.DNS_SUBDOMAIN.knary.tld
) before alerting on DNS hits. This configuration does not affect HTTP(S) requests and remains primarily to mimic legacy knary v2 functionality. Consider using a deny/allowlist instead.
A configuration of DNS_SUBDOMAIN=dns
would stop knary from alerting on DNS hits to blah.knary.tld
but not blah.dns.knary.tld
. A HTTP request to blah.knary.tld
would still notify you unless prevented by an allow- or denylist.
Sample configurations can be found in the examples with common subdomains to deny.
Using knary in a container is as simple as creating your .env
file (or setting environment variables in the docker-compose.yaml
file) and running sudo docker compose up -d
- Let's Encrypt will dox all the domains you are using with knary (and your
DNS_SUBDOMAIN
,BURP_DOMAIN
, orREVERSE_PROXY_DOMAIN
if you are using those configurations). This is due to these domains being included in the SAN certificate generated for you. A remote adversary can read the certificate and extract the list of domains within it. To avoid this, don't configureLETS_ENCRYPT
. You can use self-signed certificates withTLS_CRT=<path>
andTLS_KEY=<path>
; however, many hosts will refuse to connect reducing your visibility of incoming HTTPS connections. - With enough effort, knary is likely fingerprint-able by a remote host. i.e. it's plausible an adversary could determine you are running knary on a given host. This is because knary is not an RFC compliant nameserver (because doing so involves dark magic) and it likely behaves in an unusual / unique manner when compared to other nameservers.
These are environment variables / .env
file configurations. You can configure none, one, or many. Most common usage would be to configure one. Refer to the examples for usage help.
SLACK_WEBHOOK
The full URL of the incoming webhook for the Slack channel you want knary to notifyDISCORD_WEBHOOK
The full URL of the Discord webhook for the Discord channel you want knary to notifyTEAMS_WEBHOOK
The full URL of the Microsoft Teams webhook for the Teams channel you want knary to notifyPUSHOVER_TOKEN
The application token for the Pushover Application you want knary to notifyPUSHOVER_USER
The user token of the Pushover user you want knary to notifyLARK_WEBHOOK
The full URL of the webhook for the Lark/Feishu bot you want knary to notifyLARK_SECRET
The secret token used to sign messages to your Lark/Feishu botTELEGRAM_CHATID
The Telegram Bot chat ID you want knary to notifyTELEGRAM_BOT_TOKEN
The Telegram Bot token