내 최소 메모리 안전 Go rsync가 취약점을 피하는 방법
(michael.stapelberg.ch)- gokrazy/rsync는 Go로 작성한 최소 rsync 구현으로, 2025년 1월·2026년 5월 공개된 rsync 취약점 12개를 기준으로 검토됨
- Go의 경계 검사와 0 초기화는 힙 오버플로·스택 정보 유출을 panic 또는 무해한 값으로 바꾸지만, panic은 여전히 서비스 거부가 될 수 있음
- 경로 순회와 TOCTOU 계열은 Go 1.24의 os.Root 같은 순회 저항 파일 API가 핵심 방어가 되며, gokrazy/rsync는 전면 전환됨
- 최소 구현 전략은
--inc-recursive, 압축,--safe-links, 프록시, 호스트명 ACL 등 미구현 기능 관련 취약점을 피하게 해 공격 표면을 줄임 - 취약점 대부분은 검증 누락과 과도한 복잡성에서 나왔고, 단순한 사용 사례에는 단순한 구현이 더 적절할 수 있음
배경과 범위
- 2025년 1월 여러 보안 연구자가 upstream Samba rsync에서 총 6개 보안 취약점을 공개했고, 일부는 임의 코드 실행과 파일 유출을 허용함
- Go로 작성한 호환·최소 구현인 gokrazy/rsync가 이런 취약점 계열을 실제로 피할 수 있는지가 핵심 검토 대상임
- 분석 대상은 2025년 1월 배치와 2026년 5월 배치를 합친 12개 취약점임
- upstream rsync를 프로덕션에서 실행 중이면 3.4.3 이상으로 업그레이드해야 하며, gokrazy/rsync는 v0.3.3 이상으로 올려야 함
- gokrazy/rsync는 Linux 배포판 연구 프로젝트 distri의 소프트웨어 패키지를 router7에서 제공하려고 작성됐고, router7은 Go 어플라이언스 플랫폼 gokrazy 기반임
- 초기에는 rsync 서버만 있었지만 현재는 모든 방향을 지원하며, Go 프로그램에 링크할 수 있는 rsync 기본 기능으로 여러 gokrazy/rsync 서버에서 사용 중임
2025년 1월 취약점
-
CVE-2024-12084: 힙 버퍼 오버플로, 심각도 9.8
- rsync는 네트워크에서 받은 체크섬 길이를
MAX_DIGEST_LEN과 비교했지만, 내부 자료구조는 항상 16바이트 버퍼char sum2[SUM_LENGTH]를 사용함 MAX_DIGEST_LEN은 SHA256 또는 SHA512 체크섬 지원으로 컴파일되면 더 커질 수 있어, 공격자가sum2버퍼 한계를 최대 48바이트 넘겨 쓸 수 있었음- 문제는 SHA256/SHA512 체크섬 지원을 추가한 2022년 9월 commit
ae16850에서 도입됨 - upstream 수정은
sum2를 동적 할당되는sum2_array로 바꾸고, 전송 알고리듬의 체크섬 길이인xfer_sum_len으로 할당·검사하도록 고침 - Go에서는 잘못된 경계 검사가 힙 버퍼 오버플로로 이어지지 않고 런타임 경계 검사로 panic이 발생함
- gokrazy/rsync에도 sum header 검증 누락이 있었지만 크기 혼동은 아니었고,
ChecksumLength를512로 바꾸면 Go 런타임이panic: runtime error: slice bounds out of range [:512] with length 16을 발생시킴 - 서버 전체 크래시는 바람직한 실패 모드가 아니므로, 누락된 경계 검사를 추가해 panic을 error로 바꿈
- rsync는 네트워크에서 받은 체크섬 길이를
-
CVE-2024-12085: 스택 정보 유출로 ASLR 우회, 심각도 7.5
- CVE-2024-12084와 같은 검증 누락 때문에 공격자가 짧은 체크섬 알고리듬을 선택한 뒤 더 긴 체크섬을 보냈다고 주장할 수 있었음
- Google Security report에 따르면 힙 버퍼 오버플로와 정보 유출이 결합되면 익명 읽기 접근만 가진 클라이언트가 rsync 서버 머신에서 임의 코드를 실행할 수 있음
hash_search()는 스택의char sum2[MAX_DIGEST_LEN]에 청크 digest를 만들고memcmp()로 비교했지만, 로컬sum2스택 버퍼가 초기화되지 않아 남은 바이트가 스택 내용을 담을 수 있었음- 악성 클라이언트는 파일 다운로드당 1바이트를 유출할 수 있고, 유출 데이터에는 heap 객체 포인터, stack cookie, 지역 변수, 전역 변수 포인터, return pointer가 포함될 수 있음
- “Some checksum buffer fixes”는 공격자 제어
s->s2length가 전송 체크섬 길이보다 커질 수 없게 했고, “prevent information leak off the stack”는sum2메모리를 0으로 초기화함 - Go는 모든 변수를 zero value로 초기화하므로 이 취약점에 영향받지 않으며, gokrazy/rsync는 MD4 외 체크섬 선택이 도입된 프로토콜 버전 30이 아니라 프로토콜 버전 27을 구현함
-
CVE-2024-12087: 심볼릭 링크를 이용한 경로 순회, 심각도 7.5
- Google Security report에 따르면
-l또는-a(--archive)로 심볼릭 링크 동기화가 활성화됐을 때, 악성 서버가 클라이언트에게 대상 디렉터리 밖 임의 파일을 쓰게 만들 수 있음 - 공격은
--inc-recursive모드에서 여러 파일 목록을 보내고, 첫 목록에서는symlink를 디렉터리로, 다음 목록에서는 같은 이름을 대상 디렉터리 밖을 가리키는 심볼릭 링크로 바꾸는 방식임 - Go 자체는 이 취약점을 막지 못하며, 원인은 여러 파일 목록을 병합한 뒤 다시 검증하지 않은 논리 오류임
- upstream 수정은 누락된 검증을 추가함
- gokrazy/rsync는 incremental recursion 모드(
--inc-recursive)를 구현하지 않아 영향받지 않음 - incremental recursion은 전체 파일 집합을 전송 시작 전에 모두 스캔하지 않고 “windowed” 방식으로 처리하게 해주지만, 구현 복잡성과 자원 사용량 사이의 트레이드오프가 있음
- Google Security report에 따르면
-
CVE-2024-12088:
--safe-links우회, 심각도 7.5- Google Security report에 따르면
--safe-links는 서버에서 받은 심볼릭 링크가 대상 디렉터리 안을 가리키도록 검증하는 기능임 - 우회는 심볼릭 링크 대상 경로 중간에 다른 심볼릭 링크가 있는지를 고려하지 않아 발생함
- 예를 들어
{DESTINATION}/a -> .와{DESTINATION}/foo -> a/a/a/a/a/a/../../가 있으면,foo는 실제로 대상 디렉터리 밖을 가리키지만unsafe_symlink()는a/를 디렉터리로 가정해 안전하다고 판단함 - upstream 수정은 경로 시작 부분을 제외한 위치의
../를 허용하지 않도록unsafe_symlink()를 더 엄격하게 만듦 - Go 자체는 잘못된 검증 함수를 막지 못하며, gokrazy/rsync는 아직
--safe-links기능을 구현하지 않아 취약하지 않음
- Google Security report에 따르면
-
CVE-2024-12086: 임의 파일 유출, 심각도 6.8
- rsync receiver가 클라이언트 모드에서 rsync sender가 제공한 파일 이름을 정리하지 않았고, 대상 트리 밖 파일 열기를 막지 못함
- 악성 sender는 receiver에게 대상 트리 밖 임의 파일의 체크섬을 비교하도록 지시하고, 1바이트 체크섬에 대한 receiver 반응을 관찰해 임의 파일을 유출할 수 있었음
- Google Security report에 따르면 클라이언트가 악성 서버에 연결하면 서버는 클라이언트 머신의 임의 파일 내용을 유출할 수 있음
- upstream 수정은 sender 제공 경로를 검증해 대상 트리 밖 파일 열기를 방지함
- Go에는 이를 막을 수 있는 API가 있으며, 관련 방어로 Go의
os.Root가 제시됨 - gokrazy/rsync는 fuzzy matching 기능이 도입된 프로토콜 버전 29가 아니라 프로토콜 버전 27을 구현하므로 취약하지 않음
-
CVE-2024-12747: 심볼릭 링크 경쟁 조건, 심각도 5.6
- Red Hat Security Advisory에 따르면 rsync의 심볼릭 링크 처리 중 경쟁 조건에서 발생한 결함임
- rsync의 기본 동작은 심볼릭 링크를 만나면 건너뛰는 것이지만, 공격자가 적절한 시점에 일반 파일을 심볼릭 링크로 바꾸면 기본 동작을 우회해 심볼릭 링크를 순회할 수 있었음
- upstream 수정은 rsync sender의
open()호출에O_NOFOLLOW옵션을 사용하도록 바꿈 - gokrazy/rsync는 commit
1b1fbf6전까지 취약했으며, 이 커밋은 upstream rsync와 같은O_NOFOLLOW완화를 도입함 - Damien Neil은 gokrazy의 CVE-2024-12747 수정이 불충분하다고 봤고,
O_NOFOLLOW는 마지막 경로 구성 요소의 심볼릭 링크 순회만 막는다고 판단함 - 예를 들어
os.Open("dir/passwd")에서 이전 경로 구성 요소인dir을/etc로 향하는 심볼릭 링크로 바꾸면 여전히 우회 가능함 - 이 문제는 2025년 4월 rsync 보안 연락처로 보고됐고, 2026-05-20에 공개된 CVE-2026-29518로 이어짐
2026년 5월 취약점
-
CVE-2026-29518: 심볼릭 링크 경쟁 조건, 심각도 7.0
- rsync 3.4.3 NEWS entry에 따르면 chroot 없이 daemon mode에서 로컬 권한 상승을 허용하는 TOCTOU 심볼릭 링크 경쟁 조건임
use chroot = no로 설정된 rsync daemon은 parent path component에 대한 time-of-check/time-of-use 경쟁에 노출됨- 모듈에 쓰기 접근 권한이 있는 로컬 공격자는 receiver의 검사와
open()사이에 parent directory component를 심볼릭 링크로 바꿔, 읽기에서는 basis-file disclosure를, 쓰기에서는 module 밖 file overwrite를 유발할 수 있음 - 기본값인
use chroot = yes는 노출되지 않음 - upstream 수정은 Go의
os.RootAPI와 유사한secure_relative_open()을 사용함 - gokrazy/rsync는 sender와 receiver를 순회 저항
os.RootAPI로 전환하기 전까지 취약했음
-
CVE-2026-43618: 정수 오버플로로 원격 메모리 유출, 심각도 8.1
- rsync 수신자의 압축 토큰 디코더가 32비트 부호 있는 카운터를 오버플로 검사 없이 누적해, 악의적 송신자가 프로세스 메모리 내용을 유출할 수 있음
- 유출 대상에는 환경 변수, 비밀번호, 힙과 라이브러리 포인터가 포함될 수 있으며, ASLR을 약화하고 추가 익스플로잇을 쉽게 만들 수 있음
- 영향 범위는 압축이 활성화된 인증된 데몬 연결이며, 프로토콜 30 이상에서 양쪽 피어가 압축을 광고하면 기본값으로 활성화됨
- 우회책은 rsyncd.conf에서
refuse options = compress로 데몬 압축을 비활성화하는 것임 - upstream 수정은 누락된 검사를 추가함
- gokrazy/rsync는 압축을 구현하지 않아 취약하지 않으며, 압축 지원이 단순해 보이지만 비자명한 이유는 gokrazy/rsync issue #35에 정리돼 있음
-
CVE-2026-43620: 범위 밖 읽기 이후 서비스 거부, 심각도 6.5
- 2025년에
send_files()에 추가된parent_ndx<0가드가 시각적으로 동일한recv_files()블록에는 적용되지 않아 발생함 - 악의적 rsync 서버가
CF_INC_RECURSE호환성 플래그와 잘못 구성된 flist를 보내면 수신자가dir_flist->files[-1]을 읽고 역참조해 결정적SIGSEGV로 이어질 수 있음 - 영향 범위는 공격자가 제어하는 URL에서 일반 pull을 수행하는 모든 rsync 클라이언트이며, 프로토콜 30 이상의 기본값인
inc_recurse때문에 피해자 쪽 특수 옵션이 필요 없음 - 클라이언트에서
--no-inc-recursive를 사용하는 것이 우회책이며, upstream 수정은recv_files()에도parent_ndx<0가드를 추가함 - gokrazy/rsync는
--inc-recursive증분 재귀 모드를 구현하지 않아 CVE-2024-12087과 마찬가지로 영향을 받지 않음
- 2025년에
-
CVE-2026-43619: 추가 심볼릭 링크 경쟁 조건, 심각도 6.3
- 수신자의
open()호출에 대한 심볼릭 링크 경쟁 조건 수정(CVE-2026-29518)이chmod,lchown,utimes,rename,unlink,mkdir,symlink,mknod,link,rmdir,lstat등 다른 경로 기반 시스템 호출에는 적용되지 않았음 use chroot = no로 구성된 rsync 데몬에서 로컬 공격자가 수신자의 검사와 시스템 호출 사이에 부모 디렉터리 구성요소를 심볼릭 링크로 바꿔 내보낸 모듈 밖으로 리디렉션할 수 있음- 기본값인
use chroot = yes는 노출되지 않음 - upstream 수정은 Linux 5.6+의
openat2, FreeBSD 13+와 macOS 15+의O_RESOLVE_BENEATH, 그 외 환경의 구성요소별O_NOFOLLOW순회처럼 커널이 강제하는 제한 아래 열린 부모dirfd를 통해 영향을 받는 경로 기반 시스템 호출을 처리함 - gokrazy/rsync는 Go의
os.RootAPI를 전반적으로 사용하기 때문에 영향을 받지 않음
- 수신자의
-
CVE-2026-43617: 호스트명 기반 ACL 우회, 심각도 4.8
- 전역
daemon chroot = /Xrsyncd.conf 설정이 있는 rsync 데몬에서 접속 클라이언트의 역방향 DNS 조회가 데몬이/X로 chroot한 뒤 수행됐음 /X에 glibc 해석에 필요한/etc/resolv.conf,/etc/nsswitch.conf,/etc/hosts, NSS 서비스 모듈이 없으면 조회가 실패하고 접속 호스트명이"UNKNOWN"으로 설정됨hosts deny = *.evil.example같은 호스트명 기반 deny 규칙이 매칭되지 않아, PTR 레코드를 제어하는 공격자가 관리자가 차단하려던 호스트명에서 접속할 수 있음- IP 기반 ACL은 영향을 받지 않고, 모듈별
use chroot설정은 이 취약점과 무관함 - upstream 수정은 DNS 조회를 프로토콜의 더 이른 시점으로 이동함
- gokrazy/rsync는 호스트명 기반 allow/deny 목록을 구현하지 않고 IP 기반 allow/deny 목록만 구현해 취약하지 않음
- 전역
-
CVE-2026-45232: 스택 범위 밖 쓰기, 심각도 3.1
- rsync 클라이언트의 HTTP
CONNECT프록시 지원에는establish_proxy_connection()의 off-by-one 스택 범위 밖 쓰기가 있음 - 프록시 또는 중간자 공격자가 첫 응답 줄에
'\n'없이 1023바이트 이상을 반환하면, 후속 코드가 1024바이트 버퍼 바로 뒤에'\0'을 써 인접 스택 슬롯을 손상시킬 수 있음 - AddressSanitizer는
establish_proxy_connection프레임의socket.c:95에서stack-buffer-overflow를 보고함 - upstream 수정은 공격자가 제공한 데이터를 검증함
- gokrazy/rsync는 이런 프록시 지원을 구현하지 않아 취약하지 않음
- rsync 클라이언트의 HTTP
Go와 gokrazy/rsync가 실제로 막은 것
- Go 런타임의 경계 검사는 더 심각한 보안 문제를 panic으로 바꾸며, panic은 여전히 서비스 거부 위험이지만 더 나은 실패 모드로 평가됨
- Go는 메모리를 0으로 초기화해 CVE-2024-12085 같은 정보 유출을 불가능하게 만듦
- Go의
os.RootAPI는 남은 취약점 대부분을 방지함 - 12개 취약점 중 CVE-2026-43617 하나만 Go 사용으로 막을 수 없는 애플리케이션 로직 버그로 분류됨
- gokrazy/rsync와 공식 upstream rsync의 핵심 차이는 Go로 작성됐다는 점 외에도 구현이 최소화돼 있다는 점임
- gokrazy/rsync는
--inc-recursive,--safe-links, 압축, 프록시, 호스트명 ACL처럼 문제가 된 여러 기능을 구현하지 않아 여러 취약점을 피함 - 모든 wire protocol 호환 rsync 구현처럼 gokrazy/rsync는 프로토콜 버전 27을 대상으로 하며, 이후 프로토콜 버전은 상당한 복잡성을 도입함
- 게시 시점 기준 CVE별 gokrazy/rsync 상태:
2024-12084: 구현은 있었고 panic 발생2024-12085: 프로토콜 30 기능이라 구현하지 않아 취약하지 않음2024-12086: 프로토콜 29 기능이라 구현하지 않아 취약하지 않음2024-12087:inc-rec를 구현하지 않아 취약하지 않음2024-12088:safe-links를 구현하지 않아 취약하지 않음2024-12747: 구현이 있었고 취약했음2026-29518: 구현이 있었고 패치됨2026-43617: 호스트 deny 목록을 구현하지 않아 취약하지 않음2026-43618: 압축을 구현하지 않아 취약하지 않음2026-43619: 구현이 있었고 패치됨2026-43620:inc-rec를 구현하지 않아 취약하지 않음2026-45232: 프록시를 구현하지 않아 취약하지 않음
- gokrazy/rsync의 알려진 모든 취약점은 수정됐으며, 위 상태는 각 CVE가 공개됐던 시점의 상태를 나타냄
- 2025년 1월 취약점 공개 당시 gokrazy/rsync는 CVE-2024-12084에서 panic이 발생했고 CVE-2024-12747 TOCTOU 경쟁 조건에는 취약했음
- TOCTOU 문제를 수정하는 과정에서 CVE-2026-29518이 발견됐고, 해당 CVE 공개 전에 gokrazy/rsync에서 수정됨
- CVE-2026-43619는 더 늦게 발견됐지만, Go의
os.Root전면 사용이라는 같은 수정으로 이미 gokrazy/rsync에서 해결돼 있었음
역할 구분과 취약점 명명
- 여러 취약점 보고서는 “server”와 “client”라는 표현을 사용했지만, rsync 전송에서는 rsync 클라이언트와 서버 양쪽 모두가 송신자(sender, 파일 업로드) 또는 수신자(receiver, 파일 다운로드) 역할을 맡을 수 있음
- 데몬 모드에서는 파일시스템 접근을 미리 구성된 모듈 경로로 제한할 수 있지만, command mode에서는 그렇지 않음
- 4가지 구성과 역할/프로토콜 계층은 rsync combinations 다이어그램에서 확인 가능함
- Arbitrary File Leak 취약점(CVE-2024-12086)의 원래 제목인 “Server leaks arbitrary client files”는 오해를 부르기 쉬움
- 더 정확한 표현은 rsync 수신자가 악의적 송신자에게 임의 파일을 유출한다는 것임
- 악의적 클라이언트 송신자는 SSH를 통한 command mode 같은 환경에서 패치되지 않은 원격 rsync가 대상 트리 밖의 파일, 예를 들어
/etc/shadow시스템 비밀번호 데이터베이스를 열게 만들 수 있음 - 데몬 모드에서는 서버가 추가 경로 정규화(path sanitization)를 활성화해 이 공격을 막음
- Symlink Path Traversal 취약점(CVE-2024-12087)도 “malicious server”라고 표현하지만, 정확히는 클라이언트 또는 서버 어느 쪽이든 될 수 있는 악의적 송신자임
OpenBSD openrsync와 비교
- OpenBSD 프로젝트는 보안에 집중하는 것으로 알려져 있으며, openrsync는 체크섬 길이를 검증하고 체크섬 크기/알고리듬으로 MD4 하나만 지원해 Heap Buffer Overflow(CVE-2024-12084)와 Stack Info Leak(CVE-2024-12085)의 영향을 받지 않음
- openrsync는 gokrazy/rsync처럼 관련 기능을 구현하지 않아 CVE-2024-12086, CVE-2024-12087, CVE-2024-12088의 영향을 받지 않음
- 설령 취약했더라도 OpenBSD에서 실행될 때는 OpenBSD
unveil(2)와pledge(2)로 파일시스템 접근을 제한하는 심층 방어가 성공적 악용을 막았을 수 있음 - openrsync는 심볼릭 링크 지원을 구현한 순간부터
O_NOFOLLOW를 사용했기 때문에 CVE-2024-12747의 영향을 받지 않음 - 하지만
O_NOFOLLOW는 이 문제에 충분한 수정이 아니므로 openrsync는 CVE-2026-29518에는 영향을 받음 - 2026년 5월 묶음도 대부분 관련 기능을 구현하지 않았다는 점에서 유사함
- openrsync는 검증을 성실히 구현하고 공격 표면을 제한하며 심층 방어를 적용해 보고된 취약점 대부분의 영향을 받지 않음
심층 방어와 os.Root
-
Linux mount namespace
- gokrazy/rsync 프로젝트를 시작한 지 몇 주 안에 파일시스템 객체 접근을 제한하기 위해 Linux에서 권한 강하와 mount/pid namespace 사용을 지원하는 기능이 추가됨
- 이 접근은 경로 순회 공격 완화에 잘 작동하지만 권한이 필요해
root로 실행하거나 배포판/시스템에서 활성화된 경우 Linux user namespace 안에서 실행해야 함 - mount namespace는 서버 구성에는 적합하지만, 보통 사람 사용자의 계정으로 실행되는 대화형 일회성 전송에는 사용할 수 없는 경우가 많음
-
systemd 하드닝
- Linux mount/pid namespace 지원을 도입한 같은 커밋에는 홈 디렉터리로 파일시스템 접근을 제한하는 systemd 서비스 파일도 포함됐고, README에서는 사용 사례에 따라 파일시스템 접근을 더 제한하도록 권장함
- 이런 파일시스템 제한이 올바르게 구성되면 File Leak(CVE-2024-12086)과 Path Traversal(CVE-2024-12087) 취약점을 완화함
- Symlink Race Condition(CVE-2024-12747)은 rsync 프로세스를 통한 권한 상승에 의존하지만, DynamicUser 기능 덕분에 프로세스가 다른 사용자보다 적은 권한을 가짐
- mount namespace와 마찬가지로 서버 구성에는 좋지만 대화형 일회성 사용에 설정하기에는 번거로움
-
Linux Landlock
- Porting OpenBSD pledge() to Linux (2022)를 통해 Linux에도 OpenBSD의
unveil(2)와 유사한 비권한·프로세스별 접근 제어인 Landlock API가 있음을 상기함 - 기본 아이디어는 프로그램이 작업할 디렉터리를 알고 나면
unveil("/home/michael/backups", "rw");같은 호출로 해당 경로 외의 파일시스템 위치에 더 이상 접근하지 못하게 하는 것임 - 2025년 3월 파일시스템 접근 제한을 위해 Landlock 지원이 구현됨
- 설정이 맞춰진 뒤에는 비권한
gokrazy/rsync실행에서도 rsync 전송을 소스에는 읽기 전용, 대상 디렉터리에는 읽기·쓰기 권한으로 제한할 수 있음 - 단점은 Landlock이 프로세스 수준에서 동작한다는 점임
- Landlock 정책에는 프로그램이 필요한 파일도 포함돼야 하며, 예를 들어
gokrazy/rsync는 사용자 ID 조회를 위해/etc/passwd를 읽을 수 있어야 하므로 공격자가/etc/passwd를 노린다면 Landlock은 도움이 되지 않음
- Porting OpenBSD pledge() to Linux (2022)를 통해 Linux에도 OpenBSD의
-
Go의 os.Root
- 2025년 2월 Go 1.24는 경로 순회에 강한
os.RootAPI를 도입했으며, 관련 배경은 Damien Neil의 The Go Blog: Traversal-resistant file APIs에 정리돼 있음 os.Root는 Landlock과 비교해 파일시스템 작업별로 더 세밀한 제어를 제공함- 2025년 8월 출시된 Go 1.25는
os.Root에 더 많은 메서드를 추가해 대부분의 파일시스템 사용에 편리한 선택지가 됨 gokrazy/rsync의 모든 파일시스템 사용은os.Root로 전환됐고, 사용자가 입출력 디렉터리를 구성하지만 네트워크로 받은 파일명은 신뢰할 수 없다는 모델에 잘 맞음mknod(2)처럼 API로 직접 만들 수 없을 것 같은 시스템 호출도 안전하게 사용할 수 있음- Damien Neil은
os.Root.OpenFile로 대상의 부모 디렉터리를 열고,File.Fd로 해당 디렉터리 파일 디스크립터를 얻은 뒤,golang.org/x/sys/unix#Mknodat로 파일을 만들 수 있다고 설명함 - 실제 사용 예시는
internal/receiver/generatormknod_linux.go, line 15-29에 있음 - Linux에는
mknodat(2)는 있지만, Linux 7.0 기준bind(2)에 대응하는bindat는 없음 - Lennart Poettering은
bindat없이 경로 해석을 건너뛰는 트릭으로/proc/self/<fd>/foobar에 bind할 수 있다고 제안함 - 알려진 안전한
/proc/self/<fd>뒤에 경로가 아니라 basename, 즉 마지막 경로 구성요소만 지정하면 경로 해석이 건너뛰어지며, 관련 코드는 line 49-56에 있음 - 이 두 가지 팁을 통해
gokrazy/rsyncv0.3.1 이상은os.Root를 완전히 사용하며, 모든 파일시스템 접근이 경로 순회 안전성을 갖게 됨
- 2025년 2월 Go 1.24는 경로 순회에 강한
핵심 교훈
- TOCTOU 취약점(CVE-2024-12747, CVE-2026-29518, CVE-2026-43619)을 제외한 모든 취약점은 입력 검증 누락 또는 잘못된 입력 검증에서 비롯됨
- 세 경우에는 애초에 검증이 없었고, CVE-2024-12088은 파일시스템 경로 해석이라는 주제가 까다로워 기존 검증이 모든 경계 사례를 다루지 못한 경우임
- 가장 가치 있는 구조적 수정은 경계 검사처럼 항상 켜진 검증과 Go의
os.Root같은 기본 안전 API를 제공하는 것임 - 일부 취약점은 rsync 프로토콜 진화에서 발생했으며, 원래는 충분한 검증을 하던 코드에 새 기능이 추가되면서 검증이 올바르게 갱신되지 않았음
- 체크섬 알고리듬 협상과 증분 재귀는 프로토콜 버전 30에 추가됐고, 기존 검증은 새 기능의 처리 방식에 맞게 업데이트되지 않았음
- gokrazy/rsync와 openrsync는 취약한 기능을 구현하지 않았다는 이유만으로 12개 보안 취약점 중 8개에 취약하지 않았음
- 해당 기능들은 어느 시점에 누군가에게 가치가 있었기 때문에 rsync에 추가됐으며, 소프트웨어 개발을 멈춰야 한다는 의미는 아님
- 이상적인 선택은 사용 사례의 복잡성에 적절하고 비례하는 복잡성의 구현을 사용하는 것이며, 단순한 사용 사례에는 단순한 구현을 선택하고 필요할 때만 모든 기능을 갖춘 구현을 선택하는 것임
댓글과 토론
Lobste.rs 의견들
- 훌륭한 글임. 모든 언어가 표준 라이브러리에
os.Root같은 API를 넣어야 할 만큼 좋음
SecureDrop에서 몇 번의 경로 순회 취약점을 겪은 뒤 아주 단순화한 버전을 썼고, 이제 올바른 API를 쓰면 취약점 한 부류가 통째로 사라짐- 맞음. 이 블로그 글의 주인공은 Go라기보다
os.Root인 것 같음
- 맞음. 이 블로그 글의 주인공은 Go라기보다