1. 커널 수준에서의 root 권한 처리 메커니즘
리눅스 커널은 파일에 접근할 때 해당 프로세스의 자격 증명(Credentials)을 확인한다. 일반 사용자는 파일의 아이노드(Inode)에 기록된 9비트 권한(rwxrwxrwx)을 기준으로 통제받지만, root는 다르다.
CAP_DAC_OVERRIDE의 동작
커널 내부에는 권한을 세분화한 Capability라는 체계가 존재한다.
root(UID 0) 프로세스는 기본적으로 모든 Capability를 소유한다.
- 그 중 CAP_DAC_OVERRIDE는 파일의 소유주나 권한 비트에 상관없이 읽기, 쓰기, 실행을 허용하는 핵심 권한이다.
- 커널의 권한 확인 함수
- capability를 확인하여 CAP_DAC_OVERRIDE가 있으면 권한 부족을 무시
- SELinux/AppArmor 같은 MAC(Mandatory Access Control)은 여전히 적용
- 따라서 /etc/shadow가 000 권한이라 하더라도 커널 수준에서 root에게는 문이 열리게 된다.
2. 프로세스 권한 전이: RUID와 EUID
사용자가 명령어를 실행할 때 프로세스는 두 가지 종류의 사용자 ID를 가진다.
실제 사용자 ID (RUID, Real UID)
- 프로세스를 실행한 실제 사용자의 ID다. user01이 실행했다면 RUID는 user01의 ID가 된다.
실효 사용자 ID (EUID, Effective UID)
- 커널이 권한을 체크할 때 실제로 사용하는 ID다.
- 일반적인 명령어는 RUID와 EUID가 동일하지만, SUID가 설정된 파일을 실행하면 EUID가 파일 소유자(root)의 ID로 변경된다.
sudo의 실행 과정
- 사용자가 sudo 명령을 입력한다.
- 시스템은 /usr/bin/sudo 파일을 찾는다. 이 파일에는 SUID 비트가 설정되어 있다.
- 커널은 sudo 프로세스를 생성하며 EUID를 0(root)으로 설정한다.
- sudo 프로세스는 설정 파일을 확인하여 현재 사용자가 권한이 있는지 검증한다.
- 검증이 완료되면 sudo는 자식 프로세스로 userdel을 실행하며, 이 자식 프로세스 역시 root 권한(EUID 0)을 상속받는다.
- root 권한을 가진 userdel 프로세스가 커널의 CAP_DAC_OVERRIDE를 이용해 /etc/shadow를 수정한다.
3. 명령어 탐색 및 실행의 물리적 경로
리눅스에서 명령어는 단순히 텍스트가 아니라 디스크에 존재하는 이진 파일이다.
실행 환경의 결정
셸은 명령어를 찾기 위해 환경 변수 PATH를 사용한다. 보통 /usr/local/bin, /usr/bin, /bin 등이 포함된다.
명령어 분류 및 관리
- 일반 명령어: /usr/bin에 위치하며 모든 사용자가 실행 가능하다.
- 시스템 관리 명령어: /usr/sbin에 위치한다. userdel, visudo 등이 이에 해당하며 주로 관리자 권한이 필요하다.
- 라이브러리 의존성: 이 명령어들은 실행 시 /lib64 등에 있는 공유 라이브러리를 동적으로 로드하여 동작한다.
4. 정리
| 접근 제어 방식 | 커널 Capability (CAP_DAC_OVERRIDE) | 권한 비트(000) 무시 가능 |
| 권한 격상 도구 | sudo (SUID 활용) | EUID를 0으로 전환하여 실행 |
| 파일 수정 대상 | /etc/passwd, /etc/shadow | 시스템 계정 데이터베이스 |
| 명령어 위치 | /usr/sbin/userdel | PATH 변수를 통한 자동 탐색 |
5. 검증해보자
- 전통적 권한의 한계 확인
- 파일 권한이 000인 상태에서 일반 사용자의 접근이 완벽히 차단되는지 확인해본다
- 보안 우회 경로 이해
- 파일 자체 권한이 없더라도 실행 파일에 부여된 권한(Capability)을 통해 데이터를 읽을 수 있는지 알아본다.
[vagrant@user02 home]$ sudo userdel -r testuser
[vagrant@user02 home]$ sudo useradd -m testuser
[vagrant@user02 home]$ echo "이것은 기밀문서입니다." | sudo tee /root/secret.txt
이것은 기밀문서입니다.
[vagrant@user02 home]$ sudo chmod 000 /root/secret.txt
[vagrant@user02 home]$ sudo ls -l /root/secret.txt
----------. 1 root root 33 Jan 8 07:38 /root/secret.txt
[vagrant@user02 home]$ sudo cp /bin/cat /home/testuser/mycat
[vagrant@user02 home]$ sudo chown testuser:testuser /home/testuser/mycat
[vagrant@user02 home]$ sudo su - testuser
Welcome testuser
[testuser@user02 ~]$ cat /root/secret.txt
cat: /root/secret.txt: Permission denied
[testuser@user02 ~]$ ./mycat /root/secret.txt
./mycat: /root/secret.txt: Permission denied
[testuser@user02 ~]$ sudo ls -l /root/secret.txt
[sudo] password for testuser:
Sorry, try again.
[sudo] password for testuser:
Sorry, try again.
[sudo] password for testuser:
d
sudo: 3 incorrect password attempts
[testuser@user02 ~]$ d
-bash: d: command not found
[testuser@user02 ~]$ su - vagrant
Password:
Last login: Thu Jan 8 07:35:11 UTC 2026 on pts/0
[vagrant@user02 ~]$ sudo visode
sudo: visode: command not found
[vagrant@user02 ~]$ su - testuser
Password:
su: Authentication failure
[vagrant@user02 ~]$ sudo visudo -f /etc/sudoers.d/testuser
[vagrant@user02 ~]$ sudo su - testuser
Last login: Thu Jan 8 07:39:49 UTC 2026 on pts/0
Last failed login: Thu Jan 8 07:41:12 UTC 2026 on pts/0
There was 1 failed login attempt since the last successful login.
Welcome testuser
[testuser@user02 ~]$ passwd
Changing password for user testuser.
Current password:
passwd: Authentication token manipulation error
[testuser@user02 ~]$ su - vagrant
Password:
Last login: Thu Jan 8 07:40:38 UTC 2026 on pts/0
[vagrant@user02 ~]$ sudo rm /etc/sudoers.d/testuser
[vagrant@user02 ~]$ sudo visudo -f /etc/sudoers.d/testuser
[vagrant@user02 ~]$ id testuser
uid=2006(testuser) gid=2006(testuser) groups=2006(testuser)
[vagrant@user02 ~]$ sudo visudo -f /etc/sudoers.d/testuser
visudo: /etc/sudoers.d/testuser.tmp unchanged
[vagrant@user02 ~]$ su - testuser
Password:
su: Authentication failure
[vagrant@user02 ~]$ sudo passwd testuser
Changing password for user testuser.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
[vagrant@user02 ~]$ su - testuser
Password:
Last login: Thu Jan 8 07:41:33 UTC 2026 on pts/0
Last failed login: Thu Jan 8 07:43:21 UTC 2026 on pts/0
There was 1 failed login attempt since the last successful login.
Welcome testuser
[testuser@user02 ~]$ ./mycat /root/secret.txt
./mycat: /root/secret.txt: Permission denied
[testuser@user02 ~]$ getcap /home/testuser/mycat
[testuser@user02 ~]$ sudo setcap 'cap_dac_override+ep' /home/testuser/mycat
[sudo] password for testuser:
[testuser@user02 ~]$ getcap /home/testuser/mycat
/home/testuser/mycat cap_dac_override=ep
[testuser@user02 ~]$ ./mycat /root/secret.txt
이것은 기밀문서입니다.
[testuser@user02 ~]$ sudo ls -l /root/secret.txt
----------. 1 root root 33 Jan 8 07:38 /root/secret.txt
testuser는 권한이 없는 secret.txt 파일을 일반 cat 명령어로는 읽을 수 없다. 하지만 mycat 파일에 cap_dac_override 을 부여하는 순간, 파일에 설정된 000 권한은 무력화되었고 기밀 내용은 볼 수 있었다.
이를 통해 알 수 있는 점은 아래와 같다.
- 리눅스 보안은 단순히 파일에 걸린 9비트 권한(rwx)이 전부가 아니었다.
- 프로세스나 바이너리에 부여된 Capability가 커널 수준에서 어떻게 작동하느냐에 따라 보안의 경계는 완전히 달라질 수 있다.
- 모든 권한을 가진 root를 빌려주는 sudo 방식보다, 꼭 필요한 작업(예: DAC 무시)만 수행할 수 있도록 Capability를 쪼개어 부여하는 것이 최소 권한의 원칙을 만족하는 행동이다.
결국 시스템의 안전은 관리자는 "어떤 도구에, 어떤 특권(Master Key)을 허용했는가" 책임을 주는 것에 의해 결정된다.
반응형
'Infra > Linux' 카테고리의 다른 글
| Linux inode (0) | 2026.01.05 |
|---|