尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

SQL注入实战:从联合查询到LOAD_FILE文件读取的完整利用链

SQL注入实战:从联合查询到LOAD_FILE文件读取的完整利用链 1. 项目概述一次从黑盒到白盒的SQL注入实战最近在复盘一个经典的CTF Web题目——fakebook。这个题目本身并不复杂但它像一块多棱镜几乎折射出了SQL注入从基础探测到高级利用的完整链条。很多刚接触安全的朋友可能对SQL注入的理解还停留在“‘ or 11 --”这种万能密码的层面但fakebook这个靶场恰恰教会我们真实的漏洞利用远不止于此。它涉及了信息搜集、闭合方式判断、联合查询、绕过过滤最终利用LOAD_FILE()函数读取服务器文件拿到Flag。整个过程更像是一次对开发者思维和防护措施的“压力测试”。如果你正在学习CTF Web安全或者想深入理解SQL注入的实战变种那么跟着我一起拆解fakebook会是一个非常扎实的进阶练习。简单来说fakebook模拟了一个简易的社交博客网站用户可以注册、登录、查看他人博客。我们的目标很明确找到并利用其SQL注入漏洞获取隐藏在服务器上的Flag。题目没有给出源码黑盒/灰盒这要求我们必须从外部行为去推断内部逻辑这也是实战中最常见的情况。整个挑战的核心围绕着一个关键点展开当常见的引号、空格被过滤时我们如何构造有效的Payload当联合查询的列数对齐后如何利用有限的回显点读取系统文件这不仅仅是记忆Payload更是理解数据库、Web应用和操作系统交互的过程。2. 前期信息搜集与漏洞点探测面对一个未知的Web应用盲目测试就像在黑暗中挥舞拳头。第一步永远是信息搜集理解应用的架构和功能点。访问fakebook通常我们会看到首页、注册、登录、查看用户博客详情如view.php?no1等页面。经验告诉我像no、id、user这类用于数据库查询的参数往往是SQL注入的高发区。2.1 确定注入点与参数类型我首先关注的是查看博客详情的功能URL类似于http://target/view.php?no1。将参数no的值改为1‘单引号这是最基础的探测。如果页面返回了数据库错误如MySQL的语法错误提示或者页面布局出现异常如空白、错位那基本可以确定这里存在字符型注入并且注入点可能被单引号包裹。在fakebook中测试no1‘时页面可能没有直接报错而是返回了一个“用户不存在”或类似的统一错误页面。这提示我们应用可能做了基础的错误屏蔽但没有过滤单引号本身。为了进一步确认我会使用逻辑测试no1 and 11 如果页面正常显示id为1的用户信息。no1 and 12 如果页面返回空或错误因为12为假。如果两者返回结果不同就能基本断定no参数存在SQL注入并且是数字型或字符型。在fakebook的典型设置中no1 and 11正常no1 and 12返回异常这证实了漏洞的存在。但这里有一个陷阱应用可能过滤了and、or、空格等关键词。因此更稳妥的测试是使用算术运算no2-1。如果返回了id为1的用户信息那就铁证如山——参数被直接拼接进了SQL语句存在数字型注入。在fakebook里测试no2-1成功返回了用户1的信息这让我们避开了对关键词过滤的初期误判直接确定了注入类型为数字型。这是一个非常重要的技巧在过滤严格的场景下用运算替代逻辑判断往往能绕过第一层检测。2.2 判断列数与确定回显点确定了注入点下一步就是经典的联合查询UNION攻击。联合查询的前提是前后两个SELECT语句的列数必须相同。我通常使用ORDER BY子句来盲猜列数。Payloadno1 order by 1 然后order by 2,order by 3... 依次递增直到页面返回错误。当order by 4正常而order by 5错误时就说明主查询语句有4列。注意ORDER BY后面跟的是列索引数字或列名。这里用数字是为了方便。如果order by被过滤可以尝试用group by替代原理类似。在fakebook中我测试到order by 4时页面正常order by 5时出错确认了列数为4。接下来需要用UNION SELECT来确定哪些列的内容会回显到页面上。Payloadno-1 union select 1,2,3,4。这里把no设为-1或一个不存在的id是为了让前一个SELECT查询结果为空从而确保页面显示的是我们UNION查询的结果。执行后观察页面。数字“2”、“3”或“4”可能会出现在页面的某个位置比如用户名、博客内容区域。这些出现数字的位置就是我们可以控制并回显数据库信息的关键“窗口”。在fakebook的典型构造中往往第2列或第3列的内容会被直接输出到HTML页面上。假设我们发现数字“2”和“3”出现在了页面中那么后续我们就把需要窃取的数据放在UNION SELECT的第2和第3列。3. 核心过滤绕过技巧详解如果一切顺利我们现在应该可以通过union select 1,2,3,4看到回显点了。但在fakebook中事情往往没这么简单。题目名字就暗示了“fake”它通常会设置一些过滤规则来增加难度。常见的过滤包括对空格、注释符、关键词的过滤。下面我结合实战拆解几种最常用的绕过方法。3.1 空格绕过技巧空格在SQL语句中用于分隔关键词但很多WAFWeb应用防火墙或简单的正则过滤会检测空格。绕过的方法有很多种本质是寻找能被SQL解析器识别为“分隔符”但又不是空格的字符。使用注释符/**/这是最常用、兼容性最好的方法。/*和*/之间的内容在MySQL中会被视为注释但/**/本身中间没有内容在解析时起到了空格的作用。例如union/**/select等同于union select。使用Tab键%09或换行符%0a, %0d在URL编码中%09代表Tab%0a代表换行LF%0d代表回车CR。这些空白字符在某些上下文也能作为分隔符。例如union%09select。使用括号()在特定语境下括号不仅能改变优先级也能起到分隔作用尤其是在函数调用时。但这种方法局限性较大需要根据语法灵活构造。使用反引号MySQL反引号用于包裹数据库名、表名、字段名在某些情况下可以创造隔离效果但不如/**/通用。在fakebook的实战中如果发现union select 1,2,3,4被拦截我会首先尝试union/**/select/**/1,2,3,4。成功率非常高。3.2 关键词绕过技巧有时应用会直接过滤union、select、from、where等关键词。简单的字符串匹配过滤可以通过以下方式绕过大小写混合UnIoNSeLeCt。这是最基础的绕过针对的是不区分大小写或简单匹配的过滤。双写关键词ununionionselselectect。如果过滤规则是简单地删除匹配到的关键词那么双写后删除中间的union剩下的部分正好又组成了一个新的union。例如ununionion- 删除union- 剩下union。插入注释或特殊符号u/**/nionsel%0aect。将关键词拆散中间插入被允许的字符如注释、换行。使用等价函数或语法如果select被过滤在某些非常局限的情况下可以考虑是否能利用子查询或其它方式但这在联合查询中很难替代。对于fakebook我遇到的更多是空格过滤而非严格的关键词过滤。但掌握这些技巧是必须的因为你的Payload很可能需要同时绕过多种过滤。一个完整的绕过Payload可能长这样-1/**/UnIoN/**/SeLeCt/**/1,2,3,4。3.3 信息搜集获取数据库结构绕过初步过滤成功执行union select并看到回显点假设是第2列和第3列后我们就要开始“偷”数据了。第一步是搞清楚数据库里有什么。标准的流程是获取当前数据库名Payload:-1 union select 1,database(),3,4。database()函数返回当前查询所在的数据库名称。获取所有数据库名Payload:-1 union select 1,group_concat(schema_name),3,4 from information_schema.schemata。information_schema.schemata表存储了所有数据库的信息group_concat()函数将多行结果合并成一个字符串方便查看。获取指定数据库如fakebook的所有表名Payload:-1 union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema‘fakebook‘。这里需要注意如果单引号被过滤我们可以用十六进制hex编码字符串。‘fakebook‘的十六进制是0x66616b65626f6f6b。所以Payload变为-1 union select 1,group_concat(table_name),3,4 from information_schema.tables where table_schema0x66616b65626f6f6b。获取关键表如users的所有列名Payload:-1 union select 1,group_concat(column_name),3,4 from information_schema.columns where table_schema0x66616b65626f6f6b and table_name0x7573657273。0x7573657273是users的hex。在fakebook中我们很可能发现一个名为users的表里面包含no,username,passwd等字段。但我们的目标通常不是密码而是Flag。4. LOAD_FILE()函数的高级利用当我们按照常规思路尝试union select出users表的所有数据后可能会发现里面并没有Flag。这时就需要转换思路Flag可能不在数据库里而是以文件形式存放在服务器的某个路径下如/var/www/html/flag.php、/flag、/home/ctf/flag等。这就需要用到MySQL的LOAD_FILE()函数。4.1 LOAD_FILE()函数原理与条件LOAD_FILE(file_name)函数读取文件内容并以字符串形式返回。但它不是随便就能用的有几个严格的先决条件这也是CTF中常考的考点文件必须位于服务器主机上你只能读取数据库服务器文件系统上的文件且需要提供绝对路径。数据库用户必须具备FILE权限这是一个非常重要的MySQL权限默认情况下普通用户是没有的。但在CTF环境和一些不安全配置中Web应用使用的数据库用户可能被授予了过高的权限包括FILE权限。我们可以通过查询current_user()或尝试执行load_file(‘/etc/passwd‘)来侧面验证。文件必须可读操作系统层面的文件读取权限。文件大小必须小于max_allowed_packet通常对于Flag文件来说这不是问题。函数不能返回NULL如果路径错误、权限不足或文件为空函数返回NULL在页面上可能什么都不显示。4.2 构造文件读取Payload假设我们已经通过回显点第2列确认可以输出数据并且怀疑Flag在/var/www/html/flag.php文件中。最简单的Payload就是-1 union select 1,load_file(‘/var/www/html/flag.php‘),3,4。但是这里会遇到fakebook题目最经典的“过滤”单引号被过滤了。我们之前用hex编码绕过了where子句里的字符串这里同样适用。‘/var/www/html/flag.php‘的十六进制编码是0x2f7661722f7777772f68746d6c2f666c61672e706870。所以Payload变为-1 union select 1,load_file(0x2f7661722f7777772f68746d6c2f666c61672e706870),3,4如果页面成功回显了文件内容我们就能看到Flag。但内容可能是PHP代码Flag可能以注释形式存在如// flag{this_is_flag}我们需要仔细查看页面源代码CtrlU因为浏览器不会渲染PHP代码和注释。4.3 常见Flag路径与盲猜技巧如果不知道Flag的具体路径怎么办这就需要基于经验的盲猜和目录遍历。以下是一些常见的CTF Flag存放路径/flag/home/ctf/flag/var/www/html/flag.php/var/www/flag/tmp/flag/readflag有时是一个可执行文件当前目录下的文件./flag.php 或者利用load_file(‘/proc/self/cwd/flag.php‘)读取当前工作目录的文件。我们可以通过union select同时尝试多个路径或者写一个简单的脚本进行爆破。例如构造Payload读取/etc/passwd来确认文件读取功能是否生效-1 union select 1,load_file(0x2f6574632f706173737764),3,4。5. 实战全流程复盘与问题排查让我们把上面的步骤串联起来模拟一次完整的fakebook解题流程并记录下可能遇到的坑和解决方案。5.1 完整利用链条推演探测与确认访问view.php?no1测试no2-1确认数字型注入。判断列数使用?no1 order by 4测试确认4列。寻找回显点使用?no-1 union select 1,2,3,4。如果被拦截尝试绕过空格?no-1/**/union/**/select/**/1,2,3,4。发现数字2和3在页面显示。信息搜集查库名?no-1/**/union/**/select/**/1,database(),3,4 得到库名fakebook。查表名?no-1/**/union/**/select/**/1,group_concat(table_name),3,4/**/from/**/information_schema.tables/**/where/**/table_schema0x66616b65626f6f6b 得到users等表。查列名?no-1/**/union/**/select/**/1,group_concat(column_name),3,4/**/from/**/information_schema.columns/**/where/**/table_schema0x66616b65626f6f6b/**/and/**/table_name0x7573657273 得到no,username,passwd,data等列。尝试读取数据?no-1/**/union/**/select/**/1,group_concat(username,0x3a,passwd),3,4/**/from/**/users。可能发现data列存储着序列化信息但依然没有Flag。转向文件读取猜测Flag在web目录下。尝试读取/etc/passwd验证权限?no-1/**/union/**/select/**/1,load_file(0x2f6574632f706173737764),3,4。如果成功显示系统用户说明FILE权限存在。读取Flag尝试常见路径。最终Payload?no-1/**/union/**/select/**/1,load_file(0x2f7661722f7777772f68746d6c2f666c61672e706870),3,4。查看网页源代码在PHP注释或代码中找到Flag。5.2 常见问题与排查表在实战中几乎不可能一帆风顺。下面这个表格整理了我踩过的一些坑和解决思路问题现象可能原因排查与解决方案union select测试时页面空白或错误1. 列数不对2. 关键词被过滤3. 数据类型不匹配1. 用order by重新确认列数。2. 尝试大小写、双写、插入/**/绕过过滤。3. 尝试将union select的数字换成nullnull可匹配任何类型。load_file()返回NULL页面无显示1. 路径错误2. 权限不足无FILE权限3. 文件不存在或为空4. 单引号被过滤导致语法错误1. 先尝试读取/etc/passwd等绝对存在且可读的文件验证功能。2. 使用hex编码绕过单引号过滤load_file(0x...)。3. 检查数据库用户权限需其他漏洞配合非本题重点。4. 查看网络响应包原始数据有时NULL会以空白形式传输。回显点显示不全内容被截断1. 页面输出长度限制2. 数据本身包含HTML特殊字符被转义1. 使用substring()或mid()函数分段读取。例如load_file(‘/flag‘)改为substring(load_file(0x...),1,50)。2. 查看网页源代码而非渲染后的页面。空格绕过无效过滤规则可能识别/**/尝试其他空白符%09(Tab),%0a(换行),%0d(回车)或者组合使用。例如union%0aselect。知道Flag在文件里但路径未知需要目录遍历或信息泄露1. 尝试常见路径列表。2. 利用load_file()读取Web应用配置文件如config.php里面可能包含路径线索。3. 如果存在任意文件读取可尝试读取/proc/self/environ获取进程环境变量可能包含当前路径。5.3 个人实操心得与技巧养成Hex编码的习惯在CTF和部分真实场景中单引号、双引号是高频过滤对象。一旦发现字符串被过滤第一时间想到用十六进制编码。你可以用Python快速获取‘/flag‘.encode(‘hex‘)或在线的转换工具。在Payload中直接使用0x...形式。善用group_concat()与substring()group_concat()在爆数据时非常方便但要小心长度限制默认1024字节。如果数据太长结合substring()分段查看例如substring((select group_concat(table_name) from ...), 1, 30)。始终查看页面源代码很多关键信息如Flag、序列化数据、隐藏的注释在浏览器渲染后的页面是看不到的。CtrlU是你的好朋友。从错误中学习如果页面报出详细的数据库错误如MySQL错误不要慌这是黄金信息。它会告诉你SQL语句在哪里出错了帮你快速调整Payload语法。工具是辅助思路是关键像sqlmap这样的自动化工具很强大但在CTF中过于复杂的过滤往往会让工具失效。手动构造Payload的过程能极大地加深你对SQL语法、数据库特性、绕过技巧的理解。fakebook这类题目就是训练手动能力的绝佳沙盒。通过fakebook这道题我们完整地走通了一条“数字型注入 - 联合查询 - 绕过基础过滤 - 利用LOAD_FILE()进行文件读取”的攻击链。它像一把钥匙打开了一扇门门后是Web安全中一个经典而又深邃的领域。理解并掌握这个过程远比记住几个Payload重要得多。因为下一次你遇到的可能是过滤了union、select、load_file甚至括号的“变种题”但只要你理解了数据库如何解析语句、应用如何拼接参数、函数如何被调用的本质你总能找到那条迂回前进的路径。
返回列表