LFI(Local File Inclusion) 취약점은 웹 애플리케이션이 사용자 입력값을 제대로 검증하지 않아, 공격자가 서버 내부의 민감한 파일을 읽어오거나 임의의 코드를 실행할 수 있게 되는 웹 보안 취약점입니다.
정상적인 웹사이트가 파라미터를 통해 페이지를 불러온다고 가정해 보겠습니다.
http://example.com/index.php?page=example.php (example.php 파일을 불러옴)만약 웹 애플리케이션이 page에 들어오는 값을 아무런 검증 없이 그냥 사용한다면 경로 조작 문자(../)를 삽입하여 웹 루트 디렉터리를 벗어날 수 있습니다.
이러한 취약점이 php Wrapper와 결합되면 단순한 파일 읽기를 넘어, 필터링 우회나 원격 코드 실행 등 공격의 파급력이 극대화될 수 있습니다.
1. php://filter (소스 코드 유출 및 필터링 우회)
가장 널리 쓰이는 Wrapper입니다. 일반적으로 include() 함수로 PHP 파일을 불러오면 서버에서 해당 코드가 실행된 결과만 화면에 출력됩니다. 하지만 공격자가 웹 애플리케이션 설정 파일 등의 '원본 소스 코드'를 읽고 싶을 때 php://filter를 사용합니다.
http://example.com/index.php?page=php://filter/read=convert.base64-encode/resource=example.php2. php://input (원격 코드 실행 - RCE)
HTTP 요청의 Body 데이터를 직접 읽어올 수 있는 Wrapper입니다. 이 방법은 서버의 php.ini 설정에서 allow_url_include 옵션이 On으로 설정되어 있으면 가능합니다.
php://input을 지정하고, HTTP POST 요청의 본문 영역에 악성 PHP 코드를 삽입하여 전송합니다. 서버는 POST로 전달된 악성 코드를 마치 로컬 파일의 내용인 것처럼 인식하고 include()하여 실행하게 됩니다.http://example.com/index.php?lage=php://input<?php system('cat /etc/passwd'); ?>예시에서는 cat /etc/passwd를 사용했으나 웹쉘을 실행시켜 관리자 권한을 획득할 수도 있습니다.
3. data:// (원격 코드 실행 - RCE)
php://input과 동일하게 원격 코드 실행을 수행할 수 있습니다. 이 역시 allow_url_include 옵션이 On이어야 동작합니다.
<?php system('id'); ?> 코드를 Base64로 인코딩하여 삽입http://example.com/index.php?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOyA/Pg==평문이 아닌 Base64로 인코딩해서 전송하는 이유는 WAF 우회 목적도 존재합니다. 하지만 주된 이유는 URL 문법 충돌 방지 및 안전한 전송을 위해서입니다. 특수 문자들을 URL 파라미터에 그대로 넣게 되면 유실되거나 잘못 해석될 수 있어 에러를 일으킬 수 있기 때문입니다.
그럼 앞에서 본 php://input과 data://를 둘 다 알아야 하는 이유가 있을까요? 둘 다 allow_url_include = On이 되어 있어야 한다는 공통된 전제 조건이 필요합니다. 하지만 악성 데이터를 서버로 전달하는 방식(HTTP Body vs URL)이 다르기 때문에 POST Body와 URL의 보안 설정에 따라 각 방식의 성공 여부가 갈리게 됩니다.
따라서 php.ini에서 allow_url_include가 On으로 설정되어 있으면 php://input과 data:// 두 가지 Wrapper를 다 사용해보는 것이 좋은 접근 방식입니다.
4. zip:// 및 phar:// (압축 파일을 이용한 실행)
공격자가 서버에 이미지 파일(.jpg) 등으로 위장한 압축 파일(.zip)을 업로드할 수 있는 환경에서 사용됩니다.
zip:// 또는 phar:// Wrapper를 사용하여 해당 파일 내부의 악성 코드를 직접 지정해 실행시킵니다. 앞의 php://input이나 data:// 방식과는 다르게 allow_url_include가 Off 상태에서도 동작할 수 있어 매우 위협적입니다.http://example.com/index.php?page=zip://uploads/image.jpg#shell.php두 방식의 차이점을 간단히 보면 아래와 같습니다.
| 구분 | zip:// | phar:// |
|---|---|---|
| 구분자 (Delimiter) | # (반드시 %23으로 URL 인코딩 필요) | / (인코딩 불필요) |
| 경로 지정 방식 | 주로 절대 경로 요구 | 상대 경로 사용 가능 (유연함) |
| 지원 압축 포맷 | ZIP 전용 | PHAR, ZIP, TAR 등 다양함 |
| 추가 취약점 연계 (중요) | 해당 없음 | PHP Object Injection (Deserialization) 연계 가능 |
단순히 차이점만 보면 phar://을 두고 zip://을 쓸 이유가 전혀 없어보입니다. 하지만 필터링 로직이나 보안 설정 과정에서 zip://만 통과가 되는 경우가 존재할 수 있기 때문에 phar://으로 먼저 시도를 해본 뒤 안된다면 zip://으로도 시도를 해보는 것이 좋은 방법이라고 생각됩니다.
앞 게시글인 php wrapper에 이어 LFI 취약점과 두 방식을 결합한 취약점에 대해서 알아봤습니다. 이제 이 방식을 이용한 워게임 풀이(webhacking.kr old-25)를 다음 포스트에서 해보겠습니다.