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 문제였다고 들었는데... 생각도 못해서 아쉬웠습니다.. 유감.