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


문제에 접속하면 위와 같은 웹페이지가 나오는데, 공격 벡터로는 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




와.. 고생했다 나놈;;

출제자 본인도 ㅈ같다던 문제를 드디어...



Wargame.kr

Web_700

ip log table




700점짜리 문제도 거의 끝나가네요. Blind SQL Injection을 이용하여 풀이하는 문제인 것 같습니다.

바로 문제를 확인 해 보도록 하죠.


문제에 접속 해 보니 상단에 admin 계정으로 로그인 할 수 있는 페이지로 접근 할 수 있는 버튼이 있고, 아래 테이블에는 idx와 함께 IP가 있습니다.

해당 테이블의 항목들을 클릭하면 아래와 같은 log time이 나타납니다.




이 log time은 문제 페이지에 접속한 당시의 서버 시간을 나타내는 것 같습니다.

그리고 Request 때리는 부분을 확인해봤더니 chk.php 페이지로 POST 요청을 날려주는데, 파라미터로 idx를 같이 날려줍니다.

이 idx가 위에서 확인했던 log time을 구별해주는 지표가 됩니다.

여기서 생각 할 수 있는것은 이 idx 파라미터가 전달 될 때 select 쿼리를 사용할텐데, 여기에 single quotation이 없는 등의 코딩 실수가 있을 경우 SQL Injection이 가능할 것입니다.



그래서 위와 같이 burp suite를 이용하여 idx 파라미터에 select문을 사용하여 table명을 알아내는 코드를 짰더니, 참과 거짓에 따라 다른 값이 나타났습니다.

(chk.php 페이지에서는 idx가 문제 페이지에 나타나있는 테이블에 존재하지 않는 경우 1970년 1월 1일자로 나타납니다.)

따라서 이를 이용하여 Blind SQL Injection을 먹일 수 있습니다.



python으로 테이블명을 알아 낼 수 있는 스크립트를 작성했습니다.

돌려보도록 하죠.



페이지 내에 sleep문이 적용되어있는지 약간 느린 반응을 보여줬습니다. 이 11글자 알아내는데 30초가 넘게 걸렸습니다...

아무튼 limit의 숫자를 늘려가며 admin_table이라는 테이블을 알아냈습니다.

이 테이블 안에 아마도 admin 계정의 id와 pw를 알아 낼 수 있는 컬럼이 있을 것입니다.



위에서 사용했던 payload.py를 복사하여 약간 내용을 수정 후 column.py로 바꾸고 컬럼 값을 찾는 스크립트를 돌려봤습니다.

이 또한 limit 값을 늘려가며 찾아보았더니 id와 ps라는 컬럼을 찾아 낼 수 있었습니다.

이제 이를 이용하여 admin 계정의 id와 pw를 알아내면 될 것 같습니다.



id는 blue_admin



pw는 0h~myp4ss!인 것을 알아냈습니다.

이제 이를 admin 페이지에 접속하여 로그인 해 보도록 하죠.






짠! flag를 찾아냈습니다.

'Wargame > Wargame.kr' 카테고리의 다른 글

[Wargame.kr] Web_800_QnA  (0) 2016.05.27
[Wargame.kr] Web_700_php? c?  (0) 2016.05.27
[Wargame.kr] Web_700_lonely guys  (0) 2016.05.26
[Wargame.kr] Web_700_dmbs335  (0) 2016.05.26
[Wargame.kr] Web_650_crack crack crack it  (0) 2016.05.25

Wargame.kr

Web_700

lonely guys



웹 700점짜리 lonely guys 문제입니다.

Order by SQL Injection에 관련 된 문제라고 하네요.

문제를 확인 해 봅시다.



문제에 접속 해 보니 위와 같은 웹페이지가 나타났습니다.

reg_single(sort)라고 적혀있는 볼드체를 클릭하면 asc 기준에서 desc 기준으로 재정렬이 됩니다.

별다른 힌트는 없는 것 같습니다. 소스를 확인 해 보도록 하죠.



여기서 주의깊게 볼 부분은 update 구문입니다.

flag가 들어있는 column과 table명을 적나라하게 적어주셨습니다. 우리는 이를 이용해 문제를 쉽게 풀어나갈 수 있을 것 같습니다.

그리고 아래의 select문을 확인하면 reg_date 기준으로 오름/내림차순 정렬을 하는 쿼리문을 볼 수 있습니다.

하지만 sort 변수에는 single quotation이 없기 때문에 이를 공격벡터로 이용하여 SQLi를 때릴 수 있을 것 같습니다.

명칭은 Order By Clause SQLi라고 하는 듯 합니다. 

처음에는 IF문을 이용하여 결과 값 차이를 확인 해 보려고 노력해보았으나, order by 절에서는 IF문이 먹히질 않는 것 같습니다..

따라서 Time Based Blind SQLi로 풀이하였습니다.



위와 같이 sort 파라미터를 POST로 요청하는데, select문에 sleep을 먹여 key값의 글자수를 확인하였습니다.

뭔가.. 다른 방법으로도 글자수를 확인 할 수 있을 것 같은데, 떠오르지 않아 0xf5( _ )를 하나씩 늘려가며 노가다로 찾아냈습니다.

Key값의 글자 수는 40Byte입니다.



40byte인 것을 알아냈으니, python 스크립트를 작성하여 40글자를 알아냈습니다.

Time Based이기 때문에 요청하기 이전과 이후의 시간 차를 계산하여 Key를 찾아내는 스크립트입니다.

다른 좋은 방법을 알고 계시는 분은 댓글로 부탁드립니다..(굽신)



짠!


'Wargame > Wargame.kr' 카테고리의 다른 글

[Wargame.kr] Web_700_php? c?  (0) 2016.05.27
[Wargame.kr] Web_700_ip log table  (0) 2016.05.27
[Wargame.kr] Web_700_dmbs335  (0) 2016.05.26
[Wargame.kr] Web_650_crack crack crack it  (0) 2016.05.25
[Wargame.kr] Forensics_650_img recovery  (0) 2016.05.24

Wargame.kr

Web_700

dmbs335




드디어 700점대 문제로 넘어왔습니다.

이번에도 web 문제네요. SQL Injection 문제입니다. 살펴보도록 하죠.



들어가보니 게시판이 하나 있습니다. 검색을 할 수 있는 공간이 있고 컬럼을 선택 할 수도 있습니다. and와 or은 subject|content 컬럼을 선택했을 경우의 연산입니다.

소스를 확인해봅시다.



30라인을 확인 해 보면 parse_str($_SERVER['QUERY_STRING']);이란 구문이 있습니다.

parse_str 함수는 파라미터로 전달받은 문자열을 PHP 변수로 변환시켜주는 함수입니다.

이 때, PHP 내부에서 쓰이고 있는 변수명을 문자열로 전달 할 경우 해당 변수의 값이 바뀌기도 합니다.

이 문제는 이러한 parse_str 함수의 취약점을 이용한 문제입니다.

$_SERVER['QUERY_STRING']은 HTTP에서 GET 요청을 했을 때, 그 문자열을 반환해주는 구문입니다.

예를 들어, 


http://wargame.kr:8080/dmbs335/?search_cols=subject&keyword=1&operator=or 라는 GET요청을 때렸다면


$_SERVER['QUERY_STRING'] -> search_cols=subject&keyword=1&operator=or 이 될 것입니다.


그렇다면 우리는 GET 요청으로 PHP상에서 취약한 부분을 찾아 변수의 값을 변환해주면 될 것입니다.

where_clause 변수에 실질적인 where구문의 쿼리가 들어갑니다.

47라인을 보면 query_parts 변수가 존재 할 경우 where_clause문에 해당 변수의 내용을 넣어주는 부분이 있는데, 이는 그 위의 search_cols 변수가 존재 할 경우 들어갑니다.

따라서 search_cols 변수에 preg_match문에 부합되지 않는 값을 넣어주고, query_parts 변수에 우리가 원하는 쿼리문을 집어 넣어준다면 keyword에 추가되는 addslashes 함수를 우회하여 쿼리를 실행시킬 수 있을 것입니다.





query_parts 변수에 union 구문을 집어 넣어주었습니다.

결과 값이 1,2,3,4로 나타나는 것을 확인 할 수 있습니다.



테이블명을 알아내기 위해 select구문을 서브쿼리로 집어 넣어 실행시켜주었더니 41번째에 해당하는 테이블에서 플래그를 담고 있는 테이블로 의심되는 값이 튀어나왔습니다.



다음으로 컬럼 값을 알아내기 위해 table_name의 조건을 위에서 알아 낸 테이블명으로 설정 해 준 후 쿼리를 때렸더니 f1ag라는 컬럼명을 뱉어냈습니다.



위에서 알아낸 값들을 이용하여 Key값을 따낼 수 있었습니다.






References

- God of Webhacking Zairo(http://zairo.tistory.com)

Wargame.kr

Web_600

SimpleBoard


600점짜리 문제입니다.

Union을 이용한 SQL Injection 문제라고 하네요.

스크립트가 필요할 것이라고 합니다. 문제를 확인 해 보도록 하죠.




들어가보면 게시판이 하나 있습니다. 해당 제목을 클릭하면 글의 내용이 보여지며 HIT수가 올라갑니다.

소스를 확인해보죠.



read 함수의 where 구문에 $idx를 살펴보면 Single Quotation이 없습니다. 따라서 이 부분을 공격 벡터로 이용하여 SQL Injection을 먹일 수 있을 것 같습니다.

사실 이 문제의 경우 union을 이용한 SQL Injection이라고 하였지만, 계속 넣어봐도 query error만 뱉어내고 작동을 안해서... Blind SQL Injection을 이용하여 풀이했습니다.



파이썬 스크립트를 작성하여 테이블 이름을 찾아보았습니다.



limit 41, 1 을 걸어주었을 때 SIMPLEBOARD 테이블이 나왔고 40, 1을 걸어주었을 때 README라는 테이블 또한 뱉어냈습니다.

아마 README 안에 FLAG가 들어있을 것 같습니다.

다음은 COLUMN을 찾아보겠습니다.



limit 480,1을 걸어주었습니다.



column 중 FLAG라는 이름을 가진 컬럼이 있습니다.



이제 select 구문을 이용하여 flag를 따낼 차례입니다.



짠!

'Wargame > Wargame.kr' 카테고리의 다른 글

[Wargame.kr] Misc_650_pyc decompile  (0) 2016.05.23
[Wargame.kr] Web_650_web chatting  (0) 2016.05.23
[Wargame.kr] Web_600_tmitter  (0) 2016.05.20
[Wargame.kr] Web_550_type confusion  (0) 2016.05.19
[Wargame.kr] Web_550_strcmp  (0) 2016.05.18

+ Recent posts