πŸ“ WEB-10: File Upload (ν™•μž₯자 우회 & 심볼릭 링크)

파일 μ—…λ‘œλ“œ 필터링 우회둜 μž„μ˜ 파일 μ‹€ν–‰ λ˜λŠ” μ ‘κ·Ό

πŸ“€ 파일 μ—…λ‘œλ“œ

ν—ˆμš© ν™•μž₯자: jpg, png, gif, pdf
πŸ’‘ 우회 벑터:
β€’ shell.php.jpg (더블 ν™•μž₯자 - Apache μ„€μ •μ—μ„œ 싀행될 수 있음)
β€’ shell.jpeg β†’ JPEG 맀직 λ„˜λ²„ + PHP μ½”λ“œ ν˜Όν•©
β€’ .htaccess μ—…λ‘œλ“œ (Apache μ„€μ • λ³€κ²½)
β€’ shell.jpg.php (nginx nullbyte 우회)
β€’ 심볼릭 링크: ln -s /etc/passwd uploaded_link.jpg ν›„ μ—…λ‘œλ“œ

πŸ“‹ μ—…λ‘œλ“œλœ 파일

μ—…λ‘œλ“œλœ 파일이 μ—†μŠ΅λ‹ˆλ‹€.

🎯 곡격 μ‹œλ‚˜λ¦¬μ˜€

1. 더블 ν™•μž₯자 곡격

upload된 shell.php.jpg:

AddType application/x-httpd-php .php # Apache μ„€μ •
파일: shell.php.jpg
⚠️ ApacheλŠ” μš°μΈ‘λΆ€ν„° ν™•μž₯자 인식
β†’ .jpg둜 λ¨Όμ € 체크 (ν—ˆμš©), κ·Έ λ‹€μŒ .php둜 인식 (μ‹€ν–‰)

2. MIME νƒ€μž… 검증 우회

이미지 파일의 맀직 λ„˜λ²„(헀더) + PHP μ½”λ“œ ν˜Όν•©:

$ xxd image.jpg | head
FFD8 FFE0 0010 JFIF 0001 0100 ... (JPEG 헀더)

파일 끝에 PHP μ½”λ“œ μΆ”κ°€:
<?php system($_GET['cmd']); ?>
β†’ MIME νƒ€μž…μ€ image/jpeg둜 인식, ν•˜μ§€λ§Œ νŠΉμ • μ„€μ •μ—μ„œ PHP둜 μ‹€ν–‰

3. 심볼릭 링크 곡격

λ―Όκ°ν•œ νŒŒμΌμ— μ ‘κ·Ό:

ln -s /etc/passwd uploads/secret.jpg
β†’ /uploads/secret.jpg μ ‘κ·Ό μ‹œ /etc/passwd λ‚΄μš© 곡개

ln -s /var/www/html/.env uploads/env.jpg
β†’ DB λΉ„λ°€λ²ˆν˜Έ, API ν‚€ λ…ΈμΆœ

βœ… λ°©μ–΄ 방법

1. ν™•μž₯자 ν™”μ΄νŠΈλ¦¬μŠ€νŠΈ:

$allowed = ['jpg', 'png', 'gif'];
$ext = strtolower(pathinfo($_FILES['file']['name'], PATHINFO_EXTENSION));
if (!in_array($ext, $allowed)) die('Not allowed');

2. MIME νƒ€μž… 검증:

$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
$allowed_mimes = ['image/jpeg', 'image/png'];
if (!in_array($mime, $allowed_mimes)) die('Invalid MIME');

3. 심볼릭 링크 검증:

if (is_link($destination)) die('Symlinks not allowed');

4. μ•ˆμ „ν•œ 파일 μ €μž₯:

// μ›Ή 루트 밖에 μ €μž₯
$upload_dir = '/var/uploads/'; // /var/www/html λ°–
// λ˜λŠ” 파일λͺ… μž¬μƒμ„±
$new_name = uniqid() . '_' . bin2hex(random_bytes(8)) . '.jpg';

5. μ›Ή μ„œλ²„ μ„€μ •:

# Apache .htaccess
<Files ~ "\.jpg$">
  AddType text/plain .jpg
</Files>