개요
해당 취약점은 다음 버전에서 패치되었다.
- macOS Tahoe 26.5
- macOS Sequoia 15.7.7
- macOS Sonoma 14.8.7

macOS 26.4.1, macOS 26.5에서 각각 cups 관련 바이너리 8개를 Bindiff로 디핑해보았다.

그 결과, 다른 바이너리들은 거의 그대로였지만
cupsd에서 3개의 함수에 변화가 생긴것을 알 수 있었다.

cupsd 바이너리는 애플에서 소스를 공개하고 있기 때문에, 특정 버전 브랜치를 참고하여 디컴파일 없이 소스코드를 직접 살펴볼 수도 있다.
cups 버전을 비교해보면, 취약점이 패치된 버전은 522.4이며, 취약점이 패치되기 전 버전은 522.3이다.
https://github.com/apple-oss-distributions/cups
seo@seos-MacBook-Air ~ % otool -l /Users/seo/Desktop/macOS-1day/26.4.1_vs_26.5/26.5/cupsd | grep -A3 LC_SOURCE_VERSION
cmd LC_SOURCE_VERSION
cmdsize 16
version 522.4
Load command 13
seo@seos-MacBook-Air ~ % otool -l /Users/seo/Desktop/macOS-1day/26.4.1_vs_26.5/26.4.1/cupsd | grep -A3 LC_SOURCE_VERSION
cmd LC_SOURCE_VERSION
cmdsize 16
version 522.3
Load command 13
이를 통해 각각 패치 이전 바이너리의 sub_10000BA94와 패치 이후 바이너리의 sub_1000432B4는 전부int cupsdCheckPermissions(char *filename, const char *suffix, int mode, uid_t user, gid_t group, int is_dir, int create_dir); 함수를 가리킨다.
발생 원인
패치된 커밋을 비교해봤을때, 초기에 snprintf 과정에서 잘못되었다.

- 패치 이전
snprintf함수를 살펴보면,__str에서filename과suffix와 서로 합쳐져서 만들어질 수 있는 실제 문자열은\x00을 제외했을때 총 1023자이다. 하지만filename이suffix길이보다 훨씬 클 경우, suffix가 온전히 담기지 못하고 잘려버리는 문제가 발생한다.

- 패치 이후
snprintf반환값은 버퍼의 크기인 0x400과 무관하게, 합치면서 잘리지 않았을 때 생성되었을 원래 문자열의 전체 길이를 반환한다. 잘린 불완전한 경로인지 판단하기 위해 0x3ff 길이보다 크다면, 더이상 진행하지 않고 -1을 반환한다.

악용 가능성
취약한 함수인 cupsdCheckPermissions가 호출되는 곳은 총 15군데였다.
cupsdCheckLogFile과 cupsdReadConfiguration 2개의 함수에서 발견할 수 있다.
cupsdCheckLogFile 에서는 filename과 suffix가 이미 정해져있으므로, 넘어가자.

CacheDir값이나 ServerRoot값은 /private/etc/cups/cups-file.conf 파일 내용을 변경하여 임의로 설정할 수 있다. 하지만 이마저도 root 권한이 필요하다.


시도해본 결과, 아쉽게도 파일 이름 자체가 길어서 실패한다. 악용될 가능성이 극히 낮아보인다.
import os
# Init
target = 1022
base = "/tmp"
max_chunk = 200
# Chdir base
os.chdir(base)
current_len = len(base)
full_path = base
# Mkdir and cd sequentially
while True:
rem = target - current_len - 1
if rem <= 0:
break
chunk = "A" * min(max_chunk, rem)
# Mkdir
if not os.path.exists(chunk):
os.mkdir(chunk)
# Cd
os.chdir(chunk)
# Update path string
full_path += "/" + chunk
current_len += 1 + len(chunk)
# Check
print(full_path)
print(len(full_path))
seo@seos-Virtual-Machine ~ % sudo cupsd -t
Unable to get file information for "/tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAA/" - File name too long
Unable to open job cache file "/tmp/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAA/": File name too long
"/private/etc/cups/cups-files.conf" is OK.
"/private/etc/cups/cupsd.conf" is O

