Settings

Stop scoring the name. A domain generation algorithm is recognised by the failures it leaves in a resolver log — one machine asking for hundreds of names that do not exist, inside a few minutes — and not by how random any one of those names looks. Feed a Zeek dns.log, a SIEM export or a resolver's own log into DGA and it plots every non-existent name by machine and by time: a generator is a dense vertical band, a broken printer is an even scatter all day, and the difference is visible before any arithmetic. The log is read in your browser and goes nowhere, which matters, because a DNS log is a list of everything everyone in the building looked at.

Why scoring the name does not work

The obvious approach is to look at the string. kqjxvbnwzt.com is clearly not a word; google.com clearly is. Score every name for randomness, alert on the high ones, done.

Now do the arithmetic. A mid-sized organisation resolves something like fifty thousand distinct names a day. Suppose your scorer is good — one false positive in every five hundred and fifty names. That is ninety-one alerts a day.

On the overwhelming majority of days there is no generator on your network at all, so all ninety-one are wrong. An analyst who opens ninety-one alerts a day and finds nothing in any of them stops opening them, and they are right to: they have other work, and this queue has never once repaid the attention. By the time a real infection arrives, the detector has been muted — or it is still firing and its output is no longer read, which is the same thing with worse paperwork.

Tighten the scorer and you trade the problem rather than solve it. At one false positive in five thousand you are down to ten alerts a day, which is workable, but you have moved the threshold far enough up that the generators using pronounceable or dictionary-built names are now under it. BASERATE turns a sensitivity and a false positive rate into the number that actually matters: of what is in the queue, how much is worth opening.

The problem is not the scorer. It is that a single name carries almost no evidence. One improbable-looking string is a coin flip. Four hundred improbable strings from one laptop in ninety seconds is not.

What a generated name actually looks like

Worth knowing, even though it is not where the detection lives, because it tells you which families the string-based approach was never going to catch.

  • Character-level generators. A seed and a date produce something like hjqpwmvlkx.net. High character entropy, letter pairs that almost never occur in real names, no vowel structure. These are the ones a scorer finds, and they are the older and easier half.
  • Pronounceable generators. Built from syllables, so the output reads like a bad brand name — vortanixa.com. Entropy is ordinary. A character-level scorer sees nothing unusual.
  • Dictionary generators. Two or three real words joined: copperwindowtable.net. Entropy is lower than a normal domain, not higher, so a randomness threshold does not merely miss these — it points the wrong way.

All three, however, share the behaviour that gives them away, because all three exist for the same reason: the implant does not know which name its operator registered this week, so it tries them until one answers.

The signal that is not in the string

An algorithm might produce a thousand candidate names for today. The operator registers one, or a handful. So the infected machine asks for the rest and is told, over and over, that they do not exist.

That is the shape to look for, and it has three parts, all of which are in an ordinary resolver log:

  1. Failure. The response code is NXDOMAIN — the name has no record at all. Not a timeout, not an empty answer: no such name.
  2. Density on one machine. Hundreds of distinct failed names from a single source address. Across the whole organisation the total means nothing; per host it means everything.
  3. Compression in time. Inside minutes, not spread over a day. A machine working through a candidate list does it as fast as its resolver will answer.

Draw failed lookups with machines down one side and time across the other and a generator is a dense vertical band you can see from across the room. This is why DGA plots the grid before it scores anything: the picture is the finding, and the numbers underneath it are for the write-up.

What else makes a burst of failures

Most of them are not malicious, and a guide that skipped this would waste your afternoon.

  • Search-domain suffixing. A resolver configured with several search domains turns one bad lookup into four, so a single typo in a config file multiplies. The giveaway is that the failed names share a suffix.
  • A decommissioned server, printer or share. Something still points at a name that no longer resolves, and retries forever. This is the even scatter across the whole day rather than a band: steady, low, unchanging, and often from many hosts at once.
  • Internal names asked of an external resolver. A laptop off the VPN still looking for fileserver.corp.local. Bursty, but the names are recognisably yours.
  • WPAD and other autodiscovery. Chatty by design, and it fails by design on a network that does not run it.
  • Mistyped or dead third-party services. A retired analytics or advertising domain in a page that is still being served.

The way to tell them apart is not a cleverer score. It is to look at the failed names themselves once the burst has been found: a shared suffix, a familiar internal name or a recognisable dead vendor explains it, and a list of strings none of which you have ever seen before does not.

Reading the log you actually have

Four columns matter, and only the first is essential:

ColumnZeek dns.logWithout it
The name asked forqueryThere is nothing to analyse
The machine that askedid.orig_hEverything is one machine and every burst runs together
The timetsThe window stops working and a whole day's failures are judged as one
The response codercode_nameNothing can be shown to have failed, which is most of the signal

A resolver's own log, a SIEM export and a firewall's DNS log all carry the same four under different names. The exercise is worth doing on whatever you have rather than waiting for the ideal source: a log with names and response codes but no source address will still show you the day's failures, and that is enough to know whether there is anything to chase.

The window is part of the detection

Judge a day at a time and a machine that fails four hundred lookups in two minutes looks like a machine that fails four hundred lookups in a day, which is a broken print queue. The threshold has to be failures per host per window, and the window has to be short enough that a burst stays a burst — minutes, not hours.

This is also where a detection that worked in a pilot stops working in production. A window tuned on a quiet test network is wrong on a busy one, and a count tuned on fifty machines is wrong on five thousand. Tune on your own log, and look at the picture before you pick the number.

DNS tunnelling is a different shape

Worth separating, because it is also "a lot of odd DNS" and the response is not the same. Tunnelling carries data inside DNS itself, so it looks like: very long labels, a high volume of queries all under one registered domain, unusual record types (TXT, NULL, CNAME), and answers that succeed rather than fail. A generator produces many different domains and mostly failures; a tunnel produces many queries to one domain and mostly answers. If you see the second shape, the name you are looking at is the operator's own nameserver and you already have your indicator.

What to do with a hit

  1. Find the one that answered. In a burst of failures there is often a single success, and that is the live command-and-control name. It is the most valuable thing in the log.
  2. Pivot on the machine, not the domain. The names change tomorrow by design; the infected host does not. What else did it connect to, and when did the failures start?
  3. Check the shape of what follows. Once a name resolves, the traffic to it is usually on a timer — BEACON takes a connection log and finds the flows whose gaps are too regular to be a person, and a capture of the same period will show you what the conversation looked like.
  4. Check it is a generated name and not a lookalike. A name that failed because somebody typo-squatted your own domain is a different incident; HOMOGLYPH reduces a list of names to skeletons and shows which are imitating which, without resolving anything.

Why the file should not leave your network

A DNS log is the most quietly revealing artefact an organisation produces. It is a minute-by-minute list of everything everyone looked at: the job sites, the medical searches, the acquisition target's investor page, the union, the competitor. That is precisely why this sort of analysis normally needs a purchase order and a data processing agreement before anybody may run it, and it is why the analysis here happens in the tab you have open, with nothing sent anywhere. You can confirm that in your browser's own network panel, which is a better assurance than a promise on a pricing page.

Related tools