Dormant Code: the blindness of a passing test

Logic bombs, trigger-based evasion, and what a green test never tells you.

Dormant Code — Audio

Listen to this Signal, read by Michael. 19 min 08 sec.

0:00 0:00

Built from a coffee-break question, 7 September 2026.


1. The seed: a loop that does not run

We started from the smallest thing, coffee and a conversation that led to some research.

A function looks up suppliers by name, fetches the matches, prints a heading, and loops over the rows. Feed it a name that matches nothing and it prints the heading and stops, because fetchall returns an empty list and a for loop over an empty list runs zero times. No crash….interesting…. The empty case is handled by the loop having nothing to walk.

Then the same shape one question over. A function fetches one row and reads a column out of it, with no check for the missing case. On every tested input it is correct. Send it a key that does not exist and it does not fall quiet, it raises a NoneType error and dies. Same silence at rest, opposite behaviour when the condition finally arrives.

This whole article in a few code snippets, some cold coffee and curiosity. “A test that passes only proves the paths it actually walked.” The branch you never triggered is a room you never opened, and it can hold a safe no-op or a loaded gun, and from the outside, at rest, the two look identical. Everything that follows is what happens when an adversary decides to put the gun in that room on purpose, and to make sure your tests never open the door.


2. The family: logic bombs and the people who plant them

Code that waits for a condition before it acts has a name in security. A logic bomb is malicious code that stays inert until a logical trigger fires. When the trigger is a clock it is a time bomb. The condition can be anything the program can observe: a date, a file present or absent, a name missing from a list, a counter reaching a threshold.

The classic logic bomb is an inside job, and the case law is specific.

  • Tim Lloyd, Omega Engineering, 1996. A network administrator who saw his dismissal coming planted code that, on the 31st of July, deleted the manufacturing programs that ran the company’s production. It fired after he was gone. The cleanup and lost work ran to around ten million dollars, and it became one of the first federal computer-sabotage convictions in the United States.

  • Roger Duronio, UBS PaineWebber, 2002. Angry about his bonus, he planted a logic bomb timed for the 4th of March, and it took down roughly two thousand servers across the firm’s branches. He had even bought put options on UBS stock, betting the damage would drop the share price. He was convicted in 2006.

  • David Tinley, Siemens, roughly 2014 to 2019. A contractor wrote logic bombs into the spreadsheets he built for a client, small time-based faults that would break the sheets after a while, so the client would call him back to fix them. He was, in effect, employed by his own logic bomb, until an urgent job forced him to hand over the password and the trick surfaced.

Notice the pattern in all three. The code sat inside normal, trusted, working software for months. It compiled, it ran, it did its ordinary job, and it passed whatever review it faced, because the malicious branch was gated behind a condition that review never met. The trigger is the whole design.


3. Trigger-based evasion: hiding from the analyst, not the user

The insider logic bomb hides in time. Modern malware hides in context. The moment automated analysis became the front line of defence, attackers started writing code whose first job is to answer one question: am I being watched.

If the answer is yes, the code does nothing interesting. This is trigger-based evasion, and the triggers are environmental.

A sandbox is an instrumented machine that runs a suspicious file for a short window and records what it does. So evasive malware looks for the tells of a sandbox. Too few processor cores. An uptime measured in minutes. No mouse movement and no scrolling, because no human is present. Disk and memory sizes that are suspiciously round and small. The registry keys, drivers and MAC-address prefixes that VirtualBox and VMware leave behind. The presence of a debugger or a packet sniffer. If it sees those, it exits clean, or it runs a harmless decoy path.

The simplest evasion of all is patience. A sandbox has a time budget, often a few minutes per sample, because it has thousands of samples to clear. So the malware sleeps past the budget. It calls sleep for ten minutes, or it grinds through ten million pointless arithmetic operations first, and the analysis window closes on a program that appeared to do nothing. This is stalling code, and it works because the defender’s resource is finite and the attacker knows it.

The academic name for the general shape is split-personality, or context-aware, malware. One binary, two behaviours, and the environment decides which one you see. Point a dynamic analyser at it and you observe the innocent personality, every time, because your analyser is exactly the environment the malware is built to recognise and avoid.


4. Target gating and environmental keying: the sharp end

Evasion is defensive, it hides the payload. Gating is offensive, it aims the payload. The most disciplined malware fires on nothing except its intended target, and this is where the craft becomes precise.

Stuxnet is the reference example. It spread widely, infecting many thousands of ordinary Windows machines around the world, and on almost every one of them it did nothing at all. It was looking for one thing: a Siemens Step 7 industrial control system driving a specific model of frequency converter at a specific range of frequencies, the signature of a uranium enrichment centrifuge. Only when it found that exact configuration did the sabotage payload arm and begin quietly changing the centrifuge speeds. Millions of eyes could have watched it run and seen a dud. The target was the trigger.

Many families gate on geography and language. A large amount of malware written in the former Soviet sphere checks the system’s keyboard layouts and locale on startup, and if it finds Russian or another Commonwealth of Independent States language, it exits without acting. It is a legal and operational firewall, do not foul your own nest. For a defender it is a tell, and for an analyst it is a lever, because you can flip the locale and change the behaviour.

Then there is the killer idea in the whole field, environmental keying. Here the payload is encrypted with a key derived from the target environment itself, a specific file’s contents, a domain name, a path only present on the target machine. Gauss, uncovered by Kaspersky in 2012, carried an encrypted warhead whose key was built from properties of the machine it was hunting for. Because no one outside the operation had that exact machine, no one could derive the key, and the payload has, by public accounts, never been decrypted to this day. Think of what that means for analysis. Here the difficulty runs deeper than reach. The malicious code cannot even be read until reality supplies the trigger, and the condition is the decryption key.

Your third condition, a network address being live, is a whole technique too. Payloads that only proceed when their command server answers. Dead-drop resolvers that fetch the real address from an innocent-looking web page or a social media profile. Domain-generation algorithms that compute a fresh rendezvous domain every day. No live channel, no next stage, nothing for a sandbox to record. The malware is a seed that germinates only in the right weather.


5. The supply chain: dormancy at scale

Put trigger-gating inside trusted software that thousands of organisations install on purpose, and you have the defining threat of the decade.

SolarWinds, uncovered December 2020. Attackers compromised the build system of Orion, a widely used network-monitoring product, and shipped their backdoor inside a legitimately signed update to around eighteen thousand customers. Then it waited. The implant, called Sunburst, stayed dormant for up to about two weeks after installation before doing anything. On waking it checked its surroundings, resolved a domain-generation address for instructions, and refused to run if it saw particular security tools or analysis environments. Dormancy plus environmental checks let it sit inside some of the most heavily monitored networks in the world and stay invisible, because during every test, every scan, every build of that update, the conditions to wake were not met.

XZ Utils, disclosed March 2024, is the one that should keep you up at night, because it was nearly perfect. Over roughly two years a contributor using the name Jia Tan earned the trust of the maintainer of xz, a compression library sitting deep under Linux, and gained commit rights through patient social engineering. The backdoor they landed was gated with real discipline. It armed only when the code was loaded into the SSH daemon, only on certain distributions, only when the process and arguments matched, and it hid itself otherwise. It would have handed remote access to the attacker across a vast slice of the internet’s servers. It was caught by one engineer, Andres Freund, who noticed that SSH logins were running about half a second slower than they should and refused to let the anomaly go. A performance hiccup, chased down by one stubborn person, is the only reason it did not become the worst backdoor in the history of open source.

Sit with the shape of both. The malicious code passed through code review, automated testing, continuous integration, package signing and distribution, out to thousands of machines, and stayed silent through all of it, because the trigger was set to conditions that none of those stages reproduce.


6. Why analysis is structurally blind

This is not a matter of testing harder. The blindness is built into what a test is.

A test executes a program on an input and checks the result. Code coverage measures how much of the program your tests executed, and teams chase high coverage for good reason. But coverage counts the lines and branches you ran, and it says nothing about the behaviour of a branch on an input you never supplied. You can have every line covered and still never have supplied the date, the hostname, the file, that flips the hidden branch. Coverage of the text is not coverage of the behaviour.

Underneath sits a hard limit. Rice’s theorem says, in short, that any non-trivial question about what an arbitrary program actually does is undecidable in general. There is no procedure that reads any program and reliably tells you whether it hides a malicious behaviour, because deciding that in full generality is equivalent to problems we have proven no algorithm can solve. Trigger-hunting in the general case is provably beyond full automation, expense aside.

So dynamic analysis, running the thing, sees only what your inputs and your environment triggered. Static analysis, reading the thing without running it, is the other half, and it pays to be exact.

Parsing is not running. A disassembler reads the instructions off the binary whether or not the trigger is ever true. The gated branch sits right there in the code, a compare and a jump, and a tool like Ghidra, IDA or radare2 recovers it without the condition ever firing. So for plain gated code, the trigger being absent does not stop you seeing that the branch exists. It stops you seeing what the branch does when it runs, and that gap gets bridged, by the tools in the next section, by solving for the condition instead of waiting for it.

The genuinely hard case is when there is nothing to parse, and it takes two forms. Packed and self-modifying code ships as a small stub plus an encrypted blob, and the real instructions come into existence only in memory, after the stub unpacks them. On disk you see the stub and the blob, and if the unpack is itself gated, you are back to needing the trigger before any real code exists to read at all. Environmental keying is the wall, and Gauss is its face. The payload is ciphertext, whose key is derived from the target machine you do not have. This is a step past unreached. The malicious code is unreadable. There are no instructions to disassemble, only bytes. It now leans into cryptography.

That is where Rice’s theorem meets its cryptographic cousin. Rice already tells you that deciding what an arbitrary program does is undecidable in general. Keying stacks a second, sharper wall on top: the attacker has turned your analysis into the task of breaking an encryption whose key they threw into an environment you cannot reach. For a large keyspace and an unknown trigger, you provably cannot recover the behaviour. Gauss has been public since 2012, and its payload has never been decrypted.


7. The defensive arsenal: opening the doors on purpose

None of this is hopeless. It means detection is an active hunt rather than a passive wait, and the good techniques share one idea, force your way into the branch the attacker hid instead of waiting for it to fire.

  • Static analysis and reverse engineering. Read the code or the binary and find the guarded block even though you cannot make it run. A human or a tool sees the gate, and asks what is behind it, and what it is testing for.

  • Symbolic and concolic execution. This is the real answer to the hidden trigger. Instead of guessing inputs, you treat the input as a mathematical unknown, walk the program, and collect the constraint each branch imposes. Then you hand those constraints to a solver, an SMT solver such as Z3, and ask it to compute an input that reaches the guarded block. Tools like KLEE, angr, S2E and Triton do exactly this. The attacker hid the path behind a specific condition, and the solver solves for the condition.

  • Forced and multipath execution. Where solving is too expensive, force the branch. Flip the condition, take both directions at every gate, and watch what each one unfolds. You accept some false paths in exchange for seeing behaviours no single real run would reveal.

  • Anti-evasion analysis. Beat the sandbox detection by removing the tells. Full-system emulation, randomised or realistic-looking environments, simulated mouse movement and uptime, even bare-metal analysis on real hardware so there are no virtualisation artifacts to find. If the malware runs because it believes it is on a real victim, it shows you its real self.

  • Making the trigger present, or stealing it. When you cannot read the payload cold, supply the condition it is waiting for. Emulate or spoof the environment it checks, the file, the domain, the locale, the sandbox that does not look like one, until it decrypts and runs itself in front of you. Where you cannot fake the trigger, lift it from a machine that already met it, a live victim, a captured command server, a piece of leaked operator tooling. And where the keyspace is small enough, brute-force the key outright. This is how environmental keying is beaten, on the days it can be beaten at all.

  • Provenance and the boring controls. Software bills of materials, so you know what is actually in your dependencies. Reproducible builds, so a backdoor slipped into a build server produces a binary that no longer matches the public source, which is exactly the mismatch that flags a tampered artifact. Behavioural diffing of releases. And threat hunting for the fingerprints of dormancy itself, the periodic beacon, the long sleep, the code that checks the locale before it acts.

  • The human reflex, last and first. Assume the untested branch is where the truth hides. Feed the system the input it was never given, the name that matches nothing, the id that is not there, the date in the future, the hostname of the real target. The whole discipline is professionalised suspicion of the path nobody walked.


8. The synthesis: dual-use, ethics, and the cup you started from

Every technique here is dual-use. A condition that reveals behaviour only in the right environment is a targeted weapon, and it is also a software licence check, a beta feature flag, a regional rollout, a watermark that proves who leaked a document. The mechanism is neutral. The intent and the disclosure are what turn it into an attack or a feature. That is the exact seam my EP2 ethics pillar runs along. Regulation and code review both operate at the perimeter, and the perimeter is where a well-built trigger is designed to look innocent. What separates a kill switch from a logic bomb is whether the person who planted it told the truth about it.

Which brings it back to the cup in your hand. All of this, Stuxnet aiming itself at one cascade of centrifuges, Gauss encrypting a payload no one can read, a backdoor sleeping under every SSH server on earth, is one idea wearing heavier and heavier armour. Code that stays dormant until a condition is met.


References and cases (for the thread)

  • Logic bombs: Omega Engineering / Tim Lloyd (1996, convicted 2000). UBS PaineWebber / Roger Duronio (2002, convicted 2006). Siemens / David Tinley (guilty plea 2019).
  • Evasion and sandboxing: split-personality malware, stalling code, virtual-machine artifact checks, human-interaction checks.
  • Target gating: Stuxnet (Siemens Step 7, specific frequency-converter signature). Commonwealth of Independent States locale-exit checks.
  • Environmental keying: Gauss (Kaspersky, 2012), payload still undeciphered.
  • Supply chain: SolarWinds Sunburst (December 2020). XZ Utils backdoor, CVE-2024-3094 (March 2024, found by Andres Freund via an SSH latency anomaly).
  • Theory: Rice’s theorem, the undecidability of non-trivial program properties. Environmental keying as a reduction from program analysis to cryptanalysis. Code coverage versus behavioural coverage.
  • Detection: static disassembly and reverse engineering (Ghidra, IDA, radare2). Symbolic and concolic execution (KLEE, angr, S2E, Triton) over an SMT solver such as Z3. Forced execution. Environment spoofing and memory-dump unpacking against packed and keyed payloads. Anti-evasion sandboxing. Reproducible builds. Software bill of materials.

AI disclosure

This article uses AI tools for research and generation with a human in the loop. Drafts are reviewed, edited, and signed off by a named natural person before publication. Editorial responsibility: Thomas A. Kleppestø.

Tools used: research, drafting.