Kestrel
대시보드로 돌아가기
CVE-2026-88015MEDIUM· 5.3MITRENVDGHSA대응게시일: 2026. 09. 10.수정일: 2026. 09. 10.

rclone local: crafted Range request against a translated symlink panics (DoS)

위협 신호 · CVSS · EPSS · KEV

정기 패치· 높은 악용 신호 없음
CVSS
5.3medium

이론적 심각도 점수

EPSS

예측 데이터 없음

KEV
미등재

실측 악용 기록 없음

권장 대응 기한14일 이내CISA SSVC 기준

2주 이내 패치 — 우선 조치 대상

자동화 가능외부 노출· KEV 미등재 · 자동화 가능 · 부분 영향 · 외부 노출

CVSS 벡터 · 메트릭

악용 경로
공격 벡터네트워크
공격 복잡도낮음
필요 권한불필요
사용자 상호작용불필요
범위불변
영향
기밀성 영향없음
무결성 영향없음
가용성 영향낮음
버전별 점수
CVSS 3.15.3MODERATE
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:L

상세 설명

Summary

When backend/local is used with --links/-l (or the links=true config option), each symlink is exposed as an rclone object whose content is the target path string, suffixed .rclonelink. Object.Open() decodes an incoming fs.RangeOption via Decode(o.Size()), then for a translated-symlink object passes the decoded offset straight into openTranslatedLink, which indexes the target string directly: linkdst[offset:].

RangeOption.Decode's Start >= 0 branch (an ordinary Range: bytes=X- request) sets offset = o.Start with no upper bound, unlike its suffix-range branch (Start < 0, e.g. bytes=-N), which already clamps a too-large value to 0 - the fix for a prior, related crash (issue #6310: "bytes=-90407" against a 5-byte object panicked with "slice bounds out of range", now covered by an existing regression test). The Start >= 0 branch never received the analogous protection.

A Range: bytes=<hugeStart>- request sent to rclone serve http/webdav (or any consumer of lib/http/serve's Object(), which parses and decodes the client's own Range header) against a directory containing a symlink therefore reaches linkdst[offset:] with offset far beyond the target string's length, and Go panics with "slice bounds out of range" instead of returning an empty read.

Details

Vulnerable code (before fix):

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

PoC

Called the real production Object.Open() on a translated-symlink object (target length 12) with &fs.RangeOption{Start: math.MaxInt64, End: -1}:

text
1panic: runtime error: slice bounds out of range [9223372036854775807:8]
2 ...backend/local.(*Object).openTranslatedLink
3 ...backend/local.(*Object).Open

Impact

A remote client can send a single crafted Range header against any symlink-backed object exposed by rclone serve http/webdav/etc (backed by backend/local with --links enabled) to deterministically panic the request-handling goroutine. Go's net/http recovers panics per-connection by default, so this fails the one request/connection rather than crashing the whole server process, and no file handle is left open (the panic occurs before any read handle is acquired) - but it is fully deterministic and remotely triggerable with no authentication or race window needed, unlike some other panic-recovery findings.

Fix

Clamp offset to the length of the target string before slicing, matching how a real file read past EOF behaves (an empty read):

text
1if offset > int64(len(linkdst)) {
2 offset = int64(len(linkdst))
3}

Note: the shared RangeOption.Decode() also has a related, unaddressed issue - limit = o.End - o.Start + 1 can itself overflow to a large negative number for a huge End - but a fix attempted there during this investigation broke fs/operations/reopen.go's NewReOpen, which calls Decode with its h.end field still at its zero value at that point in construction. Flagged for awareness but not changed here to keep this patch minimal and low-risk.

AI 심층 분석

공격 시나리오 · 재현 가능한 PoC 페이로드 · 즉시 적용 가능한 차단 패치를 한 번에 받아 보세요. 보안 운영팀이 그대로 점검·티켓팅에 쓸 수 있는 형태로 정리해 드립니다.