问题

在学 sql 注入的时候,总是搞不明白 mysql 的查询条件 WHERE 子句的引号的问题。例如,对于下面这个表

将mysql查询结果转换为插入语句 mysql查询类型转换_mysql 类型隐式转换

各字段的类型如下

将mysql查询结果转换为插入语句 mysql查询类型转换_单引号_02

可以看到 id 字段是 INT 类型。然后进行如下的查询。

将mysql查询结果转换为插入语句 mysql查询类型转换_字符串_03

将mysql查询结果转换为插入语句 mysql查询类型转换_将mysql查询结果转换为插入语句_04

将mysql查询结果转换为插入语句 mysql查询类型转换_sql_05

将mysql查询结果转换为插入语句 mysql查询类型转换_mysql 类型隐式转换_06

将mysql查询结果转换为插入语句 mysql查询类型转换_将mysql查询结果转换为插入语句_07

将mysql查询结果转换为插入语句 mysql查询类型转换_mysql 类型隐式转换_08

可以看到有的感觉是明显不正确的查询条件还是可以查询出来的。然后,我就想是不是 mysql 对引号的匹配处理的问题。但是从上面的最后一个示例图片来看,好像并不是单纯的引号的问题。后来查了一下应该是 Mysql隐式类型转换 的问题。

如下图所示,是将字符串转换为 INT。

将mysql查询结果转换为插入语句 mysql查询类型转换_单引号_09

Mysql成功地将 '1 "222" 333'转换成了 1。虽然这个显式的类型转换,但是大概可以理解上面那些图片里的查询了,就是发生了隐式的类型转换,将字符串转换成了 INT,然后就查询出来了。

解释

在 Mysql 中,当一个操作符用来比较两个不同类型的时候,就会发生隐式的类型转换。具体的转换规则如下。

If one or both arguments are NULL, the result of the comparison is NULL, except for the NULL-safe <=> equality comparison operator. For NULL <=> NULL, the result is true. No conversion is needed.

如果一个或两个参数均为NULL,则比较的结果为NULL,除了NULL-safe <=> 相等比较运算符外。对于NULL <=> NULL,结果为true。无需转换。

If both arguments in a comparison operation are strings, they are compared as strings.

如果比较操作中的两个参数都是字符串,则将它们作为字符串进行比较。

If both arguments are integers, they are compared as integers.

如果两个参数都是整数,则将它们作为整数进行比较。

Hexadecimal values are treated as binary strings if not compared to a number.

如果不与数字比较,则将十六进制值视为二进制字符串。

If one of the arguments is a TIMESTAMP or DATETIME column and the other argument is a constant, the constant is converted to a timestamp before the comparison is performed. This is done to be more ODBC-friendly. This is not done for the arguments to IN(). To be safe, always use complete datetime, date, or time strings when doing comparisons. For example, to achieve best results when using BETWEEN with date or time values, use CAST() to explicitly convert the values to the desired data type.

如果参数之一是a TIMESTAMP或 DATETIMEcolumn,而另一个参数是常量,则在执行比较之前,该常量将转换为时间戳。这样做是为了使ODBC更友好。对于的参数,此操作未完成 IN()。为了安全起见,在进行比较时,请始终使用完整的日期时间,日期或时间字符串。例如,为了在BETWEEN与日期或时间值一起使用时获得最佳结果 ,可使用CAST()将值显式转换为所需的数据类型。

A single-row subquery from a table or tables is not considered a constant. For example, if a subquery returns an integer to be compared to a DATETIME value, the comparison is done as two integers. The integer is not converted to a temporal value. To compare the operands as DATETIME values, use CAST() to explicitly convert the subquery value to DATETIME.

一个或多个表中的单行子查询不视为常量。例如,如果子查询返回要与DATETIME 值进行比较的整数,则比较将作为两个整数完成。整数不转换为时间值。要将操作数作为DATETIME值进行比较 ,请使用 CAST()将子查询值显式转换为DATETIME。

If one of the arguments is a decimal value, comparison depends on the other argument. The arguments are compared as decimal values if the other argument is a decimal or integer value, or as floating-point values if the other argument is a floating-point value.

如果参数之一是十进制值,则比较取决于另一个参数。如果另一个参数是十进制或整数值,则将参数作为十进制值进行比较;如果另一个参数是浮点值,则将参数作为浮点值进行比较。

In all other cases, the arguments are compared as floating-point (real) numbers. For example, a comparison of string and numeric operands takes place as a comparison of floating-point numbers.

在所有其他情况下,将参数作为浮点数(实数)进行比较。例如,将字符串和数字操作数进行比较,将其作为浮点数的比较。

上面截图中的应该就是最后一种情况。当作浮点数进行比较。以'1 "222" 333'为例,首先将其从左向右取出数字部分转换为浮点数,然后跟表中的记录进行比较。

字符串与HEX(十六进制值)比较

在 sql 注入中,可以使用十六进制的值作为 where 的查询条件,十六进制的值两边不需要单引号('),从而可以绕过对单引号的限制。例如,0x61646d696e是admin的十六进制表示。

将mysql查询结果转换为插入语句 mysql查询类型转换_单引号_10

注意

避免发生隐式类型转换,隐式转换的类型主要有字段类型不一致、in参数包含多个类型、字符集类型或校对规则不一致等

隐式类型转换可能导致无法使用索引、查询结果不准确等,因此在使用时必须注意

数字类型的建议在字段定义时就定义为int或者bigint,表关联时关联字段必须保持类型、字符集、校对规则都一致

当字符串转换为浮点数时,数字位数至少在17位就会出现问题。

Mysql中的单双引号

双引号

双引号"在 Mysql 中并不是保留字符,就跟一般的abc...这中字符一样,也不需要转义处理什么的。

单引号

如果要想数据库中写入单引号,要写两个''或者转义\'。如下图所示。

两个单引号对应一个单引号

将mysql查询结果转换为插入语句 mysql查询类型转换_mysql 类型隐式转换_11

使用转义

将mysql查询结果转换为插入语句 mysql查询类型转换_sql_12

concat()函数中

将mysql查询结果转换为插入语句 mysql查询类型转换_sql_13

语句select concat('drop user ''''@', '''', @@hostname, '''');中,第一个参数'drop user ''''@'表示一个字符串drop user ''''@,由于两个表示一个,所以查询的结果为drop user ''@。第二个参数''''表示字符串'',由于两个表示一个,所以查询的结果为'。最后一个参数同理。