LFI 취약점 알아보기

asdf·2026년 3월 31일

web

목록 보기
18/32

1. LFI 취약점이란

LFI(Local File Inclusion) 취약점은 웹 애플리케이션이 사용자 입력값을 제대로 검증하지 않아, 공격자가 서버 내부의 민감한 파일을 읽어오거나 임의의 코드를 실행할 수 있게 되는 웹 보안 취약점입니다.

2. 동작 원리

기본 동작

정상적인 웹사이트가 파라미터를 통해 페이지를 불러온다고 가정해 보겠습니다.

  • 정상적인 요청 : http://example.com/index.php?page=example.php (example.php 파일을 불러옴)

만약 웹 애플리케이션이 page에 들어오는 값을 아무런 검증 없이 그냥 사용한다면 경로 조작 문자(../)를 삽입하여 웹 루트 디렉터리를 벗어날 수 있습니다.

php wrapper와 결합

이러한 취약점이 php Wrapper와 결합되면 단순한 파일 읽기를 넘어, 필터링 우회나 원격 코드 실행 등 공격의 파급력이 극대화될 수 있습니다.

1. php://filter (소스 코드 유출 및 필터링 우회)

가장 널리 쓰이는 Wrapper입니다. 일반적으로 include() 함수로 PHP 파일을 불러오면 서버에서 해당 코드가 실행된 결과만 화면에 출력됩니다. 하지만 공격자가 웹 애플리케이션 설정 파일 등의 '원본 소스 코드'를 읽고 싶을 때 php://filter를 사용합니다.

  • 공격 원리: 서버의 파일을 읽어올 때 Base64 등의 인코딩 필터를 적용하여 읽어옵니다. 코드가 인코딩된 문자열로 반환되기 때문에 PHP 엔진이 이를 코드로 인식하지 않고 단순 문자열로 취급하여 실행하지 않을 채 화면에 그대로 출력하게 됩니다. 공격자는 출력된 Base64 값을 디코딩하여 원본 소스 코드를 획득합니다.
  • 공격 예시: http://example.com/index.php?page=php://filter/read=convert.base64-encode/resource=example.php

2. php://input (원격 코드 실행 - RCE)
HTTP 요청의 Body 데이터를 직접 읽어올 수 있는 Wrapper입니다. 이 방법은 서버의 php.ini 설정에서 allow_url_include 옵션이 On으로 설정되어 있으면 가능합니다.

  • 공격 원리: URL 파라미터에는 php://input을 지정하고, HTTP POST 요청의 본문 영역에 악성 PHP 코드를 삽입하여 전송합니다. 서버는 POST로 전달된 악성 코드를 마치 로컬 파일의 내용인 것처럼 인식하고 include()하여 실행하게 됩니다.
  • 공격 예시
    • URL: http://example.com/index.php?lage=php://input
    • body: <?php system('cat /etc/passwd'); ?>

예시에서는 cat /etc/passwd를 사용했으나 웹쉘을 실행시켜 관리자 권한을 획득할 수도 있습니다.

3. data:// (원격 코드 실행 - RCE)
php://input과 동일하게 원격 코드 실행을 수행할 수 있습니다. 이 역시 allow_url_include 옵션이 On이어야 동작합니다.

  • 공격 원리: 별도의 파일을 서버에 업로드할 필요 없이 URL 파라미터 내에 평문이나 Base64로 인코딩된 악성 코드를 직접 주입하여 실행시킵니다.
  • 공격 예시: <?php system('id'); ?> 코드를 Base64로 인코딩하여 삽입
    http://example.com/index.php?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCdpZCcpOyA/Pg==

평문이 아닌 Base64로 인코딩해서 전송하는 이유는 WAF 우회 목적도 존재합니다. 하지만 주된 이유는 URL 문법 충돌 방지 및 안전한 전송을 위해서입니다. 특수 문자들을 URL 파라미터에 그대로 넣게 되면 유실되거나 잘못 해석될 수 있어 에러를 일으킬 수 있기 때문입니다.

그럼 앞에서 본 php://inputdata://를 둘 다 알아야 하는 이유가 있을까요? 둘 다 allow_url_include = On이 되어 있어야 한다는 공통된 전제 조건이 필요합니다. 하지만 악성 데이터를 서버로 전달하는 방식(HTTP Body vs URL)이 다르기 때문에 POST Body와 URL의 보안 설정에 따라 각 방식의 성공 여부가 갈리게 됩니다.

따라서 php.ini에서 allow_url_include가 On으로 설정되어 있으면 php://inputdata:// 두 가지 Wrapper를 다 사용해보는 것이 좋은 접근 방식입니다.

4. zip://phar:// (압축 파일을 이용한 실행)
공격자가 서버에 이미지 파일(.jpg) 등으로 위장한 압축 파일(.zip)을 업로드할 수 있는 환경에서 사용됩니다.

  • 공격 원리: 악성 PHP 코드가 담긴 파일을 압축한 뒤, 확장자를 허용하는 이미지 포맷으로 변경하여 서버에 업로드합니다. 이후 LFI 취약점을 통해 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)를 다음 포스트에서 해보겠습니다.

profile
Rainy Waltz(a_hisa)

0개의 댓글