0x00
前言
今天下午做了一道反序列化,彻底刷新了我对php
的认知。这里记录一下吧。
0x01
源码
不知道是什么比赛的题目,不过无所谓了,快乐就好了。
1 |
|
这里因为考虑的时间太长了,导致环境关闭了,所以自己在本地测试了。测试代码和环境如下:
flag.php
1 |
|
测试环境
:Windows + PHP5.6 + Apache
0x02
分析
2.1 noclass
首先题目的名称是noclass
。而本题也确实没有class
。那么应该就是寻找php
的原生类了。这个我们在极光的第二周周练的时候找过,但是肯定不是那么简单。不过首先我们要搞懂为什么我们要使用原生类。我们考虑下面的一段代码:
1 |
|
明明是可以直接反序列化的,那么我们为什么还要找原生类呢?考虑下面的一段代码:
1 |
|
除去报错可以看到,我们的属性a
的值并没有改变。这也就是为什么我们需要原生类的理由。匿名类的属性是无法被赋值的。
2.2 Exception
这个类相信大家都不陌生。这就是常见的php
反序列化中的使用__toString
方法进行XSS
的。这里我们并不需要它的__toString
方法。我们只需要它可以修改c
属性的值就可以了。参考代码如下:
1 |
|
PS: 其实这里是有个小问题的,不知道大家看出来没有。反正懒得改了。
2.3 Quote
之后在找资料的时候看到了&
这个东西,突然突发奇想,我们直接使用别的变量将其带出来不就完事了吗?我们采用下面的代码:
1 |
|
然后我们就可以将数据带出啦。
1 | http://127.0.0.1/test/flag.php?a=O%3A9%3A%22Exception%22%3A9%3A%7Bs%3A10%3A%22%00%2A%00message%22%3Bs%3A4%3A%221111%22%3Bs%3A17%3A%22%00Exception%00string%22%3Bs%3A0%3A%22%22%3Bs%3A7%3A%22%00%2A%00code%22%3Bi%3A0%3Bs%3A7%3A%22%00%2A%00file%22%3Bs%3A49%3A%22E%3A%5CphpStudy_2014.10.02_XiaZaiBa%5CWWW%5Ctest%5Ctest.php%22%3Bs%3A7%3A%22%00%2A%00line%22%3Bi%3A3%3Bs%3A16%3A%22%00Exception%00trace%22%3Ba%3A0%3A%7B%7Ds%3A19%3A%22%00Exception%00previous%22%3BN%3Bs%3A1%3A%22c%22%3Bs%3A5%3A%22wwwww%22%3Bs%3A1%3A%22m%22%3BR%3A9%3B%7D |
测试结果:
1 |
|
就将flag
带出来了。
2.3 Parameter
虽然我们成功的把flag
带了出来。但是我们本次的目的并不是flag
。而是反序列化中的类型参数。比如上图中我们的参数就是下面的样子。
1 | O:9:"Exception":9:{s:10:"*message";s:4:"1111";s:17:"Exceptionstring";s:0:"";s:7:"*code";i:0;s:7:"*file";s:49:"E:\phpStudy_2014.10.02_XiaZaiBa\WWW\test\test.php";s:7:"*line";i:3;s:16:"Exceptiontrace";a:0:{}s:19:"Exceptionprevious";N;s:1:"c";s:5:"wwwww";s:1:"m";R:9;} |
看到了R
相信好多师傅都蒙了。这就是我们今天重点提的东西:反序列化参数的类型。
我们熟知的参数有下面的几种:
1 | String : s:size:value; |
然后翻阅官方文档之后,发现有这样的解释:
1
2
3
4
5
6 after serializing and unserializing, slice 1 is no longer a reference to the array itself... I have found no way around this problem... even manually modifying the serialized string from
'a:2:{i:0;s:4:"pleh";i:1;a:2:{i:0;s:4:"pleh";i:1;R:3;}}'
to
'a:2:{i:0;s:4:"pleh";i:1;R:1;}'
to force the second slice to be a reference to the first element of the serialization (the array itself), it seemed to work at first glance, but then unreferences it when you alter it again, observe
1 |
|
然后,我们自己测试一下呗。这次把R:1
改为R:2
。
1 |
|
我们可以看到R:1
指的就是Array
本身,而R:2
就是$tmp[1][0]
的值。到这里,相信各位师傅也明白了这些东西。我就不多赘述了。
PS: 除了R
之外,我又发现了一个快乐的东西。官方文档有这样的一段代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 Serializing floating point numbers leads to weird precision offset errors:
echo round(96.670000000000002, 2);
// 96.67
echo serialize(round(96.670000000000002, 2));
// d:96.670000000000002;
echo serialize(96.67);
// d:96.670000000000002;
Not only is this wrong, but it adds a lot of unnecessary bulk to serialized data. Probably better to use json_encode() instead (which apparently is faster than serialize(), anyway).
而我在意的并不是精度,而是d
这个参数,那么下篇文章就让我们去看一下这个参数吧。(挖坑)
0x03
后记
很快乐。终于完成了。
0x04
参考
php官方文档
: