### 简要描述: phpok4.2.083,刚下的 ### 详细说明: 1.safekey固定,导致加密函数可逆 2.使用固定的safekey加密后发起攻击请求,加密内容在代码中解密,绕过了过滤 /install/index.php中 ``` $content = file_get_contents(ROOT."config.php"); //查找替换 $content = preg_replace('/\$config\["db"\]\["file"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["file"] = "'.$dbconfig['file'].'";',$content); $content = preg_replace('/\$config\["db"\]\["host"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["host"] = "'.$dbconfig['host'].'";',$content); $content = preg_replace('/\$config\["db"\]\["port"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["port"] = "'.$dbconfig['port'].'";',$content); $content = preg_replace('/\$config\["db"\]\["user"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["user"] = "'.$dbconfig['user'].'";',$content); $content = preg_replace('/\$config\["db"\]\["pass"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["pass"] = "'.$dbconfig['pass'].'";',$content); $content =...
### 简要描述: phpok4.2.083,刚下的 ### 详细说明: 1.safekey固定,导致加密函数可逆 2.使用固定的safekey加密后发起攻击请求,加密内容在代码中解密,绕过了过滤 /install/index.php中 ``` $content = file_get_contents(ROOT."config.php"); //查找替换 $content = preg_replace('/\$config\["db"\]\["file"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["file"] = "'.$dbconfig['file'].'";',$content); $content = preg_replace('/\$config\["db"\]\["host"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["host"] = "'.$dbconfig['host'].'";',$content); $content = preg_replace('/\$config\["db"\]\["port"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["port"] = "'.$dbconfig['port'].'";',$content); $content = preg_replace('/\$config\["db"\]\["user"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["user"] = "'.$dbconfig['user'].'";',$content); $content = preg_replace('/\$config\["db"\]\["pass"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["pass"] = "'.$dbconfig['pass'].'";',$content); $content = preg_replace('/\$config\["db"\]\["data"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["data"] = "'.$dbconfig['data'].'";',$content); $content = preg_replace('/\$config\["db"\]\["prefix"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["db"]["prefix"] = "'.$dbconfig['prefix'].'";',$content); $tmpcode = str_rand(rand(10,20)); $content = preg_replace('/\$config\["spam_key"\]\s*=\s*[\'|"][a-zA-Z0-9\-\_]*[\'|"];/isU','$config["spam_key"] = "'.$tmpcode.'";',$content); /*问题所在*/ file_put_contents(ROOT."config.php",$content); ``` 这段的功能是写入安装时的各个配置 注意tmpcode下面一行,正则表达式处/\$config\["spam_key"\]这里去匹配$config["spam_key"] = 然后替代成随机生成的spam_key 但是/config.php中是这样的 ``` $config['db']['cache']['folder'] = ROOT.'data/cache/'; $config['db']['cache']['server'] = 'localhost'; $config['db']['cache']['port'] = 11211; $config['db']['cache']['time'] = 86400; //Memcache限制不能超过30天,我们建议设置86400,一天 //安全密钥生成 //生成公钥时需配合此密钥进行验证 $config['spam_key'] = 'AdCFGHIjk42*$#@9dafd-0='; ?> ``` 注意最后一行,$config['spam_key']这个是单引号,而安装时,正则匹配的双引号,造成匹配失败,后果是key就一直没有变过。固定的。 现在,漏洞触发在这里,$this->u_id 可控 /framework/api/usercp_control.php 问题出在这个文件的构造函数和avatar_f ``` class usercp_control extends phpok_control { private $u_id; //会员ID private $u_name; //会员名字 private $is_client = false;//判断是否客户端 function __construct() { parent::control(); $token = $this->get('token'); if($token) { $info = $this->lib('token')->decode($token); /*无过滤*/ if(!$info || !$info['user_id'] || !$info['user_name']) { $this->json(P_Lang('您还没有登录,请先登录或注册')); } $this->u_id = $info['user_id']; 无过滤 $this->u_name = $info['user_name']; $this->is_client = true; } else { if(!$_SESSION['user_id']) { $this->json(P_Lang('您还没有登录,请先登录或注册')); } $this->u_id = $_SESSION['user_id']; $this->u_name = $_SESSION['user_name']; } } //更新会员头像 function avatar_f() { $data = $this->get('data'); if(!$data) { $this->json(P_Lang('头像图片地址不能为空')); } $pInfo = pathinfo($data); $fileType = strtolower($pInfo['extension']); if(!$fileType || !in_array($fileType,array('jpg','gif','png','jpeg'))) { $this->json(P_Lang('头像图片仅支持jpg,gif,png,jpeg')); } if(!file_exists($this->dir_root.$data)) { $this->json(P_Lang('头像文件不存在')); } //更新会员头像 $this->model('user')->update_avatar($data,$this->u_id); //非接口接入则更新相应的session信息 if(!$this->is_client) { $this->model('user')->update_session($this->u_id); /*$this->u_id 完全可控*/ } $this->json(true); } ``` $info = $this->lib('token')->decode($token);这里由于key固定,这个decode和encode完全是可逆的。 然后下面这里将decode后的字符串赋值给$this->u_id ``` $this->u_id = $info['user_id']; 无过滤 $this->u_name = $info['user_name']; $this->is_client = true; ``` 然后avatar_f中这里$this->u_id带入model层 ``` if(!$this->is_client) { $this->model('user')->update_session($this->u_id); /*$this->u_id 完全可控*/ } ``` 然后api的model在这里 /framework/model/api/user_model.php ``` //更新会员头像 function update_avatar($file,$uid) { $sql = "UPDATE ".$this->db->prefix."user SET avatar='".$file."' WHERE id='".$uid."'"; return $this->db->query($sql); } ``` 如上$this->u_id进来了就执行了 ### 漏洞证明: 这是url调用规则 http://localhost/api.php?c=usercp&f=avatar&token=$sql_injection&data=logo.gif 详细见poc