skip to content

Defense in Depth and Hardening

You will learn to pick the control class that removes what an attack depends on, and to tell removal from a setting. Interviewers ask 'how would you defend against that?' after every attack scenario.

on this pageshow

explore

questions

page 1 of 2

With non-executable memory enabled, why can a memory-corruption bug still hand an attacker execution?

level: juniorimportance: must knowfreq 68%

answer

  1. the pages changed, the flow did not
  2. stop supplying code, supply addresses
  3. resident code is already executable
  4. fragments that already end in a return
  5. first move: make a page executable again

basics

~20 s

Non-executable memory only stops injected bytes from running. It does not stop hijacked control flow, so the attacker reuses code already mapped in the process - library functions and instruction fragments that are already executable and already legitimate jump targets.

solid answer

~50 s

Non-executable memory enforces write-xor-execute: data pages cannot be executed and code pages cannot be written. That kills exactly one step of the classic exploit - writing machine code into a buffer and returning into it. Everything before that step still works: the overflow still happens, the saved return address is still overwritten, and the processor still honours it. So the attacker stops supplying code and starts supplying *addresses*. Return-into-library redirects execution to a function that is already mapped; return-oriented programming chains short instruction sequences that already end in a return, stitched together by a stack of addresses under attacker control. Every one of those bytes sits in a mapping the loader marked executable on purpose. A common first move for such a chain is to call the platform routine that changes page protections, re-creating the very capability the mitigation removed.

go deeper

for a junior

Be ready to say what write-xor-execute stops (running bytes from a data page) and what it leaves intact (the overflow and the hijacked return address), then name code reuse as the answer attackers moved to.

for a middle

Explain the mechanics: a stack of addresses instead of a payload, fragments ending in a return, and the common first goal of calling the routine that makes a controlled page executable again.

for a senior

Show that you reason in preconditions - which link the control removes, which links are untouched, and why host-level binary removal misses a technique that runs entirely inside the victim process.

for a principal

Own the framing for people who pay: mitigations set a price on one link, and a price selects which adversary can still afford the exploit. Never let it be recorded as immunity.

## What non-executable memory actually removes Non-executable memory - exposed by the hardware as the no-execute page bit, and applied as the write-xor-execute rule - marks data pages non-executable and code pages non-writable. Before it was standard, the textbook exploit for a stack overflow in a network daemon was four steps: overflow the buffer, place machine code in it, overwrite the saved return address so it points back into that buffer, and let the function return so the processor runs the attacker's bytes. The mitigation removes step four and nothing else. The buffer is still overflowable. The saved return address is still overwritable. Only *executing bytes that live on a data page* now faults. Read that list again, because it is the shape of every mitigation on this subject: one link of a chain became impossible, and the rest of the chain is untouched. That is what a mitigation is - a price on one link - and it is a different statement from a fix, which removes the defect. ## What survives: the control-flow hijack itself The defect gives the attacker a corrupted value that the processor will treat as a control-flow target: a saved return address, a function pointer, a virtual-table entry, a callback stored in a heap object. Nothing about page permissions makes that value trustworthy again. So the attacker's problem shifts from *what code do I supply* to *what code is already here that I can point at*. In a long-lived memory-unsafe daemon, the answer is: a great deal. The process has the C library mapped, plus every library the daemon links, plus the daemon's own code. All of it is executable by design, because that is what code is. ## Code reuse Two classic forms: - **Return into an existing function.** Overwrite the return address with the address of a resident library function and lay out the stack so that function sees the arguments you want. No new code anywhere. - **Return-oriented programming.** Instead of whole functions, find short instruction sequences that already end in a return instruction - a couple of useful instructions, then `ret`. Overwrite the stack with a list of such addresses. Each fragment does a little work and returns, which pops the next address and continues. The attacker has built a program out of somebody else's instruction bytes. The variant that chains indirect jumps instead of returns works the same way. A chain does not have to be long. Very often the whole goal is to call the routine that changes page protections on a region the attacker already filled with bytes, or to start a child process. In the first case the chain simply re-creates the capability the mitigation took away, then jumps into the newly executable region. ## Why hardening the host around it does not answer this Removing interpreters and shells from the file system, or blocking a binary from running, addresses a *substitutable* precondition. Reuse happens inside the victim process, using code that process already loaded, and the library's own system-call wrappers are sufficient. The precondition the technique cannot substitute is that a corrupted control-flow value is honoured. That is the thing a control has to take away. ## The control that does aim at it Control-flow integrity is the answer aimed at reuse: constrain every indirect transfer to a set of targets computed as valid, mark legal indirect entry points so a jump into the middle of an instruction stream faults, and - for the backward edge that return-oriented chains abuse - keep a shadow copy of return addresses and fault when the two disagree. It is a real price increase: coarse-grained variants shrink the usable target set, and a shadow stack removes the backward edge outright. It is still a price. Coarse policies leave enough valid entry points that call-oriented chains remain constructible, and a chain that only calls whole valid functions is inside the policy. ## What to say when someone claims immunity The honest sentence is: with these mitigations, an attacker holding this defect needs at least one more capability - typically a way to learn where things are, and a way to reach code already resident. Those are extra work, and extra work selects who can still afford the exploit. Someone firing a public proof of concept gets a crashed worker. Somebody who can spend a month finding a second defect gets execution. Both facts are true at once, and only the second one is a security position.

  • If the attacker is reusing resident code, what does control-flow integrity add?
    It constrains where an indirect transfer may land: forward edges to a computed set of valid targets, and the backward edge properly only with a shadow stack that keeps its own copy of return addresses. It shrinks the usable target set rather than removing reuse - coarse policies still leave enough valid entry points to chain whole functions - so it raises the price again instead of closing the class.
  • Does stripping shells and interpreters off the host stop this?
    No. The chain runs inside the victim process using code that process already loaded; the C library's own system-call wrappers are enough to change page protections or start a child. Removing binaries takes away a convenience, not the precondition. The precondition is that a corrupted control-flow value is still honoured.
  • State precisely what write-xor-execute promises.
    That no page is writable and executable at the same moment. It does not promise a page can never become executable - if the process retains the ability to change page protections, the chain's first job is to call that facility on memory it already controls, and then the promise has been kept while the attacker runs code anyway.

Locking the workshop so nobody can bring in their own tools does not help if every tool they need is already bolted to the bench inside.

saying these in an interview costs you the question

  • Says non-executable memory makes memory corruption unexploitable
  • Assumes every exploit must inject shellcode somewhere
  • Thinks removing shells from the host removes the capability
  • Confuses non-executable pages with read-only pages
  • Treats a mitigation and a patch as the same kind of thing

context

open as a page

Why is the protocol an attacker used usually a convenience rather than a precondition?

level: juniorimportance: must knowfreq 62%

basics

~20 s

A precondition is something the technique cannot run without. A protocol is normally one of several interchangeable routes to the same thing, so blocking it moves the operator to another route instead of ending the technique.

open as a page

In defense in depth, what makes two controls count as two layers rather than one?

level: juniorimportance: must knowfreq 58%

basics

~20 s

Two controls are two layers only if getting past them means satisfying two independent preconditions. If both are satisfied by the same fact, such as one approved job identity, whatever supplies that fact clears both at once.

open as a page

What is the difference between disabling SMBv1 with a policy setting and uninstalling the feature?

level: juniorimportance: must knowfreq 65%

basics

~20 s

Disabling writes a configuration value while the SMBv1 code stays installed, so one administrative write turns it back on. Uninstalling removes the component itself, so there is nothing to re-enable until someone installs software again.

open as a page

What does an application-control rule naming a path, publisher or hash actually evaluate?

level: juniorimportance: must knowfreq 62%

basics

~20 s

It evaluates the identity of a file at the moment something tries to load and run it: where the file sits, who signed it, or its exact bytes. It says nothing about what an already-permitted program is later handed.

open as a page

One local administrator password is identical on 4,000 laptops - what does that hand an intruder?

level: juniorimportance: must knowfreq 70%

basics

~20 s

One shared password turns a single compromised laptop into administrator on all 4,000: code already running as admin on one host reuses that secret to authenticate everywhere else. No exploit, nothing for a patch to fix.

open as a page

A host installed the fixed package but was never restarted. Is the flaw gone?

level: juniorimportance: must knowfreq 70%

basics

~20 s

No. Installing a package writes a new file to disk; every process already running keeps the old copy of the shared library mapped in memory and keeps executing it. Until those processes restart, the vulnerable code is still live.

open as a page

A pipeline enforces approved job identity, permitted tools, permitted egress and change review - how many layers is that?

level: middleimportance: must knowfreq 47%

basics

~20 s

Effectively one. All four are satisfied by the same fact: this is a legitimate pipeline job. A contributor entitled to edit the pipeline definition and start a build holds that fact honestly and clears all four without defeating anything.

open as a page

Why won't publisher or hash rules stop a script run by an already-allowlisted signed script host?

level: middleimportance: must knowfreq 58%

basics

~20 s

No new file is presented, so no rule is consulted. Path, publisher and hash rules all answer one question - may this file run - and the script host is an approved, correctly signed file. The script is just an argument.

open as a page

After removing local administrator rights from all users, which standing privileges still let an intruder move?

level: middleimportance: must knowfreq 58%

basics

~20 s

Three untouched preconditions: the built-in administrator secret that every host in the fleet still accepts, privileged accounts that sign in to low-trust machines, and privileged group memberships held permanently rather than on request. Removing user-level admin addresses none of them.

open as a page

Your cluster is 92% conformant to its hardening benchmark — is the remaining 8% 8% of your risk?

level: seniorimportance: must knowfreq 60%

basics

~20 s

No. The percentage is an unweighted count of settings, not a ranked control set, so the same 92% is compatible with every load-bearing item failing and with every failing item being irrelevant. Defend the number by naming the handful of items that remove something your adversary depends on.

open as a page

Told the estate is fully patched, how do you separate hosts that received a fix from hosts running it?

level: seniorimportance: must knowfreq 55%

basics

~20 s

Ask for two numbers, not one. Installed count is on-disk state. Running count needs a per-host comparison: did the unit that loads this code start after the fix landed - boot time, process start time, running firmware version?

open as a page

In a published Kubernetes hardening benchmark, what does one passing item assert?

level: juniorimportance: should knowfreq 55%

basics

~20 s

Only that one setting on the hosts checked matched the standard's recommended value at that moment. The standard is written for every reader of that platform, so a pass says nothing about whether your adversary lost a path.

open as a page

Address-space randomisation is on: what exactly does one leaked pointer buy an attacker?

level: middleimportance: should knowfreq 55%

basics

~20 s

Randomisation's whole protection is a secret: where things are. One leaked address recovers it, because everything inside a mapping moves by the same offset, turning a probabilistic guess into a deterministic exploit against that process.

open as a page

A stolen bearer token survives your region, client and protocol blocks — which control class remains?

level: middleimportance: should knowfreq 51%

basics

~20 s

One precondition is left: the token is accepted from whoever presents it. The control class that removes it is proof of possession - binding the credential to a key the presenter must prove they hold.

open as a page

NTLM is refused by policy: what has that taken from an intruder who already holds local administrator?

level: middleimportance: should knowfreq 50%

basics

~20 s

Almost nothing durable. The refusal is a configuration value that the administrator role is allowed to write, and the security package is still installed, so the capability sits inside the privilege the intruder already holds.

open as a page

What does time-bound, approval-gated elevation actually remove from an intruder's options?

level: middleimportance: should knowfreq 44%

basics

~20 s

Time-bound elevation removes permanent membership as a target: an account compromised at a random moment holds no active privilege, and using it needs a request and an approver. It does not touch privilege already active, and bounds when, not where.

open as a page

In a Kubernetes hardening benchmark, how do you tell an item that removes a technique from one that only narrows it?

level: middleimportance: should knowfreq 50%

basics

~20 s

Name the technique, name what it consumes, then ask whether that thing still exists after the item is applied. If the adversary can substitute something or simply ask for slightly less, the item narrows. If there is no remaining path without first obtaining something new, it removes.

open as a page

Your Linux hosts take kernel live patches. Does that remove the need to reboot?

level: middleimportance: should knowfreq 45%

basics

~10 s

No. Live patching redirects individual kernel functions in memory, so it covers self-contained kernel fixes only. Data-structure changes, all userspace code and firmware still need a restart, and each patch stream is time-bounded.

open as a page

An architect multiplies four layers' bypass costs to size an attack - when is that arithmetic wrong?

level: seniorimportance: should knowfreq 41%

basics

~20 s

Multiplication holds only across independent preconditions. Where controls are satisfied by the same fact, their costs add rather than multiply, and a control that inherits the fact entirely adds nothing — the real total is the cost of obtaining the shared fact.

open as a page

An SMBv1 session fails against every host you try - does that prove the feature is removed?

level: seniorimportance: should knowfreq 42%

basics

~10 s

No. A refused negotiation shows the dialect was declined by the hosts you reached, at that moment. Disabled and removed look identical from the client side, so the failure cannot tell them apart.

open as a page

Why is 'application control blocked it' the wrong reading when a foothold under publisher rules never progressed?

level: seniorimportance: should knowfreq 45%

basics

~20 s

A deny only happens when a new file is presented. If the operator's next move was handing text to a permitted interpreter, the rule set was never consulted, so nothing was blocked. The foothold more likely stopped for economic reasons.

open as a page

A domain administrator signs in to a user's laptop to fix a ticket - why does tiering forbid it?

level: seniorimportance: should knowfreq 52%

basics

~20 s

The laptop's administrator sits inside the boundary of that logon - the machine must be able to act for the account, so an intruder who already owns the laptop gains whatever that credential reaches. Tiering is a direction rule.

open as a page

The owner will fund one: rewriting the memory-unsafe parser, or another year of exploit mitigations. How do you frame the choice?

level: principalimportance: should knowfreq 38%

basics

~20 s

Refuse the safe-or-unsafe framing. Mitigations set a price on exploitation, and the price decides which adversaries can still pay; a rewrite removes the defect class for that component. Decide on component lifetime, exposure and which adversary you are actually funding against.

open as a page

The service-desk manager says per-host admin passwords will wreck handle time - how do you land the change?

level: principalimportance: should knowfreq 34%

basics

~20 s

Treat the objection as a cost to fund, not a blocker to overrule. Pay for password retrieval inside the console his engineers already use, sequence the invisible removals first, and commit to a measured handle-time threshold with a real rollback.

open as a page

A few percent of your estate never completes a reboot cycle and the owners refuse downtime. What do you do?

level: principalimportance: should knowfreq 38%

basics

~20 s

Treat it as a downtime-budget decision, not a patching one. Either fund the redundancy that makes restarts cheap, mandate a periodic power cycle as a condition of hosting, or take a dated acceptance from the owner who refuses.

open as a page

On a Linux jump host that only executes signed files, why does a user-shell foothold barely feel it?

level: middleimportance: nice to knowfreq 32%

basics

~20 s

Because the permitted interpreter is the shell itself. Signature enforcement judges files at execution; a shell script is text the already-approved shell reads, so the payload never becomes a file that is checked. The generality stays.

open as a page

Address-space randomisation on a pre-forking daemon: why does a crashed worker help the attacker?

level: seniorimportance: nice to knowfreq 32%

basics

~20 s

Fork copies the parent's address space, so every worker shares one randomisation draw and one stack cookie value. A dead worker is replaced by an identical twin, so the attacker retries against the same secret and can learn it byte by byte.

open as a page

An initial-access broker is reselling a long-lived token for your SaaS tenant — which control costs him?

level: seniorimportance: nice to knowfreq 31%

basics

~20 s

A broker is paid once for a position he never uses, so his product must still work later from the buyer's own infrastructure. Only short lifetimes and possession binding destroy that; region and protocol blocks cost him nothing.

open as a page

Your pipeline's four layers share one precondition and delivery refuses a human gate on every deploy - what do you change?

level: principalimportance: nice to knowfreq 29%

basics

~20 s

Re-anchor exactly one control, on the highest-consequence step only. Bind the promotion step to an approval from a human who cannot start jobs and did not author the change, leave every other job fast, and state plainly which pipelines are accepted at one precondition.

open as a page

showing 1–30 of 32