The IAM privilege escalation hidden behind a stale date
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.
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.
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" }
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.
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.
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.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.
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.
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.
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.
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.