OverlayFS
CVE-2023-0386
CVE-2023-0386은 리눅스 커널의 OverlayFS 시스템에서 setuid 매핑 버그로 인한 권한 상승 취약점이다. 취약점이 발생하는 커널 버전은 다음과 같다.
📌 분석에 앞서 SUID, FUSE, namespace가 뭔지 알고 있어야 한다.
SUID/GUID
리눅스는 사용자마다 고유한 ID(User ID) 값을 가지고 있다. 이때, SUID는 프로그램이 실행되는 동안 임시적으로 root 권한을 가지는 설정이다.
root와 yejun 계정에서 id 명령어를 쳐보면, 각 계정의 uid와 gid를 확인 할 수 있다. C언어 코드를 보면, uid와 gid를 root(0)으로 설정하고, system("/bin/sh")
를 호출한다. 해당 바이너리를 yejun 계정에서 호출하면 root 권한의 셸이 떨어진다. (소유자 : root / 권한 : SetUID - chmod 4??? [file]
)
FUSE
FUSE란 Filesystem in User Space의 약자로, 유저 레벨에서 파일시스템을 커스텀하여 파일 시스템에서 발생하는 이벤트를 원하는 흐름으로 처리할 수 있다. C언어로 fuse.c 라이브러리를 이용해서 파일시스템을 커스텀할 수 있다.
fuse_operations
구조체에 각 이벤트에 대한 함수를 만들어줄 수 있다. 대표적으로 파일에 대한 속성을 가져올 때 발생하는 .getattr
이벤트로 예시를 들어보고자 한다. .getattr
가 발생하면 hello_getattr
함수가 실행된다.
hello_getattr
함수에서 파일의 경로가 /
이면 파일에 755 권한이 부여되고, /hello.txt
파일에는 444 권한이 부여된다. 해당 C 코드를 컴파일 하고 mnt1 디렉터리에 마운트를 하면 각각 경로에 해당하는 권한이 755, 444 인 것을 볼 수 있다.
st_uid와 st_gid로 파일의 uid와 gid 또한 설정할 수 있다.
User Namespace
사용자 권한과 관련된 네임스페이스이다. 각각의 네임스페이스는 서로 독립적으로 동작한다.
예를 들어 프로세스는 user namespace 내에서 다른 UID와 GID로 실행될 수 있다. 하지만 네임스페이스 안에서의 UID이며, 실제 호스트 시스템에는 권한이 독립적이다.
uid_gid_map 구조체에서 프로세스 사용자 네임스페이스와 하위 네임스페이스 간의 uid/gid를 매핑을 정의한다. 계층 구조를 가지며, count가 계층의 level을 나타낸다.
모든 사용자 네임스페이스 최상단에는 init_user_ns
가 존재한다. yejun 계정에서 uid를 root(0)로 변경했지만, 실제 호스트에서 권한이 없는 것을 볼 수 있다.
각 유저별로 최상위 네임스페이스인 init_user_ns
의 값은 구조체에서 하드코딩 되어 있다.
CVE-2023-0386 Analysis
코드 패치
패치된 코드를 확인해서 취약점 분석을 진행할 것이다.
리눅스 커널에서 fs/overlayfs/copy_up.c
의 ovl_copy_up_one
함수는 파일 복사를 담당한다. 패치된 기록을 보면 kuid_has_mapping
함수를 통해 복사할 파일의 UID와 GID가 User Namespace 내에 매핑되어 있는지 확인하는 코드가 추가되었다.
UID와 GID의 조작을 방지하는 코드가 추가되었으므로, 패치 이전에는 UID 매핑을 확인하지 않고 파일을 복사했을 것이다.
익스플로잇 설계
- root 소유자의 setUID가 걸린 권한 상승 바이너리 생성 (파일 권한을 위해 FUSE 사용)
- Unshare system 호출 및 overlayfs 마운트 (lower :
./lower(FUSE)
/ upper :/tmp
) - merge 디렉터리에 파일 갱신 → SUID가 설정된 바이너리가 upper 디렉터리(
/tmp
)로 복사 - user namespace 종료 및 복사된 바이너리 실행 → root 권한 상승
취약점 검증
📌 앞서 설계했던 흐름을 한 단계씩 분석해보고자 한다.
1. root 소유자의 setUID가 걸린 권한 상승 바이너리 생성 (파일 권한을 위해 FUSE 사용)
FUSE를 이용해서 특정 경로를 마운트하면, root 권한의 SUID가 걸린 바이너리를 생성한다. 해당 디렉터리는 overlayfs 마운트 시 lower 디렉터리로 지정할 것이다.
2~3. Unshare system 호출 및 overlayfs 마운트 + merge 디렉터리에 파일 갱신
📌 lower : ./lower (FUSE Path)
📌 upper : /tmp 또는 전체 쓰기가 가능한 곳
unshare -r -m sh -c 'mount -t overlay overlay -o lowerdir=./lower,upperdir=./upper,workdir=./work ./merge && ls -la ./merge && touch ./merge/file'
unshare 명령어로 일반 사용자 네임스페이스에서 root 네임스페이스를 생성한다. root 이므로 overlayfs를 마운트가 가능해지며, 앞서 FUSE로 마운트한 lower 디렉터리 안의 file 바이너리를 merge 디렉터리에서 확인할 수 있다. 또한, touch 명령어를 통해 merge에 있는 file 바이너리를 갱신해주면, 파일의 수정이 발생했기 때문에 upper 디렉터리로 COW(Copy On Write) 한다. 따라서, SUID가 설정된 바이너리가 upper 디렉터리(/tmp)로 복사된다.
4. user namespace 종료 및 복사된 바이너리 실행 → root 권한 상승
unshare로 만든 user namespace를 나가서, upper 디렉터리에 존재하는 file 바이너리를 실행하면 권한 상승 취약점이 발생한다.
Another overlayfs vuln
CVE-2023-0386 취약점은 사용자 네임스페이스 내의 사용자에게만 파일 생성을 제한하여 루트 파일 생성을 방지했다. 하지만 파일에 특별한 보안 기능을 부여하는 다른 방법(setcap)을 통해 권한 상승 취약점(CVE-2023-2640 / CVE-2023-32629)이 또 발생하였다.
익스플로잇 과정은 동일하기 때문에, 간략하게 어떤 방식으로 권한을 부여했는지 확인해보자.
CVE-2023-2640
Overlayfs에서 파일 확장 속성을 설정할 때 호출되는 ovl_copy_xattr
함수에서 취약점 발생한다. 파일의 속성을 설정할 때 vfs_setxattr
함수 대신에 __vfs_setxattr_noperm
함수를 호출한다. vfs_setxattr
함수는 상위 디렉터리에 복사하기 전에 사용자 네임스페이스로 제한하지만,
__vfs_setxattr_noperm
함수는 변환 없이 직접 복사하기 때문에 취약점이 발생한다.
CVE-2023-32629
Overlayfs에서 ovl_do_setxattr
함수를 호출할 때 취약점 발생한다. Overlayfs 마운트 시 metacopy=on
설정을 하면, 파일의 메타 테이터가 바뀔 때 메타 데이터를 복사한다. 해당 과정에서 권한 확인을 하지 않아 파일에 SUID 권한을 설정을 할 수 있다.
취약점 검증
앞서 분석한 CVE-2023-0386과 동일한 방식으로 권한 상승이 발생하고, 이때 python3 에 부여되는 특수한 권한은 setcap+eip
를 통해 부여된다.
Ref
vuln report
Code Patch
user namesapce