前言
几年前,PHPWIND出过一个MD5 Padding可实现调用任意管理api方法的问题自己写了个exp, 不过遇到个老gbk(2014)版本没法成功利用, 下载了个GBK版本看了下代码。之前漏洞成因可以查看Reference链接,这里不再细谈。
分析
直接对比一下两个版本关键代码的不同之处。
UTF版本 src/windid/service/user/srv/WindidUserService.php
1 2 3
| public function showFlash($uid, $appId, $appKey, $getHtml = 1) { $time = Pw::getTime(); $key = WindidUtility::appKey($appId, $time, $appKey, array('uid'=>$uid, 'type'=>'flash', 'm'=>'api', 'a'=>'doAvatar', 'c'=>'avatar'), array('uid'=>'undefined'));
|
src/windid/service/base/WindidUtility.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public static function appKey($apiId, $time, $secretkey, $get, $post) { $array = array('windidkey', 'clientid', 'time', '_json', 'jcallback', 'csrf_token', 'Filename', 'Upload', 'token', '__data'); $str = ''; ksort($get); ksort($post); foreach ($get AS $k=>$v) { if (in_array($k, $array)) continue; $str .=$k.$v; } foreach ($post AS $k=>$v) { if (in_array($k, $array)) continue; $str .=$k.$v; }
return md5(md5($apiId.'||'.$secretkey).$time.$str); }
|
GBK版本 src/windid/service/user/srv/WindidUserService.php
1 2 3
| public function showFlash($uid, $appId, $appKey, $getHtml = 1) { $time = Pw::getTime(); $key = WindidUtility::appKey($appId, $time, $appKey, array('uid'=>$uid, 'type'=>'flash'), array('uid'=>'undefined'));
|
src/windid/service/base/WindidUtility.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public static function appKey($apiId, $time, $secretkey, $get, $post) { $array = array('m', 'c', 'a', 'windidkey', 'clientid', 'time', '_json', 'jcallback', 'csrf_token', 'Filename', 'Upload', 'token'); $str = ''; ksort($get); ksort($post); foreach ($get AS $k=>$v) { if (in_array($k, $array)) continue; $str .=$k.$v; } foreach ($post AS $k=>$v) { if (in_array($k, $array)) continue; $str .=$k.$v; } return md5(md5($apiId.'||'.$secretkey).$time.$str); }
|
这里很大的一个区别是在GBK版本中appKey签名方法, 竟然不将m、c、a参数值加入签名中计算, 那么相当于我们能够随意修改这些参数。GBK版本中, showFlash方法中调用appKey方法时也未将m、c、a参数加入到get数组中。
那么相当于我们在访问/index.php?m=profile&c=avatar&_left=avatar
1
| <param name="FlashVars" value="postAction=ra_postAction&redirectURL=/&requestURL=http%3A%2F%2Flocalhost%2Fphpwindgbk%2Fwindid%2Findex.php%3Fm%3Dapi%26c%3Davatar%26a%3DdoAvatar%26uid%3D2%26windidkey%3D7d0cff9b85cc0f62fe062421d1caf067%26time%3D1567155029%26clientid%3D1%26type%3Dflash&avatar=http%3A%2F%2Flocalhost%2Fphpwindgbk%2Fwindid%2Fattachment%2Favatar%2F000%2F00%2F00%2F2.jpg%3Fr%3D66455"/>
|
拿到的windidkey(7d0cff9b85cc0f62fe062421d1caf067)是md5(md5($apiId.’||’.$secretkey).$time.’typeflashuid2uidundefined’)的值, 从FlashVars中可以拿到time和apiId(clintid)所以也就是md5(md5(‘1’.’||’.$secretkey).’1567155029’.’typeflashuid2uidundefined’) uid2这个每个环境都不同,不过uid也输出到了FlashVars中。
接着来看调用管理的api时是如何对签名进行校验的,
src/applications/windidserver/api/controller/OpenBaseController.php
1 2 3 4 5 6 7 8 9 10 11 12
| public function beforeAction($handlerAdapter) { parent::beforeAction($handlerAdapter); $charset = 'utf-8'; $_windidkey = $this->getInput('windidkey', 'get'); $_time = (int)$this->getInput('time', 'get'); $_clientid = (int)$this->getInput('clientid', 'get'); if (!$_time || !$_clientid) $this->output(WindidError::FAIL); $clent = $this->_getAppDs()->getApp($_clientid); if (!$clent) $this->output(WindidError::FAIL); if (WindidUtility::appKey($clent['id'], $_time, $clent['secretkey'], $this->getRequest()->getGet(null), $this->getRequest()->getPost()) != $_windidkey) $this->output(WindidError::FAIL); $time = Pw::getTime();
|
time、clientid都已知, 这里因为m、c、a参数并不会加入签名计算,我们只要构造出typeflashuid2uidundefined就能通过判断进而调用任意Api方法。
如何判断是否为GBK版本
1 2
| <meta charset="GBK" /> <title>本站新帖 - phpwind 9.0 - Powered by phpwind</title>
|
直接查看meta标签的charset属性就能确定。
References
- https://mp.weixin.qq.com/s?__biz=MzA5NzQxOTQ1MA==&mid=2247483676&idx=1&sn=161d456328bdcf71b65a03a3376891dc