파일 · 권한 · 사용자 관리 17 / 50 · Part 2. Linux 파일 종류와 속성
실습 환경: Rocky Linux 9.8 · Ubuntu 24.04.5 (Docker 격리 컨테이너, 테스트 계정 analyst)
기초편 연계: 「리눅스 시스템 기초」 13. Linux 파일 종류

1. 들어가며

Windows에서는 확장자가 파일 형식을 결정한다. .exe를 .txt로 바꾸면 더블클릭해도 실행되지 않는다. Linux는 다르다. 확장자는 사람과 일부 응용 프로그램을 위한 힌트일 뿐이고, 커널과 대부분의 도구는 파일 내용 으로 형식을 판단한다.

이 차이는 웹 업로드 공격의 근본 원리다. 이미지 확장자를 가진 PHP 파일이 서버에서 실행되는 웹셸 사고가 여기서 시작된다. 이번 글에서는 확장자와 내용이 다른 파일을 직접 만들어 Linux가 확장자를 신뢰하지 않는다는 것을 확인한다.


2. 핵심 개념

2-1. 확장자를 보는 주체와 보지 않는 주체

확장자를 보지 않는 것확장자를 보는 것
커널의 실행 (execve) — shebang/ELF 매직으로 판단데스크톱 파일 관리자 (아이콘·연결 프로그램)
cat, cp, chmod 등 대부분의 명령일부 응용 프로그램 (확장자로 처리 방식 선택)
file 명령 (내용 기반)웹 서버의 핸들러 매핑 (.php → PHP 처리기)

특히 웹 서버 는 확장자로 처리기를 고르는 경우가 많다. 그래서 "커널은 내용을 보고, 웹 서버는 확장자를 본다"는 인식 차이 가 공격 표면이 된다.

2-2. Magic Number

대부분의 파일 형식은 시작 부분에 고유한 바이트 시그니처(Magic Number)를 가진다. file 명령은 이 시그니처를 데이터베이스(/usr/share/misc/magic)와 대조해 형식을 판정한다. (자세한 시그니처는 18편)


3. 동작 원리

확장자 ≠ 파일 형식

커널이 파일을 실행할 때(execve)는 확장자를 전혀 보지 않는다. 파일 앞부분을 읽어서 다음을 판단한다.

  • #!로 시작하면 → 뒤에 지정된 인터프리터로 실행 (스크립트)
  • \x7fELF로 시작하면 → ELF 바이너리로 실행
  • 그 외 → Exec format error

웹 서버는 반대로 동작한다. Apache의 AddHandler나 .php 매핑은 파일 이름의 확장자 를 보고 PHP 처리기로 넘길지 결정한다. 그래서 업로드된 avatar.jpg가 어떤 이유로든 PHP로 해석되는 경로에 저장되면, 그 안의 PHP 코드가 실행된다.


4. 명령어 실습

실습 파일 avatar.jpg에는 실제 공격 코드 대신 echo "upload test"만 담긴 무해한 PHP 예시를 넣었다. 목적은 형식 판정 동작 확인 이다. 격리된 테스트 계정에서만 진행한다.

mkdir ~/lab17 && cd ~/lab17

# 1) 확장자와 내용이 다른 파일들 만들기
printf '<?php echo "upload test"; ?>\n' > avatar.jpg        # jpg 이름, PHP 내용
base64 -d <<< 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC' > real.png   # 진짜 PNG
cp real.png report.txt                                       # txt 이름, PNG 내용
cp avatar.jpg shell.php.jpg                                  # 이중 확장자
cp /bin/true invoice.pdf                                     # pdf 이름, ELF 내용

# 2) 내용으로 형식 판정
file avatar.jpg real.png report.txt shell.php.jpg invoice.pdf

# 3) MIME 타입
file --mime-type avatar.jpg real.png invoice.pdf

# 4) 위험 패턴 내용 검색 (이름 무시)
grep -lE '<\?php|system\(|eval\(' * 2>/dev/null

5. 실행 결과

실제 실행 결과 — Rocky Linux 9.8 · analyst@rocky9-lab — 확장자와 실제 형식

텍스트 원본(실제 출력):

[analyst@rocky9-lab ~]$ mkdir ~/lab17 && cd ~/lab17
[analyst@rocky9-lab lab17]$ printf '<?php echo "upload test"; ?>\n' > avatar.jpg
[analyst@rocky9-lab lab17]$ base64 -d <<< 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADElEQVR4nGP4z8AAAAMBAQDJ/pLvAAAAAElFTkSuQmCC' > real.png
[analyst@rocky9-lab lab17]$ cp real.png report.txt
[analyst@rocky9-lab lab17]$ cp avatar.jpg shell.php.jpg
[analyst@rocky9-lab lab17]$ cp /bin/true invoice.pdf
[analyst@rocky9-lab lab17]$ file avatar.jpg real.png report.txt shell.php.jpg invoice.pdf
avatar.jpg:    PHP script, ASCII text
real.png:      PNG image data, 1 x 1, 8-bit/color RGB, non-interlaced
report.txt:    PNG image data, 1 x 1, 8-bit/color RGB, non-interlaced
shell.php.jpg: PHP script, ASCII text
invoice.pdf:   ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=bde0209a3faec51dc64dbdbb9f2b5cd733ccae6b, for GNU/Linux 3.2.0, stripped
[analyst@rocky9-lab lab17]$ file --mime-type avatar.jpg real.png invoice.pdf
avatar.jpg:  text/x-php
real.png:    image/png
invoice.pdf: application/x-pie-executable
[analyst@rocky9-lab lab17]$ grep -lE '<\?php|system\(|eval\(' * 2>/dev/null
avatar.jpg
shell.php.jpg

6. 결과 해석

출력해석
avatar.jpg: PHP script.jpg 이름이지만 내용은 PHP. file은 이름을 무시한다
real.png: PNG image data, 1 x 1진짜 PNG (Magic Number 89 50 4E 47)
report.txt: PNG image data.txt 이름이지만 내용은 PNG
shell.php.jpg: PHP script이중 확장자 우회 시도. 내용은 PHP
invoice.pdf: ELF 64-bit LSB pie executable.pdf 이름이지만 실제로는 실행 파일
avatar.jpg: text/x-phpMIME 판정도 내용 기준
invoice.pdf: application/x-pie-executable실행 파일 MIME
grep -l → avatar.jpg, shell.php.jpg이름과 무관하게 PHP 코드가 든 파일을 찾아냄

핵심은 grep -lE다. 업로드 디렉터리에서 위험 파일을 찾을 때 *.php로만 검색하면 avatar.jpg로 위장한 웹셸을 놓친다. 이름이 아니라 내용으로 검색 해야 한다.


7. 보안 관점

공격원리방어
이미지로 위장한 웹셸.jpg 확장자로 업로드 필터 통과 후 PHP로 해석되는 경로에 저장① 내용 검사(file/MIME) ② 업로드 경로에서 스크립트 실행 차단 ③ 저장 시 이름·확장자 재생성
이중 확장자shell.php.jpg — 서버 설정에 따라 .php로 해석마지막 확장자만 신뢰하지 않음, 허용 목록(allowlist) 방식
매직 바이트 삽입파일 앞에 GIF89a를 붙여 이미지처럼 보이게 한 뒤 PHP 코드확장자·매직만으로 판단하지 않고 실행 경로 자체를 차단
위장한 실행 파일invoice.pdf가 ELF다운로드·첨부 파일은 내용 기준 검사

핵심 방어 원칙: 입력 검증(내용 확인) 과 실행 환경 차단(업로드 경로에서 코드 실행 불가) 을 함께 적용한다. 어느 하나만으로는 부족하다.


8. 보안관제 관점

[Detection]  WAF/웹로그: /uploads/avatar.jpg 에 대한 POST 후 GET 요청 반복
     ↓
[Hunt]       find /var/www/html/uploads -type f -exec file {} + | grep -iE 'php|executable|script'
             grep -rlE '<\?php|eval\(|base64_decode\(|system\(' /var/www/html/uploads
     ↓
[Evidence]   해당 파일 stat(생성 시각·소유자=웹 계정) + sha256sum
     ↓
[Timeline]   웹 액세스 로그에서 그 파일의 최초 업로드(POST)·실행(GET) 요청 IP
     ↓
[Response]   파일 격리, 업로드 경로 실행 차단 확인, 취약 업로드 기능 패치
관점내용
Hunt업로드 디렉터리는 확장자가 아니라 file·내용 패턴으로 점검
IOC웹셸은 이름이 매번 다르므로 해시·코드 패턴 이 이름보다 유효한 지표
오탐 주의이미지 메타데이터(EXIF)에 우연히 매칭되는 문자열이 있을 수 있다. file 판정과 실제 코드 블록을 함께 확인

9. 실무에서 자주 발생하는 실수

실수결과예방
업로드를 확장자만으로 검증위장 파일 통과내용(MIME/매직) 검사 병행
*.php로만 웹셸 검색위장 파일 누락file + 내용 grep
업로드 경로에 실행 권한 허용웹셸 실행업로드 디렉터리 스크립트 실행 차단
MIME를 클라이언트가 보낸 값으로 신뢰위조 가능서버에서 file로 재확인
확장자만 바꿔 "안전"하게 만들려 함내용은 그대로형식은 내용이 결정

10. 실습 체크리스트

[ ] 확장자와 내용이 다른 파일을 여러 개 만들었다
[ ] file 이 이름을 무시하고 내용으로 판정하는 것을 확인했다
[ ] MIME 타입도 내용 기준임을 확인했다
[ ] grep 으로 이름과 무관하게 PHP 코드를 찾아냈다
[ ] 이중 확장자 우회 원리를 이해했다
[ ] 업로드 방어의 두 축(내용 검사 + 실행 차단)을 설명할 수 있다

11. 핵심 정리

  • Linux에서 확장자는 힌트일 뿐, 커널과 대부분의 도구는 내용으로 형식을 판단 한다.
  • 커널은 실행 시 shebang·ELF 매직을 보고, 웹 서버는 확장자를 본다. 이 인식 차이가 공격 표면이다.
  • file과 MIME 판정은 모두 내용 기준이다.
  • 웹셸 헌팅은 *.php가 아니라 file·내용 패턴으로 한다.
  • 업로드 방어는 내용 검사 + 업로드 경로 실행 차단 을 함께 적용한다.
  • 웹셸은 이름이 매번 달라지므로 해시·코드 패턴이 더 유효한 IOC다.

12. 다음 편 예고

다음 글 「18. file 명령어와 Magic Number」 에서는 file이 어떻게 형식을 알아내는지, ELF·PNG·gzip·ZIP·PE의 실제 매직 바이트를 xxd로 직접 확인하고, file이 틀릴 수 있는 경우까지 살펴본다.


참고 자료


시리즈 이동

profile
코드에 숨겨진 위협을 읽고 AI로 보안의 미래를 설계합니다. 프론트엔드 개발 경험을 자산 삼아 더 견고하고 지능적인 보안 운영 시스템을 구축해 나가는 과정을 기록합니다

0개의 댓글