모의해킹 스터디 복습
모의해킹 스터디 15주차(1): 이미지 웹쉘의 실체
whydontyoushovel
2025. 2. 11. 04:51
목차
- 웹쉘과 이미지 웹쉘
- 이미지 웹쉘이 가능한 상황
1) 확장자 검증 미흡
2) Null Byte Injection
3) .htaccess 설정 파일
4) File Include 취약점
- 정리
File Upload
공격자가 원하는 임의의 파일을 서버에 업로드할 수 있는 공격
웹쉘은 대표적인 File Upload 공격 시나리오 중 하나
웹쉘 & 이미지 웹쉘
1) 웹쉘
- 인터넷 상에서 OS 명령을 내릴 수 있게 하는 파일
- OS 실행 코드를 파일에 포함시켜 대상 서버에 업로드한 후 해당 파일을 웹으로 요청
- 서버는 요청한 파일의 서버측 스크립트를 모두 실행시킨 뒤 결과를 클라이언트에게 반환함
실행 예시 |
2) 이미지 웹쉘
- 확장자가 .jpg나 .png, .php.jpg 등의 이미지 파일인데 내부의 웹쉘 코드가 실행되는 경우 이미지 웹쉘이라고 함
▷문제
- 웹쉘 공격은 확장자가 서버측 실행코드(php 등)여야 서버에서 WAS로 코드를 넘겨 실행시킬 수 있음
- .jpg나 .php.jpg 등의 확장자는 절대 실행 안되는 게 맞음
- 근데 이미지 웹쉘이 실행된다는 글이 자주 목격됨 (특히 이중확장자)
▷목표
- 이미지 웹쉘이 실행되는 경우를 살펴보고 이게 어째서 실행이 되었으며 이것이 왜 이미지 웹쉘이라고 할 수 없는지 생각해보기
이미지 웹쉘이 실행되는 케이스
▷ webshell.jpg.php 된당께요
업로드 파일에 대한 확장자 검증이 미흡한 경우
- 예시 코드
<?php
function double_extPass($filename) {
$name = explode('.',$filename);
$ext = $name[1];
if($ext == "png") {
return true;
} else {
echo "<script>alert('png 형식만 지원됩니다.');</script>";
exit;
}
}
$explode = double_extPass($_FILES['image']['name']); //.png.php 테스트
if (isset($_FILES['image']) && $_FILES['image']['error'] === 0 && $explode) {
$img_name = basename($_FILES['image']['name']);
$file_path = "img/";
$targetPath = $file_path . $img_name;
// 서버에 파일 저장 + DB에 경로 저장
} else {
// 돌아가
}
?>
- example.png.php 파일을 업로드할 경우 " . "을 기준으로 문자열 분리 후 배열로 저장
- 분리된 문자열 중 [1]번째 요소가 "png"이면 true 반환
- 업로드한 파일이 있고 에러가 없으며, [1]번째 문자열이 "png"일 경우 img/example.png.php라는 경로에 파일 저장
★ 내 서버에서 실습해보기
.png.php 확장자를 가진 웹쉘 파일을 업로드 |
정상 업로드됨 |
업로드한 게시물로 리다이렉션 |
파일을 실행시키기 위해 이미지 아이콘을 우클릭한 뒤 새 탭에서 파일 요청 |
새로 연 탭에서 cmd 명령어를 입력하고 실행 결과를 확인 |
서버의 파일 저장 경로에도 정상적으로 저장됐음을 확인함 |
- 확장자를 하나만 썼을 거란 생각으로 앞에서부터 검증하고 파일 이름 전체를 저장
- 이 경우 .jpg.php 등의 확장자는 서버에 저장될 수 있음
- 악성코드를 유포할 때 이와 비슷한 케이스가 이용되기도 함. 운영체제 내 기본 확장자를 숨기는 옵션이 설정되었을 경우 example.pdf.exe 파일도 example.pdf로 표시되어 악성 프로그램을 실행시킬 수 있음.
▷ webshell.php%00.jpg는 된당께요
Null Byte Injection 취약점
- 예시 코드
<?php
function null_test($filename) {
$name = explode('.',$filename);
$ext = end($name);
if($ext == "png") {
return true;
} else {
echo "<script>alert('" . $ext . ": 이 형식은 안됨.');</script>";
exit;
}
}
$null_test = null_test($_FILES['image']['name']);
if (isset($_FILES['image']) && $_FILES['image']['error'] === 0 && $null_test) {
$img_name = basename($_FILES['image']['name']);
$file_path = "img/";
$targetPath = $file_path . $img_name;
//서버에 파일 저장
//DB에 경로 저장
} else {
//돌아가
}
?>
- example.php%00.png 같은 파일명에서 " . " 을 기준으로 문자열 분리
- 분리한 뒤 얻은 배열의 가장 마지막 값(png)을 변수에 저장
- 이 값이 png일 경우 true 반환
- 업로드한 파일이 있고 에러가 없으며, 파일명 마지막에 쓰인 확장자가 png일 경우 "img/example.php%00.png"라는 경로에 파일 저장
- 이때 서버에는 %00 이후 문자열이 잘린 채 example.php만 저장됨
★ 내 서버에서 실습해보기
이름에 널 바이트가 삽입된 파일과 함께 게시글 작성 |
정상 업로드 되었다는 알림 확인 |
리다이렉션 된 페이지에서 파일을 새 탭으로 엶 |
안됨 |
널 바이트를 생략한 뒤 요청해도 안됨 |
서버를 확인해보니 webshell.php%00.png 로 널 바이트 이후 문자열이 포함된 채 저장됨 널 바이트 인젝션 공격에 실패했다는 뜻. 널 바이트 인젝션은 php 5.3.3 이하 버전 및 windows 서버 (IIS) 특정 버전에서 볼 수 있는 취약점이라고 함 |
- $_FILES로 이름을 받을 때도, move_uploaded_file로 경로를 지정할 때도 널 바이트 이후는 잘리지 않았다는 뜻
echo phpversion(); 으로 php 버전을 확인해본 결과. |
왜 404가 뜰까 고민해봤는데 이미지 확장자인데 내용이 이미지가 아니라서는 아닌 것 같음. .png로 업로드했을 땐 실행만 안될뿐 이미지 아이콘이 뜨긴 했기 때문에. 아파치 서버가 .php%00.png 파일 요청 시 %00을 처리하지 못하는 것 아닐까...생각.. |
- 웹 애플리케이션(백엔드)가 png를 검증한 뒤 이미지 파일로 생각하여 통과시킬 때
- 운영체제 버전에 따라 %00 이하의 문자열은 탈락시키고 저장해서 webshell.php로 저장될 가능성이 있음
▷ webshell.php.jpg 진짜 된당께요
.htaccess 설정 파일 덮어쓰기
- 디렉토리별로 규칙을 직접 지정하여 이미지 확장자도 php처럼 동작하게 할 수 있음
AddType application/x-httpd-php .png
★ 내 서버에서 실습해보기
php 파일을 포함한 게시글을 작성 |
php 형식은 안된다는 alert 확인 |
버프 스위트의 인터셉트를 활성 |
다시 게시글을 작성하여 업로드 |
파일명을 ".htaccess"로, 파일 내용을 "Addtype application/x-httpd-php .hellyeah"로 변경한 뒤 요청 전송 |
파일 업로드 성공 |
.htaccess가 저장된 경로를 확인하기 위해 새 탭으로 파일을 요청 |
해당 설정 파일이 업로드되었으나 접근 권한이 없어서 403 코드가 뜸 어쨌든 img 디렉토리의 파일 중 .hellyeah 확장자를 가진 파일은 php처럼 실행시킬 것 |
ls -al 명령어 실행 결과 img 디렉토리에 설정 파일이 저장된 것을 확인할 수 있음 |
웹쉘을 올리기 위해 다시 인터셉트를 켬 |
확장자를 변경시킬 파일과 함께 게시글을 작성 |
인터셉트한 요청 데이터에서 파밀명의 확장자를 .hellyeah로 변경한 뒤 전송 |
파일이 서버에 업로드됨 |
웹쉘 파일을 실행시키기 위해 새 탭에서 파일 요청 |
cmd 명령어를 GET 방식으로 전달한 결과 서버가 명령어를 실행했음을 알 수 있음 |
설정 파일을 다시 업로드하여 png 파일이 php처럼 실행되게 할 수 있음 |
대신 설정 파일을 또 업로드할 경우 원래 있던 설정 파일의 내용을 덮어쓰기 때문에 이전에 설정한 hellyeah 확장자는 더이상 실행되지 않음 (덮어쓰기 때문에 서버에 저장된 .htaccess 파일 수는 여전히 1개임) |
- 파일 업로드 취약점이 있다면 .htaccess 파일을 업로드하여 파일 저장 경로의 설정을 변경할 수 있음
- 이때 어떤 확장자든 php처럼 서버가 코드를 실행시키게 설정할 수 있기 때문에 jpg 등의 확장자도 웹쉘이 됨
- 이런 이유로 .php.jpg 같은 이중확장자도 실행시킬 수 있음
▷ webshell.jpg 이거 진짜 됨. 내가 봤음.
File Include 취약점
- include 기능: 다른 파일을 그대로 포함시켜 무조건 실행함
- include 함수 안에 들어가는 파일을 GET 파라미터로 받을 경우 png 확장자인 웹쉘을 실행시킬 수 있음
- 예시 코드
<?php
if(isset($_GET['page'])){
include($_GET['page']);
}
?>
- page 파라미터가 있으면 그 파일을 현재 파일에 포함시킴
- php 코드 내부에 있으므로 무조건 실행됨
★ 내 서버에서 실습해보기
인클루드 취약점을 만들어 둔 게시글 읽기 페이지에 접근 |
테스트 삼아 로그인 페이지 요청 css는 개박살났지만 login.php를 포함시키고 있는 것을 확인 |
파일 인클루드 취약점을 이용해 이미지 웹쉘을 실행시키기 위해 png 확장자를 가진 웹쉘 파일 업로드 |
파일이 서버에 업로드됨 |
리다이렉션된 페이지에서 파일을 우클릭하여 새 탭으로 엶 |
이미지 파일이라 실행되지 않음 |
파일 인클루드 취약점이 있는 페이지에서 이미지 웹쉘을 요청했으나 아무 변화가 없음 당연함. cmd 파라미터를 전달하지 않았음 |
웹쉘의 파라미터인 cmd명령어를 포함하여 전송한 결과 웹쉘 코드가 실행되고 있음을 확인 |
웹쉘 파일을 새 탭으로 열 때와 달리 현재는 게시글 읽기 페이지에서 실행되고 있기 때문에 ls 명령어 결과 img 상위 디렉토리의 파일 목록이 출력되고 있음 |
명령이 실행되는 현재 위치 |
- 파일 인클루드 취약점이 있는 페이지에서 png 등의 이미지 확장자를 가진 웹쉘 코드가 실행될 수 있음
로그파일 읽기 권한 문제
정리
1) 확장자 검증 미흡
원인 | 개발 시 업로드 파일 확장자를 앞에서부터 검사함 |
결과 | .jpg.php 등의 이중확장자를 이미지 파일로 착각하고 서버에 저장 |
이미지 웹쉘이 아닌 이유 |
이미지 파일이 아니라 걍 php 파일임 |
2) Null Byte Injection
원인 | 서버측 개발 환경 상의 취약점 |
결과 | .php%00.jpg 로 업로드 시도할 경우 뒤에서부터 확장자를 검사해도 서버에는 %00 이후 문자열을 제외한 채 저장 |
이미지 웹쉘이 아닌 이유 |
얘도 걍 php 파일임 |
3) .htaccess 설정 파일 덮어쓰기
원인 | .htaccess 파일이 서버에 업로드됨 |
결과 | 실행되지 않는 확장자를 php처럼 실행되게 함 |
이미지 웹쉘이 아닌 이유 |
굳이 이미지 확장자일 필요도 없음 |
4) File Include 취약점
원인 | 사용자가 include 함수에 포함될 파일을 지정할 수 있으며 파일에 포함된 코드는 무조건 실행시킴 |
결과 | .jpg 같은 이미지 확장자를 가진 웹쉘 파일을 포함시켜 코드를 실행시킬 수 있음 |
이미지 웹쉘이 아닌 이유 |
이미지 웹쉘이긴 하지만 파일 업로드 취약점 이외의 별개의 취약점이 필요함 |