{"repro_id":"REPRO-2026-00066","version":5,"title":"Skipper Lua Filter Arbitrary Code Execution","repro_type":"security","status":"published","severity":"high","description":"Arbitrary code execution through [lua filters](https://opensource.zalando.com/skipper/reference/scripts/).\n\nThe default skipper configuration before v0.23 was `-lua-sources=inline,file`. \nThe problem starts if untrusted users can create lua filters, because of `-lua-sources=inline` , for example through a Kubernetes Ingress resource. The configuration `inline` allows these user to create a script that is able to read the filesystem accessible to the skipper process and if the user has access to read the logs they an read skipper secrets.\n\nKubernetes example (vulnerability is not limited to Kubernetes)\n```lua\nfunction request(ctx, params)\n  local file = io.open('/var/run/secrets/kubernetes.io/serviceaccount/token', 'r')\n  if file then\n    local token = file:read('*all')\n    file:close()\n    error('[EXFIL] ' .. token)  -- Exfiltrate via error logs\n  end\nend\n```","root_cause":"## Summary\nSkipper releases prior to v0.23.0 enable inline Lua filters by default (`-lua-sources=inline,file`). Any user able to submit a Lua filter (for example through an Ingress resource) can run arbitrary Lua code inside the Skipper process and access the local filesystem. Our reproduction shows that a crafted inline Lua script can read `/etc/hostname` and leak its contents through Skipper’s logs.\n\n## Impact\n- **Component:** `github.com/zalando/skipper` proxy binary\n- **Affected Versions:** all versions before v0.23.0 (where Lua is enabled by default)\n- **Risk:** High (CVSS 8.8) – attackers can execute arbitrary Lua code, read sensitive files (tokens, secrets), and exfiltrate data via logs or upstream responses. If the Skipper process holds credentials or tokens, they can be stolen.\n\n## Root Cause\nSkipper allowed inline Lua scripts to be embedded directly into route definitions and executed without sandboxing. When `-lua-sources` includes `inline` (default before v0.23.0), user-controlled Lua scripts run in the Skipper process with access to the standard Lua IO library. This enables arbitrary file reads/writes and system interaction. The issue was mitigated in v0.23.0 by disabling Lua filters by default (commit `0b52894570773b29e2f3c571b94b4211ef8fa714`).\n\n## Reproduction Steps\n1. Run `repro/reproduction_steps.sh` from the workspace root.\n2. The script downloads Skipper v0.22.0, writes a malicious inline Lua route that reads `/etc/hostname`, starts Skipper with the route, and triggers it via `curl`.\n3. Successful reproduction is evidenced by the Skipper log entry containing `[EXFIL] <hostname>` and the summary message printed by the script. Logs are stored under `logs/`.\n\n## Evidence\n- **Logs:**\n  - `logs/reproduction.log` – full script run output\n  - `logs/skipper_inline.log` – Skipper process logs\n  - `logs/exfiltration.txt` – filtered `[EXFIL]` line showing leaked hostname\n  - `logs/request.log` – HTTP status (404) from trigger request\n- **Key Excerpt:** `logs/exfiltration.txt` contains `Error calling request ... [EXFIL] <hostname>` proving filesystem data leakage.\n- **Environment:** `uname -a` → `Linux runsc 4.4.0 ... x86_64 GNU/Linux`\n\n## Recommendations / Next Steps\n- Upgrade Skipper to v0.23.0 or later, where Lua filters are disabled by default.\n- If Lua scripts are required, explicitly limit `-lua-sources=file` and ensure only trusted scripts are deployed on the filesystem.\n- Harden Lua execution by removing IO modules or running in a restricted sandbox, and add automated tests to ensure inline Lua stays disabled unless explicitly allowed.\n\n## Additional Notes\n- The reproduction script is idempotent: it reuses downloaded artifacts, stops Skipper between runs, and was executed successfully twice in a row.\n- The exploit demonstrates read access; write/execute primitives are also possible via Lua IO APIs under the same configuration.\n","ghsa_id":"GHSA-cc8m-98fm-rc9g","package":{"name":"github.com/zalando/skipper","ecosystem":"go"},"reproduced_at":"2026-01-17T14:24:32.441874+00:00","duration_secs":545.143123626709,"tool_calls":73,"handoffs":2,"total_cost_usd":0.8861945000000001,"agent_costs":{"repro":0.853173,"support":0.0330215},"cost_breakdown":{"repro":{"gpt-5.1-codex":0.853173},"support":{"gpt-5.1-codex":0.0330215}},"quality":{"idempotent_verified":false,"community_verifications":0},"key_moments":[{"agent":"repro","description":"The vulnerable version of the software was downloaded for setup and testing.","index":142,"time":244482.58805274963,"title":"Downloaded vulnerable version","type":"env_setup"},{"agent":"repro","description":"The downloaded vulnerable package was extracted to prepare the environment.","index":148,"time":254184.94510650635,"title":"Extracted vulnerable package","type":"env_setup"},{"agent":"repro","description":"The exploit code (PoC) was written and saved to trigger the vulnerability.","index":163,"time":327355.12137413025,"title":"Created exploit payload","type":"poc_created"},{"agent":"repro","description":"The exploit script was executed to trigger the vulnerability in the environment.","index":190,"time":398578.69935035706,"title":"Executed exploit script","type":"vuln_triggered"},{"agent":"repro","description":"The presence of exfiltrated data in logs confirmed successful exploitation.","index":211,"time":525804.7497272491,"title":"Verified data exfiltration","type":"confirmation"}],"published_at":"2026-01-17T14:24:43.462889+00:00","retracted":false,"artifacts":[{"path":"repro/rca_report.md","filename":"rca_report.md","size":2909,"category":"analysis"},{"path":"repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":4223,"category":"reproduction_script"},{"path":"bundle/ticket.md","filename":"ticket.md","size":2160,"category":"ticket"},{"path":"logs/exfiltration.txt","filename":"exfiltration.txt","size":425,"category":"other"},{"path":"logs/reproduction.log","filename":"reproduction.log","size":1402,"category":"log"},{"path":"logs/request.log","filename":"request.log","size":3,"category":"log"},{"path":"logs/skipper_inline.log","filename":"skipper_inline.log","size":2132,"category":"log"}]}