小密圈里有人提出的问题,大概代码如下:
看了一下,明显考点是这几行: if ($username === 'admin') {
if ($_SERVER['REMOTE_ADDR'] !== '127.0.0.1') {
die('Permission denied!');
}
}
$result = $mysqli->query("SELECT * FROM z_users where username = '{$username}' and password = '{$password}'");这个if语句嫌疑很大,大概是考我们怎么登陆 本文中利用
可见,我传入的 那么,为什么执行 刚好这段时间有人问我为什么在他的计算机上无法复现,我们来深入研究研究。 编写如下代码: <?php
$mysqli = new mysqli("localhost", "root", "root", "cat");
/* check connection */
if ($mysqli->connect_errno) {
printf("Connect failed: %s\n", $mysqli->connect_error);
exit();
}
$mysqli->query("set names utf8");
$username = addslashes($_GET['username']);
/* Select queries return a resultset */
$sql = "SELECT * FROM `table1` WHERE username='{$username}'";
if ($result = $mysqli->query( $sql )) {
printf("Select returned %d rows.\n", $result->num_rows);
while ($row = $result->fetch_array(MYSQLI_ASSOC))
{
var_dump($row);
}
/* free result set */
$result->close();
} else {
var_dump($mysqli->error);
}
$mysqli->close();然后在数据库 CREATE TABLE `table1` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(255) COLLATE latin1_general_ci NOT NULL,
`password` varchar(255) COLLATE latin1_general_ci NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=latin1 COLLATE=latin1_general_ci;我特地将字符集设置为 插入一个管理员账户: INSERT `table1` VALUES (1, 'admin', 'admin');然后,我们访问
假设我们将 经过0x02中对该Mysql Trick的复现,大概也能猜到原理了。 造成这个Trick的根本原因是, Mysql字段的字符集和php mysqli客户端设置的字符集不相同 。
如上图,在默认情况下,mysql字符集为latin1,而执行了 这就是该Trick的核心,因为这一条语句,导致客户端、服务端的字符集出现了差别。既然有差别,Mysql在执行查询的时候,就涉及到字符集的转换。 2008年鸟哥曾在 博客 中讲解了Mysql字符集:
在我们这个案例中, utf8 --> utf8 --> latin1 最后执行比较 那么,字符集转换为什么会导致 说一下我的想法,虽然我没有深入研究,但我觉得原因应该是, Mysql在转换字符集的时候,将不完整的字符给忽略了 。 举个简单的例子, http://localhost:9090/test.php?username=admin%e4
http://localhost:9090/test.php?username=admin%e4%bd
http://localhost:9090/test.php?username=admin%e4%bd%ac可以发现,前两者都能成功获取到
为什么会抛出错误?原因很简单,因为latin1并不支持汉字,所以utf8汉字转换成latin1时就抛出了错误。 那前两次为什么没有抛出错误?因为前两次输入的编码并不完整,Mysql在进行编码转换时,就将其忽略了。 这个特点也导致,我们查询 0x05 为什么只有部分字符可以使用我在测试这个Trick的时候发现, 我简单fuzz了一下,如果在admin后面加上一个字符,有如下结果:
这就涉及到Mysql编码相关的知识了,先看看 维基百科 吧。 UTF-8编码是变长编码,可能有1~4个字节表示:
然后根据RFC 3629规范,又有一些字节值是不允许出现在UTF-8编码中的:
所以最终,UTF-8第一字节的取值范围是:00-7F、C2-F4,这也是我在admin后面加上80-C1、F5-FF等字符时会抛出错误的原因。 关于所有的UTF-8字符,你可以在这个表中一一看到: http://utf8-chartable.de/unicode-utf8-table.pl 那么,为什么 这又涉及到Mysql中另一个特性: Mysql的utf8其实是阉割版utf-8编码,Mysql中的utf8字符集最长只支持三个字节 , 所以,我们回看前文列出的UTF-8编码第一字节的范围, 三字节时范围是[E0-EF][80-BF][80-BF] 四字节时范围是[F0-F7][80-BF][80-BF][80-BF] F0-F4是四字节才有的,所以我传入 如果你需要Mysql支持四字节的utf-8,可以使用
已经成功得到结果。 本文深入研究了Mysql编码的数个特性,相信看完本文,对于第一章中的CTF题目也没有疑问了。 通过这次研究,我有几个感想:
|
|
声明:文章版权归原作者所有 部分文章转自互联网 如有侵权请联系
[邮箱地址] 删除
|