代碼審計之TP6任意文件操作漏洞


前言:

審計代碼能力太弱了,每次一看到審計代碼的題目就不知道要如何進行,恰好做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,而$sessionIdcookie中名為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') { return 'hello,' . $name; } } 

除此之外,還需要開啟一下session,刪除/app/middleware.php最后一行的注釋即可

0x04:本地復現

修改好文件之后,進行抓包

注意:要刪除官方修補添加的ctype_alnum函數,否則會實驗不成功

只需要構造PHPSESSID的值即可,值為string且長度為32

\runtime\session查看一下生成的session文件

查看一下內容

a:1:{s:4:"name";s:8:"thinkphp";} 

session的內容經過了序列化操作,只要將session的內容反序列化即可getshell,但前提是后端需要有類似的Session::Set('name',$_POST['i'])代碼才可以利用。

0x05:總結:

  • 在環境開啟Session的條件下,此漏洞經過構造可進行任意文件刪除
  • 在環境開啟Session且Session中的數據可控的情況下,可以通過此操作達到構造webshell的目的

0x06:[GYCTF2020]EasyThinking

用御劍掃發現有源碼文件,下載下來發現是Thinkphp6.0

題目提示是漏洞搜索,那就很容易聯想到TP6任意文件操作漏洞,先觀察一下Store.php文件

vendor/topthink/framework/src/think/session/Store.php 

沒有加上ctype_alnum函數,所以應該是存在這個漏洞的。

在注冊時就會將session文件寫入到\runtime\session中,所以從注冊開始就進行修改,只要滿足32位就可以

寫好之后,發現有搜索功能,可以傳入key值,感覺應該是搜索的內容直接被寫入了SESSION,就先傳入一句話木馬試試

發現上傳木馬成功

使用蟻劍進行連接

一看這種格式的就是需要繞過disable_functions,傳一個phpinfo看看

先嘗試下bypass_disablefunc_via_LD_PRELOAD方法,上傳下文件

這種方法不行就換另外一種常見的,恰好是這題是php7的,可以使用下bypass PHP7.0-7.3 disable_function這個exp,上傳進去訪問后便可獲取到flag

參考博客:

https://blog.csdn.net/zhangchensong168/article/details/104106869
https://paper.seebug.org/1114/
http://www.pdsdt.lovepdsdt.com/index.php/2020/05/15/thinkphp6-getshell/

轉:先知社區 lemon


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM