Page-1(Basic Challenges)
Less 1-4
Less-(1-4)是最常规的SQL查询,分别采用单引号闭合、无引号、括号单引号闭合、括号双引号闭合,没有过滤;可以采用and '1'='1
的方式闭合引号,或注释掉引号来执行SQL语句。也可以使用报错注入。
Less 5-6
Less-5为盲注,即我们不能直接看到查询的返回结果,只能看到“查询成功”或“查询失败”,在sqli-labs中是以“You are in...”或无回显的形式来表明查询成功和失败,通常遇到的盲注形式是正常返回页面/错误显示页面。对于有回显的盲注,使用报错函数来注入也是一个思路,这就需要积累一些报错公式。
@@version MySQL的版本信息
@@datadir MySQL的安装路径
and extractvalue(1,concat(0x7e,(select @@version),0x7e))
and (updatexml(1,concat(0x7e, (select @@version),0x7e),1))
and (extractvalue(1,concat(0x7e,(select @@version),0x7e)))
and (select 1 from (select count(*),concat(floor(rand(0)*2),(select (select(select @@version)) from information_schema.tables limit 0,1))x from information_schema.tables group by x)a)
继续说盲注,我们无法直接看到查询结果,那么我们的思路便是传入一个判断语句,询问服务器某某字段的某个字母是否等于a?是否等于b?是否等于c?……以此来判断字段的名称,所以我们还需要截断字符串的函数,可以选择的有:left(a, b)
从左侧截取a的前b位、substr(a,b,c)
从b开始截取字符串a,长度为c、mid(a,b,c)
从b位置 开始截取a字符串,长度为c。仅仅知道如何截取也是不够的,还需要知道如何查询字段的长度length(database())
。可以是left(database(),1)>'s'
的形式,也可以是ascii(left(database(),1))>115
的形式。对于盲注,手工注入是很繁琐的,因此大多使用Python脚本来进行盲注。
payload:?id=1' and substr(database(),1)>'s' and '1'='1
上面的盲注思路是构造判断,观察页面的返回异同。SQL中有一个sleep()
函数,可以让服务器“沉睡”一段时间,构造判断语句执行sleep,根据页面返回时间的长短来确定语句是否为真。
Less-6与Less-5的区别在于前者采用双引号闭合,后者采用单引号闭合。
Less 7
本关需要用到SQL语句中的select XX into outfile '路径'
将查询结果导出为一个文件。一般用来写入一句话木马,再连接shell获取服务器的权限。
payload:?id=0')) union select 1,2,'shell' into outfile 'C:\\phpStudy_new\\WWW\\1.php' --+
这里的shell,可以是一句话木马,也可以是要查询的内容,这样访问生成的文件就可以获得查询的结果了。
Less 8
布尔盲注,与Less-5的区别是不会显示SQL查询的错误信息。
payload:http://192.168.137.130/sqli-labs/Less-8/?id=1%27%20and%20substr(database(),1)%3E%27s%27%20and%20%271%27=%271
Less 9-10
这一关无论什么样的查询,什么页面返回都不会报错,起初很好奇这是如何做到的呢?看过源码后发现在判断查询成功失败的if语句中,if和else里面都是“You are in...”,因此不管是否查询成功,返回的都是成功。于是布尔盲注不能用了,可以考虑基于时间的盲注。下面的payload中正确的时候会直接返回,错误则会等待5秒。
Less-10将单引号闭合改为了双引号闭合。
payload:?id=1'and If(ascii(substr(database(),1,1))=115,1,sleep(5))--+
Less 11-12
开始进入POST类型的注入,这一关中的用户名和密码均存在注入点,单引号闭合的布尔注入。由于存在报错回显,也可以使用报错注入。
关于“万能密码”的原理,万能密码1' or '1'='1
,用户名可以存在或者随意,查看此时的SQL查询语句SELECT username, password FROM users WHERE username='2' and password='2' or '1'='1' LIMIT 0,1
,or连接的1=1永远为真,故该查询将直接返回users表中的第一条记录。
同时,采用--+
的注释是因为在方式为GET的参数传递过程中,--+为注释符--
的URL编码。在POST类型的注入中不能使用,应该使用#
注释掉后续语句或采用闭合引号的方式。
Less-12与Less-11的区别在于闭合方式为")
。
Less 13-14
在Less-13中,即使登录成功也无法看到回显信息,意味着需要进行盲注,因为成功与失败返回的是图片是不一样的,因此布尔盲注可以行得通。存在数据库查询报错信息回显,因此也可以使用报错注入。
Less-14与13的区别在于Less-14使用双引号闭合。
Less 15-16
单引号闭合,没有报错回显,没有查询回显,因此报错注入不能使用,可以采用布尔盲注和延时注入。
Less-16与15的处理方法类似,闭合方式改为")
。
Less-17
这一关是一个修改密码的界面,跟前面的不太一样了。前面的关往往都是查询用户的相关信息,用的是SQL的查询语句select ... from ...
,查看源码,这里首先执行了查询语句@$sql="SELECT username, password FROM users WHERE username= $uname LIMIT 0,1";
,如果查询成功,再执行更新表中密码的语句$update="UPDATE users SET password = '$passwd' WHERE username='$row1'";
。
对传入的用户名参数经过了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;
}
check_input首先判断参数是否为空,若为空将其转换为整型,即若为空返回0。若非空,则只保留参数的前15位。然后检测magic_quotes_gpc
是否开启,若开启,则返回去除转义符的字符串。接着进行一个纯数字检测,若果不是纯数字,则将SQL语句中使用的特殊字符转义并在两端添加一个单引号。
magic_quotes_gpc
若开启,则会将GET、POST、COOKIE传递来的特殊字符添加转义符“\”。check_input()
中,有一步检测该参数是否开启,若开启,将转义符去掉,起初很是疑惑为什么要将用来转义的\
去掉,后面看到了mysql_real_escape_string()
才明白,该函数针对在SQL语句中将引起歧义的特殊字符进行过滤,“\”的存在反而会引起歧义。
虽然对用户名进行了过滤,但是密码并没有做任何处理。新密码以单引号闭合,报错信息有回显,因此可以采用报错注入,但是要注意需要输入正确的用户名才可以进行注入。
除了报错注入,堆叠注入理论上也是行得通的,不过在实际测试中没有成功,将更新的SQL语句显示出来后,在命令行里执行该SQL语句却可以将shell导出。堆叠注入,即是利用分号将SQL语句分隔开,从而执行不同的SQL语句。
Less 18-19
18关主要告诉我们http的请求头也可以存在注入点,抓包后修改user-agent,与前面的用户名、密码的注入并无二致。19关注入点在referer上,类同。
Less 20-22
在20关这里,开启了一个新的环境。这里首先需要登录,在登录时用户名和密码都经过了check_input()
的处理,登录后获取cookie,若cookie未删除且未过期时,服务器会首先检查cookie中的uname是否设置,然后执行查询,于是cookie中的uname字段便可以进行注入。
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
$result=mysql_query($sql);
另外cookie中的uname以明文显示且与用户名相同,刷新页面修改uname字段的值为其他用户可导致水平越权。
在21关中,将uname进行了base64加密,处理方式类同。
22关在21关的基础上修改了uname参数的闭合方式,在21关中以')
闭合,22关中以双引号闭合。