ホームページ  >  記事  >  バックエンド開発  >  PHPのオフセット文字による脆弱性を回避した結果を詳しく解説

PHPのオフセット文字による脆弱性を回避した結果を詳しく解説

巴扎黑
巴扎黑オリジナル
2017-08-11 13:33:561163ブラウズ

この記事は、PHP の文字オフセット機能に起因するバイパス脆弱性に関する関連情報を主に紹介します。この記事では、脆弱性の発生状況を詳しく紹介するだけでなく、さらに重要なのは、確実な参考となる修復方法も紹介します。学習の価値をすべての人に。それを必要としている友人は、ぜひ下記をご覧ください。

phpの文字オフセット機能

phpの文字列は、配列のように評価することもできる非常に興味深い機能があります。


$test = "hello world";
echo $test[0];

最終結果はhです。

しかし、上記の機能は時々予期せぬ効果をもたらします


$mystr = "hello world";
echo $mystr["pass"];

上記のコードの出力結果はなぜでしょうか?実際、他の多くの言語と同様に、PHP の文字列は配列と同じように添字を使用して値を取得できます。 $mystr["pass"] に渡すと暗黙的に 0 に変換されるため、$mystr[0] の出力結果は頭文字 h になります。

同様に、次のコードを試してみます: $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
# &#39;id&#39; => 8,
# &#39;name&#39; => &#39;jimbo18714&#39;,
# &#39;pass&#39; => &#39;MAYBECHANGED&#39;,
# &#39;level&#39; => 1
# );
require &#39;db.inc.php&#39;;

function mres($str)
{
 return mysql_real_escape_string($str);
}

$userInfo = @unserialize($_GET[&#39;userInfo&#39;]);

$query = &#39;SELECT * FROM users WHERE id = \&#39;&#39; . mres($userInfo[&#39;id&#39;]) . &#39;\&#39; AND pass = \&#39;&#39; . mres($userInfo[&#39;pass&#39;]) . &#39;\&#39;;&#39;;

$result = mysql_query($query);
if (!$result || mysql_num_rows($result) < 1) {
 die(&#39;Invalid password!&#39;);
}

$row = mysql_fetch_assoc($result);
foreach ($row as $key => $value) {
 $userInfo[$key] = $value;
}

$oldPass = @$_GET[&#39;oldPass&#39;];
$newPass = @$_GET[&#39;newPass&#39;];
if ($oldPass == $userInfo[&#39;pass&#39;]) {
 $userInfo[&#39;pass&#39;] = $newPass;
 $query = &#39;UPDATE users SET pass = \&#39;&#39; . mres($newPass) . &#39;\&#39; WHERE id = \&#39;&#39; . mres($userInfo[&#39;id&#39;]) . &#39;\&#39;;&#39;;
 mysql_query($query);
 echo &#39;Password Changed.&#39;;
} else {
 echo &#39;Invalid old password entered.&#39;;
}

这道题目网上也仅仅只是给了一个最终的答案,其中的原理都没有说或者没有说得很详细。其实原理就是上面讲到的php的字符特性。

题目要求很简单就是修改admin的密码,admin的id为1。我们需要思考以下几个问题:

  • 如何在更新的时候将id修改为1

  • $userInfo['pass'] = $newPass;这行代码有什么作用,为什么会在if判断语句中存在这种的代码

想通了这两个问题,那么最终的解决方法也有了。将id为8的用户的密码修改为8,然后传入一个userInfo的字符串‘8',突破查询防护,最后利用$userInfo['pass'] = $newPass将id修改为1。

最终的payload就是;

第一次提交, index.php?userInfo=a:2:{s:2:"id";i:8;s:4:"pass";s:12:"MAYBECHANGED";}&oldPass=MAYBECHANGED&newPass=8,目的是将id为8的用户的密码修改为8

第二次提交,index.php?userInfo=s:1:"8";&oldPass=8&newPass=1,这样序列化$userInfo得到的就是字符串‘8',即$userInfo = ‘8' ,这样数据库查询验证就可以通过。之后的if验证也可以通过,通过这行代码$userInfo['pass'] = $newPass;,由于$newpass的值为1,那么上述代码变为了$userInfo['pass'] = 1; ,$userInfo由于一个字符串类型,最后得到的是$userInfo='1'

rrreee 出力結果は e です。 1pass は暗黙的に 1 に変換されるため、 $mystr[1] の出力結果は 2 番目の文字 e になります。

キャラクターの特性に起因する脆弱性

🎜🎜 phpspy2006ではログイン時に使用するコードを決定するために以下のコードが使用されています。 🎜🎜🎜🎜rrreee🎜上記の機能を使用すると、このような検証ロジックは簡単にバイパスできます。 $admin は最初は配列型として定義されていないため、文字列を指定して phpsyp.php?admin=1abc を送信すると、PHP は文字列 1xxx の最初のビットを取得し、if 条件判定を正常にバイパスします。 。 🎜🎜上記のコードはコード スニペットで、次のコードは完全なロジック コードです。これは php4fun の質問 5 からのもので、非常に興味深いものです。 🎜🎜🎜🎜rrreee🎜この質問はオンライン上で最終的な答えのみが提供されますが、原理は説明されていない、または詳細に説明されていません。実はその原理は、上で述べたPHPの特性にあります。 🎜🎜🎜質問の要件は非常に簡単で、管理者のパスワードを変更するというものです。管理者の ID は 1 です。次の質問について考える必要があります: 🎜
  • 🎜更新中に ID を 1 に変更する方法🎜
  • 🎜$userInfo[ 'pass'] = $newPass;このコード行は何をするのでしょうか? なぜこの種のコードが if 判定ステートメントに存在するのでしょうか?
🎜 これら 2 つの問題を理解した後、最終的な解決策も利用可能です。 ID 8 のユーザーのパスワードを 8 に変更し、userInfo 文字列「8」を渡してクエリ保護を突破し、最後に $userInfo['pass'] = $newPass を使用して変更します。 IDは1です。 🎜🎜🎜最後のペイロードは次のとおりです。🎜🎜🎜最初の送信、index.php?userInfo=a:2:{s:2:"id";i:8;s:4:"pass"; 12:"MAYBECHANGED";}&oldPass=MAYBECHANGED&newPass=8、目的は ID 8 のユーザーのパスワードを 8 に変更することです🎜🎜🎜 2 番目の送信、index.php?userInfo=s :1 :"8";&oldPass=8&newPass=1、このように $userInfo をシリアル化して取得した文字列 '8'、つまり $userInfo = '8' となります。 、データベースクエリ検証 パスするだけです。後続の if 検証も、このコード行 $userInfo['pass'] = $newPass; を通過できます。$newpass の値は 1 であるため、上記のコードは $userInfo[ になります。 'pass'] = 1; ,$userInfo文字列型のため、最終結果は $userInfo='1' となり、最終的に ID を取得できます。更新される 1 ユーザーのパスワードが失われています。 🎜🎜🎜🎜修正方法🎜🎜🎜🎜 この種の脆弱性の修復方法も非常に簡単で、事前にデータ型を定義し、使用する際に使用されるデータ型が予想されるデータ型と一致しているかどうかを確認します。そうしないと、前述のバイパスの問題が発生します。同時に、入力を制御し、入力データをチェックし、不用意に使用しないようにする必要があります。 🎜

以上がPHPのオフセット文字による脆弱性を回避した結果を詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。