읽기 안내
문제에서 제공한 코드와 직접 작성한 풀이를 함께 보존했습니다. 원문 페이로드의 reousrce 표기는 그대로 두었습니다. PHP 공식 문서의 올바른 옵션명은 resource입니다.
CODE
index.php
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css">
<title>PHP Back Office</title>
</head>
<body>
<!-- Fixed navbar -->
<nav class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="/">PHP Back Office</a>
</div>
<div id="navbar">
<ul class="nav navbar-nav">
<li><a href="/">Home</a></li>
<li><a href="/?page=list">List</a></li>
<li><a href="/?page=view">View</a></li>
</ul>
</div><!--/.nav-collapse -->
</div>
</nav><br/><br/>
<div class="container">
<?php
include $_GET['page']?$_GET['page'].'.php':'main.php';
?>
</div>
</body>
</html>
list.php
<h2>List</h2>
<?php
$directory = '../uploads/';
$scanned_directory = array_diff(scandir($directory), array('..', '.', 'index.html'));
foreach ($scanned_directory as $key => $value) {
echo "<li><a href='/?page=view&file={$directory}{$value}'>".$value."</a></li><br/>";
}
?>
view.php
<h2>View</h2>
<pre><?php
$file = $_GET['file']?$_GET['file']:'';
if(preg_match('/flag|:/i', $file)){
exit('Permission denied');
}
echo file_get_contents($file);
?>
</pre>
설명
<?php
include $_GET['page']?$_GET['page'].'.php':'main.php';
?>
page의 파라미터를 받으면 그 파라미터를 실행시키고 없으면 자동으로 main.php를 실행시킨다.
다음은 list.php다.
<?php
$directory = '../uploads/';
$scanned_directory = array_diff(scandir($directory), array('..', '.', 'index.html'));
foreach ($scanned_directory as $key => $value) {
echo "<li><a href='/?page=view&file={$directory}{$value}'>".$value."</a></li><br/>";
}
?>
디렉토리 내를 순회하며 .., ., index.php 를 제외한 모든 파일을 읽어 출력한다.
view.php
if(preg_match('/flag|:/i', $file)){
exit('Permission denied');
}
/flag 문자열이 있으면 Permission denied를 출력한다.
list.php를 통해 flag.php에 접속하면 Permission denied가 뜬다.

view.php에서는 flag를 필터링하기 때문에 필터링을 거치지 않는 index.php를 통해 flag.php를 접속해보자.
http://host3.dreamhack.games:20870/index.php?page=../uploads/flag

flag가 아닌 can you see $flag? 라는 문장이 나왔다. 이 말은 html 부분에 flag가 있지 않고 실제 php 코드 내부에 있는 것으로 예상된다.
php wrapper 를 사용해 php 내부의 flag를 긁어보자.
php wrapper란? php wrapper는 파일 시스템 함수들을 사용할 때, 파일뿐만 아니라 다양한 프로토콜이나 리소스에 접근할 수 있도록 중간에서 연결을 담당하는 인터페이스를 말한다.
즉, 단순 파일 경로만 여는게 아니라, http:// , ftp://, php://, data://, zip:// 같은 특정 프로토콜을 경로처럼 취급할 수 있게 해주는 기능이다.
| Wrapper | 설명 |
|---|---|
| file:// | 기본 파일 시스템 접근 |
| http://, https:// | HTTP, HTTPS 프로토콜 파일 접근 |
| ftp://, ftps:// | FTP 파일 접근 |
| php://input, php://memory | PHP 내부 입출력 스트림 |
| data:// | 데이터 URI 읽기 |
| zip:// | ZIP 아카이브 내부 파일 접근 |
| 시스템 함수 | 기능 |
|---|---|
| file:// | Accessing local filesystem |
| http:// | Accessing HTTP(s) URLs |
| ftp:// | Accessing FTP(s) URLs |
| php:// | Accessing various I/O streams |
| zlib:// | Compression Streams |
| data:// | Data (RFC 2397) |
| glob:// | Find pathnames matching pattern |
| phar:// | PHP Archive |
특히 내가 사용할 php wrapper는 php://filter다. 이 wrapper는 encode/decode 옵션으로 서버 안에 존재하는 문서를 열람할 수 있다. 그리고 zip파일의 압축을 풀고 해당 파일을 실행한다.
페이로드는 다음과 같다.
http://host3.dreamhack.games:20870/index.php?page=php://filter/convert.base64-encode/reousrce=../uploads/flag

base64로 인코딩된 문자열이 출력되었다. 이 문자열을 디코딩 해 보면 다음과 같은 내용이 출력된다.

플래그가 출력되었다.
작성 기록과 문제 출처
원문과 출처
풀이와 설명: MadEyE / 이선용의 개인 Notion 기록.
문제에서 제공한 코드·서비스 화면과 외부 참고자료의 권리는 해당 작성자와 플랫폼에 있습니다.
원문 본문·코드·실습 증적을 보존했으며, 원문에 포함된 외부 글은 참고 링크로 안내합니다.
다음 글