리눅스 시스템 기초 · 입문편 — 본문의 "N편"은 입문 과정 번호다. 번호별 글과 전체 250편 구성은 통합 로드맵에서 확인할 수 있다.
리눅스 시스템 기초 17 / 50 · Part 2. 파일·명령어·권한
실습 환경: Rocky Linux 9 (10.0.0.200) · Apache httpd
이전 글: 16. chmod와 권한 관리
🔗 심화 시리즈 — 「파일 · 권한 · 사용자 관리」
이 주제를 더 깊게 다룬 실무형 보안 시리즈: 26. chown으로 파일 소유자 변경 · 27. chgrp로 그룹 소유권 변경
15편에서 권한 검사가 "프로세스 UID와 파일 소유자가 같은가"에서 시작한다는 것을 봤다. 즉 누가 파일을 소유하느냐 가 권한의 출발점이다. 이번 글은 소유권을 바꾸는 chown, chgrp를 다룬다.
소유권 설계 실수는 실제 침해사고로 자주 이어진다. 대표적인 예가 웹 서버 계정이 웹 루트를 소유하는 구성 이다. 웹 취약점 하나로 실행된 코드가 웹 서버 권한을 가지므로, 그 계정이 소유한 코드 파일을 그대로 수정하거나 새 웹셸을 저장할 수 있다.
이번 글에서 답할 질문:
chown, chgrp는 누가, 어떻게 쓸 수 있는가?| 명령 | 예 | 의미 |
|---|---|---|
chown user file | chown roror a.txt | 소유자 변경 |
chown user:group file | chown root:apache config.php | 소유자·그룹 동시 변경 |
chown :group file | chown :dev a.txt | 그룹만 변경 |
chgrp group file | chgrp dev a.txt | 그룹만 변경 |
chown -R | chown -R root:root /var/www/html | 하위 전체 |
chown -h | chown -h roror link | 심볼릭 링크 자체 변경 (대상이 아니라) |
chown --reference=A B | A와 같은 소유권으로 |
| 작업 | 가능한 사람 |
|---|---|
| 소유자 변경 | root만 (CAP_CHOWN) |
| 그룹 변경 | root, 또는 파일 소유자가 자신이 속한 그룹으로만 |
일반 사용자가 자기 파일을 다른 사람 소유로 넘길 수 없는 이유는, 그게 가능하면 디스크 할당량(quota) 회피나 책임 전가가 가능해지기 때문이다.
실행 파일의 소유자나 그룹을 바꾸면 커널이 SUID·SGID 비트를 자동으로 해제 한다. 소유자가 바뀐 파일이 이전 설정대로 새 소유자 권한으로 실행되는 사고를 막기 위해서다. 따라서 SUID 파일의 소유권을 바꾼 뒤에는 필요하면 특수 권한을 다시 설정해야 한다.

inode에는 사용자 이름이 아니라 UID·GID 숫자 가 저장된다. ls -l이 이름을 보여주는 것은 /etc/passwd, /etc/group에서 번호를 이름으로 바꿔 출력하기 때문이다.
그래서 계정을 삭제해도 그 계정이 만든 파일은 숫자 UID로 남는다(orphan file). 나중에 같은 UID로 새 계정을 만들면 새 계정이 옛 파일의 소유자가 된다. 퇴사자 파일에 신규 입사자가 접근하게 되는 식의 사고가 여기서 생긴다.
Apache httpd는 root로 시작해 80번 포트를 연 뒤, 실제 요청 처리 프로세스는 apache 계정 으로 실행한다. 웹 애플리케이션 코드(PHP 등)도 이 apache 권한으로 동작한다. 따라서 권장 구성은 다음과 같다.
| 대상 | 소유 | 권한 | 이유 |
|---|---|---|---|
| 웹 루트 코드 | root:root | 파일 644, 디렉터리 755 | apache는 읽기만 |
| 설정 파일 (DB 비밀번호) | root:apache | 640 | apache만 읽기, 다른 사용자 차단 |
| 업로드 디렉터리 | root:apache | 770 (웹 루트 밖) | 쓰기는 여기서만, 실행 금지 |
| 로그 | root:root | 600/640 | 웹 계정이 로그 변조 불가 |
# 1) 소유권 확인 — 이름과 숫자
ls -l /var/www/html | head
ls -ln /var/www/html | head # UID/GID 숫자로
stat -c '%U(%u):%G(%g) %n' /etc/shadow /var/www/html
# 2) 소유권 변경
sudo mkdir -p /srv/lab17 && sudo touch /srv/lab17/app.conf
sudo chown root:apache /srv/lab17/app.conf
sudo chmod 640 /srv/lab17/app.conf
ls -l /srv/lab17/app.conf
# 3) 일반 사용자는 소유자를 넘길 수 없음
touch ~/mine.txt
chown root ~/mine.txt # Operation not permitted
chgrp wheel ~/mine.txt && ls -l ~/mine.txt # 속한 그룹이면 가능
# 4) chown 시 SUID 해제 확인
sudo cp /usr/bin/true /srv/lab17/t && sudo chmod 4755 /srv/lab17/t
ls -l /srv/lab17/t # -rwsr-xr-x
sudo chown roror /srv/lab17/t
ls -l /srv/lab17/t # -rwxr-xr-x (s 사라짐)
# 5) orphan 파일 만들기와 찾기
sudo useradd tempuser && sudo -u tempuser touch /tmp/lab17_orphan
sudo userdel tempuser
ls -l /tmp/lab17_orphan # 소유자가 숫자로 표시
sudo find / -xdev \( -nouser -o -nogroup \) -ls 2>/dev/null | head
sudo rm -f /tmp/lab17_orphan; sudo rm -rf /srv/lab17
아래 출력은 형식 설명용 예시다.
① 웹 루트 점검 결과
drwxr-xr-x. 3 apache apache /var/www/html
-rw-r--r--. 1 apache apache index.php
-rw-r--r--. 1 apache apache config.php
-rw-r--r--. 1 apache apache .cache.php ← 2026-09-23 04:12 생성
| 발견 | 판단 |
|---|---|
웹 루트 전체가 apache 소유 | 코드 변조가 가능한 구성 — 설계 문제 |
config.php가 644 | 서버의 모든 사용자가 DB 비밀번호 열람 가능 |
숨김 이름 .cache.php, 새벽 생성 | 웹셸 의심 — 내용·접근 로그 확인 |
② orphan 파일
-rw-r--r--. 1 1003 1003 0 Sep 23 11:40 /tmp/lab17_orphan
이름 대신 숫자가 보이면 해당 UID의 계정이 없다 는 뜻이다.
| 위험 | 원인 | 대응 |
|---|---|---|
| 웹셸 저장·코드 변조 | 웹 계정이 웹 루트 소유 | 코드는 root 소유, 쓰기 영역 분리 |
| 설정 파일 비밀번호 노출 | 민감 파일 644 | root:<서비스그룹> 640 |
| 퇴사자 파일 승계 | 계정 삭제 후 UID 재사용 | 삭제 전 파일 처리, -nouser 점검 |
chown -R 사고 | 심볼릭 링크를 따라가 의도치 않은 경로 변경 | -h, --no-dereference 확인, 기본은 -P |
| 공격자의 흔적 위장 | root 획득 후 악성 파일 소유자를 정상 계정으로 변경 | ctime 변화, auditd chown 기록 |
# 웹 루트에서 웹 계정이 소유하거나 쓸 수 있는 파일
sudo find /var/www/html \( -user apache -o -group apache -perm -g+w \) -type f -ls
# 최근 1일 내 웹 루트에 생성·변경된 스크립트
sudo find /var/www/html -type f \( -name '*.php' -o -name '*.jsp' \) -newermt '1 day ago' -ls
# 소유자·그룹 없는 파일
sudo find / -xdev \( -nouser -o -nogroup \) -ls 2>/dev/null
# 소유권 변경 감사 기록 (15편 perm_mod 규칙)
sudo ausearch -k perm_mod -sc chown -i | tail
1. stat → 생성(btime)·변경(ctime) 시각, 소유자
2. file / head → 실제 형식, 내용 일부 (실행하지 않는다)
3. sha256sum → 해시 기록, 위협 인텔리전스 조회
4. access_log → 해당 파일로 들어온 요청, 요청 IP, 파라미터
5. 같은 시각 → 업로드 요청, 다른 새 파일 (14편 find -newerct)
[Alert] FIM(파일 무결성): /var/www/html/.cache.php 신규 생성 (소유자 apache)
↓
[확인] btime 04:12, 내용에 외부 입력을 명령으로 실행하는 코드
↓
[IOC] access_log: 04:11 upload.php POST (IP 203.0.113.50) → 04:13부터 .cache.php 요청
↓
[Root Cause] 업로드 검증 부재 + apache 소유 웹 루트 → 웹셸 저장
↓
[Response] 파일 격리·증적, IP 차단, 웹 루트 소유권 root:root 재설정, 업로드 취약점 수정
-nouser/-nogroup 파일, chown 감사 기록.다음 글 「18. Linux 사용자와 그룹」 에서는 소유권의 주체인 계정 자체를 다룬다. /etc/passwd, /etc/shadow, /etc/group의 필드 구조, UID 범위와 시스템 계정, 계정 생성·변경 로그를 관제에서 확인하는 방법을 정리한다.