개발 꿀팁/PHP

php: // filter의 묘사에 대해 이야기하다

Jammie 2022. 7. 4. 12:04
반응형

hp: // filter는 PHP만의 합의입니다. 이 합의로 많은 "묘용"을 만들 수 있습니다. 본문에서는 재미있는 점을 몇 가지 말하고 나머지는 여러분이 직접 체험해 보세요. 제가 상반기에 XDCTF2016의 제목을 만들려고 했는데, 세 개의 흰 모자 중 하나를 먼저 썼어요. 저도 미리 공유할 수밖에 없었어요.

XXE에서의 사용
php: // filter 이전에 가장 자주 등장한 곳은 XXE입니다. XXE 취약성으로 인해 HTML, PHP 등의 파일을 읽을 때 parser error: StartTag: invalid element name 오류가 발생할 수 있습니다. 이 이유는 PHP가 기반이기 때문입니다.태그의 스크립트 언어, <?php...?> 이 문법도 XML과 일치하기 때문에 XML을 해석할 때 XML로 오인되고, 그 내용(예를 들어 특수문자)이 표준 XML과 충돌할 가능성이 있어 오류가 발생합니다.

그러면 민감한 정보가 담긴 PHP와 같은 소스 파일을 읽기 위해서는 먼저 '충돌 가능성이 있는 PHP 코드'를 한 번 코딩해야 하는데 여기서 php://filter가 사용된다.

php: // filter는 PHP 언어에서 고유한 프로토콜 스트림으로 다른 스트림을 처리하는 역할을 합니다. 예를 들어 다음 코드와 같이 POST 콘텐츠를 base64 인코딩으로 변환하여 출력할 수 있습니다.

readfile("php://filter/read=convert.base64-encode/resource=php://input");

다음과 같다.

따라서, XXE에서도 PHP와 같이 충돌을 일으키기 쉬운 파일 스트림을 php:/filter 프로토콜 스트림으로 한 번 처리할 수 있으므로, 특수 문자에 의한 혼란을 효과적으로 회피할 수 있다.

다음과 같이 우리가 사용하는 것은php://filter/read=convert.base64-encode/resource=./xxe.php

교묘하게 인코딩하고 디코딩하다
인코딩 사용만 할 수 있는 것이 아니라서류도 챙기고 불필요한 번거로움도 덜어줄 수 있다.

얼마 전에 세 개 기억나는데흰색 모자에는 다음과 유사한 코드가 있는 경기가 있다.

<?php
$content = '<?php exit; ?>';
$content .= $_POST['txt'];
file_put_contents($_POST['filename'], $content);

$content가 exit 프로세스를 추가했기 때문에 문장을 성공적으로 쓰더라도 실행할 수 없습니다. (실전에서는 일반적으로 사용되며 캐시, 프로필 등에 나타나며 사용자가 직접 액세스할 수 없는 파일에는 if(!)가 추가됩니다.defined(xxx))exit;와 같은 제한).그렇다면 이 '죽음의 exit'을 어떻게 우회할 수 있을까.

다행히 여기서 $_POST['filename']는 제어 프로토콜이 가능하기 때문에 php://filter 프로토콜을 사용하여 마법을 부릴 수 있다: php://filter 스트림의 base64-decode 방법을 사용하여 $content를 디코딩하고 php base64_decode 함수 특성을 이용하여 "죽음 exit"을 제거한다.

아시다시피 base64 인코딩에는 64개의 인쇄 가능한 문자만 포함되어 있습니다. PHP는 base64 디코딩 시 존재하지 않는 문자가 있을 경우 이 문자를 건너뛰고 올바른 문자만 새 문자열로 구성하여 디코딩합니다.

따라서 정상적인 base64_decode는 실제로 다음과 같은 두 단계로 이해될 수 있다.

<?php
$_GET['txt'] = preg_replace('|[^a-z0-9A-Z+/]|s', '', $_GET['txt']);
base64_decode($_GET['txt']);

그래서 $content가 추가되었을 때 <?php exit; ?> 이후, php: // filter/write=convert.base64-decode를 사용하여 먼저 디코딩할 수 있습니다.복호화 과정에서 base64 인코딩에 적합하지 않은 문자 <, ?,;, >, 공백 등 총 7개의 문자는 무시됩니다. 따라서 최종적으로 복호화된 문자는 "phpexit"과 우리가 가져온 다른 문자뿐입니다.

phpexit는 모두 7글자인데 base64 알고리즘이 4byte를 1개씩 디코딩하기 때문에 a를 8글자 추가해 준다. 이렇게 하면 phpexita는 정상적으로 디코딩되고 그 뒤에 우리가 들어오는 webshell의 base64 콘텐츠도 정상적으로 디코딩된다.결국 <?php exit; ?>가 없어졌다.

마지막 효과:

문자열을 이용한 조작 방법
어떤 학우들은 "베이스.ase64의 알고리즘을 나는 이해할 수 없다. 위의 방법은 너무 복잡하다.

사실 bas를 쓰는 것 말고e64 특성의 방법 이외에 php: // filter 워드를 이용할 수 있습니다.'죽음의 exit'을 제거하는 부케다.관찰해보죠,이거<?php exit;? > 실제로 무엇입니까?

실제로는 하나의 XML 표식이다싸인, XML 태그인 이상 strip_tags 함수를 이용할 수 있습니다.제거하는데 php: // filter가 바로 이 방법을 지원합니다.

아래와 같이 테스트 코드를 작성하시면 됩니다.php 보기: // filter/ read= string.strep_tags/resource=php://input 효과:

echo readfile('php://filter/read=string.strip_tags/resource=php://input');

알 수 있듯이 <?php exit; ?>이 제거되었습니다.그러나 위의 제목으로 돌아가면 최종 목적은 웹쉘을 쓰는 것이고, 웹쉘도 php코드로 strip_tags를 사용하면 제거된다.

다행히 php://filter는 여러 필터를 사용할 수 있어 웹쉘을 base64로 먼저 코딩할 수 있다.strip_tags를 호출한 후 base64-decode를 진행한다.'죽음의 exit'은 제2위한 단계는 제거되고 웹쉘은 두 번째 단계에서 복원된다.

최종 패킷은 다음과 같습니다.

이 밖에도 rot13 코드로 독립적으로 임무를 수행할 수 있다.원리는 위와 유사하게 '죽음의 exit'을 제거하는 것이 핵심이다.<?php exit; ? > rot13 코딩을 거치면 <?로 바뀝니다.cucrkvg; ? > php가 short_open_tag를 켜지 않을 때 php는 이 문자열을 알지 못하므로 실행하지 않습니다

물론 이 방법은 짧은 라벨을 풀지 않는 조건이다

반응형