| name | identity-fabric |
| description | Concrete endpoints, probes, and enumeration techniques for identity provider fingerprinting and auth surface mapping. Covers Microsoft Entra (Azure AD), Okta, ADFS, Google Workspace, generic OIDC (Keycloak/Auth0/Ping/OneLogin/Duo), SAML metadata, AWS account-ID extraction, Microsoft 365 deep enumeration (Teams/SharePoint/OneDrive/OAuth/Power Platform), GraphQL field-suggestion enumeration when introspection is disabled, and LinkedIn employee enumeration with role prioritization. Use when mapping an org's auth architecture, enumerating SSO/IdP surfaces, or building a target employee list. |
| version | 1.0.0 |
| triggers | ["identity fabric","SSO discovery","IdP fingerprinting","tenant fingerprinting","okta enum","entra enum","azure AD enum","ADFS enum","SAML metadata","Microsoft 365 deep","Teams federation","SharePoint enum","OneDrive enum","GraphQL field suggestion","LinkedIn enumeration","employee enumeration","auth architecture","OIDC discovery","Keycloak enum","Auth0 enum","user enumeration","getuserrealm","GetCredentialType","device code phishing","Power Platform","M365 OAuth"] |
Identity Fabric — Concrete Endpoints
Sub-skill of offensive-osint. For pipeline and triage context load osint-methodology.
Authorized targets only.
BEHAVIORAL CONTRACT
When triggered: SSO/IdP fingerprinting, tenant discovery, auth architecture mapping, Microsoft 365 enumeration, Okta/Entra/ADFS probing, OIDC discovery, LinkedIn employee enumeration, or device-code phishing feasibility assessment is needed.
Execute:
- Probe OIDC discovery endpoints (§1.1-1.5) on every alive subdomain and known SSO prefixes (auth., login., sso., idp., iam., identity., accounts., oauth.). Probe
/.well-known/openid-configuration on every alive subdomain regardless of prefix.
- Extract tenant GUIDs from OIDC metadata issuer fields.
- Run getuserrealm.srf to classify Managed vs Federated (§1.1).
- If deep mode authorized, run GetCredentialType user-enum capped at 20 attempts (§1.1). Medium detectability.
- Probe M365 deep surfaces: SharePoint, OneDrive, Teams federation (§1.8).
- Check device-code phishing feasibility (§1.8).
- Extract AWS account IDs, OAuth client_ids, and scopes from JSON/HTML/JS (§1.7).
- For LinkedIn employee enum: use Google dorking (§2.1), prioritize by role tier (§2.2), derive candidate emails (§2.3), output per §2.4 schema.
- Feed discovered tenants to
people-breach-intel for SSO_EXPOSURE correlation.
Output: Per-tenant and per-person findings. Tenant = asset type sso_tenant with GUID. Person = asset type person with derived emails at TENTATIVE confidence.
Severity rules: SharePoint anonymous share = HIGH. Device-code phishing feasible = MEDIUM. GetCredentialType user-enum success = INFO (asset only, not a vulnerability).
Gating rules: GetCredentialType and Okta authn are medium-detectability; cap at 20 per tenant. LinkedIn enum: throttle to <20 profile views/day per persona. For sock-puppet discipline, see osint-methodology §6.1.
Chain to: Feed tenant list to people-breach-intel for SSO_EXPOSURE correlation. Load osint-methodology §12 for breach × identity correlation logic. Feed derived emails to secrets-and-dorks for breach lookups.
1. Identity Fabric — Concrete Endpoints
Methodology lives in the companion osint-methodology skill §11. This is the URL/payload reference.
1.1 Microsoft Entra (Azure AD)
OIDC metadata + tenant GUID extraction:
GET https://login.microsoftonline.com/{tenant-or-domain}/.well-known/openid-configuration
Response field issuer contains the tenant GUID. GUID regex:
\b[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\b
Detectability: low.
getuserrealm.srf — managed vs federated probe:
GET https://login.microsoftonline.com/getuserrealm.srf?login=<probe-user>@<domain>
Response: JSON with NameSpaceType field (Managed / Federated / Unknown). Federated also includes FederationBrandName and AuthURL (the upstream IdP URL). Detectability: low.
Autodiscover v2:
POST https://autodiscover-s.outlook.com/autodiscover/metadata/json/1
Body: {"Email": "<probe-user>@<domain>"}
Returns the protocol endpoint for the user; presence indicates tenant membership. Detectability: low.
Autodiscover IP correlation (passive M365 confirmation):
Resolve autodiscover.<domain> and check if it lands in Microsoft Exchange Online IP space. This works even when MX is wrapped by Mimecast/Proofpoint/Barracuda inbound filtering, where MX alone doesn't reveal the underlying mail platform.
dig +short A autodiscover.target.example
Resolve-DnsName "autodiscover.$D" -Type A | Select Name,IPAddress
Microsoft Exchange Online IPs (truncated common ranges): 40.96.0.0/13, 52.96.0.0/14, 13.107.6.152/31, 13.107.18.10/31, 40.99.0.0/16, 40.104.0.0/15, 52.98.0.0/15. Full list: Office 365 URLs and IP address ranges.
If autodiscover.<domain> lands in that space → M365_CONFIRMED even when nothing else does. Detectability: low (passive DNS).
GetCredentialType — user-enum (deep mode only):
POST https://login.microsoftonline.com/common/GetCredentialType
Content-Type: application/json
Body:
{
"username": "<email>",
"isOtherIdpSupported": true,
"checkPhones": false,
"isRemoteNGCSupported": true,
"isCookieBannerShown": false,
"isFidoSupported": true,
"originalRequest": "",
"country": "US",
"forceotclogin": false,
"isExternalFederationDisallowed": false,
"isRemoteConnectSupported": false,
"federationFlags": 0
}
Response field IfExistsResult indicates user existence: 0 = exists, 1 = doesn't exist, 5 = exists in federated tenant. Detectability: medium (logged in tenant audit). Cap at 20 attempts per tenant.
1.2 Okta
Org slug derivation: start with stems from discovered subdomains and root-domain stem. Probe <slug>.okta.com and <slug>.oktapreview.com. Slug regex:
[a-z0-9][a-z0-9-]{1,40}\.okta(?:preview)?\.com
OIDC fingerprint:
GET https://<slug>.okta.com/.well-known/openid-configuration
/api/v1/authn user-enum (deep mode):
POST https://<slug>.okta.com/api/v1/authn
Content-Type: application/json
Body: {"username": "<email>", "password": "invalid_password_for_enum"}
Response distinguishes user existence:
400 with errorCode: E0000004 → user doesn't exist (or generic password error in some configs).
401 with status: PASSWORD_WARN / LOCKED_OUT / MFA_REQUIRED → user exists.
Detectability: medium (audit-log per attempt). Cap at 20 attempts per tenant.
1.3 ADFS
Passive fingerprint:
GET https://{domain}/adfs/idpinitiatedsignon.aspx
A 200 OK with a urn:com:microsoft:ADFS: reference in HTML indicates ADFS. Version-string greppable in HTML resource references.
Mex endpoint (deep mode):
GET https://{domain}/adfs/Services/Trust/mex
Returns SOAP federation metadata including endpoint URLs, signing certs, and supported claim types.
1.4 Google Workspace
OIDC discovery:
GET https://{domain}/.well-known/openid-configuration
Google-Workspace-hosted-domain customers expose discovery endpoints with characteristic issuer URI (https://accounts.google.com) and JWKS URI. MX records pointing to aspmx.l.google.com are a corroborating signal.
1.5 Generic OIDC (Keycloak / Auth0 / Ping / OneLogin / Duo)
Discovery: probe /.well-known/openid-configuration on every alive subdomain. The issuer and authorization_endpoint field URLs fingerprint the product:
| Product | URL pattern in issuer |
|---|
| Auth0 | https://*.auth0.com |
| OneLogin | https://*.onelogin.com |
| Ping | https://*.pingone.com, https://*.pingidentity.com |
| Duo | https://*.duosecurity.com |
| Keycloak | URL contains /realms/<realm> |
1.6 SAML metadata
Probe paths (try each against every alive subdomain):
/saml/metadata
/FederationMetadata/2007-06/FederationMetadata.xml
/federationmetadata/2007-06/federationmetadata.xml
/simplesaml/saml2/idp/metadata.php
/auth/saml2/metadata
Parsing guidance: Reachable SAML metadata XML reveals EntityID, signing certs (cert-reuse pivot), SingleSignOnService URL, NameIDFormat. Mark as MISCONFIG (LOW severity unless metadata leaks internal hostnames or non-public certs, then MEDIUM).
1.7 AWS account-ID extraction
S3 bucket region header (passive):
HEAD https://<known-bucket>.s3.amazonaws.com/
Response includes x-amz-bucket-region. Cross-reference with bucket name entropy and known patterns to scope the account.
ARN regex (in any JSON / HTML / JS response):
arn:aws:[a-z0-9\-]+:[a-z0-9\-]*:([0-9]{12}):
Capture group: 12-digit AWS account ID.
AccountId property pattern:
(?i)["']?account[_\-]?id["']?\s*[:=]\s*["']([0-9]{12})["']
Google OAuth client_id:
\b\d{8,}-[a-z0-9]{10,40}\.apps\.googleusercontent\.com\b
MSAL / Microsoft client_id (GUID property):
(?i)["']?client[_\-]?id["']?\s*[:=]\s*["']([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})["']
OAuth scope extraction:
(?i)["']?scope["']?\s*[:=]\s*["']([^"']+)["']
1.8 Microsoft 365 Deep Enumeration (Teams / SharePoint / OneDrive / OAuth)
Teams federation status:
curl -sk -m 10 "https://login.microsoftonline.com/${TARGET_DOMAIN}/.well-known/openid-configuration" | jq -r '.issuer'
curl -sk -m 10 "https://teams.microsoft.com/api/mt/emea/beta/users/<email>/externalsearchv3"
SharePoint subdomain probe:
STEM=$(echo $TARGET_DOMAIN | cut -d. -f1)
for sub in "" "-my" "-admin"; do
echo "=== ${STEM}${sub}.sharepoint.com ==="
curl -sk -m 10 -I "https://${STEM}${sub}.sharepoint.com/" -w '%{http_code}\n'
done
Reading the result correctly: HTTP 200 from these probes means the tenant exists (Microsoft serves a generic redirect-to-auth page) — it does NOT mean anonymous access is granted to the tenant's content. Distinguish:
- 200 → tenant provisioned (INFO).
- 200 + redirect to a custom anonymous-share URL (
/sites/<x>/Lists/<y>/AllItems.aspx?guestaccesstoken=...) discovered via dorks → HIGH (data exposure).
- 401/403 → tenant exists but auth required (INFO).
- 404 / NXDOMAIN → tenant not provisioned at this stem (or vanity-named — check known stems from cert transparency).
PowerShell:
$STEM = ($D -split '\.')[0]
foreach ($s in @("","-my","-admin")) {
try {
$r = Invoke-WebRequest -Uri "https://${STEM}${s}.sharepoint.com/" -Method Head -UseBasicParsing -TimeoutSec 10
"${STEM}${s}.sharepoint.com -> HTTP $($r.StatusCode) (tenant exists)"
} catch {
$code = $_.Exception.Response.StatusCode.value__
if ($code) { "${STEM}${s}.sharepoint.com -> HTTP $code" } else { "${STEM}${s}.sharepoint.com -> no host" }
}
}
OneDrive personal site probe (for a known email alice@acme.com):
USER_TOKEN=$(echo "alice@acme.com" | tr '@.' '__')
STEM="acme"
curl -sk -m 10 -I "https://${STEM}-my.sharepoint.com/personal/${USER_TOKEN}/Documents/" -w '%{http_code}\n'
M365 OAuth client_id discovery in JS:
curl -sk -m 10 "https://app.target.example/main.js" | \
grep -oE 'clientId["'\''[:=]+ ?["'\'']?[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}'
Device-code phishing target check (look for device_authorization_endpoint in OIDC metadata):
curl -sk -m 10 "https://login.microsoftonline.com/${TARGET_DOMAIN}/v2.0/.well-known/openid-configuration" | \
jq '.device_authorization_endpoint'
If non-null and tenant doesn't restrict device-code: MEDIUM finding (device-code phishing feasible).
Power Platform / Dynamics URLs to check:
*.crm.dynamics.com (per-region: crm, crm2-crm15, crm.dynamics.com).
*.api.crm.dynamics.com (Web API).
make.powerapps.com / flow.microsoft.com (auth-required dashboards).
Severity:
- Discovered SharePoint/OneDrive tenants → INFO (asset only).
- Anonymous SharePoint anonymous-share link → HIGH (data exposure).
device_authorization_endpoint enabled on tenant → MEDIUM (operational risk).
- Multi-tenant OAuth app with broad Graph scopes published by target → HIGH.
1.9 GraphQL Field-Suggestion Enumeration (when introspection disabled)
When the standard introspection query (web-surface §2) returns "errors":[{"message":"GraphQL introspection is disabled"}], fall back to field-suggestion enumeration. Apollo and most GraphQL libraries enable "did you mean" suggestions by default.
Detection probe:
curl -sk -m 10 -X POST "$T/graphql" \
-H 'Content-Type: application/json' \
-d '{"query":"{ __schema { types { name } } }"}' | jq -r '.errors[0].message'
Field-suggestion probe (intentionally typo a field name to trigger suggestions):
curl -sk -m 10 -X POST "$T/graphql" \
-H 'Content-Type: application/json' \
-d '{"query":"{ usre { id } }"}' | jq -r '.errors[].message'
Iterate over a candidate-field wordlist (use SecLists Discovery/Web-Content/graphql.txt or clairvoyance library's seed list). Each suggestion reveals real field names. Continue until no new suggestions emerge.
Tooling:
- Clairvoyance — automated field-suggestion enumerator.
clairvoyance -w wordlist.txt -o schema.json https://target.example/graphql. (Install: see docs/reference/tooling-install.md)
- GraphQL-Cop — auditor that probes for introspection, batching, depth-limit, suggestion config. (Install: see
docs/reference/tooling-install.md)
- InQL (Burp extension) — Burp Suite extension for GraphQL endpoint analysis.
- GraphQL Voyager — visualize once schema is reconstructed.
Other GraphQL-when-introspection-disabled techniques:
-
Alias-based query batching (rate-limit / auth-bypass surface):
{
"query": "{ a:user(id:1){name} b:user(id:2){name} c:user(id:3){name} ... }"
}
Many APIs rate-limit per-request, not per-alias. Test 100+ aliases per request.
-
Query-depth-limit bypass (DoS / introspection bypass):
{
"query": "{ user { friends { friends { friends { friends { id } } } } } }"
}
If server allows arbitrary depth → DoS surface; if depth-limited but doesn't strip nested __type/__schema → introspection-via-depth.
-
Subscription enumeration via WebSocket:
wscat -c "wss://target.example/graphql" -s graphql-ws
> {"type":"connection_init"}
> {"id":"1","type":"start","payload":{"query":"subscription { __schema { types { name } } }"}}
-
Batched query bypass (some servers process all queries in batch even if first fails):
[
{"query":"{ __schema { types { name } } }"},
{"query":"{ user(id:1) { name } }"}
]
Severity:
- Field-suggestion enumeration succeeds (50+ fields recoverable) → MEDIUM
MISCONFIG.
- Alias batching not rate-limited → MEDIUM (rate-limit-bypass surface).
- Subscription endpoint exposed without auth → MEDIUM (often used for real-time data exfil).
2. LinkedIn Employee Enumeration
LinkedIn is the highest-signal source for employee enumeration during external red-team work. Use it for: target list generation, role prioritization, email-pattern derivation, pretext development.
2.1 Search techniques
Free LinkedIn (no Sales Navigator):
- People-search by company:
https://www.linkedin.com/search/results/people/?currentCompany=["<company-id>"]. Get company-id from the company's LinkedIn URL or profile JSON.
- Bypass connection-degree filter: search shows 1st/2nd-degree only by default; use Google dorking instead.
Google dork for LinkedIn employee enum:
site:linkedin.com/in "<company name>"
site:linkedin.com/in "<company name>" "engineer" # role filter
site:linkedin.com/in "<company name>" "<location>" # location filter
site:linkedin.com/in "<company name>" -inurl:/posts
Bing/DuckDuckGo equivalents — sometimes return different result sets; cross-engine union.
LinkedIn Sales Navigator (paid):
- Most efficient if available. Lead lists by company × role × seniority. Export CSV.
Tools:
- LinkedInDumper / Linkook — open-source enum tools (verify currency; they break frequently).
- PhantomBuster / Apollo.io / RocketReach / Hunter.io Email Finder — paid SaaS that does the enum + email derivation in one workflow.
2.2 Role inference for prioritization
For each enumerated employee, capture:
- Name (canonical form: First Last; remove suffixes like "PMP", "PhD" for email-pattern matching).
- Job title (raw + normalized to a role tier).
- Tenure (years at company; longer = more access typically).
- Location (city / region; informs phishing time-of-day).
- Recent activity (posts, comments, articles — informs pretext).
Role priority for breach lookup + phishing target list:
| Role tier | Examples | Why |
|---|
| P0 | CEO, CFO, CTO, CISO, CIO, COO, GC, CRO | Exec accounts; BEC + finance + legal authority. |
| P1 | VP / Director of IT / Security / Engineering / Finance / HR | Privileged tool access; reset workflows. |
| P2 | DevOps, SRE, Platform, Security Engineer, DBA | GitHub / cloud / CI access; secrets in their accounts. |
| P3 | Software Engineer, Architect, Senior Developer | Code + occasional cloud access. |
| P4 | Sales, Marketing, HR, Finance Analyst, Customer Support | SaaS access (Salesforce, HubSpot, Workday); BEC enabler. |
| P5 | Generic individual contributor, intern, contractor | Lowest single-account value but breadth matters. |
2.3 Email-pattern derivation from confirmed names
For each captured name, derive candidate emails using people-breach-intel §2 templates. Cross-reference against:
- Hunter.io
domain-search to confirm pattern.
- Breach corpus (HudsonRock + HIBP + DeHashed + IntelX) to find matches.
For sock-puppet discipline, see osint-methodology §6.1.
2.4 Output
Per discovered employee:
Person:
name: "Alice Doe"
title: "Senior DevOps Engineer"
role_tier: P2
company: "Acme Corp"
location: "Boston, MA"
linkedin_url: https://www.linkedin.com/in/alicedoe
derived_emails:
- alice.doe@acme.com (TENTATIVE)
- adoe@acme.com (TENTATIVE)
- alice@acme.com (TENTATIVE)
breach_hits:
- alice.doe@acme.com (HudsonRock; cleartext password redacted; FIRM)
pretext_hooks:
- "DevOps tooling vendor evaluation" (recent posts)
- "Boston DevOps Days speaker" (conference activity)