리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
리눅스 시스템 기초 19 / 50 · Part 2. 파일·명령어·권한
실습 환경: Rocky Linux 9 (10.0.0.200) · 비교: Ubuntu 22.04
이전 글: 18. Linux 사용자와 그룹
🔗 심화 시리즈 — 「파일 · 권한 · 사용자 관리」
이 주제를 더 깊게 다룬 실무형 보안 시리즈: 41. su와 sudo의 차이 · 42. sudoers와 sudo 권한 관리 · 43. sudo 잘못된 설정과 권한 상승
18편에서 관리자 권한이 wheel(RHEL)·sudo(Ubuntu) 그룹으로 주어진다고 했다. 이번 글은 일반 계정이 실제로 root 권한을 얻는 두 가지 방법, su와 sudo 를 비교한다.
관제 관점에서 이 둘의 차이는 추적성 이다.
sudo는 누가, 언제, 어떤 명령을 root로 실행했는지 명령 단위로 로그에 남긴다.su는 root 쉘을 연 시점 만 남고, 그 안에서 실행한 명령은 기본 로그에 남지 않는다.권한 상승 흔적을 추적하려면 이 로그 형식을 정확히 읽을 줄 알아야 한다.
| 구분 | su / su - | sudo |
|---|---|---|
| 요구 비밀번호 | 대상 계정(root) 의 비밀번호 | 본인 비밀번호 |
| 권한 범위 | 대상 계정의 쉘 전체 | sudoers에서 허용한 명령만 |
| 환경 | su -: 대상 계정의 로그인 환경 / su: 현재 환경 일부 유지 | env_reset으로 초기화 (9편) |
| 로그 | 세션 시작·종료 | 명령마다 기록 |
| root 비밀번호 공유 | 필요 | 불필요 |
현재 대부분의 배포판은 sudo 사용을 권장 하고, Ubuntu는 기본적으로 root 비밀번호가 설정되어 있지 않다(root 직접 로그인 불가).
사용자 호스트=(실행계정:실행그룹) [옵션:] 명령
roror ALL=(ALL) ALL
%wheel ALL=(ALL) ALL
backup ALL=(root) NOPASSWD: /usr/bin/rsync
webops ALL=(root) /usr/bin/systemctl restart httpd
| 요소 | 의미 |
|---|---|
%wheel | % = 그룹 |
ALL= | 모든 호스트에서 |
(ALL) | 어떤 계정으로든 실행 가능 |
NOPASSWD: | 비밀번호 없이 실행 |
| 명령 | 전체 경로로 지정. ALL이면 무엇이든 |
sudoers는 반드시 visudo로 수정 한다. 문법 오류가 있으면 저장을 막아 주기 때문에, 잘못 저장해서 sudo 자체가 망가지는 사고를 막는다. 개별 규칙은 /etc/sudoers.d/에 파일로 분리하는 것이 관리하기 좋다.

sudo는 root 소유 SUID 프로그램이다(16편). 그래서 일반 사용자가 실행해도 root 권한으로 동작하며, 다음 순서로 처리한다.
/etc/sudoers와 /etc/sudoers.d/*에서 실행자·명령에 맞는 규칙을 찾는다./var/log/secure(Ubuntu auth.log)와 journal에 남긴다.su는 PAM으로 대상 계정의 비밀번호 를 확인한 뒤 그 계정의 쉘을 실행한다. 이후 쉘 안의 명령은 su가 관여하지 않으므로 기록되지 않는다. su 이후 활동을 추적하려면 auditd의 execve 규칙(5편)이 필요하다. 이때 auid는 원래 로그인 계정(roror)으로 유지 되므로, root 쉘에서 실행된 명령도 원래 사용자에게 연결할 수 있다.
# 1) 내 sudo 권한 확인
sudo -l
# 2) sudoers 구조 확인
sudo grep -vE '^#|^$' /etc/sudoers
sudo ls -l /etc/sudoers.d/
getent group wheel
# 3) 제한된 sudo 규칙 추가 (visudo로 별도 파일 편집)
sudo useradd -m webops && sudo passwd webops
sudo visudo -f /etc/sudoers.d/webops
# webops ALL=(root) /usr/bin/systemctl restart httpd
sudo visudo -c # 문법 검사
sudo -u webops sudo -l # webops가 쓸 수 있는 명령 확인
# 4) 로그 생성과 확인
sudo cat /etc/shadow > /dev/null # 성공 로그
su - # root 비밀번호 입력 후 exit
sudo grep -E 'sudo|su\[' /var/log/secure | tail -10
sudo journalctl _COMM=sudo --since today --no-pager | tail
# 5) 정리
sudo rm /etc/sudoers.d/webops && sudo userdel -r webops
아래 출력은 형식 설명용 예시다.
① sudo 성공 로그
Sep 23 12:01:10 rocky sudo[5402]: roror : TTY=pts/0 ; PWD=/home/roror ; USER=root ; COMMAND=/bin/cat /etc/shadow
| 필드 | 값 | 의미 |
|---|---|---|
| 실행자 | roror | 누가 |
| TTY | pts/0 | 원격 터미널(SSH) |
| PWD | /home/roror | 어디서 |
| USER | root | 어떤 권한으로 |
| COMMAND | /bin/cat /etc/shadow | 무엇을 |
② sudo 실패 로그
sudo[5410]: lab : user NOT in sudoers ; TTY=pts/1 ; PWD=/home/lab ; USER=root ; COMMAND=/usr/bin/id
sudo[5415]: roror : 3 incorrect password attempts ; TTY=pts/0 ; PWD=/home/roror ; USER=root ; COMMAND=/bin/bash
| 로그 | 판단 |
|---|---|
user NOT in sudoers | 권한 없는 계정의 sudo 시도 — 탈취된 일반 계정의 권한 상승 시도 일 수 있음 |
incorrect password attempts | 비밀번호를 모르는 사람이 세션을 사용 중일 가능성 |
COMMAND=/bin/bash 등 쉘 실행 | 명령 단위 추적을 피하려는 시도 |
③ su 로그
su[5500]: pam_unix(su-l:session): session opened for user root(uid=0) by roror(uid=1000)
su[5530]: FAILED SU (to root) roror on pts/0
su-l은 su -(로그인 쉘)로 전환했다는 뜻이다. 세션 시작 이후의 명령은 이 로그에 없다.
| 위험 설정 / 행위 | 문제 | 대응 |
|---|---|---|
ALL=(ALL) NOPASSWD: ALL | 계정만 탈취하면 비밀번호 없이 root | NOPASSWD 최소화, 명령 한정 |
| 쉘 탈출 가능한 명령 허용 | vi, less, find, 인터프리터 등을 sudo로 허용 → 그 안에서 root 쉘 실행 가능 | 명령 한정 시에도 쉘 탈출 가능 여부 확인 (GTFOBins) |
| 와일드카드 규칙 | /usr/bin/cat /var/log/* → ../로 다른 파일 지정 가능 | 와일드카드 지양 |
| 사용자 쓰기 가능 스크립트 허용 | 허용된 스크립트 내용을 바꿔 임의 명령 실행 | 허용 대상은 root 소유·쓰기 불가 |
| root 비밀번호 공유 + su | 누가 했는지 불명확 | sudo 사용, root 원격 로그인 금지 |
env_keep에 위험 변수 | PATH·LD_PRELOAD 전달 (9편) | 기본 설정 유지 |
# sudo로 실행된 명령 목록 (실행자별)
sudo grep 'COMMAND=' /var/log/secure \
| sed -E 's/.*sudo\[[0-9]+\]: +([^ ]+) : .*COMMAND=(.*)/\1 \2/' \
| sort | uniq -c | sort -rn | head
# 실패·거부
sudo grep -E 'NOT in sudoers|incorrect password|FAILED SU' /var/log/secure
# 쉘을 sudo로 연 경우
sudo grep -E 'COMMAND=.*(/bash|/sh|su -|-i$)' /var/log/secure
# sudoers 변경 감사
sudo ausearch -k sudoers_change -i | tail
# /etc/audit/rules.d/priv.rules
-w /etc/sudoers -p wa -k sudoers_change
-w /etc/sudoers.d/ -p wa -k sudoers_change
# root 권한으로 실행된 명령을 원래 사용자(auid)와 함께 기록
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=unset -k root_exec
[Alert] SIEM: 'user NOT in sudoers' 5회 (계정 intern01, 23:10~23:12)
↓
[맥락] intern01 은 평소 sudo를 쓰지 않는 계정 / 23:08 SSH 로그인, 출발지 해외 IP
↓
[IOC] 출발지 IP, 시도한 COMMAND (id, bash, cat /etc/shadow)
↓
[추가 확인] 이후 su 시도(FAILED SU), SUID 파일 탐색(find -perm -4000) audit 기록
↓
[판단] 탈취 계정으로 권한 상승 경로 탐색 중 → 침해 의심
↓
[Response] 세션 종료·계정 잠금, 출발지 차단, 비밀번호 재설정, 같은 IP의 다른 서버 접속 조회
su는 대상 계정 비밀번호 로 쉘 전체를 얻고, sudo는 본인 비밀번호 로 허용된 명령만 실행한다./var/log/secure(Ubuntu auth.log)에 USER, COMMAND를 남긴다. su는 세션 시작만 남는다.visudo로만 수정하고, 규칙은 /etc/sudoers.d/로 분리한다.NOPASSWD: ALL, 쉘 탈출 가능한 명령 허용, 와일드카드, 사용자 쓰기 가능 스크립트 허용은 위험 설정이다.user NOT in sudoers, incorrect password attempts, FAILED SU는 권한 상승 시도 의 신호다.execve + auid 로 원래 사용자에게 연결한다.다음 글 「20. 최소 권한 원칙」 은 Part 2의 마무리다. 11~19편에서 다룬 파일 권한, 소유권, 계정, sudo를 최소 권한 원칙(Least Privilege) 이라는 하나의 기준으로 묶고, 서비스 계정·systemd·SSH까지 적용하는 점검 체크리스트를 만든다.