確認版本
首先可以通過接口來確認一下當前禪道的版本。
http://example.com/index.php?mode=getconfig
SQL注入分析
網上之前有過一個9.1.2
的orderBy
函數的分析,但是沒想到9.2.1
也存在此問題,(2018.3.2
號看到目前最新版本是9.8.1
)。
出問題的地方是此文件的orderBy
函數:\lib\base\dao\dao.class.php
對於limit
后未做嚴格的過濾與判斷,然后拼接到了order by
后面導致產生注入.
$order = join(',', $orders) . ' ' . $limit;
看了一下9.8.1
的修補是對limit進行正則限制,但是事實上感覺此處正則是寫了一個bug,比如正常調用orderBy($order)
的時候,其中$order
為abc desc limit 1,1
的時候,進入$limit
則是limit 1,1
,導致匹配失敗。
如果想要造成前台注入(無需登錄)的話,就得先看看禪道開放了哪些接口,看是否有調用orderBy
函數。
\zentao\module\common\model.php
其中的if($module == 'block' and $method == 'main') return true;
,也就是本次漏洞的主角,繼續跟進。
\zentao\module\block\control.php
首先看__construct
中,$this->selfCall
是在驗證referer
的值,如果為真的話則后面的if
將不會進入die
語句里面
接下來跟進main
函數,可以看到最后的$func = 'print' . ucfirst($code) . 'Block';
,會對一些函數進行調用,與此同時,我們搜索orderBy
的調用的時候可以發現printCaseBlock
函數的存在
\zentao\module\block\control.php
所以前台注入的整個過程便比較清晰了,那么如何利用?
SQL注入利用
回過頭來,因為禪道有windows直接的一鍵化安裝程序,其數據庫使用的也是root
權限,導致可直接導出shell,但是如果沒有這么高權限的時候,對於這個注入應該如何出數據。
禪道是支持多語句的,這也為后面的利用提供方便。
注入出數據庫名和表段名后,當我想繼續注入出用戶賬號密碼的時候,意外地發現沒有出數據。
sql = 'select 12345 from zt_user'
還是沒有出數據,猜測是管理員改了表前綴,所以想去通過information_schema
查詢一下表名,但是意外地發現,也不能讀取?難道被刪了?但是我還是想知道一下表前綴。
請求的時候加了一個單引號,並且加上referer,看一下報錯信息。
因為PDO的關系,SQL中的表名是%s
替代的,所以未能夠得到庫名。
那么就利用報錯去得到當前SQl語句里面查詢的表名,比如利用polygon
函數。
此注入點可以理解為limit后的注入點,因為使用多語句的話,報錯效果不明顯,所以就直接在limit后面進行注入。
上圖為本地測試,但是limit的注入和mysql版本還有一些關系,目前網上的payload僅限於低版本才可報錯注入出數據,很不幸運的是,目標使用的是高版本mysql。
那既然可以多語句,在不能用information_schema
的情況下,可以通過下面語法來進行盲注:
show table status where name = 'xxx' and sleep(2)
寫到py里面的payload是這樣的
經過一番折騰發現,表前綴就是默認的zt_
,但是為啥又不能夠讀取到用戶數據呢?
仔細看到禪道里面的orderBy
函數,發現做了過濾。
把下划線給過濾掉了,那這種在多語句下,可以利用mysql的預查詢來繞過,值得注意的是,這個版本語法大小寫敏感。
注入出admin密碼的時候,驚喜地發現不能解開,無奈之下,只能先拿到一個普通賬號。
Getshell
禪道在防止getshell方面還花了一點心思,曾經挖到一個可以任意寫文件getshell(最新版本還存在這段代碼),不過需要的權限是管理員權限。
看了一下禪道里面人員組織架構情況,有研發、項目經理、產品經理,高層管理,系統管理員等角色,其中系統管理員雖然密碼解不開,但是我們可以去解密一下高層管理的密碼,因為這個角色的權限是可以修改某用戶的用戶組權限。在高層管理賬號中,我們可以將一個普通賬號修改為管理員。
接下來就是寫文件Getshell:
/xampp/zentaopro/module/api/control.php
可以看到是進入了call_user_func_array,也就是我們可以任意實例化一個module方法,方法的參數也是可控的,可以通過,
來分割參數。
/zentaopro/module/editor/model.php
在editor中是可以寫一個文件的,filePath可控,fileContent也是可控的,這下就是可以任意寫一個文件。
Exp:
但是問題又來了,前面報錯里面得到的路徑目錄感覺像是做了權限(這里繞彎了,路徑少加了一個www,所以以為是沒權限寫),最終從數據庫中的zt_file
獲取上傳文件的路徑,然后再將shell寫入當中才得以結束。
總結
對於order by
的漏洞如何進行防御的時候,我覺得上面代碼在部分上有可取之處。
1、去掉limit
部分,然后限制格式
2、然后循環對每個字段進行反引號的添加