Pre-Beta Pre-beta signups are open now. The full audytx engine is live for pre-beta. Everything free today stays free — paid tiers arrive Sep 01, 2026 from $20/month for unlimited repositories.
← Notes
Security · July 1, 2026

The IAM privilege escalation hidden behind a stale date

TL;DR: BishopFox's iam-vulnerable ships a matched pair of IAM policies differing by one condition operator. fn3 uses a stale DateGreaterThan bound every credential already clears — the escalation is live. fp5 uses DateLessThan on the same date, which no current credential meets — it's dead. Trivy false-positives fp5; Checkov evaluates neither. audytx reads the condition semantically and gets both right.
The setup

Scanners treat a condition as a boolean

Most Terraform security scanners read an IAM Condition block as present-or-absent: if a dangerous grant carries any condition, they assume it's constrained and stay quiet. That's a reasonable default, and it's wrong often enough to matter.

The standard test for exactly this is BishopFox's iam-vulnerable, the reference corpus of real AWS IAM privilege-escalation paths. Buried in it is a matched pair named for what scanners get wrong: fn3 (a false negative waiting to happen) and fp5 (a false positive). They're almost identical.

The pair

One operator apart

Both policies grant iam:CreatePolicyVersion on Resource: "*" — a classic escalation (rewrite a policy you're attached to, grant yourself admin). Both add one condition on aws:TokenIssueTime, the moment a temporary credential was issued. They differ by a single operator:

fn3  →  "DateGreaterThan": { "aws:TokenIssueTime": "2020-01-01T00:00:01Z" }
fp5  →  "DateLessThan":    { "aws:TokenIssueTime": "2020-01-01T00:00:01Z" }
fn3 · exploitable

DateGreaterThan → live

"Issued after Jan 1, 2020" is true for every credential in use today. The condition constrains nothing — dead weight, and the escalation is fully open.

fp5 · dead

DateLessThan → noise

"Issued before Jan 1, 2020" is true for no credential today — temporary credentials last hours, not years. The grant is unreachable; flagging it is noise.

AWS frames this pattern the same way: their example of DateGreaterThan aws:TokenIssueTime notes the policy "can be updated programmatically every day to ensure that account members use fresh credentials." The date is meant to be refreshed to roughly yesterday. Left stale at 2020, it's a lock with no bolt.
The field

Who gets it right

To resolve the pair, a scanner has to read the condition itself — the operator, the key, the value — and reason about whether it still constrains anything. We ran the pair through the field.

Scanner fn3 (should flag) fp5 (should stay quiet) Result
audytx flags ✓ quiet ✓ both correct
Trivy flags ✓ flags ✗ false positive on fp5
Checkov doesn't evaluate the condition

Trivy raises AWS-0143 on both policies identically — it never looks at the operator, so it catches the real one only by also crying wolf on the dead one. Checkov doesn't model this escalation at all. Neither reads the condition, and on a real codebase that's the difference between one alert you act on and a hundred you learn to ignore.

The method

How audytx reads IAM condition operators

audytx keeps the operator, key, and values through parsing and evaluates the condition against the scan date. It fires through a condition only when that condition is provably vacuous — a stale, past-dated lower bound on a request-time key that every current request already clears — and never on a hunch.

stale past-dated lower bound → flagged future-dated bound aws:PrincipalOrgID MFA required source-IP / source-ARN limit operator we don't model

Everything but that first case keeps its benefit of the doubt and stays suppressed. The check is deliberately one-directional: a wrong guess here means a false positive, and false positives are the one thing the engine is built to avoid. It's the same principle behind cross-resource reasoning that suppresses findings context proves benign — now applied to the fine print of a policy condition.

The idea

What this proves

This is one policy in one benchmark, chosen because it separates scanners that read conditions from scanners that count them. A security tool should understand what a control does before it trusts it — and stay quiet when the control is real.

audytx gets both halves of the fn3/fp5 pair right, with no false positive on either — something we haven't found another shift-left scanner doing.

Open beta

Start free during the open beta

audytx reviews AWS Terraform on every pull request — IAM privilege-escalation and cross-resource attack paths, with context-aware false-positive suppression. Read the verdict on your next PR.

Prefer the agent surface? Wire audytx into your coding agent over MCP. For full IAM attack-path coverage, see /iam-security.