代码审计之Catfish CMS v4.5.7后台作者权限越权两枚+存储型XSS一枚
首先本地搭建環境,我所使用的是Windows PHPstudy集成環境。使用起來非常方便。特別是審計的時候。可以任意切換PHP版本。
本文作者:226safe Team – Poacher
0×01 CMS簡介:
開源免費的PHP內容管理系統,不需要高深專業技術輕松搭建網站,使用簡單 靈活方便 穩定快捷,風格切換 想換就換 適應不同需求。
0×00?正文:
漏洞所在處:/application/admin/controller/Index.php
漏洞所在行:即為以下代碼的內容
兩個越權以及XSS都在同一個方法內。所以直接貼上整個方法的代碼(下文說的行數為本文章所貼代碼的行數,并非實際cms漏洞文件的行數)
<?phppublic function rewrite(){$this->checkUser();$this->checkPermissions(6);if(Request::instance()->has('postId','post')){//驗證輸入內容$rule = ['biaoti' => 'require','neirong' => 'require'];$msg = ['biaoti.require' => Lang::get('The title must be filled in'),'neirong.require' => Lang::get('Article content must be filled out')];$data = ['biaoti' => Request::instance()->post('biaoti'),'neirong' => Request::instance()->post('neirong')];$validate = new Validate($rule, $msg);if(!$validate->check($data)){$this->error($validate->getError());//驗證錯誤輸出return false;}$neirong = str_replace('<img','<img class="img-responsive"',Request::instance()->post('neirong'));$guanjianci = str_replace(',',',',Request::instance()->post('guanjianci'));$data = ['post_keywords' => htmlspecialchars($guanjianci), 'post_source' => htmlspecialchars(Request::instance()->post('laiyuan')), 'post_content' => $neirong, 'post_title' => htmlspecialchars(Request::instance()->post('biaoti')), 'post_excerpt' => htmlspecialchars(Request::instance()->post('zhaiyao')), 'comment_status' => Request::instance()->post('pinglun'), 'post_modified' => date("Y-m-d H:i:s"), 'post_type' => Request::instance()->post('xingshi'), 'thumbnail' => Request::instance()->post('suolvetu'), 'istop' => Request::instance()->post('zhiding'), 'recommended' => Request::instance()->post('tuijian')];Db::name('posts')->where('id', Request::instance()->post('postId'))->update($data);Db::name('term_relationships')->where('object_id',Request::instance()->post('postId'))->delete();// var_dump(Db::name('term_relationships')->getLastsql());exit;if(isset($_POST['fenlei']) && is_array($_POST['fenlei'])){$data = [];foreach($_POST['fenlei'] as $key => $val){$data[] = ['object_id' => Request::instance()->post('postId'), 'term_id' => $val];}Db::name('term_relationships')->insertAll($data);}Hook::add('rewrite_post',$this->plugins);$params = ['id' => Request::instance()->post('postId')];Hook::listen('rewrite_post',$params,$this->ccc);Hook::add('rewrite_post_later',$this->plugins);Hook::listen('rewrite_post_later',$params,$this->ccc);}$classify = Db::name('term_relationships')->field('term_id')->where('object_id',Request::instance()->get('art'))->select();$fenlei =$this->getfenlei();foreach($fenlei as $key => $val){$fenlei[$key]['classify'] = 0;foreach($classify as $cval){if($val['id'] == $cval['term_id']){$fenlei[$key]['classify'] = 1;break;}}}$wzid = 0;if(Request::instance()->has('postId','post')){$wzid = Request::instance()->post('postId');}elseif(Request::instance()->has('art','get')){$wzid = Request::instance()->get('art');}$data = Db::name('posts')->where('id',$wzid)->select();$data[0]['post_content'] = str_replace('<img class="img-responsive"','<img',$data[0]['post_content']);$this->assign('data', $data[0]);$this->writeAlias($wzid);$this->switchEditor();$this->assign('backstageMenu', 'neirong');$this->assign('option', 'articles');$this->assign('fenlei', $fenlei);return $this->view();}我大概講一下這兩個越權所在的地方。
第一處:在點擊編輯文章的時候將文章內容查詢出來并且遍歷在頁面中。
第二處:點擊提交保存時候將修改的文章內容保存。
首先我們先來看第一處。我們先看代碼的第六行(很明顯的可以看出來是使用THINKPHP5開發的)。
這里是先判斷是否存在postId請求參數并且請求類型為POST。就是說如果是以POST方式將postId傳了過來的話就進入真區間。
看上述代碼得知7-52行是進行的編輯修改操作。由于我們這里先說第一處。第一處的請求是為GET。所以我們需要繼續往下走。從第53行開始處理的是正常get請求的一些操作。
從69行開始看。如果是get方式并且art值存在的話。那么$wzid則被賦值為get傳過來的art值。接下來走到73行。我們可以清楚的看到。該行代碼的where條件僅是id等于$wzid值即可查詢。并沒有進行所屬用戶的權限判斷。上面得知我們$wzid的值是可控的。所以我們只要修改art值即可獲取其他用戶的文章內容。
以下面為例,test002有一篇文章id為6。
直接將art=4改成art=6
來查看一下所執行的SQL語句。
這樣就可以非常明顯的看出來了。并沒有判斷該文章所屬用戶。
?
接下來,我們看第二處。第二處跟第一處原理差不多。所以我也就不多講了。直接定位到30行代碼。
Db::name(‘posts’)->where(‘id’, Request::instance()->post(‘postId’))->update($data);
繼續瞄準where條件。條件直接就是id等于傳過來的postId值。在我們修改的時候。直接抓包。將POST包里面的postId修改成任意一篇文章的id。即可越權修改。
、
直接改成我們剛才的那篇文章id為6
修改成功。
?
存儲型XSS:
我們看到29行。即為以下代碼:
$data = ['post_keywords' => htmlspecialchars($guanjianci), 'post_source' => htmlspecialchars(Request::instance()->post('laiyuan')), 'post_content' => $neirong, 'post_title' => htmlspecialchars(Request::instance()->post('biaoti')), 'post_excerpt' => htmlspecialchars(Request::instance()->post('zhaiyao')), 'comment_status' => Request::instance()->post('pinglun'), 'post_modified' => date("Y-m-d H:i:s"), 'post_type' => Request::instance()->post('xingshi'), 'thumbnail' => Request::instance()->post('suolvetu'), 'istop' => Request::instance()->post('zhiding'), 'recommended' => Request::instance()->post('tuijian')];我們可以看到在這里的。能夠給用戶看到的參數? ? 該過濾的已經過濾了。正常情況下呢。是不會存在xss了。但是呢。開發者可能忽略了一個地方。就是:’thumbnail’ => Request::instance()->post(‘suolvetu’)。這里呢。是獲取的圖片縮略圖內容。如果上傳了圖片則有值。否則就為空。但是呢。由于前面并沒有對該值做限制。所以呢這里我們是可以任意的構造POST參數的。首先我們先定位到POST這個方法來看看。
<?php/*** 設置獲取獲取POST參數* [url=home.php?mod=space&uid=65943]@Access[/url] public* @param string??????? $name 變量名* @param mixed???????? $default 默認值* @param string|array? $filter 過濾方法* [url=home.php?mod=space&uid=126298]@return[/url] mixed*/public function post($name = '', $default = null, $filter = null){if (empty($this->post)) {$this->post = $_POST;}if (is_array($name)) {$this->param?????? = [];return $this->post = array_merge($this->post, $name);}return $this->input($this->post, $name, $default, $filter);}對應這個方法。如果單單傳一個參數進來。那么將不會進行過濾的。我們可以看到。$filter默認是等于null的,如果$filter不傳的話則為null,所以我們這里可以直接插入xss代碼。只要在POST參數插入:&suolvetu=”/><script>alert(‘xss’)</script> 即可
我們使用管理員進入后臺。看看是否觸發。
成功觸發鳥。
PS:本著交流分享。如果有好的方法或者思路以及上文講述不正確的地方歡迎指出。謝謝!
總結
以上是生活随笔為你收集整理的代码审计之Catfish CMS v4.5.7后台作者权限越权两枚+存储型XSS一枚的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [POJ2184] Cow Exhibi
- 下一篇: CentOS修改yum源为阿里云