흐드콘 첫날 아침부터 


1. 독도 수호 마라톤 10km에.. 

2. BOB 프로젝트 중간발표

3. 회식


3콤보에 당하고 밤 11시에 잠깐 웹2번 깔짝대다 피곤해서 잠들었습니다...

오늘 아침 8시쯤에 일어나서 웹3번 잠깐 보다가 시스템같아서 던지고..

4번으로 넘어가서 거의 다 풀었는데 시간이 모잘라서 못푸는 등..

아쉬운 점이 많았던 흐드콘이었습니다.


뭔가 첫날부터 제대로 조졌으면 랭크테이블에는 올릴 수 있었을 것 같은데.. 유감.




다들 고생했습니다!

RebForPwn 팀

- k3y6reak, wjdebug, 호롤룰루

'CTF > HDCON 2016' 카테고리의 다른 글

[HDCON 2016] 후기  (2) 2016.10.16
[HDCON 2016] WEB_4(2-4)..Unsolved  (0) 2016.10.16
[HDCON 2016] WEB_2(2-2)  (0) 2016.10.16
  1. k3y6reak 2016.10.17 02:56 신고

    갓!

500점짜리 문제입니다. 개인적으로 2-2인 200점짜리 문제 푸는 시간보다 이 문제의 flag에 접근하는 시간이 더 적게 걸렸던 것 같습니다.


먼저 문제에 접속하면 rubiya님이 만들었다는 생각이 바로 스쳐 지나가는 웹페이지가 나타납니다.


login, join 메뉴가 있고 photo, m/v 등의 카테고리가 있습니다.


join의 경우 id,pw,icon을 선택하는 가입 폼이 나타나며, 가입을 하면 선택한 icon에 해당하는 icon이 mypage 메뉴에 나타납니다.


icon의 경로는 /geek/icon/icon_[number].gif입니다.


여기서 파일명을 지워주면 icon 디렉토리가 리스팅 되는데, 여기에 view.zip을 다운받아 열어보면 /icon/view.php의 소스코드를 확인 할 수 있습니다.


<?php
//      mysql_query("insert into members values('{$uid}','{$upw}','./icon_{$icon}.gif')");
  session_start();
  if($_SESSION['icon']){
    $img = file_get_contents($_SESSION['icon']);
    header('Content-type:image/gif');
    echo $img;
    echo $_SESSION['icon'];
  }


/*
CREATE TABLE `members` (
  `uid` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
  `pw` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL,
  `icon` varchar(32) CHARACTER SET utf8 COLLATE utf8_unicode_ci DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
*/
?>

view.php


아래 테이블 구조를 살펴보면 uid, pw, icon가 컬럼으로 존재한다는 것을 알 수 있으며, file_get_contents에서 icon 세션에 들어간 값을 읽어준다는 것을 확인 할 수 있습니다.

여기서 생각 할 수 있는 것은 icon이 32바이트의 varchar형이기 때문에 32바이트를 넘는 icon 값을 준다면 뒤의 .gif를 자르고 원하는 php 파일을 열 수 있을 거라는 것입니다.

따라서 join 부분에서 icon의 값을 "1/../../././././index.php"와 같이 ./icon_을 포함하여 32바이트를 맞춰준 후 가입을 해줍니다.

그리고 view-source:[IP]/icon/view.php에 접속해보면 index.php를 얻어 낼 수 있습니다.


<?php
  session_start();
  include("5f0c2baaa2c0426eed9a958e3fe0ff94.php");
  $filter_list = array("filter_lfi","filter_sqli","filter_xss","filter_length");
  function filter_lfi($v){
    if(preg_match("/sess/")) exit("no hack");
  }
  function filter_sqli($v){
    if(preg_match("/\'|\"|\\\/",$v)) exit("no hack");
  }
  function filter_xss($v){
    if(preg_match("/<|>/",$v)) exit("no hack");
  }
  function filter_length($v){
    if(strlen($v) > 32) exit("length too big");
  }

  extract($_REQUEST);
  if($page == "login"){
?>
    <h3>Login</h3>
    <p>
      <form action="./?page=login_chk" method="POST">
      <table>
      <tr><td>ID</td><td><input type="text" name="uid" id="uid"></td>
      <td rowspan="3"><img src="./images/login.jpg" width="270" style="margin-left: 20px; margin-top: -38px; position:fixed;"></td></tr>
      <tr><td>PW</td><td colspan="2"><input type="text" name="upw" id="upw"></td></tr>
      <tr><td colspan="2"><input type="submit" value="Login" style="width: 100%;"></td></tr>
      </table>
      </form>
    </p>
<?php
  }
  else if($page == "login_chk"){
    if(($uid) && ($upw)){
      foreach($filter_list as $filter) array_map($filter,$_REQUEST);
      include "dbconn.php";
      dbconnect();
      $r = mysql_fetch_array(mysql_query("select * from members where uid='{$uid}' and pw='{$upw}'"));
      if($r['uid']){ $_SESSION['uid'] = $r['uid']; $_SESSION['icon'] = $r['icon']; exit("<script>location.href='./';</script>"); }
      else exit("<script>alert('login fail');history.go(-1);</script>");
    }
    else exit("<script>alert('login fail');history.go(-1);</script>");
  }
  else if($page == "join"){
?>
    <h3>Join</h3>
    <p>
      <form action="./?page=join_chk" method="POST">
      <table>
      <tr><td>ID</td><td><input type="text" name="uid" id="uid"></td>
      <td rowspan="3"><img src="./images/login.jpg" width="270" style="margin-left: 20px; margin-top: -38px; position:fixed;"></td></tr>
      <tr><td>PW</td><td colspan="2"><input type="text" name="upw" id="upw"></td></tr>
      <tr><td>Icon</td><td><select name=icon><?php for($i=1;$i<=10;$i++) echo "<option value={$i}>$i.gif</option>"; ?></select></td></tr>
      <tr><td colspan="2"><input type="submit" value="Join" style="width: 100%;"></td></tr>
      </table>
      </form>
    </p>
<?php
  }
  else if($page == "join_chk"){
    if(($uid) && ($upw) && ($icon)){
      foreach($filter_list as $filter) array_map($filter,$_REQUEST);
      include "dbconn.php";
      dbconnect();
      $r = mysql_fetch_array(mysql_query("select * from members where uid='{$uid}'"));
      if($r['uid']) exit("uid already existed");
      $icon = substr("./icon_".$icon.".gif",0,32);
      mysql_query("insert into members values('{$uid}','{$upw}','{$icon}')");
      exit("<script>alert('join ok');location.href='./?page=login';</script>");
    }
    else exit("<script>alert('join fail');history.go(-1);</script>");
  }
  else if($page == "photo"){
?>
    <h3>Photo</h3>
    <p><img src="./images/1.jpg" width="430"></p>
    <p><img src="./images/2.jpg" width="430"></p>
    <p><img src="./images/3.png" width="430"></p>
    <p><img src="./images/4.gif" width="430"></p>
<?php
  }
  else if($page == "video"){
?>
    <h3>Music Video</h3>
    <p><iframe width="520" height="293" src="//www.youtube.com/embed/iv-8-EgPEY0?rel=0" frameborder="0" allowfullscreen></iframe></p>
    <p><iframe width="520" height="293" src="//www.youtube.com/embed/xnku4o3tRB4?rel=0" frameborder="0" allowfullscreen></iframe></p>
    <p><iframe width="520" height="293" src="//www.youtube.com/embed/n8I8QGFA1oM?rel=0" frameborder="0" allowfullscreen></iframe></p>
    <p><iframe width="520" height="293" src="//www.youtube.com/embed/kKS12iGFyEA?rel=0" frameborder="0" allowfullscreen></iframe></p>
<?php
  }
  else if($page == "me"){
    echo "<p>uid : {$_SESSION[uid]}</p><p>icon<img src=./icon/view.php></p>";
  }
  else if($page == "logout"){
    session_destroy();
    exit("<script>location.href='./';</script>");
  }
  else{
?>
    <h3>ㅋrystal :/</h3>
    <p><img src="./images/k_03.jpg" width="430" style="position:fixed;"></p>
<?php
  }
  include("4bbc327f5b0fd076e005961bcfc4a9ee.php");
?>

index.php


여기서 생각 할 수 있는 것은 마지막 include하는 파일에서 플래그를 뱉어줄수도 있다고 생각하여 삽질해봤는데, md5로 hash 되어 있기 때문에 읽어오지 못했습니다.

다음으로 생각 할 수 있는 것은 extract로 $_REQUEST를 변수화 시켜주기 때문에 get으로 filter_list를 []로 초기화 시켜준다면 SQLi를 login 부분에서 맥일 수 있을 것이라고 생각하고 SQLi까지 조질 수 있었습니다.




dbconn.php도 확인해봤었네요.



SQLi를 써먹을 수 있다고 생각 했을 때가 9시 58분이었습니다.


flag 테이블이 있을거라 생각했기에 union으로 테이블과 컬럼만 조지면 됬었는데 시간이 부족해서 시도 해 보지 못하고 여기서 대회가 종료되었습니다.


유감..


 





P.S - Adm1nkyj님이 file_get_contents에서 windows wild card를 이용해서도 flag를 얻어 올 수 있었다고 합니다. 1번 문제도 wild card 문제였다고 들었는데... 생각도 못해서 아쉬웠습니다.. 유감.




'CTF > HDCON 2016' 카테고리의 다른 글

[HDCON 2016] 후기  (2) 2016.10.16
[HDCON 2016] WEB_4(2-4)..Unsolved  (0) 2016.10.16
[HDCON 2016] WEB_2(2-2)  (0) 2016.10.16


문제에 접속하면 위와 같은 웹페이지가 나오는데, 공격 벡터로는 Keyword 입력 폼과 로그인 폼 단 두개입니다.

따라서 200% SQLi라고 생각하고 Keyword 부분을 조졌는데, 웬만한 SQL 명령어는 다 막혀있었습니다.

그 와중에 limit와 procedure analyse()는 막혀 있지 않아서 이를 이용해 column명을 알아냈습니다.

Keyword 부분에서는 no와 k2e3w0r4d2e98xi2t 컬럼이 존재했는데, 어차피 같은 테이블을 참조하고 있기 때문에 Keyword 부분에서 2번째 컬럼의 값을 뽑아 낼 수 있을 거라 생각하고 blind SQLi로 key를 알아냈습니다.


위와 같이 ID와 PW의 값을 알려줍니다.

뒤의 부분은 게싱으로 INJECTION이라는 것을 유추 할 수 있었습니다.


위의 아이디와 패스워드로 로그인 하면 flag를 뱉어냅니다.

'CTF > HDCON 2016' 카테고리의 다른 글

[HDCON 2016] 후기  (2) 2016.10.16
[HDCON 2016] WEB_4(2-4)..Unsolved  (0) 2016.10.16
[HDCON 2016] WEB_2(2-2)  (0) 2016.10.16

+ Recent posts