1. 漏洞描述
ThinkCMF是一款基於ThinkPHP+MySQL開發的中文內容管理框架,其中X系列基於ThinkPHP 3.2.3開發,最后更新到2.2.2版本。最近剛好在滲透測試項目中遇到這個CMS,便審了下源碼發現多處SQL注入漏洞,在Github給項目方提issues后,提交到CVE官方后很快就拿到了分配的多個編號:CVE-2018-19894、CVE-2018-19895、CVE-2018-19896、CVE-2018-19897、CVE-2018-19898。
2. 影響版本
ThinkCMF X2.2.2(https://github.com/thinkcmf/cmfx)
3. 漏洞分析
3.1 CommentadminController.class.php check、delete方法SQL注入(CVE-2018-19894)
漏洞位於/application/Comment/Controller/CommentadminController.class.php的check、delete方法, 以62行為例,$_POST['ids']參數通過join后,傳遞到where語句中,但並沒有使用where語句的in方法,而是直接拼接到SQL語句中,導致SQL注入。
測試Pyload為
http://127.0.0.1/cmfx/index.php?g=Comment&m=commentadmin&a=check&check=1
POST: ids[]=1&ids[]=2 and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1)
3.2 NavController.class.php中edit_post方法SQL注入(CVE-2018-19895)
跟進`application/Admin/Controller/NavController.class.php`,在文件的173行。`$parentid`直接由`$_POST['parentid']`傳遞進來,隨后被直接拼接到where語句中。
測試Payload為
http://127.0.0.1/cmfx/index.php?g=Admin&m=nav&a=edit_post
POST: parentid=1 and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1)
3.3 SlideController.class.php delete方法SQL注入(CVE-2018-19896)
在application/Admin/Controller/SlideController.class.php的93行,delete方法中,$_POST['ids']通過implode方法變成字符串,隨后直接拼接進入where語句的in子句中。
測試payload:
http://127.0.0.1/cmfx/index.php?g=Admin&m=slide&a=delete
POST: ids[]=1&ids[]=0 and updatexml(1, concat(0x7e,user(),0x7e),1)
3.4 AdminbaseController.class.php中_listorders方法存在SQL注入(CVE-2018-19897)
_listorders方法用於排序,在很多地方被調用。這里以LinkController.class.php中的listorders()為例進行分析,這里主要用做友情鏈接的排序。我們以phpstorm+phpstudy+ xdebug打下斷點,一步步追蹤。測試payload為:
http://127.0.0.1/cmfx/index.php?g=Admin&m=Link&a=listorders
POST: listorders[key][0]=exp&listorders[key][1]=0 and updatexml(1, concat(0x7e,user(),0),1)
首先進入application/Admin/Controller/LinkController.class.php 70行的listorders方法,71行調用父類的_listorders()方法。
跟到application/Admin/Controller/LinkController.class.php 166行的_listorders()方法,$_POST['listorders']為二維數組傳遞給$ids,經過foreach循環,輸入的payload進入$data中,仍然為二維數組,而$data則進入save方法。
跟進到simplewind/Core/Library/Think/Model.class.php 396行的save方法,該方法為thinkphp的核心函數。由於$data不為空,跳過之前的很多判斷直接到452行,$data和$options進入update方法,$data仍然為二維數組不變。
跟進到simplewind/Core/Library/Think/Db/Driver.class.php 893行的update()方法,$data經過parseSet方法后拼接到$sql中。跟到371行parseSet方法的定義,$data經過foreach循環后,$val變為一維數組,$key為鍵值。而當$val為數組並且數組的第一個元素為exp時,$val[1]會和$key直接用等號拼接傳遞到$set,387行數組$set被逗號implode后拼接到SET子句中。
返回到update方法,SET子句被拼接到$sql中,最終執行的sql語句為
UPDATE `cmf_links` SET `listorder`=0 and updatexml(1, concat(0x7e,user(),0),1) WHERE `link_id` = 'key'
3.5 ArticleController.class.php edit_post方法SQL注入(CVE-2018-19898)
ThinkCMF X2.2.2是基於ThinkPHP 3.2.3開發的,ThinkPHP 3.x版本之前被爆出存在bind注入,這個漏洞就是ThinkPHP3.x注入的典型案例。漏洞位於前台文章編輯處,測試payload如下:
http://127.0.0.1/cmfx/index.php?g=portal&m=article&a=edit_post
POST: post[id][0]=bind&post[id][1]=0 and updatexml(1, concat(0x7e,user(),0x7e),1)-- -
在application/Portal/Controller/ArticleController.class.php 182行,輸入的參數通過I("post.post")傳遞到$article;跟進到/simplewind/Core/Common/functions.php的I方法定義,在428行,會調用think_filter方法對參數進行過濾。
由於正則字符中沒有匹配bind,所以導致了后面的注入漏洞,ThinkPHP官方的修復措施就是在此處匹配時加上了bind。接下來進入典型的數據庫更新操作了,$articles為多維數組包含payload。
我們F7繼續跟進,會進入simplewind/Core/Library/Think/Model.class.php的where方法,給$this->options['where']賦值后,返回當前對象。隨后進入simplewind/Core/Library/Think/Model.class.php的save方法,隨后執行到update方法
繼續往下,進入到parseSet方法,可以看到傳遞的參數在進行參數綁定操作,其中時間字符串被賦於占位符0,此處會進行循環操作,將所有的參數進行綁定。
隨后進入parseWhere函數
分析parseWhere后,發現會執行 parseWhereItem方法,當$exp=='bind'的時候即$val[0]=='bind',會對$val[1]進行拼接,仔細看這里會多一個`:`,表示為參數綁定時的占位符。
這里也就理解為什么第二個數組構造的時候需要添加一個數字0,這是由於parseSet方法以及賦值了一個占位符:0,用來替代時間字符串,在隨后的SQL語句執行階段可被用來賦值,否則占位符沒被賦值會因語法問題產生報錯。
隨后可以看到bindValue將:0綁定為時間字符串,實際上這里有三個參數需要綁定,因此第二個數組的首位值可以為0、1、2。
執行時產生XPATH異常報錯,得到我們想要的數據。
4. 修復建議
由於ThinkCMF X系列在2.2.2版本后已經不再更新,建議用戶及時升級到Think CMF5。
轉載:https://anquan.baidu.com/article/490 本文來自百度安全SiemPent Team
歡迎關注,有問題一起學習歡迎留言、評論