0x00 前言
MCMS是政府、教育等其他行業常用的CMS,應用廣泛,但是底層的代碼中仍然遺留不少的問題。這篇文章主要針對SQL注入進行審計并探讨如何快速定位SQL注入漏洞,以及其他工具的應用。
MCMS,是完整開源的Java CMS!基于SpringBoot 2架構,前端基于vue、element ui。每兩個月收集issues問題并更新版本,為開發者提供上百套免費模闆,同時提供适用的插件(文章、商城、微信、論壇、會員、評論、支付、積分、工作流、任務排程等…),一套簡單好用的開源系統、一整套優質的開源生态内容體系。銘飛的使命就是降低開發成本提高開發效率,提供全方位的企業級開發解決方案。
0x01 審計環境
Mingsoft MCMS v5.2.8
Mysql 8.0.29
Openjdk 19.0.1
0x02 審計思路
現代做代碼審計的工具市面上已經有很多了,在拿到源碼的時候,第一時間當然是先使用工具進行掃描,節省人工時間。盡管有fortify、奇安信代碼衛士這些工具在,但避免不一些纰漏,仍然需要人工經驗的判斷。
利用自動化代碼審計工具
這裡我們使用 fortify 工具對源碼進行掃描,記錄了我遇到的兩個問題。該 fortify 工具為收費工具,本文不提供破解版的下載下傳方式。
【一一幫助安全學習,所有資源關注我,私信回複“資料”擷取一一】
①網絡安全學習路線
②20份滲透測試電子書
③安全攻防357頁筆記
④50份安全攻防面試指南
⑤安全紅隊滲透工具包
⑥網絡安全必備書籍
⑦100個漏洞實戰案例
⑧安全大廠内部視訊資源
⑨曆年CTF奪旗賽題解析
1.反編譯 jar 包
使用 fortify 導入MCMS 源碼進行代碼審計,但效果往往不如意。因為這一套源碼使用 Maven 作為項目管理工具軟體,其中包括依賴管理,MCMS項目依賴的jar包都是遠端拉取的。其中net.mingsoft.ms-base、ms-basic、ms-mdiy為底層邏輯代碼。拉取的時候是以jar包的形式存在的,而自動化工具不會對jar包進行掃描。
我們可以将 jar 包重命名為 zip,然後解壓就能獲得 class 檔案。但 fortify 同樣不會掃描 class 檔案,我們需要進一步反編譯。這裡我們可以使用 jadx 進行反編譯。
我們可以直接打開 jar 檔案,通過快捷鍵 Control+S 将反編譯後的資源進行儲存。
導出之後就是 java 檔案,是可以被掃描的檔案類型。
2.不掃描 JS 檔案
在掃描代碼的實踐中,我遇到 JS 檔案數量過多情況,導緻整體的掃描進度大幅度拉長,這不是我想要的。
找到 fortify/Core/config 目錄下的 fortify-sca.properties
其中 com.fortify.sca.DefaultFileTypes 一項規定了被掃描檔案的類型。我們把其中的 ,js删掉就可以了。
人工審計SQL注入思路
造成SQL注入一般需要滿足以下兩個條件:
(1)輸入參數内容使用者可控。
(2)直接或間接拼入SQL語句執行。
且在執行SQL語句時有不同的方式:
(1)直接使用 JDBC 的類方法。
針對這種執行SQL語句的方式,我們可以全局搜尋 SELECT、DELETE、UPDATE 等 SQL 關鍵詞或者搜尋 Statement、PreparedStatement方法名稱來定位執行語句的地方。
(2)使用 MyBatis 持久化層作SQL語句執行代理。
MyBatis 持久化層中一般使用 #{} 在底層實作上使用 “?”作為占位符,是預編譯的機制。在實踐過程中,類似order by等不能使用單引号的地方都不可以使用預編譯,轉而使用 ${}直接拼接到SQL語句中。一般這種情況需要手動增加内容的嚴格過濾步驟。是以盡管預編譯很強大但也有用不上的地方,而這些地方就是我們的突破口。
0x03 審計過程
由上面的描述可以知道使用 ${}的地方往往可能存在SQL注入風險,是以我們審計過程中可以直接全局搜尋${}。
1. 底層映射存在注入漏洞引發多個前台注入
原因
在SQL持久化層 IBaseDao.xml 檔案中可以看到綁定id 為 sqlWhere 的 Sql 映射裡使用了 ${} 導緻存在SQL注入的風險。
第一處 GET類型
在 IDictDao.xml 中引入 IBaseDao.xml 映射語句。
在 IDictBiz 這個業務層是繼承了 IBaseBiz 進而有 query 确定傳回類型為 DictEntity。
在控制層 web/DictAction.class 中可以看到這裡請求資料包的資料變成了實體,然後直接傳入 dictBiz.query 中。
我們請求這個接口時,所有傳入的參數與值會别當作 DictEntity,是以這裡直接傳 sqlWhere 即可。
sqlWhere的值為 [{"action":"","field":"extractvalue(0x7e,concat(0x7e,(database())))","el":"eq","model":"contentTitle","name":"文章标題","type":"input","value":"a"}]
第二處 GET 類型
在 IDictDao.xml 中引入 IBaseDao.xml 映射語句。id 為 queryExcludeApp。
在控制層 web/DictAction.class 中可以看到這裡請求資料包的資料變成了實體,然後直接傳入 dictBiz.queryExcludeApp中。
同樣的這裡所有傳入的參數與值會别當作 DictEntity,存在同樣的問題。
第三處 POST類型
在 ICategoryDao.xml 中引入 IBaseDao.xml 映射語句。
在控制層 web/CategoryAction.java 中可以看到這裡請求資料包的資料變成了實體,然後直接傳入categoryBiz.query 中。
這裡的實體類型有了變化,但不妨礙我們傳入 sqlWhere導緻漏洞的執行。
第四處 POST類型
在 IContentDao.xml 中引入 IBaseDao.xml 映射語句。
在控制層 web/ContentAction.java 中可以看到這裡請求資料包的資料變成了實體,然後直接傳入contentBiz.query 中。
隻要引入之後,如果沒有過濾都是存在漏洞的。
2. 背景自定義模型任意SQL語句執行
在持久化層 IBaseDao.xml 中存在一處綁定了 id 為 excuteSql 的 SQL 操作語句。該地方直接執行了傳入 SQL 語句。
持久化層代理 IBaseDao.class 寫好了對應 IBaseDao.xml 的接口
IModelDao.class 繼承了 IBaseDao 确定了類型為 ModelEntity
業務層 IModelBiz.class 定義了一些接口
業務實作層 ModelBizImpl.class 實作了 IModelBiz.class 接口,通過閱讀代碼,可以發現實際上在 importModel 函數裡面使用了 IModelDao.class 中的 excuteSql 方法。
在控制層 ModelAction.class 中 importJson 函數裡調用了 ModelBizImpl.class 的 importModel 函數。
該漏洞産生位置存在背景自定義模型的導入功能處,要使用該功能需要到 https://code.mingsoft.net/ 生成代碼。
建立業務表單 ——> 拖動表單元件 ——> 填寫字段名和預設值 ——> 生成代碼
可以看到生成的自定義模型代碼,我們複制出來将 sql 字段的 value 改成我們自定義的即可。
任意都行沒有過濾和限制,語句的行是通過 split(';')來分割的。
這個其實是MCMS的核心業務,無法避免的使用,是以隻要使用 MCMS 擁有自定義模型的導入功能的權限就可以利用SQL注入擷取資料或者系統權限。
3.校驗參數接口前台SQL注入
因為使用了 mybatis 架構這裡就全局搜使用 $ 進行拼接的,發現在/net/mingsoft/ms-base/2.1.13/ms-base-2.1.13.jar!/net/mingsoft/base/dao/IBaseDao.xml
進一步跟進queryBySQL
檢視對應接口中的實作方法
然後在/net/mingsoft/base/biz/impl/BaseBizImpl.java這裡進行了重寫queryBySQL,然後調用getDao().queryBySQL
然後發現在/net/mingsoft/basic/action/BaseAction.class#validated 驗證的時候進行調用
繼續跟,這時候隻要找到前端路由中能調用validated就可以了,然後發現在/net/mingsoft/ms-mdiy/2.1.13.1/ms-mdiy-2.1.13.1-sources.jar!/net/mingsoft/mdiy/action/PageAction.java#verify
調用了validated方法
尋找路由,通過分析我們這個是個GetMapping 然後參數fieldName、fieldValue、id、idName 随便構造一下,最開始我們看到的key對應的就是前端傳進來的fieldName
http://127.0.0.1:8008/ms/mdiy/page/verify.do?fieldName=1;select/**/if(substring((select/**/database()),1,4)='mcms',sleep(5),1)/**/and/**/1&fieldValue=b&id=c&idName=1
fieldName`是傳入了 `${key}直接拼接到SQL語句導緻SQL注入。
0x04 總結
代碼審計論證了預編譯不是萬能的,否則不會出現這麼多的 SQL 注入漏洞。在不能使用預編譯處理參數值,隻能通過拼接進行操作的地方,除了手工寫過比對危險字元濾函數之外還有什麼方法嗎?我們還可以嚴格要求傳入的參數類型,例如數字的地方将使用者輸入的内容進行強制轉化成 int 不行就報錯處理,這種稱之為表單過濾層。如果我們的代碼體積龐大無法花費大量人力去排查漏洞存在,可以購買安全公司的代碼審計服務和WAF防火牆産品。
0x05 參考
* [https://gitee.com/mingSoft/MCMS/issues/I5OWGU](https://gitee.com/mingSoft/MCMS/issues/I5OWGU)
* [https://gitee.com/mingSoft/MCMS/issues/I5X1U2](https://gitee.com/mingSoft/MCMS/