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_2(2-2) (0) | 2016.10.16 |