這個洞出來也有一段時間了,看了創宇的paper後覺得蠻簡單的,決定自己在本地搭建複現一下,記錄一下學習的過程。
環境準備
apache + thinphp(<=6.0.0版本<=6.0.2) + php7以上
-
ThinkPHP6起隻能使用composer來安裝,安裝composer、php、apache的過程我就不贅述了。
執行指令:
composer create-project topthink/think tp 6.0.0
,其中tp是你的檔案夾命名,6.0.0是版本号,6.0.1也可。
這裡說一個問題,我這個時間Thinkphp的最新版是6.0.2,用上面的指令下載下傳下來framework是6.0.2版本的,我們需要再執行一條指令:
:此時就會把将6.0.0的版本把6.0.2給替換掉composer require topthink/framework:6.0.0
- 進入tp的安裝目錄,執行
,它會開啟一個臨時的開發環境的伺服器,預設運作在php think run
,打開浏覽器通路顯示正常即可localhost:8000
漏洞複現在apache下進行
漏洞分析
漏洞影響的版本:top-think/framework 6.x < 6.0.2
-
官方資訊
ThinkPHP釋出的更新檔聲稱修複了一處由于不安全的SessionId導緻的任意檔案操作漏洞:在開啟Session的情況下可以導緻建立任意檔案以及删除任意檔案,特定情況下可以getshell
- 根據這些資訊,我們到官方GitHub的commit頁面找一下相關的送出記錄:
ThinkPHP6任意檔案操作漏洞分析環境準備漏洞分析本地環境複現
可以看到位于src/think/session/Store.php中212行在設定
id
時增加了一個函數:
ctype_alnum($text)
。
查一下PHP官方手冊,這個函數是用來檢測輸入的
$text
中所有的字元全部是字母和(或者)數字,傳回 TRUE 否則傳回FALSE
根據檔案目錄和更改的函數部分猜測:可能是存儲Session時導緻的檔案寫入;然後跟進找一下相關的函數,可以看到
vendor/topthink/framework/src/think/session/Store.php:254
的save()函數,265行還可以對檔案進行删除操作,并且對後端業務邏輯依賴較低
可以看到設定了$sessionId,并且調用了一個write函數,繼續跟進,找到write()函數
vendor/topthink/framework/src/think/session/driver/File.php:210
繼續跟進,找到writeFile()函數
可以看到調用了
file_put_contents()
函數,這裡是真正寫入檔案的操作了
- 接下來我們反向分析一下,看看能不能找到可控點
- 函數
中參數file_put_contents($path,$content,LOCK_EX)
來源于函數$path,$content
writeFile($path,$data)
- 函數
中參數writeFile($path,$data)
來源于函數$path,$data
write(String $sessionID,String $sessiData)
- 函數
中參數write(String $sessionID,String $sessiData)
來源于$sessionID,$sessiData
中調用了save()
,同時傳入的參數write()
的值是調用$sessionId
getId()
傳入的
綜上:檔案名來源于
$sessionId
- 當傳入的id值長度為32并且…etc時,建立
,然後進行sessionId
gitId()
ThinkPHP6任意檔案操作漏洞分析環境準備漏洞分析本地環境複現 - 接下來找調用
的地方setId()
vendor/topthink/framework/src/think/middleware/SessionInit.php:46
ThinkPHP6任意檔案操作漏洞分析環境準備漏洞分析本地環境複現
其中
cookieName
的值為
PHPSESSID
,而
$sessionId
是
cookie
中名為
PHPSESSID
的值,是以是攻擊者可控的,進而導緻寫入的檔案名可控。
但是預設環境下,
session
的内容由
vendor/topthink/framework/src/think/session/Store.php:261
的變量
$data
傳入:
$data
在預設環境中為空:
/**
* Session資料
* @var array
*/
protected $data = [];
寫入的
session
内容是由實際的後端業務邏輯來決定的,是以說隻有苛刻的條件下才能寫入webshell。并且一開始就說了需要在環境開啟
session
的情況下才可以實作任意檔案操作(預設環境不開啟session)
- 我們在
中增加一些代碼後,如下:app\controller\index.php
<?php
namespace app\controller;
use think\facade\Session;
use app\BaseController;
class Index extends BaseController
{
public function index()
{
Session::set('name','thinkphp');
return 1;
return '<style type="text/css">*{ padding: 0; margin: 0; } div{ padding: 4px 48px;} a{color:#2E5CD5;cursor: pointer;text-decoration: none} a:hover{text-decoration:underline; } body{ background: #fff; font-family: "Century Gothic","Microsoft yahei"; color: #333;font-size:18px;} h1{ font-size: 100px; font-weight: normal; margin-bottom: 12px; } p{ line-height: 1.6em; font-size: 42px }</style><div style="padding: 24px 48px;"> <h1>:) </h1><p> ThinkPHP V6<br/><span style="font-size:30px">13載初心不改 - 你值得信賴的PHP架構</span></p></div><script type="text/javascript" src="https://tajs.qq.com/stats?sId=64890268" charset="UTF-8"></script><script type="text/javascript" src="https://e.topthink.com/Public/static/client.js"></script><think id="eab4b9f840753f8e7"></think>';
}
public function hello($name = 'ThinkPHP6')
{
return 'hello,' . $name;
}
}
忘了說了thinkphp6開啟session的方法:删除最後一行的注釋
/app/middleware.php
![]()
ThinkPHP6任意檔案操作漏洞分析環境準備漏洞分析本地環境複現
本地環境複現
很簡單,隻需要構造PHPSESSID的值即可,值為
string
&&長度為32
此時檢視一下生成的session,生成的session檔案儲存在
\runtime\session
下
session裡的内容:
可以看到session的内容經過了序列化操作,隻要将session的内容反序列化即可getshell
- 如果要getshell的話,後端需要有類似的
代碼才可以利用Session::Set('name',$_POST['i'])
總結:
在複現的過程,也遇到了不少問題:首先ThinkPHP6開始不支援git了,隻能通過composer來操作,由于從來沒用過它也沒經驗,一開始安裝環境一直下載下傳不到舊版本,後來得到師傅的幫助終于下好了ThinkPHP6.0.0的環境,在這裡感謝一下師傅@P1an0對我的幫助。
這個漏洞其實很簡單,就是使用者可控變量導緻的,也沒有對一些資料的過濾等等。需要一定條件才可以利用,也就是開啟session;寫webshell還要看具體的後端業務邏輯等等。我覺得就這個架構來看其實可以更深入的進行挖掘,希望有大佬可以和我一起探讨學習
參考的paper:ThinkPHP6 任意檔案操作漏洞分析