• SQL注入总结


    SQL注入类型及检测注入

    字符型注入

    对于字符型注入,SQL语句如下

    SELECT * FROM user_info WHERE id = '1';

    其实就是用户输入在引号内啦

    就上面的定义来说,简单的检测方法就是用户输入带引号,以导致SQL语句结构不完整而报错或没有响应

    True表示语句可用(正确显示内容或者操作有效)

    False表示语句不可用(Mysql报错或者无内容显示)

    引号使用举例

    SELECT * FROM user_info WHERE id = '1''
    SELECT 1 FROM user_info WHERE 1 = '1'''''''''''''UNION SELECT '2';

    备注:多个引号的作用在一个智能WAF场景下,可以绕过语法检测

    只要能够闭合,那么你就可以使用尽可能多的引号,在闭合完成的引号后面可以添加语句,利用引号闭合让原本的引号逃逸

    数字型注入

    相对字符型的,数字型的就是不加引号,用户输入是数字,SQL语句如下:

    SELECT * FROM Table WHERE id = 1;

    通过运算符判断注入的存在

    注意 : 数字型注入中,true相当与1,false相当与0.

     

    mysql的注释符

    很多时候,用户的输入往往是被拼接到Sql语句的中间部分而非结尾,这样的话,可控部分后面的Sql代码往往会对我们的注入语句产生影响,造成意料之外的错误。注释查询往往能很好的解决这个问题。

    #Hash语法
    /* C-style语法
    -- - SQL语法
    ;%00 空字节
    ` 反引号

    用法

    SELECT * FROM user_info WHERE id = '' OR 1=1 -- ' AND last_name = '';
    SELECT * FROM user_info WHERE id = '' OR 1=1 #' AND last_name = '';
    SELECT * FROM user_info WHERE id = '' UNION SELECT 1, 2, 3`';
    SELECT * FROM user_info WHERE id = '' OR 1=1 /*' AND last_name = '';

    在Sql注入中,因为语法规则的限制,所以很多注入语句都需要满足情况,否则整 个语句将会出错,无法得到我们想要的结果,就比如当用户获取的参数被拼接到Sql 中,如果是字符型注入,那么我们每次都需要去在最后添加一个单引号去闭合原本 语句的第二个单引号,但是如果后面还有其余语句的限制的话,往往会出错,因此 直接使用注释的话,就能够无视注释之后的语句,从而方便我们的注入

    数据库系统表功能

    mysql需要root权限,存储用户信息及权限控制
    information_schema Version 5及更高,存储的的是mysql的结构信息
    performance_schema 数据库性能方案,注入一般用不到

    SQL注入只要使用information_schema跟mysql,这两个表里的数据对我们比较有用

    比如:

    mysql.user存储用户信息

    information_schema.tables存储所有表信息

    information_schema.columns存储所有表字段信息

    information_schema.schemata存储所有数据库信息

    在注入的时候可以利用这些表来获取想要的信息,为注入提供了便利。

    SQL注入的利用方式

    获取列数目

    可以使用GROUP/ORDER BY

    select * from test order by 4;
    select * from test group by 3;

    GROUP/ORDER BY一般我们都是接字段名的,不过我们也可以使用字段位数,从1开始计数的,如果不存在,则n-1是列数,下面我们看下例子,test表是三列

    select * from test order by 4;

    根据返回持续递增或者递减数字,直到返回一个Fal se输出

    注意:这个必须得输出所有的信息才可以,如果SQL语句是下面这样的,则不能得到全表的列数了

    select name from test order by 4;

    只返回name的值,则order by就只能是 1 了,其他数值都会报错

    获取数据库版本

    Mysql不同的版本往往带来不同的语句差异,因此探测版本往往也是必须的一环。Mysql的注入探测版本往往有两种方式,一种是通过内置函数,还有一种是类似于盲目注入的手法,定位出版本。

    内置函数

    SELECT VERSION();
    SELECT @@VERSION;
    SELECT @@GLOBAL.VERSION;

    特殊结构

    /*!VERSION SpecificCode*/
    VERSION:数据库版本
    SpecificCode:当数据库版本等于或高于指定版本时返回的数据
    : /*后加了'!' 仅当MySQL的版本等于或高于指定的版本号时才会执行注释中的语法
    : /*后无'!'' 为Mysql的注释语句 为Mysql的注释语句

    使用例子(我的数据库版本是5.5.53)

    SELECT @@GLOBAL.VERSION,/*!50540 8*/;

    如果数据库版本低于指定版本,则报错

    这种可以在过滤了内置函数的时候使用。

    获取数据库名

    获取当前数据库名除了内置函数,

    select database();
    select schema();

    还有就是从系统表中获取,比如:

    表information_schema.schemata的schema_name字段

    表information_schema.columns的table_schema字段

    表information_schema.tables的table-schema字段

    表mysql.db的db字段

    SELECT schema_name FROM information_schema.schemata; #获取了所有数据库
    SELECT DISTINCT(db) FROM mysql.db; #db里面是针对数据库某个用户拥有的权限,所以可能不能获取所有的数据库

    获取数据库凭证

    想要获取数据库凭证,我们可以查询用户信息相关的表,有哪些表会存储用户凭证信息呢

    表mysql.user的user,password字段

    用户名这个有一些内置函数可以使用,有哪些呢

    select user();
    select current_user();
    select current_user;
    select system_user();
    select session_user();

    获取了账号密码也没有用,一般密码是动态md5加密的,可以使用hashcat工具尝试破解

    hashcat破解密码

    hashcat号称世界上最快的密码破解,世界上第一个和唯一的基于GPGPU规则引擎,免费多GPU(高达128个GPU),多哈希,多操作系统(Linux和Windows本地二进制文件),多平台(OpenCL和CUDA支持),多算法,资源利用率低,基于字典攻击,支持分布式破解等等

    hashcat使用教程

    我是安装的Windows版,但是使用的时候报错了

    访问错误提示中的链接

    To disable WDDM recovery, create a file called wddm_timeout_patch.reg with the following contents:

    Windows Registry Editor Version 5.00
    
    [HKEY_LOCAL_MACHINESYSTEMCurrentControlSetControlGraphicsDrivers]
    "TdrLevel"=dword:00000000

    Run this registry file as Administrator and reboot.

    意思是让我们添加一个注册表信息,添加后我们就可以正常使用了

    hashcat64.exe -a 0 -m 300 --force 81F5E21E35407D884A6CD4A731AEBFB6AF209E1B password.txt

    注:password.txt字典是自己写的,里面就写了我的真实密码,其实就是为了测试工具,这个自己需要自己慢慢收集,字典越庞大爆破率越大

     

    获取表名

    这个可以利用系统表进行查询的,

    表information_schema.columns的table_name字段

    表information_schema.tables的table_name字段

    select table_name from information_schema.columns;
    select table_name from information_schema.tables;

    获取列名

    这个其实跟表明类似,也是利用系统表的

    表information_schema.columns的column_name字段

    select column_name from information_schema.columns;

    一个特殊的获取列名的方法limit后注入

    看名字就很明显看出注入点在哪里啦,limit后面,一般limit都是在SQL语句最后的,用来控制输出的数量或者区域

    哪利用了什么呢?就是PROCEDURE ANALYSE()

    语法eg:select column from table_name procedure analyse();

    这个是用来干嘛的呢?是通过分析select查询结果对现有的表的每一列给出优化的建议

    SELECT * FROM user_info FROM Users WHERE id = {INJECTION POINT};
    #1 PROCEDURE ANALYSE() Get the first column's name
    #1 LIMIT 1,1 PROCEDURE ANALYSE() Get the second column's name
    #1 LIMIT 2,1 PROCEDURE ANALYSE() Get the third column's name

    Union联合查询

    这里先介绍一下Union,Union 操作符用于连接两个以上的 SELECT 语句的结果组合到一个结果集合中。多个 SELECT 语句会删除重复的数据,下面就是例子;union同一个表的数据,最后显示的是一个表的数据,没有重复

    union all则会包含重复数据

    Union语法:

    SELECT expression1, expression2, ... expression_n
    FROM tables
    [WHERE conditions]
    UNION [ALL | DISTINCT]
    SELECT expression1, expression2, ... expression_n
    FROM tables
    [WHERE conditions];

    参数

    • expression1, expression2, ... expression_n: 要检索的列。因为合并到一个集合里,所以标的检索列数要是一致的

    • tables: 要检索的数据表。

    • WHERE conditions: 可选, 检索条件。

    • DISTINCT: 可选,删除结果集中重复的数据。默认情况下 UNION 操作符已经删除了重复数据,所以 DISTINCT 修饰符对结果没啥影响。

    • ALL: 可选,返回所有结果集,包含重复数据。

    上面了解了union的语法,其实回想为什么SQL注入要用这个呢,其实一般情况业务中的SQL语句都是固定表的查询,但我如果查询其他表的数据呢,这个时候就要使用union了,把其他表的数据合到同一个结果集合中返回。下面说个例子

    业务的SQL语句

    select name,age from test where id={inject}

    这个语句限定在test这个表,但如果我想查询表字段怎么办呢,这个表字段我们前面提到了information_schema.columns表有,那我们联合这个表进行查询,下面是SQL语句

    select name,age from test where id=0 union select group_concat(column_name,','),2 from information_schema.columns ;

    注:group_concat函数将所有数据拼接为一个数据

    union还可以用来怕段注入输出点

    select name,age from test where id=0 union select 1,2;

    通过控制第二个查询语句的输出来判断输出点,

    即改变1,2的值,看输出的是哪个值。

    盲注查询

    盲注其实就是没有回显,不能直观地得到结果来调整注入数据,只能通过其他方式来得到是否注入成功,主要是利用了一些数据库内置函数来达到的

    布尔盲注

    布尔很明显Ture跟Fales,也就是说它只会根据你的注入信息返回Ture跟Fales

    其实登录处的注入就是布尔型的,万能密码就是构造一个永真的查询,比如下面的

    select user from test where passwd=‘{injuct}’;
    #构造永真,即令where的条件用于为真
    select user from test where passwd=‘aa‘or’1’=‘1’;
    #注入的数据是aa‘or’1’=‘1

    密码输入无论是否正确,查询都成立。

    布尔盲注其实就是利用了这种,我们什么时候需要采用这种呢

    1)当没有数据输出点时,我们没有办法直观的判断注入的sql执行情况,

    2)有正确或者错误的两种返回,比如查询正确返回一个页面,失败返回另一个页面,但是没有数据

    时间盲注

    界面返回值只有一种,true 无论输入任何值 返回情况都会按正常的来处理。加入特定的时间函数,通过查看web页面返回的时间差来判断注入的语句是否正确

    利用的内置函数

    sleep(n):将程序挂起一段时间 n为n秒
    if(expr1,expr2,expr3):判断语句 如果第一个语句正确就执行第二个语句如果错误执行第三个语句

    注入的语句

    select user from test where passwd=‘aa‘and (if(ascii(substr(database(),1,1))>100,sleep(10),null));
    #注入的数据是aa‘and (if(ascii(substr(database(),1,1))>100,sleep(10),null));--+

    我们什么时候需要采用这种呢

    1)当没有数据输出点时,我们没有办法直观的判断注入的sql执行情况,

    2)无论查询结果都返回同一个数据,无法判断SQL语句执行情况

    基于错误查询

    有如下报错注入方法

    #报错注入floor
    (select 1 from (select count(*),concat((payload[]),floor(rand()*2))a from information_schema.columns group by a)b)limit 0,1
    #报错注入extractvalue
    select extractvalue(1,concat(0x5c,([payload])))
    #报错注入updatexml
    select 1=(updatexml(1,concat(0x3a,([payload])),1))

    floor报错注入

    floot是区镇函数,返回小于或等于 x 的最大整数

    上面floor报错例子中floor中传入的是一个rand函数(返回 0 到 1 的随机数)。

    floor报错注入主要利用的group by的机制,下面先来了解一下原理:

    group by key的原理是循环读取数据的每一行,将结果保存于临时表中。读取每一行的key时,如果key存在于临时表中,则不在临时表中更新临时表中的数据;如果该key不存在于临时表中,则在临时表中插入key所在行的数据。group by floor(random(0)2)出错的原因是key是个随机数,检测临时表中key是否存在时计算了一下floor(random(0)2)可能为0,如果此时临时表只有key为1的行不存在key为0的行,那么数据库要将该条记录插入临时表,由于是随机数,插时又要计算一下随机值,此时 floor(random(0)*2)结果可能为1,就会导致插入时冲突而报错。即检测时和插入时两次计算了随机数的值不一致,导致插入时与原本已存在的产生冲突的错误。

    主要检测时和插入时两次计算的所以输不一致就会报错。

    extractvalue报错注入

    ExtractValue(xml_frag, xpath_expr)

    ExtractValue()接受两个字符串参数,

    一个XML标记片段 xml_frag

    一个XPath表达式 xpath_expr(也称为 定位器);

    第一个参数可以传入目标xml文档,

    第二个参数是用Xpath路径法表示的查找路径

    原理

    如果Xpath格式语法书写错误的话,就会报错。这里就是利用这个特性来获得我们想要知道的内容。

     

    updatexml报错注入

    首先了解下updatexml()函数

    UPDATEXML (XML_document, XPath_string, new_value);

    第一个参数:XML_document是String格式,为XML文档对象的名称,文中为Doc

    第二个参数:XPath_string (Xpath格式的字符串) ,如果不了解Xpath语法,可以在网上查找教程。

    第三个参数:new_value,String格式,替换查找到的符合条件的数据

    函数作用:改变文档中符合条件的节点的值

    原理

    如果XPath_string的值不符合xpath的语法格式则会报错,报错信息会提示这个数据错误

    所以我们就在这个参数里注入我们的返回数据结果

    数据库读写文件

    mysql数据库在渗透过程中能够使用的功能还是比较多的,除了读取数据之外,还可以进行对文件进行读写(但前提是权限足够)

    读写文件前提: 1、用户权限足够高,尽量具有root权限。 2、secure_file_priv不为NULL

    secure-file-priv参数是用来限制LOAD DATA, SELECT ... OUTFILE, and LOAD_FILE()传到哪个指定目录的。

    • 当secure_file_priv的值为null ,表示限制mysqld 不允许导入|导出

    • 当secure_file_priv的值为/tmp/ ,表示限制mysqld 的导入|导出只能发生在/tmp/目录下

    • 当secure_file_priv的值没有具体值时,表示不对mysqld 的导入|导出做限制

    如何查看secure_file_priv参数的值,如下

    show global variables like "%secure_file_priv%";

    如果为null,可以修改my.ini文件,如下

    怎么判断权限是否足够呢

    就看你有没有权限访问mysql.user表了

    and (select count(*) from mysql.user)>0 /如果结果返回正常,说明具有读写权限./
    and (select count(*) from mysql.user)>0 /* 返回错误,应该是管理员给数据库账户降权了*/

    load_file读文件

    额外条件:

    1)文件必须位于服务器主机上

    2)必须知道绝对路径

    3)必须有FILE权限

    4)文件所有字节可读,但文件内容必须小于max_allowed_packet

    SQL语句

    select load_file(‘E://test.txt’);

     

    into outfile写文件

    额外条件:

    1)输出文件必须还不存在。这防止MySQL弄错文件很重要

    2)必须知道绝对路径,如果不指定路径的话是默认在当前数据库目录下创建文件

    语法结构

    select 【内容】 into outfile 【文件路径】

    前面的内容可以使用16进制编码的,会自动解码的,这种就可以绕过过滤了引号的情况

    select 0x61 into outfile 【文件路径】

     

     

    参考链接

    https://blog.csdn.net/jpygx123/article/details/84191704

    https://blog.csdn.net/vspiders/article/details/77430024

    https://www.yiibai.com/mysql/mysql_function_load_file.html

     

  • 相关阅读:
    决定你人生高度的,不是你的才能,而是你的态度
    享受六一的最后几分钟
    DB9 公头母头引脚定义及连接
    bzoj3207--Hash+主席树
    bzoj1901 [ Zju2112 ] --树状数组套主席树
    bzoj1723 [ Usaco2009 Feb ] --前缀和(水题)
    bzoj3932 [ CQOI2015 ] --可持久化线段树
    bzoj3037--贪心
    bzoj3388 [ Usaco2004 Dec ] (神奇的解法)
    bzoj2693--莫比乌斯反演+积性函数线性筛
  • 原文地址:https://www.cnblogs.com/alummox/p/11373981.html
Copyright © 2020-2023  润新知