This is leaving a side door cracked open, not discovering a new hole in the wall
Tenable plugin 111351 is not a software flaw with a fixed version; it is a configuration exposure finding for HashiCorp Consul. The risky pattern is: the Consul HTTP/UI listener is bound off-loopback via client_addr, the optional Web UI is enabled, and unauthenticated requests inherit permissions from either acl.tokens.default or the built-in anonymous token. HashiCorp documents that client_addr defaults to 127.0.0.1, the UI is not enabled by default for non-dev agents, and ACLs are what secure the API/UI/CLI.
Tenable's HIGH / 8.8 overstates reality for most enterprises because the attack path depends on multiple admin choices that are all non-default and deployment-specific: reachable network exposure, permissive token behavior, and in the worst case additional dangerous settings like enable_script_checks. If those prerequisites are all true on an internet-facing node, the risk climbs fast; but as a fleet-wide patching priority this behaves like a context-heavy hardening issue, not a universally exploitable RCE.
4 steps from start to impact.
Reach the Consul HTTP surface with curl
8500 over HTTP or 8501 over HTTPS. This only exists when operators bind client_addr to a non-loopback address or otherwise publish the service through a proxy, load balancer, or Kubernetes service.- Consul is installed and running
client_addror equivalent exposure is non-loopback- Network path exists from attacker to the Consul listener
- HashiCorp documents the default
client_addras127.0.0.1 - Many enterprise deployments keep Consul on management or cluster-only networks
- Firewalls, security groups, or service meshes often block direct access
Inherit permissions through the default or anonymous token
default ACL token, or the built-in anonymous token if no default token exists. This is the decisive branch: if those effective permissions are restrictive, the finding collapses into mere exposure; if they are permissive, the attacker gets unauthenticated read or write capability.- ACLs are enabled or token behavior is otherwise active
- Unauthenticated requests map to a token with meaningful permissions
- A correctly configured anonymous token with
default_policy = denyblocks useful unauthenticated access - The blast radius is constrained by the policy attached to the effective token
- Tenable does not prove the token policy is actually dangerous on every flagged host
acl.tokens.default; passive network scanners usually cannot determine the attached policy safely.Enumerate topology and service data via the API/UI
curl or the Consul UI to query nodes, services, health checks, and possibly key/value data depending on policy. In a real environment that can leak internal hostnames, addresses, service names, and trust relationships that materially help later movement.- Effective unauthenticated token has read access to catalog, health, or KV endpoints
- Many hardened deployments separate read from write and keep KV secrets elsewhere
- Read-only exposure is damaging but usually not immediate host takeover
- Some API endpoints still require stronger privileges than basic catalog discovery
Modify service state or weaponize checks with curl/consul
service:write or related agent privileges, an attacker may register or alter services and health checks. That becomes materially worse only if operators also enabled dangerous options such as enable_script_checks or remote exec-related capabilities, which HashiCorp explicitly warns can introduce remote execution risk when the API is exposed.- Effective unauthenticated token includes write permissions
- The target agent accepts the relevant registration endpoints
- For code execution scenarios, script checks or remote exec features are enabled
- Service registration write access is not implied by mere UI exposure
enable_script_checksis not on by defaultdisable_remote_execchanged to secure-by-default behavior in newer Consul releases
PUT /v1/agent/service/register or new check definitions.The supporting signals.
| In-the-wild status | No CVE and not listed in CISA KEV; this is a misconfiguration exposure pattern rather than a tracked vulnerability campaign. |
|---|---|
| PoC availability | No exploit is needed for the base condition; curl against the Consul API is sufficient if the listener is exposed and the effective unauthenticated token is permissive. HashiCorp's own docs show the relevant API paths. |
| EPSS | N/A because this finding has no mapped CVE. |
| KEV status | Not applicable / not listed; there is no CVE record to track in KEV. |
| Vendor score baseline | Tenable assigns CVSS 8.8 with CVSS:3.0/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H, but that assumes attacker reachability and high-impact permissions that are not inherent to every flagged Consul node. |
| Affected scope | Potentially any Consul version/configuration where the HTTP/UI listener is exposed beyond loopback and unauthenticated requests inherit meaningful ACL rights. This is configuration-dependent, not version-bound. |
| Fixed version | None. There is no patched Consul release for this finding; the remedy is configuration hardening: loopback-only or tightly filtered exposure, restrictive anonymous/default token behavior, ACLs, and TLS. |
| Default behavior that matters | HashiCorp says client_addr defaults to 127.0.0.1 and the Web UI is not enabled by default for non-dev agents. Those defaults are major downward pressure on severity. |
| Exposure/scanning reality | Consul commonly uses port 8500 for API/UI, and internet-scanning ecosystems index that port. Exposure is easy to find when operators publish it, but enterprise reachability is usually limited to internal management networks. |
| Disclosure / reporting | Tenable plugin published 2018-07-26; HashiCorp's support article discussing this scanner finding was updated 2025-01-25. |
noisgate verdict.
The decisive factor is compounded prerequisite friction: this requires non-default network exposure and useful unauthenticated token permissions before it becomes more than a hardening smell. The reachable population is much narrower than the vendor CVSS suggests, and the blast radius is bounded by the effective ACL policy rather than automatic full host compromise.
Why this verdict
- Downward pressure: requires reachable Consul API/UI. If the listener is left at the documented default
127.0.0.1, the attack chain dies immediately. - Downward pressure: requires permissive effective token behavior. Unauthenticated requests only matter if
acl.tokens.defaultoranonymoushas useful rights; a deny-by-default anonymous token turns this into noise. - Downward pressure: host takeover is not intrinsic. Tenable hints at service registration and remote access, but that escalated outcome generally needs extra risky features like
enable_script_checksor remote exec-related capabilities. - Upward pressure: if exposed, the intel value is real. Even read-only catalog/KV visibility can materially aid lateral movement by mapping services, nodes, health, and trust edges.
- Upward pressure: Consul is high-value control-plane tech. Bad permissions here can let an attacker manipulate discovery and traffic decisions, so you should not ignore confirmed unsafe instances.
Why not higher?
This is not a universal unauthenticated RCE and not even a universal unauthorized read. It depends on a stack of non-default conditions: off-loopback binding, network path, permissive token semantics, and possibly additional dangerous features for execution impact.
Why not lower?
A confirmed exposed Consul API with inherited read or write permissions is not harmless. Because Consul is a service-discovery/control-plane component, even partial anonymous access can expose topology or let an attacker poison service state in ways that matter operationally.
What to do — in priority order.
- Bind Consul back to loopback where possible — Set
client_addrto loopback or remove the override so the documented default applies. This is the cleanest risk kill-switch and, for a MEDIUM verdict, there is no mitigation SLA — go straight to the 365-day remediation window, but confirmed externally reachable cases should be corrected much sooner as operational hardening. - Strip permissions from unauthenticated paths — Ensure unauthenticated requests fall to a tightly restricted
anonymoustoken and avoid assigning broad rights toacl.tokens.default. This limits exposure even when the API must remain reachable for operational reasons; complete within the same remediation cycle. - Require TLS and trusted network paths — Use TLS for API access and restrict ingress with host firewall rules, security groups, or cluster-only service exposure. This reduces opportunistic discovery and credential leakage while you normalize Consul configs across the fleet.
- Keep script-based execution paths off — Verify
enable_script_checksis disabled unless there is a documented exception, preferenable_local_script_checkswhen needed, and keep remote exec disabled. This prevents the finding from turning into a much nastier execution path. - Alert on anonymous API use — Log and alert on requests lacking
X-Consul-Tokenor bearer auth, plusPUToperations to service/check registration endpoints. This gives you immediate detection value even if the configuration cannot be changed in one maintenance window.
- A generic EDR on the host does not solve the base exposure; it may catch a later script-execution stage, but it won't stop topology leakage or service-catalog tampering via the API.
- Turning on the Web UI login experience alone does not help if unauthenticated API requests still inherit a permissive default or anonymous token.
- Relying on 'internal only' network assumptions is weak; if the attacker already has a foothold on a workstation, VPN segment, or peered VPC, this becomes reachable post-initial-access.
Crowdsourced verification payload.
Run this on the target Consul host as root or with enough privileges to read Consul config files and inspect listening sockets. Example: sudo bash ./check_consul_exposure.sh /etc/consul.d; it checks whether Consul is bound off-loopback and whether unauthenticated reads succeed against the local API.
#!/usr/bin/env bash
# check_consul_exposure.sh
# Exit codes:
# 0 = PATCHED (not vulnerable based on current checks)
# 1 = VULNERABLE
# 2 = UNKNOWN
set -u
CONFIG_DIR="${1:-/etc/consul.d}"
API_PORT="8500"
HTTPS_PORT="8501"
status="UNKNOWN"
reason=""
have_cmd() { command -v "$1" >/dev/null 2>&1; }
json_get_http_code() {
local url="$1"
curl -ksS -o /tmp/consul_check_body.$$ -w "%{http_code}" "$url" 2>/dev/null || echo "000"
}
cleanup() {
rm -f /tmp/consul_check_body.$$ 2>/dev/null || true
}
trap cleanup EXIT
if ! have_cmd curl; then
echo "UNKNOWN - curl not found"
exit 2
fi
# 1) Check listening addresses for Consul on 8500/8501.
listen_non_loopback=0
if have_cmd ss; then
if ss -lntp 2>/dev/null | grep -E ':(8500|8501)\b' | grep -vqE '127\.0\.0\.1:|\[::1\]:'; then
listen_non_loopback=1
fi
elif have_cmd netstat; then
if netstat -lntp 2>/dev/null | grep -E ':(8500|8501)\b' | grep -vqE '127\.0\.0\.1:|::1:'; then
listen_non_loopback=1
fi
else
echo "UNKNOWN - neither ss nor netstat found"
exit 2
fi
# 2) Query unauthenticated local endpoints.
# If HTTP is disabled locally, try HTTPS.
code_catalog="$(json_get_http_code "http://127.0.0.1:${API_PORT}/v1/catalog/services")"
if [[ "$code_catalog" == "000" ]]; then
code_catalog="$(json_get_http_code "https://127.0.0.1:${HTTPS_PORT}/v1/catalog/services")"
fi
code_self="$(json_get_http_code "http://127.0.0.1:${API_PORT}/v1/agent/self")"
if [[ "$code_self" == "000" ]]; then
code_self="$(json_get_http_code "https://127.0.0.1:${HTTPS_PORT}/v1/agent/self")"
fi
# 3) Optional config hints.
ui_enabled="unknown"
client_addr_external="unknown"
default_token_present="unknown"
script_checks_enabled="unknown"
if [[ -d "$CONFIG_DIR" ]]; then
if grep -RqsE 'ui_config\s*\{[^}]*enabled\s*=\s*true|^\s*-ui\b|^\s*ui\s*=\s*true' "$CONFIG_DIR" 2>/dev/null; then
ui_enabled="true"
else
ui_enabled="false"
fi
if grep -RqsE 'client_addr\s*=\s*"(0\.0\.0\.0|[^1][^"]*|{{.*}})"' "$CONFIG_DIR" 2>/dev/null; then
client_addr_external="true"
else
client_addr_external="false"
fi
if grep -RqsE 'tokens\s*\{[^}]*default\s*=' "$CONFIG_DIR" 2>/dev/null; then
default_token_present="true"
else
default_token_present="false"
fi
if grep -RqsE 'enable_script_checks\s*=\s*true' "$CONFIG_DIR" 2>/dev/null; then
script_checks_enabled="true"
else
script_checks_enabled="false"
fi
fi
# 4) Decision logic.
if [[ "$listen_non_loopback" -eq 1 && ( "$code_catalog" == "200" || "$code_self" == "200" ) ]]; then
status="VULNERABLE"
reason="Consul listens off-loopback and unauthenticated API reads succeeded"
elif [[ "$listen_non_loopback" -eq 0 ]]; then
status="PATCHED"
reason="Consul listener appears loopback-only"
elif [[ "$listen_non_loopback" -eq 1 && ( "$code_catalog" == "403" || "$code_self" == "403" ) ]]; then
status="PATCHED"
reason="Consul is reachable locally but unauthenticated requests are denied; exposure remains a hardening concern if network-published"
else
status="UNKNOWN"
reason="Could not conclusively verify listener exposure and unauthenticated access"
fi
echo "${status} - ${reason} | ui_enabled=${ui_enabled} client_addr_external=${client_addr_external} default_token_present=${default_token_present} script_checks_enabled=${script_checks_enabled} http_catalog=${code_catalog} http_self=${code_self}"
case "$status" in
VULNERABLE) exit 1 ;;
PATCHED) exit 0 ;;
*) exit 2 ;;
esac
If you remember one thing.
client_addr is loopback-only unless there is a documented need, unauthenticated requests inherit no useful rights, and risky execution features remain off. If you confirm any instance is reachable from untrusted networks *and* anonymous/default-token reads or writes work, do not wait for the general schedule: fix the exposure immediately and complete the permanent configuration correction inside the noisgate remediation SLA of ≤ 365 days.Sources
- Tenable Nessus Plugin 111351
- HashiCorp Help Center: Consul Web UI and API is Accessible Remotely if not Configured Properly
- HashiCorp Consul UI documentation
- HashiCorp Consul security architecture
- HashiCorp HTTP API structure and authentication
- HashiCorp ACL token documentation
- HashiCorp default token and anonymous token relationship
- HashiCorp health checks and script-check security warning
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.