前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >安全科普:SQLi Labs 指南(Part 3)

安全科普:SQLi Labs 指南(Part 3)

作者头像
FB客服
发布2018-02-09 15:32:39
9770
发布2018-02-09 15:32:39
举报
文章被收录于专栏:FreeBuf

Freebuf上有两篇SQLi Labs的教程安全科普:SQLi Labs 指南 Part 1和安全科普:SQLi Labs 指南 Part 2。这两篇教程只讲到了第八课。

我最近也在学习SQLi Labs,接合youtube上作者的视频教程和网上的一些资料,自己做了后面的一些课程,想发出来和大家一起学习交流。

作者应该是在出了视频教程之后又对课程进行了一些修改但是没有再传新的视频,所以现在从github上下载到的最新版的课程和视频教程有一些不一样的地方。

希望大家一定看完前面的教程再看这一篇教程,后面的课程都是在前面的课程基础之上变化而来,前面提到过的后面就不再讲了。本人也是刚刚接触SQL注入,才疏学浅,文中若有错误疏漏之处,恳请不吝赐教。

点击阅读原文查看我自己写的前八课的教程:(参考了youtube的视频和别人写的解答)

sqli-labs学习教程(一)

sqli-labs学习教程(二)

sqli-labs学习教程(三)

搭建环境:xp sp3虚拟机+xampp

使用工具:firefox浏览器+hackbar插件+tamper data插件+Cookie Editor插件

第九课:GET – Blind – Time based – Single Quotes(基于时间-单引号-盲注)

这一课我们发现,加不加单引号页面返回的信息都是一样的。

看一下代码。果然,不论输入是什么,返回的结果都是一样的。这里就要使用基于时间的盲注方法了。

在mysql中if(condition,A,B)表示当condition为true时,返回;当condition为false时,返回B。因此我们可以构造下面的查询。如果数据库名的第一个字母ascii值大于120,则立即返回结果;否则10秒之后才返回结果。

我们当然可以这样手工猜解数据库名,表名,列名……也可以自己编写脚本来完成。通过sqli-labs学习sql注入——基础挑战之less1-10这篇博客中给出了第八课的python脚本,我稍稍修改了一下来完成第九课。详情点击阅读原文。

效果是这样的。

代码水平不高,让大家见笑了。因为是安装的虚拟机,基本不存在延时的问题,所以查询语句中写的是sleep(0.1)。

如果写成sleep(x)那么判断条件最好比x小一点,比如我这里是sleep(0.1)但是只要时间差0.08秒以上就认为sleep(0.1)执行了。因为运行环境什么的都不一样,可能sql语句里面写的sleep(0.1)结果时间差算出来是零点零九几秒。

第十课:GET – Blind – Time based – double quotes(基于时间-双引号-盲注)

这里把单引号改成双引号就可以,脚本同样改改就行。

第十一课:POST – Error Based – Single quotes – String(基于错误-单引号-字符串)

输一个单引号,错误信息如图所示。

那么我们可以得出SQL语句应该是select * from where username=” and password=” LIMIT 0,1。注入1′ or 1=1 #试试。

果然以用户名Dumb,密码Dumb成功登录了。接下来猜字段。注入1′ order by 3#试试。

注入1′ order by 2#。这次没有报错,说明有两列,也就是说SQL语句是select col1,col2 from where username=” and password=” LIMIT 0,1。

查询数据库名和版本,注入1′ union select database(),version() #试试。

爆表名,注入1′ union select 1,table_name from information_schema.tables where table_schema=’security’ limit 0,1#试试。

拿到了第一个表名。后面就和前几课一样了,再次希望大家从第一课开始做起,可以参考我在文章开头给出的链接、freebuf上以前的教程和youtube上的视频资料等等。

第十二课:POST – Error Based – Double quotes – String – with twist(基于错误-双引号-字符串-括号)

输一个单引号,没有任何返回结果。

输入一个双引号,错误信息如图所示。

那么我们可以得出SQL语句应该是select * from where username=(“”) and password=(“”) LIMIT 0,1。注入1″) or 1=1 #试试。

果然以用户名Dumb,密码Dumb成功登录了。接下来猜字段什么的和前面一样,就不再重复了。

第十三课:POST – Double Injection – Single quotes – String – with twist(双注入-单引号-字符串-括号)

输一个单引号,错误信息如图所示。

那么我们可以得出SQL语句应该是select * from where username=(”) and password=(”) LIMIT 0,1。注入1′) or 1=1 #试试。

为什么没有结果呢,因为这里和第五课一样,登录成功以后不返回用户名和密码。那么我们这里用的办法和第五课一样了,注入1′) and (select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2)) as a from information_schema.columns group by a) #试试。

返回的错误信息是Operand should contain 1 column(s),也就是说只能返回一列。

注入1′) and (select 1 from(select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2)) as a from information_schema.columns group by a)) #试试。

返回的错误信息是Every derived table must have its own alias,也就是说每个派生出来的表都必须有一个自己的别名。

注入1′) and (select 1 from(select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2)) as a from information_schema.columns group by a)b) #试试。可能要多试几次,因为rand不设置种子的话产生的序列是随机的。

后面和第五课一样,就不再重复了。

第十四课:POST – Double Injection – Single quotes – String – with twist(双注入-单引号-字符串-括号)

这一课和上一课名字一样,但是看了代码发现其实这一课是双引号,没有括号。

注入1″ and (select 1 from(select count(*),concat(0x3a,0x3a,(select database()),0x3a,0x3a,floor(rand()*2)) as a from information_schema.columns group by a)b) #试试。可能要多试几次,因为rand不设置种子的话产生的序列是随机的。

第十五课:POST – Blind – Boolian/time Based – Single quotes(盲注-基于布尔结果/基于时间延迟-单引号)

输一个单引号,显示登录失败。

输入1′ or 1=1 #,显示登录成功。

那么我们先猜数据库名的长度。输入1′ or length(database())=x#,(x=1,2,3……)显示登录失败;直到输入1′ or length(database())=8#才显示登录成功。

那么这里数据库名的长度就是8了。后面就是一个一个猜解数据库名,表的数量,表名,列的数量,列名……。

第十六课:POST – Blind – Boolian/time Based – Double quotes(盲注-基于布尔结果/基于时间延迟-双引号)

输一个双引号,显示登录失败。

输入1″) or 1=1 #,显示登录成功。

那么我们先猜数据库名的长度。输入1″) or length(database())=x#,(x=1,2,3……)显示登录失败;直到输入1″) or length(database())=8#才显示登录成功。那么这里数据库名的长度就是8了。

后面就是一个一个猜解数据库名,表的数量,表名,列的数量,列名……。

第十七课:POST – Update Query – Error Based – String(更新查询-基于错误-字符串)

先在User Name中输入admin,New Password中随便输1234,页面显示成功更改了密码。那么这里执行的sql语句应该是update users set password=xxx where username=xxx。

如果我们按照下图所示输入,执行的sql语句应该是update users set password= ‘ ‘,所有用户的密码都被我们重置。大家别忘了试验结束以后在主页Setup/reset Database for labs,把密码什么的改回来。

现在New Password中输入a’ or 1=updatexml(1,concat(0x5e24,(select database())),1) #,User Name中输入admin,因为代码中name参数进行了过滤,所以必须要先有一个有效的用户名。

这里再总结一下基于错误的mysql注入中常用的函数。

extractvalue(xml_frag,xpath_expr)

extractvalue()接受两个字符串参数,一个xml标记xml_frag的片段和一个xpath表达式xpath_expr(也称为定位符)。这个函数返回第一个文本节点的文本。在mysql 5.6.6及更早版本中,xpath表达式最多可以包含127个字符。

这个限制在mysql 5.6.7中解除。我们可以在xpath中填写获得我们想要的信息的语句。

updatexml(xml_target,xpath_expr,new_xml)

此函数用新的xml片段new_xml替换xml标记xml_target的给定片段的单个部分,然后返回更改的xml。被替换的xml_target的部分与用户提供的xpath表达式xpath_expr匹配。在 mysql 5.6.6及更早版本中,xpath表达式最多可以包含127个字符。

这个限制在mysql 5.6.7中解除。如果没有找到匹配xpath_expr的表达式,或者找到多个匹配项,函数将返回原始的xml_target片段。 所有三个参数应该是字符串。我们可以在xpath中填写获得我们想要的信息的语句。

name_const(name,value)

用于生成结果时,name_const()会使列具有给定的名称。参数应该是常量。

它和select value as name是等价的。我们可以构造两个列使得它们名字一样并在列名中填写获得我们想要的信息的语句。

当然用前面双注入的方法也是可以的。New Password中输入’ and (select 1 from (select count(*),(concat(“~”,(select database()),”~”,floor(rand()*2)))c from information_schema.tables group by c)a) #,User Name中输入admin,也能达到同样的效果。

到这儿基本上就和前面第五课差不多了,后面就不再讲了。还想啰嗦一句,乌云上有一篇mysql报错注入原理分析(count()、rand()、group by),虽然现在乌云上不了但是这篇文章在网上也搜得到,把原理讲得很清楚。

第十八课:POST – Header Injection – Uagent field – Error based(头部注入-Uagent字段-基于错误)

大家应该也都感觉到了,只要前面的几课都弄懂了,这几课基本上都没什么难度。这一课开始又有一些新的知识了。我们先看看代码,这里对name和password都进行了过滤。

然后如果登录成功了会显示User Agent的信息。

登录一下,用户名和密码都用admin。果然返回了我们的user agent信息。

这个时候就要上tamper data插件了。安装好这个插件之后在firefox浏览器最上方右键勾选菜单栏。

在工具中选择Tamper Data。

点击Start Tamper。

然后输入用户名admin和密码admin,点击submit之后弹出来一个对话框。点击Tamper。

我们可以对这张表里的选项进行编辑。比如这里我把User-Agent改成了helloworld。

点击确定,再看浏览器,果然我们修改成功了。

前面代码我们也看到这个信息被插到uagents表里面了。那我在User-Agent里面来一个反斜杠。

刷新一下。

现在正式开始注入。把User-Agent改成’,updatexml(0,concat(0x2b5e,database()),0),’,')#。

这样一来执行的sql语句应该是INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES (”,updatexml(0,concat(0x2b5e,database()),0),’,')#’, ’192.168.153.1′, ‘admin’)#后面被注释掉了,所以也就是INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES (”,updatexml(0,concat(0x2b5e,database()),0),’,')#。

执行结果如图所示,果然我们成功得到了数据库名,后面的步骤就不再讲了。

第十九课:POST – Header Injection – Referer field – Error based(头部注入-Referer字段-基于错误)

登录一下,用户名和密码都用admin。果然返回了我们的Referer信息。

接下来步骤和上节课差不多,直接上图吧。

第二十课:POST – Cookie injections – Uagent field –error based(cookie注入-Uagent字段-基于错误)

登录一下,用户名和密码都用admin。果然返回了我们的Cookie信息。

这个时候就要上Cookie Editor插件了。选择好需要修改的cookie以后点editor。

来一个单引号。

再刷新一下页面,果然报错了。

再来一个’ order by 3#。

刷新页面。

再来一个’ order by 4#。

刷新页面,那这下我们确定了有3列。接下来就和以前一样。

第二十一课:POST – Dump into file – String(转储到文件-字符串)

登录一下,用户名和密码都用admin。果然返回了我们的Cookie信息。和上一节课有什么不同呢?我们的cookie被base64编码了。

随便找个在线编码/解码的网站验证一下。

那这个就很简单,我们编码一下就行。

搞定。

到此为止Basic Challenges就全部完成了。接下来将会继续学习作者的视频教程,带来Advanced Injections的内容。

* 文章作者houjingyi,转载请注明来自FreeBuf(FreeBuf.COM)

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2016-12-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 FreeBuf 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
数据库
云数据库为企业提供了完善的关系型数据库、非关系型数据库、分析型数据库和数据库生态工具。您可以通过产品选择和组合搭建,轻松实现高可靠、高可用性、高性能等数据库需求。云数据库服务也可大幅减少您的运维工作量,更专注于业务发展,让企业一站式享受数据上云及分布式架构的技术红利!
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档