파일 업로드 실습 (4) www-data에서 root로 권한 상승
이전 게시글에서 이어서 씀~~
CVE-2023-0386 익스플로잇 시도
CVE-2023-0386 설명
CVE-2023-0386 CVE-2023-0386은 OverlayFS라는 파일시스템 기능의 일종의 버그이다.
OverlayFS: 읽기전용 원본(lower) 위에 쓰기가능한 층(upper)을 겹쳐서, 원본은 안 건드리면서 수정한 것처럼 보이게 하는 기능. 도커가 이걸로 굴러간다.
문제는 파일을 아래층에서 위층으로 복사(copy-up)할 때 커널이 소유권 검증을 제대로 안 한다는 것.
이를 악용하면,
- FUSE로 “root 소유 + SUID 걸린 파일”이 있다고 커널을 속인다
- OverlayFS로 그 파일을 upper로 copy-up 시킨다
- 커널이 검증 없이 그대로 복사 → 진짜로 root 소유 SUID 파일이 생긴다
- 그걸 실행하면 SUID라서 root 권한으로 실행 → root 획득
따라서 root 소유 SUID 파일을 생성할 수 없는 www-data 계정으로도 이 OverlayFS를 악용하여 root 소유의 SUID를 만들 수 있다. CVE-2023-0386는 이와 같이 금지된 걸 OverlayFS를 통로로 밀수하는 거라 “suid smuggle”이라고 부르기도 한다.
우분투 22.04에서 CVE-2023-0386 익스플로잇 전제 조건
- unprivileged user namespace가 열려 있는지.
1 2
cat /proc/sys/kernel/unprivileged_userns_clone → 1
1이면 비특권 사용자가 user namespace를 만들 수 있다 = 익스 조건 충족.
- 실행 커널 vs 설치 커널
1 2
uname -r → 5.19.0-38-generic
1 2 3
dpkg -l | grep linux-image → ii linux-image-5.19.0-38-generic (실행 중) → ii linux-image-6.8.0-124-generic (설치됨, 실행 안 됨)
디스크에는 훨씬 최신인 6.8.0-124가 깔려 있는데, 실제 돌고 있는 건 5.19.0-38이다. 새 커널을 깔아놓고 리부트를 안 한 것. 패치는 설치됐지만 리부트 안 하면 실행 커널은 옛날 것이다. 의도한 건 아니었는데.. 내가 진짜 리부트를 안 했었다. uptime도 길더라
- 실행 커널 5.19.0-38이 CVE-2023-0386 패치를 받았는지?
1 2
zgrep -c "CVE-2023-0386" /usr/share/doc/linux-image-5.19.0-38-generic/changelog.Debian.gz → 0
0 = 이 커널 changelog에 해당 CVE 패치 기록 없음 = 패치 안 됨.
Ubuntu Security 페이지도 직접 확인해봤다. CVE-2023-0386은 커널 6.2 미만이면 취약 범위이고(5.19 해당), CISA가 2025년 7월에 실제 악용 목록(KEV)에 추가했다.
공개된 PoC 이용한 익스플로잇
https://github.com/sxlmnwb/CVE-2023-0386
위의 PoC를 이용함. 마침 우분투22.04에 git도 있고… 그런데… C소스라 컴파일 해야하는데… 일단 타겟(우분투22.04)에는 gcc, make 등의 컴파일러는 있다(예전에 개발용으로 설치해뒀었음). 문제는 fuse.h가 없어…
fuse(Filesystem in Userspace)는 커널 모듈을 직접 건드리지 않고 사용자 공간(userspace)에서 파일시스템을 구현한다. 일반적인 파일시스템은 커널 공간에서 동작하지만 fuse는 이를 우회해서, 사용자 공간 프로그램이 커널의 FUSE 모듈과 통신 > 커널이 파일 연산 요청(open, read, write 등)을 사용자 프로그램에 전달 > 사용자 프로그램이 그 요청을 처리해서 결과를 돌려줌…
과 같은 과정을 거친다.
따라서 CVE-2023-0386 익스플로잇을 위한 fuse의 역할을 대략 이런 식으로 이해했다:
커널에게 코드로 이 파일 시스템에는 이런 파일이 있다고 얘기할 수 있는 느낌이니까… root 소유의 SUID 비트붙은 이런 파일도 있던데, 하고 커널을 속이는 역할. 커널은 fuse를 믿고 존재하지 않는 파일을 생성시킨다.
아무튼 타겟에는 libfuse-dev 컴파일용 헤더가 없는 게 문제였다.
이 PoC는 /usr/include/fuse.h 가 있어야 fuse 파일 시스템을 만들 수 있는 프로그램이라.. 꼭 있어야 하는데…ㅜㅜ
컴파일을 타겟에서 못 하는게 문제니까… 칼리에서 아예 컴파일을 해가지고 바이너리로 전송해서 어떻게든 써먹는 방법을 생각해봤는데,
칼리에서 “apt-cache search fuse | grep -i dev” 해보니 libfuse3-dev - Filesystem in Userspace (development) (3.x version) 이거만 떴었음. FUSE3만 설치 가능했고, 이 PoC는 FUSE2전용이라 불가능. 찾아보니 FUSE2는 이제 신규 기능도 추가 안 되는 구버전으로 잘 안 쓰이고 있었다.
직접 PoC를 수정해서 써먹기? fuse가 핵심 기능인데… fuse3로 돌아가게끔 만들면 칼리에서 써먹을 수 있을까 싶었는데.. 현재 내 실력으로 부족하고…
같은 환경 우분투22.04를 구성해서 libfuse-dev를 설치한뒤 거기서 컴파일해서 바이너리로 싹 변환해서 전송하기… 생각도 해봤는데 뭔가 버전이 안 맞다거나 해서 또 환경 지옥 설정 지옥에 빠지면 어캄? 하는 걱정이 들었고…
이외에도 타겟에서 어떻게 이 PoC를 컴파일하지,를 오래 고민했지만… 결국 타협해서 타겟(우분투 22.04)에 걍 직접 libfuse-dev를 설치했다. 지금은 학습 목적이니까ㅜㅜㅠㅠㅜ 순수 시뮬레이션은 나중에 재도전해야지.
검색해보니 “취약해도 온-호스트 컴파일 불가”로 막히는 상황이었고, 이런 일이 비일비재한 것 같다. 타겟에서 컴파일이 안 되니 동일한 환경을 세팅해서 거기서 컴파일해서(이를 오프타겟 컴파일이라고 함) 바이너리 전송하는 방법이 실제로 많이 쓰는 듯. 아니면 아예 사전 컴파일된 바이너리를 이용하거나…
그래도 혼자 생각해낸 방법이 실무에서도 곧잘 쓰는 방법이라길래, 해결할 간단한 방법 있는데 내가 생각 못 해낸 게 아닌 듯 해서 약간 안심함ㅎㅜㅜ.. 나중에 우분투 22.04 설치해서 PoC 컴파일하고 바이너리로 전송해보는거 해볼 것.
아무튼 돌아와서,
저 PoC의 간단한 readme를 따라하면 된다.
터미널 두 개 열어서 하나는 ./fuse ./ovlcap/lower ./gc, 다른 하나는 ./exp 실행하라는 게 전부.
터미널 1 (./fuse …): FUSE로 “root 소유 SUID 파일이 있다”고 커널한테 거짓말하는 가짜 파일시스템을 띄운다. 이건 계속 떠 있어야 해서(FUSE가 살아있는 동안만 그 거짓말이 유효하니까) 터미널을 점유한 채 대기 상태가 된다. 멈춘 것처럼 보여도 정상.
터미널 2 (./exp): 그 가짜 파일시스템을 이용해 실제 공격을 수행하고, 여기서 root를 획득한다.
일단 /tmp/CVE-2023-0386(git으로 받아놨었음)에서 make all로 컴파일 한 뒤 파일들이 잘 생성되었는지 확인하고,
칼리에서 포트 4445도 추가로 열어서 리버스 쉘을 하나 더 받아서, /tmp/CVE-2023-0386 으로 이동해 ./fuse ./ovlcap/lower ./gc을 실행한다.
두번째 리버스쉘 : ./fuse ./ovlcap/lower ./gc
포트 4444로 열어 받아온 처음의 리버스 쉘에서 /tmp/CVE-2023-0386 에서 ./exp를 실행하면 nobody 소유의 suid 설정된 파일이 생성되면서 mount success 된다.
id를 입력하면 uid, gid가 0으로 뜨고, /etc/shadow 파일 등도 볼 수 있었다.

