Are PDO prepared statements sufficient to prevent SQL injection?

addslashes() Versus mysql_real_escape_string()

SQL injection that gets around mysql_real_escape_string()

GBK内码查询

宽字节注入

上文中提到了这种类型的攻击对于任何字符编码都是可能的,只要这个编码集中有一个0x5c结尾的有效多字节字符,并且0x27是单字节字符': 例如gbk中0xbf5c是一个有效的多字节字符,而0x27是一个有效的单字符',而utf-8不符合这样的要求。符合要求的编码集有:big5, cp932, gb2312, gbk and sjis.

使用addslashes()导致的单引号逃逸

gbk0xbf5c是一个有效的多字节编码,而0xbf27则被当作两个单字节字符对待。

我的payload为0xbf27,其中的0x27为单引号',如果调用了addslashes()则会对这个单引号进行转义(也就是单引号前面加上反斜杠\),反斜杠的编码为0x5c。则我的payload变成了0xbf5c27,然后前四位0xbf5cgbk认为是一个有效的双字节编码字符,0x27'。最终结果就成了一个有效字符加上一个单引号,单引号逃逸成功!

PDO中prepare()导致的单引号逃逸

The important thing to realize here is that PDO by default does NOT do true prepared statements. It emulates them (for MySQL). Therefore, PDO internally builds the query string, calling mysql_real_escape_string() (the MySQL C API function) on each bound string value.

PDO在默认情况下不执行真正的预处理语句。它模拟它们。因此,PDO在内部构建查询字符串,在每个绑定字符串值上调用mysql_real_escape_string() (mysql C API函数)。

C API调用mysql_real_escape_string() 不同于addslashes(),因为它知道连接使用的字符集。然而,到目前为止,客户端认为我们仍然在为连接使用latin1,因为我们从来没有告诉它不是这样的。我们确实告诉服务器我们正在使用gbk,但是客户端仍然认为它是latin1。

构造思路就是用一种编码传递例如0xbf27

image-20200222102349036

sql使用gbk字符时,一个gbk编码的汉字占两个字节。比如:%df\’ =%df%5c%27,如果程序的默认字符集是GBK等宽字节字符集,则会认为 %df%5c 是一个宽字符,也就是縗’,也就是说:%df\’ = %df%5c%27=縗’。