漏洞文件:/member/index.php
$M_MODULE='web'; if(@$_GET['m'])$M_MODULE=$_GET['m']; if(@!$_GET['n'])$_GET['n']="user"; if(@!$_GET['c'])$_GET['c']="profile"; if(@!$_GET['a'])$_GET['a']="doindex"; @define('M_NAME', $_GET['n']); @define('M_MODULE', $M_MODULE); @define('M_CLASS', $_GET['c']); @define('M_ACTION', $_GET['a']); require_once '../app/system/entrance.php';
我这里简单的说明一下这里的功能处,这里是为了调用class文件
先跟入entrance
if (!defined('M_MODULE')) { define ('M_MODULE', 'include'); define ('M_CLASS', $_GET['c']); define ('M_ACTION', $_GET['a']); } //µ±Ç°Îļþ¼ÐµØÖ· if(M_TYPE == 'system'){ if(M_MODULE == 'include'){ define ('PATH_OWN_FILE', PATH_APP.M_TYPE.'/'.M_MODULE.'/module/'); }else{ define ('PATH_OWN_FILE', PATH_APP.M_TYPE.'/'.M_MODULE.'/'.M_NAME.'/'); } }else{ define ('PATH_OWN_FILE', PATH_APP.M_TYPE.'/'.M_NAME.'/'.M_MODULE.'/'); define ('PATH_APP_FILE', PATH_APP.M_TYPE.'/'.M_NAME.'/'); } define ('PATH_MODULE_FILE', PATH_APP.'system'.'/'.M_MODULE.'/'); load::module();
这里是链接变量定义常量然后载入module,跟入
public static function module($path = '', $modulename = '', $action = '') { if (!$path) { if (!$path) $path = PATH_OWN_FILE; if (!$modulename) $modulename = M_CLASS; if (!$action) $action = M_ACTION; if (!$action) $action = 'doindex'; } return self::_load_class($path, $modulename, $action); }
对相应的变量进行判断赋值载入_load_class
$classname=str_replace('.class.php', '', $classname); $is_myclass = 0; if(!self::$mclass[$classname]){ if(file_exists($path.$classname.'.class.php')){ require_once $path.$classname.'.class.php';
替换后缀判断class中是否存在classname然后再链接后缀判断是否存在文件,存在则包含
if ($action) { if (!class_exists($classname)) { die($action.' classÿfile is not exists!!!'); } if(self::$mclass[$classname]){ $newclass = self::$mclass[$classname]; }else{ if($is_myclass){ $newclass = new $myclass; }else{ $newclass = new $classname; } self::$mclass[$classname] = $newclass; } if ($action!='new') { if(substr($action, 0, 2) != 'do'){ die($action.' function no permission load!!!'); }
判断action,action非new的时候前两位必须为do也就是只能加载doxx的action
来找一个有问题的class文件
漏洞文件:/app/system/include/module/uploadify.class.php
public function doupfile(){ global $_M; $this->upfile->set_upfile(); $info['savepath'] = $_M['form']['savepath']; $info['format'] = $_M['form']['format']; $info['maxsize'] = $_M['form']['maxsize']; $info['is_rename'] = $_M['form']['is_rename']; $info['is_overwrite'] = $_M['form']['is_overwrite']; $this->set_upload($info); $back = $this->upload($_M['form']['formname']); if($_M['form']['type']==1){ if($back['error']){ $back['error'] = $back['errorcode']; }else{ $backs['path'] = $back['path']; $backs['append'] = 'false'; $back = $backs; } } echo jsonencode($back); }
从这里可以看见是没有任何问题的,先记住$_M[‘form’]后面的内容
然后先跟入set_upload
public function set_upload($info){ global $_M; $this->upfile->set('savepath', $info['savepath']); $this->upfile->set('format', $info['format']); $this->upfile->set('maxsize', $info['maxsize']); $this->upfile->set('is_rename', $info['is_rename']); $this->upfile->set('is_overwrite', $info['is_overwrite']); }
继续跟入set
public function set($name, $value) { if ($value === NULL) { return false; } switch ($name) { case 'savepath': $this->savepath = path_standard(PATH_WEB.'upload/'.$value); break; case 'format': $this->format = $value; break; case 'maxsize': $this->maxsize = min($value*1048576, $this->maxsize); break; case 'is_rename': echo $value; $this->is_rename = $value; break; } }
先说问题所在savepath,当name有savepath的时候则连接value也就是我们的变量值。
那么也就是路径可控0.0所以你懂的了,别问我为什么不继续看后面的上传,因为没意义后缀是白名单效验
【via@Joseph-91RI-team】