← Back to Blog

Your Logs Have a Gap. That Is Not Evidence Anybody Deleted Them.

15 September 2026 Incident Response & Investigation 9 min read
A GAP IN THE LOGS: WORK DOWN, AND REACH THE BOTTOM LAST Each step is minutes of work. Each one you skip is an assumption the report inherits. exclude in this order 1. The machine was off the commonest explanation by far a shutdown entry before the gap, a boot entry after 2. The machine was asleep and the rate tapers rather than stops sleep and wake entries bracketing the window 3. Nobody was using it a floor, not a cliff idle hosts still write hundreds of entries an hour 4. The window fell off the end retention explains it and nothing else needs to the oldest surviving entry sits after your window 5. Your filter is wrong the cheapest test on this list remove every clause and count again 6. Entries were removed the last resort, and only with a positive tell requires one to five excluded AND a tell present

Reaching step six without excluding the five above it is how an absence becomes an accusation.

An analyst pulls a week of logs from a host that is part of an investigation. Tuesday afternoon, between 14:00 and 15:20, there is nothing. Every other hour that week has entries. The report gets written, and it says the logs were cleared.

That conclusion might be right. Most of the time it is not, and the difference is about twenty minutes of work nobody did.

A gap is an absence, and an absence has several ordinary causes. A finding of tampering needs something stronger than a quiet hour: it needs something present that could not have arisen normally. Those are two different kinds of evidence and they carry very different weight. Getting them confused is one of the most common ways a competent examination produces a conclusion that does not survive review.

The five explanations that come first

Work down the ladder. Each step costs minutes, and each one you skip becomes an assumption the report silently inherits.

The machine was off. By a considerable distance the commonest explanation, and the easiest to confirm. A shutdown entry immediately before the gap and a boot entry immediately after it closes the question entirely.

The machine was asleep. Look for sleep and wake entries bracketing the window. The signature differs from a shutdown: logging rate tapers rather than stopping cleanly, because some subsystems keep writing during the transition.

Nobody was using it. This one produces a floor, not a cliff. An idle workstation still writes hundreds of entries an hour from scheduled tasks, update checks, network stack activity and the logging subsystem describing itself. If your gap is genuinely zero events rather than few events, idleness does not explain it, and that distinction is worth measuring rather than assuming.

The window fell off the end. Check the oldest surviving entry in the store. If it is dated after the window you are asking about, retention explains the absence and nothing else has to. This is the step most often skipped, because it feels like a formality until the day it is the answer.

Your filter is wrong. The cheapest test on the list. Remove every clause and count again. A gap that disappears when the filter comes off was never in the store, and it is remarkable how often this is the answer after an analyst has spent an hour on the other four.

Benchmarks worth knowing for your own estate. An idle Windows workstation with default auditing typically writes several hundred Security and System events per hour. A quiet Linux host with journald writes tens to low hundreds. macOS unified logging writes vastly more, thousands per minute on an active machine, which is why its retention is measured in days rather than weeks. Measure your own numbers once and write them down: the figure you need is what a quiet hour looks like on your hosts, not on somebody else's.

Retention is measured in volume, not time

The single most misunderstood property of a log store is that most of them discard by size rather than by age.

That means retention is not a duration you can quote from documentation. It is a function of how hard the machine was worked. A busy host and a quiet host with identical configuration will hold very different windows, and a figure taken from a vendor page describes neither of them.

// Hypothesis: the apparent gap is retention, not removal.
// Establish the actual edge of the store before calling anything missing.
// Run this FIRST. If the oldest entry sits after your window, stop here.
SecurityEvent
| where Computer == "HOST-NAME"
| summarize
    OldestEntry = min(TimeGenerated),
    NewestEntry = max(TimeGenerated),
    TotalEvents = count()
// Compare OldestEntry against the window you are asking about.
// A window that predates OldestEntry is outside coverage, which is
// a fact about the store rather than a finding about anybody's conduct.

The same hypothesis in Splunk:

index=wineventlog host=HOST-NAME
| stats min(_time) AS oldest, max(_time) AS newest, count AS total
| eval oldest=strftime(oldest,"%Y-%m-%d %H:%M:%S"), newest=strftime(newest,"%Y-%m-%d %H:%M:%S")

Record both the figure and the date you measured it. Coverage moves while a case runs, so a retention edge quoted without a date is an assumption wearing a number.

Measure the gap against the host's own baseline

Once coverage is established, the question is whether the quiet period is quiet or empty. Those are different findings.

// Hypothesis: if this is a real gap, the hourly rate drops to zero,
// not merely below average. A taper is sleep. A cliff needs explaining.
SecurityEvent
| where Computer == "HOST-NAME"
| where TimeGenerated between (datetime("2026-09-08") .. datetime("2026-09-16"))
| summarize EventCount = count() by bin(TimeGenerated, 1h)
| order by TimeGenerated asc
// Read the shape, not just the zero. Hours at 400, 380, 410, then 0, 0, 0,
// then 390 is a cliff. A run of 400, 210, 60, 12, 0 is a machine going to sleep.

Then check the two boundary explanations directly:

// Power state transitions around the window. 6005/6006 are the event log
// service starting and stopping cleanly; 6008 is an unexpected shutdown;
// 1074 carries who initiated it and why; 42 is sleep, 107 is resume.
Event
| where Computer == "HOST-NAME"
| where EventID in (6005, 6006, 6008, 1074, 42, 107)
| where TimeGenerated between (datetime("2026-09-08 12:00") .. datetime("2026-09-08 17:00"))
| project TimeGenerated, EventID, RenderedDescription
| order by TimeGenerated asc

If a clean shutdown sits at 13:58 and a boot at 15:21, the gap is explained and the examination moves on.

Checking it without a SIEM

Plenty of gaps get investigated on a host that was never onboarded, or from an image after the fact. The same ladder works with nothing but the box in front of you.

# Coverage first: what is the actual edge of this log?
$log = Get-WinEvent -ListLog Security
"MaxSize: {0:N0} bytes  Retention: {1}" -f $log.MaximumSizeInBytes, $log.LogMode

# The oldest and newest records actually present
$all = Get-WinEvent -LogName Security -MaxEvents 1
$oldest = Get-WinEvent -LogName Security -Oldest -MaxEvents 1
"Newest: $($all.TimeCreated)  Oldest: $($oldest.TimeCreated)"

# Hourly counts across the week, to see shape rather than a single zero
Get-WinEvent -FilterHashtable @{LogName='Security'; StartTime=(Get-Date).AddDays(-7)} |
    Group-Object { $_.TimeCreated.ToString('yyyy-MM-dd HH') } |
    Select-Object Name, Count | Sort-Object Name

LogMode is worth reading rather than skimming. A log set to Circular discards the oldest records as it fills, which is the default and the reason most gaps at the far end of a window are retention. A log set to AutoBackup or Retain behaves differently, and an estate that changed this setting has changed what every absence in it means.

When the explanation is itself the finding

The ladder is not only a way of ruling things out. Twice in the five steps, the thing that explains the gap turns out to be more interesting than the gap was.

An unexpected shutdown, event 6008, sitting exactly at the start of a window somebody is asking about is a legitimate explanation for the absence and a question in its own right. So is a clean shutdown at 02:00 on a server that has not been rebooted outside a maintenance window in a year. The gap is explained; what explained it now needs explaining.

The same is true of step five. A filter that produced a false gap because it was scoped to a channel the host stopped writing to is a finding about collection coverage, and collection coverage is usually worth more to an organization than the original question. An estate that has quietly stopped forwarding a log source has a gap on every host, not just the one under investigation.

This is why the order matters as much as the content. Working down the ladder surfaces those two cases naturally. Jumping to the bottom skips them both and produces a report that is simultaneously more dramatic and less useful.

What a positive tell actually looks like

Now the part that matters. Suppose all five ordinary explanations are excluded. The machine was on, awake, in use, the window is inside coverage, and the filter is sound. You still do not have a finding of tampering. You have an unexplained gap, which is a legitimate thing to report and a weaker claim than most reports make.

A finding needs a positive tell: something present on the system that could not have arisen from ordinary operation.

On Windows, the clearing of an event log writes its own record. Event ID 1102 in the Security log and 104 in System both survive the clearing that produced them, which is deliberate design.

// A positive tell rather than an absence: the record of the clearing itself.
// 1102 (Security log cleared) and 104 (other log cleared) name the account.
SecurityEvent
| where EventID == 1102
| project TimeGenerated, Computer, SubjectUserName, SubjectDomainName
| union (
    Event
    | where EventID == 104
    | project TimeGenerated, Computer, RenderedDescription
)
| order by TimeGenerated asc

The corresponding Sigma rule, for anyone running detection rather than investigating after the fact:

title: Event Log Cleared
id: 6c0b8c8f-4c1a-4b6f-9c1e-3f2d1a7b5e90
status: stable
description: Detects clearing of Windows event logs, which writes its own record
references:
    - https://attack.mitre.org/techniques/T1070/001/
logsource:
    product: windows
    service: security
detection:
    selection:
        EventID: 1102
    condition: selection
falsepositives:
    - Administrative log rotation during planned maintenance
    - Imaging or provisioning workflows that reset a host
level: high

On Linux, journald keeps sequence numbers, and a missing range is visible as a discontinuity rather than as silence. journalctl --verify checks the integrity of sealed journals directly.

On macOS, the unified log store holds files with consecutive identifiers, and the system writes a purge record for material it discards on its own schedule. Ordinary retention leaves consecutive numbering and a purge record. Manual file deletion leaves a hole in the numbering, no purge record, and a folder count that does not match. The two look nothing alike once you know to compare them, and neither resembles a quiet afternoon.

The asymmetry to carry into every report. A positive tell supports a finding of tampering, because it is a state that cannot arise from normal operation. An absence supports a statement about coverage, and nothing about anybody's conduct. Reported as the same kind of thing, the absence borrows weight it has not got and the tell lends it credibility it should not.

Write the honest version

If the five explanations are excluded and no tell is present, the correct sentence is that the gap is unexplained. That is unsatisfying and it is accurate, and it leaves the door open for the log-clearing record to turn up on a second host later.

The version to avoid is the one that names conduct the evidence does not carry. "Logs were cleared to conceal activity" is a claim about intent built on an absence. "No entries survive for the period 14:00 to 15:20, within a store whose coverage I measured as reaching back to 2 September; power state and filter explanations are excluded; no log-clearing record is present" is a claim a reviewer can check.

What to do this week

  1. Measure the retention edge on your three noisiest hosts. Run the min/max query above, write the figure down with today's date beside it, and repeat quarterly. You now have a real number instead of a vendor estimate.
  1. Establish your quiet-hour baseline. Bin a week of events hourly on an idle workstation and record what the floor looks like. The next time somebody reports a gap, you will know whether zero is unusual on that host.
  1. Confirm 1102 and 104 are being collected, not just generated. Plenty of estates generate the record and never forward it, which means the one positive tell available on Windows never reaches the place anybody would look.
  1. Run the Atomic Red Team test for T1070.001 in a lab and watch what your own pipeline does with it. The test is wevtutil cl against a test log; the thing to verify is that the resulting 1102 arrives in your SIEM with the account name intact.
  1. Add the ladder to your investigation notes template. Five checkboxes, each naming the evidence that closed it. It costs a line and converts "the logs were cleared" into a conclusion somebody else can follow.

The discipline generalizes well beyond logs. Any time an examination rests on something not being there, the same question applies: have the ordinary explanations been excluded, and is there anything present that could not have arisen normally. An absence with the cheap explanations worked is evidence. An absence without them is an assumption with a timestamp on it.

Ridgeline Cyber Defence Written by security professionals. Published weekly on Tuesdays.

Related Articles

12 September 2026

The Linux Process That Lies About Its Own Name

A process can rewrite what ps reports about it in three lines of C. One field on the same host cannot be rewritten, and

11 August 2026

The Mac With No Malware On It: When Consent Is the Attack Path

A managed Mac, a clean scanner result, and a notarized tool holding Full Disk Access. What to read on disk when nothing

21 July 2026

One Failed Login Is Noise. The Same Failure Across Sixty Accounts Is a Spray Your Rule Can't Count.

Brute force and password spray produce the same failed sign-in event. A per-account threshold catches one and misses the