php中的字符offset特性
php中的字符串存在一个非常有趣的特性,php中的字符串也可以像数组一样进行取值。
$test = "hello world";echo $test[0];
最后的结果就是h。
但是上述的这种特性有时会有意想不到的效果,看下面这段代码
$mystr = "hello world";echo $mystr["pass"];
上述的代码的输出结果是h.这是为什么呢?其实很简单,和很多其他的语言一样,字符串在php中也像数组一样可以使用下标取值。$mystr["pass"]
中pass会被进行隐性类型转换为0,这样$mystr[0]
的输出结果就是首字母h.
同样地,如果尝试如下的代码:
$mystr = "hello world";echo $mystr["1pass"];
输出结果就是e.因为1pass会被隐性类型转换为1,$mystr[1]
的输出结果就是第二个字母e.
字符特性造成的漏洞
下面这段代码是在在phpspy2006中用于判断登录时所使用的代码。
$admin['check'] = "1";$admin['pass'] = "angel";......if($admin['check'] == "1") {....}
这样的验证逻辑如果利用上述的特性就很容易地就可以被绕过。$admin没有被初始定义为数组类型,那么当我们用字符串提交时phpsyp.php?admin=1abc
时,php会取字符串1xxx的第一位,成功绕过if的条件判断。
上面那段代码是一个代码片段,接下来的这段代码是一段完整的逻辑代码,来自于php4fun中第5题,比较有意思。
<?php# GOAL: overwrite password for admin (id=1)# Try to login as admin# $yourInfo=array( //this is your user data in the db# 'id' => 8,# 'name' => 'jimbo18714',# 'pass' => 'MAYBECHANGED',# 'level' => 1# );require 'db.inc.php';function mres($str){ return mysql_real_escape_string($str);}$userInfo = @unserialize($_GET['userInfo']);$query = 'SELECT * FROM users WHERE id = /'' . mres($userInfo['id']) . '/' AND pass = /'' . mres($userInfo['pass']) . '/';';$result = mysql_query($query);if (!$result || mysql_num_rows($result) < 1) { die('Invalid password!');}$row = mysql_fetch_assoc($result);foreach ($row as $key => $value) { $userInfo[$key] = $value;}$oldPass = @$_GET['oldPass'];$newPass = @$_GET['newPass'];if ($oldPass == $userInfo['pass']) { $userInfo['pass'] = $newPass; $query = 'UPDATE users SET pass = /'' . mres($newPass) . '/' WHERE id = /'' . mres($userInfo['id']) . '/';'; mysql_query($query); echo 'Password Changed.';} else { echo 'Invalid old password entered.';}
这道题目网上也仅仅只是给了一个最终的答案,其中的原理都没有说或者没有说得很详细。其实原理就是上面讲到的php的字符特性。
题目要求很简单就是修改admin的密码,admin的id为1。我们需要思考以下几个问题:
如何在更新的时候将id修改为1新闻热点
疑难解答