前言:
審計代碼能力太弱了,每次一看到審計代碼的題目就不知道要如何進行,恰好做Web題的時候發現了一個有關ThinkPHP6.0漏洞的題目,趁這個機會也練習一下。
0x00:漏洞介紹
2020年1月10日,ThinkPHP團隊發布一個補丁更新,修復了一處由不安全的
SessionId導致的任意文件操作漏洞。該漏洞允許攻擊者在目標環境啟用session的條件下創建任意文件以及刪除任意文件,在特定情況下還可以getshell。
具體受影響版本為ThinkPHP6.0.0-6.0.1。
0x01:環境准備
Windows:Apache + Thinkphp(6.0.0) + php7.2.10
先安裝composer工具,我這里使用的是phpstudy,在php目錄找到一個php7版本的即可,安裝好之后
composer create-project topthink/think tp 6.0.0 #tp是文件夾命名 #6.0.0是版本號
進入安裝目錄,執行命令
php think run #會開啟一個臨時的開發環境的服務器,默認運行在localhost:8000
0x02:漏洞分析
根據官方Github上提供的
修改的文件是
src/think/session/Store.php
先來看一下,添加ctype_alnum這個函數有什么作用
這個函數是用來檢測輸入的
$text中所有的字符全部是字母和(或者)數字,返回 TRUE 否則返回FALSE
通過官方的修復可以推出來漏洞的成因,主要原因應該是出現在Session中,那下面就來分析一下Session的相關代碼
/vendor/topthink/framework/src/think/session/Store.php #TP6存儲Session文件的目錄
漏洞介紹中說到該漏洞允許攻擊者在目標環境啟用session的條件下可以刪除任意文件和創建任意文件,那就先定位一下關鍵詞delete,既然是任意文件刪除,那么參數就一定是用戶可控的。
含delete關鍵字的一共有三處,可以推測在函數265行的位置很可能是該漏洞的形成位置,因為涉及到了SessionId。而且漏洞介紹中也說了可以任意創建文件,在delete關鍵字上面就有一個write函數且包含有SessionId
跟蹤一下這個write函數,在全局搜索函數中找到了write()和delete()函數的詳細定義
vendor/topthink/framework/src/think/session/driver/File.php
發現有一個writeFile函數,再跟進一下看看
發現了函數file_put_contents(),這里進行了寫入文件操作,根據所含的參數進行反向分析一下,看能否找到可控點:
file_put_contents函數中的兩個參數來源於writeFile($path, $content)函數中
而writeFile這個函數中的參數又是從write()函數的兩個參數$sessID、$sessData中獲取到的
全局搜索一下參數write函數
發現很可疑的$sessionID和$data,進去仔細查看一下,這里就可看出來了,這兩個參數來源是來自Save()函數調用write()函數
而且$sessionID是從getId()函數得到了,可以直接搜這個函數,也可以進行猜測,既然有getId函數,就一定有setId()
兜兜轉轉又回到了官方修補的地方,再繼續審計
$this->id = is_string($id) && strlen($id) === 32 && ctype_alnum($id) ? $id : md5(microtime(true) . session_create_id());
當傳入的參數$id滿足32位的時候,便會將該值設置為$this->id。查找一下setId被調用的地方
vendor/topthink/framework/src/think/middleware/SessionInit.php
簡單分析一下,變量cookieName的值來源於getName函數
找一下屬性name
所以cookieName的值為PHPSESSID,而$sessionId是cookie中名為PHPSESSID的值,因此是攻擊者可控的,從而導致寫入的文件名可控。
既然寫入文件名可控,那么寫入的內容是否可控那?在默認環境下,session的內容vendor/topthink/framework/src/think/session/Store.php:261的變量$data傳入
找一下$data,發現默認為空值,默認環境是不開啟session的
寫入的內容就是創建session使用的內容。但是session的創建是由實際的后端業務邏輯來決定的,而默認環境下並沒有創建session。因此,默認環境下無法做到任意文件寫入。
參考了Is4b3lla3師傅的方法,嘗試復現復現
在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 'lemon'; 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') 
















