// 若加载不出来代码高亮插件请手动 F5
最近工作太忙了, 博客都长草了, 拿出来之前 Bloody https://lolimoe.cn/ 发我一个洞分析一下, 水一篇博文.
目前禅知 Pro 1.6.1 已经修复了这个漏洞.
D:phpStudyWWWchanzhipro.full.5.4wwwfile.php
- <?php
- $pathname = '';
- $objectType = '';
- $imageSize = '';
- $extension = '';
- $version = '';
- if(isset($_GET['pathname'])) $pathname = $_GET['pathname'];
- if(isset($_GET['objectType'])) $objectType = $_GET['objectType'];
- if(isset($_GET['imageSize'])) $imageSize = $_GET['imageSize'];
- if(isset($_GET['extension'])) $extension = $_GET['extension'];
- if(isset($_GET['f'])) $pathname = $_GET['f'];
- if(isset($_GET['o'])) $objectType = $_GET['o'];
- if(isset($_GET['s'])) $imageSize = $_GET['s'];
- if(isset($_GET['t'])) $extension = $_GET['t'];
- if(isset($_GET['v'])) $version = $_GET['v'];
- $dataRoot = rtrim(dirname($_SERVER['SCRIPT_FILENAME']), '/') . '/data/';
首先初始化一些配置, 定义我们传进来的参数. $dataRoot 我们可读取文件的路径
- if($objectType == 'source' or $objectType == 'slide')
- {
- if($objectType == 'slide' and !preg_match('/^slides/[0-9_0-9]/', $pathname)) die('The file does not exist!');
- $savePath = $dataRoot;
- }
- else
- {
- if(!preg_match('/^[0-9]{6}/f_[a-z0-9]{32}/', $pathname)) die('The file does not exist!');
- $savePath = $dataRoot . 'upload/';
- }
- $realPath = $savePath . $pathname;
这段代码中我们可控的参数有 $objectType , $pathname
$objectType 传入字符串 source 后 $dataRoot 会赋给 $savePath .
接着走出判断, $realPath 把我们传入的 $pathname 拼接和 $savePath 拼接起来
- if(!file_exists($realPath))
- {
- $realPath = $savePath . (strpos($pathname, '.') === false ? $pathname : substr($pathname, 0, strpos($pathname, '.')));
- }
- $filePath = $realPath;
- if($imageSize == 'smallURL') $filePath = str_replace('f_', 's_', $realPath);
- if($imageSize == 'middleURL') $filePath = str_replace('f_', 'm_', $realPath);
- if($imageSize == 'largeURL') $filePath = str_replace('f_', 'l_', $realPath);
- if(!file_exists($filePath)) $filePath = $realPath;
- if(!file_exists($filePath)) die('The file does not exist!');
- $seconds = 3600 * 24 * 30;
- $expires = gmdate("D, d M Y H:i:s", time() + $seconds) . "GMT";
- header("Expires: $expires");
- header("Pragma: cache");
- header("Cache-Control: max-age=$seconds");
- $mime = getMimetype($extension);
- header("Content-type: $mime");
- $handle = fopen($filePath, "r");
再经过一些没有什么卵用的判断后, 我们的 $realPath 变量又被赋值到 $filePath .
接着往下走到 $mime 这里, 看一下 getMimetype 函数
- function getMimetype($extension)
- {
- $mimeTypes = array(
- 'ez' => 'application/andrew-inset',
- 'anx' => 'application/annodex',
- 'atom' => 'application/atom+xml',
- 'atomcat' => 'application/atomcat+xml',
- 'atomsrv' => 'application/atomserv+xml',
- 'lin' => 'application/bbolin',
- 'cu' => 'application/cu-seeme',
- 'davmount' => 'application/davmount+xml',
- 'dcm' => 'application/dicom',
- 'tsp' => 'application/dsptype',
- 'es' => 'application/ecmascript',
- 'otf' => 'application/font-sfnt',
- 'ttf' => 'application/font-sfnt',
- 'pfr' => 'application/font-tdpfr',
- 'woff' => 'application/font-woff',
- 'spl' => 'application/x-futuresplash',
- 'gz' => 'application/gzip',
- 'hta' => 'application/hta',
- 'jar' => 'application/java-archive',
- 'ser' => 'application/java-serialized-object',
- 'class' => 'application/java-vm',
- 'js' => 'application/javascript',
这个函数只是定义 http mime 头, 这函数太长了, 我只截取了一部分. OK 我们在回到 $mime 这里.
$extension 变量在代码开头被定义为 $_GET['o'] , 那么往下 fopen() 直接读取了我们的路径
$handle = fopen($filePath, "r");
那么最终 payload 为:
http://localhost/www/file.php?pathname=../file.php&o=txt&o=source
漏洞修复
在最新版拼接路径处加入 realpath() 函数去除目录跳跃的操作
并使用 strpos() 判断我们传入的路径 ($realPath) 是否在可读取的路径 ($dataRoot) 中
- <?php
- $pathname = '';
- $objectType = '';
- $imageSize = '';
- $extension = '';
- $version = '';
- if(isset($_GET['pathname'])) $pathname = $_GET['pathname'];
- if(isset($_GET['objectType'])) $objectType = $_GET['objectType'];
- if(isset($_GET['imageSize'])) $imageSize = $_GET['imageSize'];
- if(isset($_GET['extension'])) $extension = $_GET['extension'];
- if(isset($_GET['f'])) $pathname = $_GET['f'];
- if(isset($_GET['o'])) $objectType = $_GET['o'];
- if(isset($_GET['s'])) $imageSize = $_GET['s'];
- if(isset($_GET['t'])) $extension = $_GET['t'];
- if(isset($_GET['v'])) $version = $_GET['v'];
- $dataRoot = rtrim(dirname($_SERVER['SCRIPT_FILENAME']), '/') . '/data/';
- if($objectType == 'source' or $objectType == 'slide')
- {
- if($objectType == 'slide' and !preg_match('/^slides/[0-9_0-9]/', $pathname)) die('The file does not exist!');
- $savePath = $dataRoot;
- }
- else
- {
- if(!preg_match('/^[0-9]{6}/f_[a-z0-9]{32}/', $pathname)) die('The file does not exist!');
- $savePath = $dataRoot . 'upload/';
- }
- $realPath = realpath($savePath . $pathname);
- if(!file_exists($realPath))
- {
- $realPath = realpath($savePath . (strpos($pathname, '.') === false ? $pathname : substr($pathname, 0, strpos($pathname, '.'))));
- }
- if(strpos($realPath, realpath($dataRoot)) === false) die('The file does not exist!');
- $filePath = $realPath;
- if($imageSize == 'smallURL') $filePath = str_replace('f_', 's_', $realPath);
- if($imageSize == 'middleURL') $filePath = str_replace('f_', 'm_', $realPath);
- if($imageSize == 'largeURL') $filePath = str_replace('f_', 'l_', $realPath);
- if(!file_exists($filePath)) $filePath = $realPath;
- if(!file_exists($filePath)) die('The file does not exist!');
- $seconds = 3600 * 24 * 30;
- $expires = gmdate("D, d M Y H:i:s", time() + $seconds) . "GMT";
- header("Expires: $expires");
- header("Pragma: cache");
- header("Cache-Control: max-age=$seconds");
- $mime = getMimetype($extension);
- header("Content-type: $mime");
- $handle = fopen($filePath, "r");
来源: https://www.thinksaas.cn/group/topic/839572/