| 항목 | 내용 |
|---|---|
| CVE ID | CVE-2018-12559 |
| 영향 소프트웨어 | Cantata 2.3.1 이하에서 cantata-mounter D-Bus 서비스를 포함한 빌드 |
| 취약 지점 | devices/mounter/mounter.cpp의 mpOk() 마운트 지점 검사 |
| CWE | CWE-22 (Improper Limitation of a Pathname to a Restricted Directory, NVD) |
| CVSS v3.0 | CVSS 8.8 High — CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H (NVD) |
| 공개일 | 2018-06-19 (NVD) |
| upstream 수정 | 커밋 afc4f8315d3e96574925fb530a7004cc9e6ce3d3, 2018-06-15 |
NVD의 CVSS 벡터는 네트워크 공격으로 분류하지만, 공개 소스에서 확인되는 직접 진입점은 로컬 system D-Bus다. 이 보고서는 공식 점수를 그대로 싣되 공개 코드로 확인할 수 없는 네트워크 직접 공격 경로는 가정하지 않는다.
Cantata는 일반 사용자 프로세스가 Samba 공유를 음악 폴더처럼 쓸 수 있도록 root 권한의
cantata-mounter 서비스를 제공했다. 사용자가 system D-Bus의 mount() 메서드에
Samba URL과 로컬 마운트 지점을 넘기면, 서비스는 마운트 지점이 /home/으로 시작하는지
검사한 뒤 mount.cifs를 실행했다.
문제는 이 검사가 파일시스템 경로의 의미를 확인하지 않고 문자열 앞부분만 비교했다는 점이다.
/home/../usr는 문자열로는 /home/으로 시작하지만, 운영체제가 ..를 해석하면 /usr다.
따라서 일반 사용자는 의도된 홈 디렉터리 범위를 벗어난 위치에 공격자가 정한 CIFS 공유를
root 권한으로 마운트하거나, 같은 우회 문자열로 마운트 해제를 요청할 수 있었다. 깨진
불변식은 **“검사를 통과한 마운트 지점의 실제 해석 결과도 반드시 /home 아래에 있어야
한다”**는 것이다.
D-Bus는 프로세스 사이에 구조화된 메시지를 전달하는 IPC다.
Cantata의 mpd.cantata.mounter 서비스는 system bus에 등록되고 User=root로
실행됐다. 버스 정책은 일반 프로세스가 이 서비스에 메시지를 보내는 것을 허용했다.
일반 사용자 Cantata 또는 로컬 D-Bus 클라이언트
│ mount(smbUrl, mountPoint, uid, gid, pid)
▼
┌───────────────────────────────────────────┐
│ system D-Bus │
│ mpd.cantata.mounter를 User=root로 활성화 │
└──────────────────┬────────────────────────┘
▼
┌───────────────────────────────────────────┐
│ cantata-mounter │
│ 1. smb URL인지 확인 │
│ 2. mpOk(mountPoint) 확인 │
│ 3. mount.cifs 실행 │
└───────────────────────────────────────────┘
파일시스템 마운트는 한 경로 아래에서 보이는 파일 트리를 다른 파일시스템으로 바꾸는
권한 있는 작업이다. 예를 들어 CIFS 공유를 /home/alice/Music에 마운트하면 이후 그
경로를 여는 프로세스는 로컬 디렉터리의 원래 내용 대신 원격 공유의 내용을 본다.
마운트가 원래 파일을 지우는 것은 아니지만, 마운트가 유지되는 동안 기존 디렉터리를
가리고 다른 파일 트리를 같은 경로에 제시한다.
따라서 root helper가 일반 사용자 대신 마운트할 때는 “어디에 마운트할 수 있는가”가
핵심 권한 경계다. Cantata의 설계 의도는 사용자 홈 아래의 음악 폴더만 대상으로 삼는
것이었고, mpOk()가 그 경계를 지키는 유일한 검사였다.
경로 문자열은 파일시스템 객체 자체가 아니다. 운영체제가 각 구성요소를 순서대로
해석하면서 .은 현재 디렉터리, ..은 부모 디렉터리를 뜻한다. 다음 두 문자열은
표면적으로 다르지만 같은 위치를 가리킨다.
입력 문자열 구성요소 해석 실제 위치
──────────────────────────────────────────────────────────────
/home/alice/Music home → alice → Music /home/alice/Music
/home/../usr home → .. → usr /usr
이 차이를 악용해 허용 디렉터리를 벗어나는 것이
Path Traversal이다. 안전한 포함 검사는 보통 경로를
정규화하거나 실제 객체를 기준으로 해석한 뒤, 결과가 허용 루트와 같거나 그 자손인지
확인해야 한다. 단순한 startsWith("/home/")는 문자열 모양만 확인하므로 .., 심볼릭
링크, 바인드 마운트처럼 해석 결과를 바꾸는 요소를 다루지 못한다.
Mounter::mount()는 URL scheme이 smb이고 mpOk(mountPoint)가 참일 때만
마운트 프로세스를 시작했다. Samba URL에서 호스트·경로·계정 정보를 분리한 뒤 다음과
같은 인자 배열을 만들었다.
if (u.scheme()=="smb" && mpOk(mountPoint)) {
// URL과 자격 증명 처리 생략
proc->start(".../mount.cifs.wrapper",
QStringList() << path << mountPoint
<< "-o" << options);
}umount()도 동일한 mpOk()를 사용했다.
if (mpOk(mountPoint)) {
proc->start("umount", QStringList() << mountPoint);
}즉 한 개의 잘못된 경로 판정 함수가 마운트와 마운트 해제라는 두 root 작업의 권한 경계를 동시에 맡고 있었다.
취약한 검사는 한 줄이다.
static inline bool mpOk(const QString &mp)
{
return !mp.isEmpty() && mp.startsWith("/home/");
}QString::startsWith()는 파일시스템을 조회하거나 ..를 접지 않는다. 공격자가
mountPoint로 /home/../usr를 보내면 조건은 참이 되고, helper는 그 문자열을
mount.cifs의 대상 인자로 그대로 전달한다. 실제 마운트 시스템 호출이 경로 구성요소를
해석할 때 대상은 /usr가 된다.
D-Bus 입력
mountPoint = "/home/../usr"
│
▼
┌──────────────────────────────────────────┐
│ mpOk() │
│ startsWith("/home/") == true │
│ 파일시스템 의미 확인 없음 │
└──────────────────┬───────────────────────┘
│ 허용
▼
root helper → mount.cifs SHARE /home/../usr -o ...
│
│ 커널의 경로 탐색
▼
/home → .. → /usr
│
▼
공격자 지정 공유가 /usr를 가림
검사 시점의 문자열 관점에서는 “홈 아래”였지만 사용 시점의 파일시스템 관점에서는 “시스템 디렉터리”가 된다. 이 불일치는 별도의 경쟁 조건 없이 같은 문자열을 두 계층이 다르게 해석해서 발생한다.
영향은 마운트 지점과 공유 내용에 따라 달라진다. 공격자는 root helper의 권한으로 홈
바깥 디렉터리 위에 자신이 정한 원격 파일 트리를 표시해 기존 경로의 가용성을 깨뜨리거나,
그 경로를 읽는 다른 프로세스에 원격 공유의 내용을 제시할 수 있다. umount()에도 같은
검사가 있으므로 /home/../... 형태로 홈 밖의 기존 마운트를 해제하려는 요청도
검사를 통과한다.
다만 마운트 자체가 가려진 로컬 파일을 덮어쓰는 것은 아니다. 또한 어떤 후속 프로세스가 공격자 공유의 파일을 신뢰하고 실행하는지까지는 공개된 CVE별 자료가 규정하지 않는다. upstream 커밋 메시지는 이 구성요소의 “privilege escalation issues”를 언급하지만, 이 CVE 하나만으로 성립하는 구체적인 권한 상승 체인은 확인되지 않았으므로 임의 코드 실행이나 root 획득을 확정된 결과로 단정하지 않는다.
upstream 커밋 afc4f831은 mpOk()에 canonicalization을 추가하는 국소 패치 대신
내부 Samba 마운트 기능을 제거했다. 삭제된 범위에는 다음 구성요소가 포함된다.
devices/mounter/의 root helper, D-Bus 서비스·정책·인터페이스mount.cifs.wrapper- mounter와 Samba 탐색 코드를 빌드하던 CMake 항목
- Cantata UI의 Samba 공유 추가 경로
- qt5_add_dbus_interfaces(CANTATA_SRCS
- devices/mounter/${PROJECT_REV_URL}.mounter.xml)
- add_subdirectory(devices/mounter)
- add_subdirectory(devices/avahi)따라서 실제 수정의 보안 불변식은 “경로 문자열을 더 잘 검사한다”가 아니라 **“일반 사용자 D-Bus 입력을 root의 마운트·마운트 해제 작업으로 바꾸는 내부 경로가 존재하지 않는다”**다.
패치 전
일반 사용자 → system D-Bus → root cantata-mounter
→ mpOk("/home/../...") → mount.cifs / umount
패치 후
일반 사용자 → Cantata
│
├─ 이미 로컬에 마운트된 폴더를 사용
└─ sshfs 같은 외부 마운트 방식을 별도 구성
삭제됨: root D-Bus helper · mpOk() · 내부 Samba 마운트 UI
배포판에서는 upstream 버전 번호만 보지 말고 패키지의 helper 포함 여부를 확인해야 한다.
Debian Security Tracker는 2.3.0.ds1-2에서 cantata-mounter를 비활성화한 패키지를
수정 상태로 기록한다. Ubuntu의 유지 중인 해당 패키지는 영향 없음으로 분류한다.
자체 빌드나 오래된 패키지라면 mpd.cantata.mounter 서비스와 관련 D-Bus 정책,
mount.cifs.wrapper가 설치돼 있는지 확인하고 이 기능을 포함하지 않는 빌드로
교체해야 한다.
경로 정규화 후 /home 포함 관계를 다시 검사하는 것은 .. 우회의 최소 방어 원리다.
그러나 심볼릭 링크와 마운트 지점 변화까지 안전하게 다루려면 문자열 canonicalization만으로
충분하지 않다. upstream은 같은 helper에서 발견된 여러 권한 경계 문제를 함께 없애기 위해
기능 전체를 제거했으므로, mpOk() 한 줄만 바꾸는 대응은 upstream 수정과 동등하지 않다.
권한 있는 파일 작업에서 문자열 정규화와 디렉터리 파일 디스크립터 기준 해석은 심볼릭 링크 경계를 어떻게 다르게 보장할까?
하나의 root helper에 마운트와 마운트 해제를 함께 노출할 때 각 작업의 허용 범위는 왜 별도로 모델링해야 할까?
- CVE-2018-12559 — NVD — 영향 버전, CWE, CVSS와
/home/../usr우회 설명 - Cantata 수정 커밋
afc4f831— root mounter·내부 Samba 기능 삭제 diff, GPL-2.0-or-later - 삭제 전
mounter.cpp—mpOk(),mount(),umount()구현, GPL-2.0-or-later - CVE-2018-12559 — Debian Security Tracker — helper 비활성화와 배포판 수정 버전
- CVE-2018-12559 — Ubuntu Security — Ubuntu 패키지 영향 상태
이 메일링 리스트 글은 게시물의 재이용 조건이 명확하지 않아 본문 작성의 참고자료로 사용하지 않고, 최초 공개 맥락을 추가 확인하는 링크로만 둔다.
NVD의 네트워크 CVSS 벡터와 공개 코드에서 확인되는 로컬 system D-Bus 진입점 사이에는 차이가 있다. 홈 밖에 마운트한 공격자 공유를 어떤 privileged consumer가 사용해 권한 상승으로 연결하는지에 대한 CVE별 공개 체인도 확인되지 않았다. 따라서 이 보고서는 코드에서 검증되는 root 마운트 위치 제한 우회와 그 직접적인 파일시스템 영향까지만 확정적으로 설명한다.