Step 99. Bandit 16~20 — setuid 첫 만남

Step 99. Bandit 16~20 — setuid 첫 만남

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

전제: Step 97의 find 조건과 SUID 개념, Step 98의 nc·openssl·키 접속을 마쳤다. Bandit 15→16 비밀번호를 가지고 있다.

  • 준비물: SSH 접속 환경, 비밀번호 기록, 로컬 실험용 리눅스/WSL.
  • ⚠️ 이 챕터의 실습은 내 랩·합법 플랫폼 전용입니다. 허가 없는 시스템에 적용하면 범죄입니다.
  • 합법 연습장 안내: OverTheWire Bandit은 공식 공격 연습 플랫폼입니다. setuid 실험은 이 서버와 내 컴퓨터의 연습 폴더에서만 하며, 내가 소유한 파일에만 권한 실험을 적용합니다. 실험 파일은 끝나는 즉시 정리합니다.

지금까지의 Bandit은 "찾고 읽는" 게임이었습니다. 오늘부터 격이 다른 개념이 등장합니다 — 남의 권한으로 실행되는 프로그램. 질문 하나로 시작합니다. 내 계정으로는 절대 못 여는 파일이 있습니다. 그런데 이상한 프로그램이 하나 놓여 있습니다. 이 프로그램을 실행하면, 실행하는 순간만큼은 내가 그 파일의 주인이 됩니다. 그 프로그램에게 "저 파일을 읽어 줘"라고 시키면? 읽어 줍니다. 이것이 setuid — 리눅스 권한 상승의 알파이자 오메가입니다.


1. 학습 목표

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

  • 포트 범위 스캔으로 열린 서비스를 찾고 TLS 여부를 확인한다
  • diff로 두 파일의 차이만 읽어 낸다
  • ssh 주소 "명령" 형태로 쉘 없이 명령만 직행한다
  • ls -ls 표식으로 setuid 파일을 알아보고, id 실험으로 권한 교체를 증명한다
  • nc 리스너와 클라이언트를 두 터미널로 연결해 "상대가 나에게 오게" 만든다

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

오늘의 도구 한눈에 보기

구분 내용
언어·환경 리눅스 셸(Bandit 서버 + 로컬 WSL)
오늘의 명령 nmap -p 범위, diff, ssh ... "명령", id, chmod u+s, nc -l
필요한 개념 setuid 비트, uid/gid, 권한 상승, 리스너-클라이언트 구도
오늘의 산출물 Bandit 16→21 비밀번호 체인 + setuid 개념 정리 문서

2-1. setuid의 원리 — 권한의 탈을 쓴 프로그램

ls -l에서 소유자 실행 자리가 x가 아니라 s인 파일을 봅시다.

-rwsr-xr-x 1 bandit20 bandit19 ... bandit20-do

s가 setuid 비트입니다. 의미는 이렇습니다 — "이 파일을 실행하면, 실행한 사람이 누구든 소유자의 권한으로 프로세스가 돈다."

왜 이런 장치가 존재할까요? 대표 예가 passwd입니다. 내 비밀번호를 바꾸려면 root만 쓸 수 있는 시스템 파일을 건드려야 합니다. 일반 사용자에게 root를 줄 수는 없으니 "passwd를 실행하는 순간만 root 권한"이라는 안전핀을 단 것입니다. 문제는 그 안전핀이 달린 프로그램에 구멍이 있을 때 — 구멍을 통해 임의의 명령을 실행시키면 그 명령이 주인의 권한으로 돕니다. Step 97에서 "목록을 뽑아 보는" 개념으로 만난 SUID가, 오늘은 실제 계급 사다리가 됩니다.

2-2. diff — 두 파일의 차이만 보기

diff 파일1 파일2는 두 파일을 줄 단위로 비교해 다른 부분만 보여 줍니다. 설정 파일이 업데이트 전후로 뭐가 바뀌었는지, 두 비밀번호 목록 중 어느 줄이 새 것인지 — "차이"가 곧 정보인 상황에서 씁니다.

2-3. ssh의 숨은 재주 — 접속과 동시에 명령 전달

ssh 사용자@주소 명령 형태로 접속 끝에 명령을 붙이면, 쉘에 들어가지 않고 그 명령만 실행하고 결과를 가져옵니다. 로그인하자마자 쫓겨나는 까다로운 계정을 우회하는 비상구이고, 자동화 스크립트의 기본 형태이기도 합니다.

2-4. 리스너와 클라이언트 — 내가 벌이는 만남

Step 98에서 nc의 두 얼굴을 봤습니다 — 접속하는 쪽과 기다리는 쪽(-l). 오늘은 이 둘을 같은 사람(나)이 동시에 합니다. 한 터미널에서 기다리고, 다른 터미널에서 프로그램이 나에게 접속하게 만드는 것. "내가 판을 깔고, 상대가 걸어오게 한다"는 구도는 리버스 쉘(공격 대상이 거꾸로 공격자에게 접속하는 기법)의 뼈대입니다.


3. 따라 하기

3-1. Level 16 → 17 — 열린 포트를 스스로 찾기

목표: "localhost의 31000~32000 사이에서 열린 포트를 찾아, SSL로 접속해 키를 제출하라."

입력 (서버 안에서, 출력 예시)

nmap -p 31000-32000 localhost

이 스캔을 내 컴퓨터에서 실측했습니다. 31500번에 nc 서버를 띄워 두고 31490~31510을 스캔한 결과입니다 (2026-09-09 WSL 실측, Nmap 7.94):

PORT      STATE  SERVICE
31490/tcp closed unknown
...
31500/tcp open   unknown
31501/tcp closed unknown
...
Nmap done: 1 IP address (1 host up) scanned in 0.10 seconds

읽는 법: 21개 중 하나만 open입니다. Bandit에서는 열린 포트가 몇 개 나오고, 그중 어느 것이 SSL인지는 각각 openssl s_client로 시험해 봐야 합니다 (Step 98의 기술). 스캔 → 확인 → 대화의 3단 콤보입니다.

3-2. Level 17 → 18 — diff로 다른 줄 찾기

passwords.oldpasswords.new가 있고, 바뀐 한 줄이 답입니다.

입력 (서버 안에서, 출력 예시)

diff passwords.old passwords.new

로컬 재현 (2026-09-09 WSL 실측):

printf 'aaaa\nbbbb\noldpass_w0rd\ndddd\n' > passwords.old
printf 'aaaa\nbbbb\nNEWPASS_XYZ\ndddd\n' > passwords.new
diff passwords.old passwords.new
3c3
< oldpass_w0rd
---
> NEWPASS_XYZ

읽는 법: 3c3은 "3번째 줄이 바뀌었다(change)"는 뜻입니다. <는 왼쪽(옛) 파일, >는 오른쪽(새) 파일의 내용. 바뀐 그 한 줄이 정답입니다. diff는 설정 변경 추적, 코드 리뷰, 침해 흔적 비교("원본과 지금의 차이")까지 쓰임이 끝없는 도구입니다.

3-3. Level 18 → 19 — 로그인하자마자 쫓겨나는 계정

이번 계정은 접속하면 즉시 튕깁니다 — .bashrc(로그인 때 자동 실행되는 설정 스크립트)에 exit이 심어져 있습니다.

입력 (쉘에 들어가지 않는 방법, 출력 예시)

ssh bandit18@bandit.labs.overthewire.org -p 2220 "cat readme"

비밀번호를 묻고, 입력하면 쉘 프롬프트 없이 readme의 내용만 출력되고 연결이 끊깁니다.

읽는 법: 접속 끝에 붙인 명령이 로그인 쉘을 거치지 않고 직행했기에, .bashrc의 퇴장 명령을 피해 갔습니다. "자동 실행되는 것"이 흐름을 바꾼다는 감각 — 이것이 바로 다음 단원(cron)의 예고편입니다.

3-4. Level 19 → 20 — setuid 바이너리 실행

입력 (서버 안에서, 출력 예시)

ls -l
./bandit20-do
./bandit20-do id
./bandit20-do cat /etc/bandit_pass/bandit20

출력 예시 (핵심 장면):

$ id
uid=11019(bandit19) gid=11019(bandit19) groups=11019(bandit19)
$ ./bandit20-do id
uid=11020(bandit20) gid=11019(bandit19) groups=11019(bandit19)

읽는 법: 프로그램을 거치자 uid가 bandit20으로 바뀝니다. 내 권한으로는 못 읽는 파일을 이 프로그램이 대신 읽어 줍니다. 이 세 줄이 권한 상승(privilege escalation)의 축소 모형입니다.

3-5. setuid 비트 관찰 — 내 컴퓨터에서

시스템을 바꾸지 않고, 내 소유 파일에만 비트를 켜서 표식을 관찰합니다 (2026-09-09 WSL 실측):

cp /bin/echo myecho
ls -l myecho
chmod u+s myecho
ls -l myecho
find . -perm /4000 -type f

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

-rwxr-xr-x 1 root root 35208 Sep  9 14:31 myecho
-rwsr-xr-x 1 root root 35208 Sep  9 14:31 myecho
./myecho

읽는 법: chmod u+s 한 번에 소유자 실행 자리의 xs로 바뀌었고, Step 97의 정찰 명령(find -perm /4000)이 이 파일을 정확히 집어냈습니다. 만드는 쪽 명령과 찾는 쪽 명령이 하나의 고리로 이어졌습니다. 실험이 끝나면 이 파일은 지우세요.

3-6. Level 20 → 21 — 내가 판을 깔고 기다리기

목표: suconnect라는 setuid 프로그램이 "지정한 포트로 접속해, 현재 비밀번호를 보내고, 맞으면 다음 비밀번호를 보내 준다."

입력 (서버 안, 터미널 1 — 내가 서버, 출력 예시)

nc -l -p 1234

입력 (터미널 2)

./suconnect 1234

터미널 1에 연결이 들어옵니다. 현재 비밀번호를 치면 검증 후 다음 비밀번호가 터미널 1에 도착합니다.

읽는 법: 접속하는 쪽(setuid 프로그램)과 기다리는 쪽(내 nc)이 만났습니다. Step 98의 로컬 nc 실험(2026-09-09 실측)과 같은 구조인데, 이번엔 접속하는 주체가 "높은 권한의 프로그램"이라는 점이 다릅니다. 통신의 두 끝을 내가 동시에 쥐는 경험입니다.

3-7. id와 권한의 해부 — 나는 누구인가

오늘의 핵심 명령 id를 뜯어 봅시다 (2026-09-09 WSL 실측 — 실험 계정이 root라 출력도 root입니다):

id
uid=0(root) gid=0(root) groups=0(root)

읽는 법: uid는 사용자 번호, gid는 주 그룹 번호, groups는 속한 모든 그룹입니다. 리눅스의 권한 판정은 이름이 아니라 이 숫자들로 일어납니다. root의 uid가 0인 것도 확인됩니다 — 시스템의 왕은 번호가 0번입니다.

: setuid 프로그램이 하는 일의 실체가 보입니다 — 그 프로그램을 거치면 프로세스의 uid가 파일 주인의 번호로 교체됩니다. "권한 상승"이라는 거창한 말의 실체가 숫자 하나의 교체입니다.


4. 미션과 연습문제

미션 — Bandit 16→21 체인과 setuid 정리 문서

  1. bandit20까지 체인을 완성하고 각 레벨을 write-up 형식으로 기록합니다
  2. setuid 확인 절차를 정리합니다 — ls -l에서 s 찾기 → find / -perm /4000 -type f 2>/dev/null로 목록화
  3. 내 컴퓨터에서 3-5의 관찰 실험을 재현하고 실험 파일을 즉시 지웁니다
  4. 위키에 setuid.md를 씁니다 — 원리 / 왜 존재하는가(passwd 예시) / 왜 위험한가 / 수비자의 대책(목록 감시)
  5. "권한 상승"을 자기 말로 세 문장으로 정의해 문서 맨 위에 적습니다

연습문제

문제 1. -rwsr-xr-x에서 s의 의미와, 이 비트가 켜진 프로그램이 실행될 때 프로세스에 일어나는 일을 설명해 보세요.

문제 2. diff 출력의 3c3, <, >가 각각 무엇을 뜻하나요?

문제 3. Level 18 계정에서 ssh ... "cat readme".bashrcexit을 피해 가는 원리를 설명해 보세요.

문제 4. 어떤 서버에 cat 프로그램이 root 소유 + setuid로 설정되어 있습니다. 일반 사용자가 할 수 있는 일은 무엇이며, 이것이 위험한 이유는 무엇인가요?


5. 모범 답안과 완료 기준

미션 모범 답안

체인의 뼈대 (서버 안 명령은 출력 예시):

nmap -p 31000-32000 localhost                    # L16→17 (열린 포트 후보)
openssl s_client -connect localhost:<열린포트>    # SSL 포트 확인 후 키 제출
diff passwords.old passwords.new                 # L17→18
ssh bandit18@bandit.labs.overthewire.org -p 2220 "cat readme"   # L18→19
./bandit20-do cat /etc/bandit_pass/bandit20      # L19→20
nc -l -p 1234   (다른 터미널)  ./suconnect 1234   # L20→21

검증하는 법: ① ./bandit20-do id의 출력에서 uid가 바뀐 것을 눈으로 확인했는가. ② setuid.md에 "존재 이유"와 "위험 이유"가 둘 다 적혀 있는가 — 하나만 적혀 있으면 반쪽짜리 이해입니다. ③ 로컬 실험 파일(myecho)을 지웠는가.

연습문제 해답

문제 1 해답. s는 setuid 비트로, 이 파일을 실행하면 실행자가 누구든 프로세스의 유효 사용자 번호(uid)가 파일 소유자의 번호로 교체됩니다. 즉 소유자의 권한으로 프로그램이 돕니다 (3-4절의 id 출력 대조 참조).

문제 2 해답. 3c3은 "왼쪽 3번째 줄이 오른쪽 3번째 줄로 바뀌었다(change)"는 뜻이고, <는 왼쪽 파일의 내용, >는 오른쪽 파일의 내용입니다 (2026-09-09 로컬 실측 출력 참조).

문제 3 해답. .bashrc는 대화형 로그인 쉘이 시작될 때 실행됩니다. ssh 주소 "명령"은 쉘에 들어가지 않고 명령만 직행하므로 대화형 쉘이 뜨지 않고, 따라서 .bashrcexit도 실행되지 않습니다.

문제 4 해답. 시스템의 모든 파일을 root 권한으로 읽을 수 있습니다 — /etc/shadow(전 사용자 비밀번호 해시)까지. cat은 "인자로 준 파일을 읽는" 도구이고, setuid는 그 읽기를 root 권한으로 일어나게 합니다. setuid의 위험은 프로그램의 기능이 아니라 "높은 권한 × 내가 조작 가능한 입력"에서 나옵니다.

완료 기준 체크리스트

  • [ ] nmap -p 범위 주소로 열린 포트를 찾을 수 있다
  • [ ] ls -l 출력에서 setuid 비트(s)를 읽을 수 있다
  • [ ] id 앞뒤 비교로 권한 교체를 증명할 수 있다
  • [ ] diff 출력(줄번호, <, >)을 읽을 수 있다
  • [ ] ssh ... "명령" 형태로 비대화형 실행을 할 수 있다
  • [ ] nc 리스너와 클라이언트를 두 터미널로 연결할 수 있다
  • [ ] 미션: 체인 완성과 setuid.md 작성을 끝냈다

6. 흔한 실수와 해결

벽 1. nmap에서 포트가 전부 closed로 나온다

증상 (2026-09-09 실측 형태):

PORT      STATE  SERVICE
31000/tcp closed unknown
... (전부 closed)

원인: 대상 호스트를 잘못 적었거나(localhost여야 함), 범위를 오타했거나, 내 컴퓨터에서 치고 있거나.
해결: nmap -p 31000-32000 localhost의 주소와 범위를 다시 확인하고, Bandit 서버 안에서 치는지 확인하세요. 서버 안에서 쳐야 서버의 포트가 보입니다.

벽 2. SSL 포트에 접속했는데 키를 내도 침묵한다

증상: s_client로 접속했는데 대화가 안 됩니다.
원인: SSL이 아닌 다른 포트에 접속했거나, 제출할 값(이전 레벨의 키)이 틀렸습니다.
해결: 열린 포트가 여러 개면 하나씩 시험하세요. 대화가 성립하는 포트가 정답입니다 — "스캔 → 확인 → 대화"의 확인 단계를 건너뛰면 생기는 벽입니다.

벽 3. bandit18에 접속하면 명령 칠 새도 없이 튕긴다

증상 (출력 예시):

Byebye !
Connection to bandit.labs.overthewire.org closed.

원인: .bashrcexit — 문제의 의도 그 자체입니다.
해결: 3-3의 "접속과 동시에 명령" 형태를 쓰세요. ssh ... "cat readme" — 따옴표로 명령을 감싸는 것을 잊지 마세요.

벽 4. suconnect를 실행했는데 nc 쪽에 아무것도 안 온다

증상: 양쪽 터미널이 서로를 모릅니다.
원인: 포트 번호가 양쪽에서 다르거나, nc -l을 먼저 안 띄웠거나(기다리는 쪽이 먼저!), 다른 레벨의 홈에서 실행 중일 수 있습니다.
해결: 순서 확인 — ① nc -l -p 포트 대기 시작 → ② 다른 터미널에서 ./suconnect 포트 → ③ 같은 포트 번호. localhost라 이 셋만 맞으면 됩니다.

벽 5. setuid 파일을 만들었는데 find가 못 찾는다

증상: chmod u+s를 했는데 find . -perm /4000에 안 나옵니다.
원인: 실행 파일이 아니거나(x 비트 부재), 다른 폴더에서 검색 중이거나.
해결: ls -l-rws 표식을 먼저 확인하세요 (3-5절 실측처럼 s가 보여야 합니다). 비트가 켜져 있는데도 안 나오면 검색 경로를 확인하세요.


7. 정리

오늘의 개념

개념 한 줄 설명
setuid 실행 순간 소유자 권한으로 도는 비트 — ls -ls
uid / gid 권한 판정에 쓰이는 사용자·그룹 번호 (root는 0번)
권한 상승 내 권한이 아닌 것을 빌려 더 높은 권한으로 올라서기
비대화형 ssh 쉘 없이 명령만 직행하는 ssh ... "명령"
리스너 구도 내가 기다리고 상대가 오게 만드는 통신 설계

오늘의 명령어

명령 하는 일
nmap -p 시작-끝 주소 포트 범위 스캔
diff A B 두 파일의 차이만 보기
ssh 주소 "명령" 쉘 없이 명령 직행
id 현재 uid/gid/그룹 확인
chmod u+s 파일 setuid 비트 켜기 (내 파일에만)
find / -perm /4000 -type f 2>/dev/null setuid 파일 목록
nc -l -p 포트 기다리는 서버 되기

명령어보다 중요한 감각

앞으로 프로그램이나 스크립트를 볼 때마다 물을 질문을 하나 가져가세요 — "이건 누구의 권한으로 도는가?" 내가 실행하면 내 권한, setuid면 소유자 권한, 자동 작업이 돌리면 그 작업의 주인 권한입니다. 그리고 위험 평가 공식: 높은 권한 곱하기 조작 가능한 입력은 위험입니다. 오늘 여러분은 처음으로 "나의 것이 아닌 권한"을 손에 쥐었습니다. 이 사다리를 만드는 쪽 명령(chmod u+s)과 찾는 쪽 명령(find -perm /4000)을 둘 다 알게 됐으니, 수비자의 눈도 함께 길러 두세요.


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