天天看點

Citrix CVE-2022-27518 漏洞分析

作者:區塊軟體開發

漏洞介紹

Citrix在2022年12月份釋出了CVSS評分9.8的CVE-2022-27518遠端代碼執行漏洞通告,距今已經過去兩個多月了,由于漏洞環境搭建較為複雜,一直沒有相關的分析文章。經過一段時間的diff分析及驗證後,發現漏洞成因在于Citrix netscaler在解析SAML xml時對SignatureValue字段校驗不嚴格導緻了棧溢出。

漏洞影響版本:

  • Citrix ADC and Citrix Gateway 13.0 before 13.0-58.32
  • Citrix ADC and Citrix Gateway 12.1 before 12.1-65.25
  • Citrix ADC 12.1-FIPS before 12.1-55.291
  • Citrix ADC 12.1-NDcPP before 12.1-55.291
CVE-ID Description CWE Affected Products Pre-conditions
CVE-2022-27518 Unauthenticated remote arbitrary code execution CWE-664: Improper Control of a Resource Through its Lifetime Citrix Gateway, Citrix ADC Citrix ADC or Citrix Gateway must be configured as a SAML SP or a SAML IdP

根據披露資訊,隻有當ADC或者Gateway配置為SAML SP(資源提供方伺服器)或者SAML IdP(身份認證伺服器)時,才會受到漏洞影響。

不熟悉SAML協定流程的可以參考[1][2],本文不再詳細闡述,基本的認證流程如下:

Citrix CVE-2022-27518 漏洞分析

漏洞環境搭建

漏洞環境搭建非常複雜(甚至比漏洞分析分析耗時久:( ),在查閱大量資料後,我使用Citrix Gateway作為SAML SP,使用Microsoft Azure作為SAML IDP(可能需要進階賬号)建構了SAML單點登入環境。如果使用虛拟機搭建Citrix SAML服務,需要三台虛拟機,同時比較麻煩的一點是,Citrix的SAML服務隻有鉑金版、企業版才能提供,是以需要相應的進階版本激活碼,可以去閑魚上找一找,好在404師傅們直接把激活流程給hack了(Orz。

具體搭建過程可以參考後面的文章。

配置詳情

配置後的網絡拓補圖如下[3],三台虛拟機在同一内網環境中,分别對應NSIP、MIP(SNIP)、VIP。其中NSIP是Citrix ADC/Gateway裝置的自身IP,用于管理、對NetScaler自身進行正常通路以及在高可用性配置中實作裝置間通信的IP位址;MIP是映射IP,是裝置向後端真實伺服器發送請求包中的源位址。VIP是虛拟伺服器IP,客戶對可以對其直接進行通路,真正響應的請求是其後端的衆多真實伺服器。管理多種流量的一個裝置可配置有多個VIP。

還需要一台域控伺服器用來給Citrix伺服器發放證書。(理論上來說自簽名證書也可以,我直接建構了一個DNS通配符證書,可以參考Create a Wildcard Certificate using MMC in Windows Server 2019 - YouTube)

Citrix CVE-2022-27518 漏洞分析

在我的環境中配置清單如下:

IP位址 域名 用途
Citrix Gateway13.0-52.24 10.0.25.171 NSIP
Citrix Gateway13.0-52.24 10.0.25.172 MIP
Citrix Gateway13.0-52.24 10.0.25.173 gateway.nstest.local VIP
Windows Server 2019 10.0.25.174 ad.nstest.local 域控伺服器,用于給Citrix伺服器發放證書
Windows 11 10.x.x.x 本機Client機器,能夠通路Citrix VIP即可

由于SAML服務需要使用域名進行通路,還需要在本機hosts檔案中新加入一個DNS解析條目

Citrix CVE-2022-27518 漏洞分析

通路方式

當我們通路https://gateway.nstest.local時,浏覽器會自動跳轉到Microsoft的認證界面

Citrix CVE-2022-27518 漏洞分析
Citrix CVE-2022-27518 漏洞分析

輸入使用者名密碼後,會重定向到Gateway的管理界面,到這裡就算搭建成功了。

Citrix CVE-2022-27518 漏洞分析

測試方式

推薦使用BurpSuite的SAMLRaider: SAML2 Burp Extension插件進行滲透測試[4],可以很友善地編碼解碼并修改認證請求包和認證響應包,我們可以設定參數過濾隻用來捕獲SAML認證過程中的SAMLResponse包,這是IDP認證後通過浏覽器發給登入服務的認證響應包,包含了關鍵的身份認證資訊。

Citrix CVE-2022-27518 漏洞分析

如下所示,這個插件可以很友善地修改SAML斷言資訊,還可以進行常用的SAML攻擊。(在Citrix環境下,我測試了所有的這些攻擊,都能夠被Citrix過濾)

Citrix CVE-2022-27518 漏洞分析

定位漏洞程式

在漏洞通告剛釋出時,Citrix官網删除了受漏洞影響版本的上一版本的下載下傳連結,給漏洞diff分析造成了一定困難,而近期Citrix官網放出了距受漏洞影響版本的較近版本12.1-64.17,故重新從diff層面對其分析。

下載下傳Citrix-Gateway-KVM-12.1-64.17(受影響)和Citrix-Gateway-KVM-12.1-65.25(修複版本) 對應的虛拟機鏡像,運作後嘗試通過挂載提取檔案,由于檔案系統不同,此種方法較為複雜。所幸Gateway支援ssh連接配接,可以通過ssh提取出相關檔案。

根據NSA披露的緩解措施[5]判斷很可能是netscaler元件的nsppe檔案出問題,如下圖所示。同時 根據資訊可以推斷很可能是緩沖區溢出類型的漏洞。

Citrix CVE-2022-27518 漏洞分析

漏洞分析

繞過看門狗程序pitboss

Citrix Gateway虛拟機中自帶gdb工具,雖然版本有點低而且缺少很多指令,但也将就能用。當我嘗試用gdb對nsppe程序進行attach時,發現一旦attach該程序,該程序就會自動重新開機,看來是有反調試。

通過檢視dmesg系統日志得知,有一個pitboss程序會接收nsppe程序的心跳包,如果心跳包丢失超過一定門檻值,pitboss程序會向nsppe程序發信号終止掉程序然後重新開機該程序,當gdb對nsppe程序attach時導緻nsppe程序被挂起,pitboss程序接收不到心跳包了也就重新開機nsppe程序,這就導緻無法正常調試nsppe程序。

Citrix CVE-2022-27518 漏洞分析

推測系統應該有自帶的工具可以更改這些政策。找了一下果然發現netscaler目錄下的pb_policy程式可以設定這些政策,忽略程序挂起的指令如下所示:

root@ns# /netscaler/pb_policy -h nothing
Current pitboss policy is 0x29b4 (10676):
        PB_ABRT_CULPRIT | PB_RESTART_CULPRIT | PB_RESTART_SYSTEM | PB_KILL_USER_PROCS | PB_WAIT_CORES | PB_REBOOT_ON_SLOW_WARMSTART | PB_REBOOT_ON_INCOMPLETE_REG

Hung processes will be sent a SIGABRT (PB_ABRT_CULPRIT).
Monitored processes which exit will be restarted up to 5 times, except for
packet engines (PB_RESTART_CULPRIT).
If pitboss decides not to restart some failing process(es) all non-failing
processes will be sent a SIGKILL (PB_KILL_USER_PROCS).

Pitboss will then wait for all core dumps to complete (PB_WAIT_CORES) and then
do a warm restart (if a packet engine failed) and otherwise reboot the system (PB_RESTART_SYSTEM).

If startup failure is detected do nothing.

If warmstart takes too long pitboss will reboot the system (PB_REBOOT_ON_SLOW_WARMSTART).

On incomplete registration of mandatory processes after warmstart pitboss will
reboot the system (PB_REBOOT_ON_INCOMPLETE_REG).

Log messages from pitboss will take the default path.

New pitboss policy is 0x29b0 (10672):
        PB_RESTART_CULPRIT | PB_RESTART_SYSTEM | PB_KILL_USER_PROCS | PB_WAIT_CORES | PB_REBOOT_ON_SLOW_WARMSTART | PB_REBOOT_ON_INCOMPLETE_REG

Hung processes will be ignored.
Monitored processes which exit will be restarted up to 5 times, except for
packet engines (PB_RESTART_CULPRIT).
If pitboss decides not to restart some failing process(es) all non-failing
processes will be sent a SIGKILL (PB_KILL_USER_PROCS).

Pitboss will then wait for all core dumps to complete (PB_WAIT_CORES) and then
do a warm restart (if a packet engine failed) and otherwise reboot the system (PB_RESTART_SYSTEM).

If startup failure is detected do nothing.

If warmstart takes too long pitboss will reboot the system (PB_REBOOT_ON_SLOW_WARMSTART).

On incomplete registration of mandatory processes after warmstart pitboss will
reboot the system (PB_REBOOT_ON_INCOMPLETE_REG).

Log messages from pitboss will take the default path.           

執行指令後就可以愉快的調試nsppe程序了,對freebsd的核心互動機制不太熟悉,是以就沒再詳細分析這種看門狗機制,後面有時間可以研究下怎麼實作的。

diff分析

将Gateway-12.1-64.17和Gateway-12.1-65.25不同版本的nsppe程式導入 IDA 分析,使用 bindiff 插件進行比較,程式較大需要分析較長時間。diff完成後按照相似度排序,可見新版本修改了一些saml相關的函數,此版本還一并修複了更早的一個身份認證繞過漏洞CVE-2022-27510。

Citrix CVE-2022-27518 漏洞分析

逐個分析代碼差異,重點關注邊界條件修改的函數。一通分析後,發現ns_aaa_saml_entity_encode_decode函數比較可疑,這個函數在新版本被改名為ns_aaa_entity_encode_decode,兩者控制流圖差異如下,很明顯的發現新版本多了一條條件判斷路徑。

Citrix CVE-2022-27518 漏洞分析

具體來說,老版本12.1-64.17該函數簡化後的反彙編代碼:

__int64 __fastcall ns_aaa_saml_entity_encode_decode(__int64 a1, __int64 a2, int a3, __int64 a4)
{
  __int64 v5; // rax
  __int64 v6; // rbx
  __int64 v7; // rbx
  int v8; // r9d
  int v9; // r9d
  unsigned __int16 v10; // ax
  unsigned int v11; // eax
  unsigned int v12; // r12d
  __int64 v14; // [rsp+18h] [rbp-58h] BYREF
  __int64 v15[2]; // [rsp+20h] [rbp-50h] BYREF
  int v16; // [rsp+30h] [rbp-40h]
  int v17; // [rsp+34h] [rbp-3Ch]
  int v18; // [rsp+38h] [rbp-38h]
  int v19; // [rsp+3Ch] [rbp-34h]
  int v20; // [rsp+40h] [rbp-30h]

  v15[0] = 0LL;
  v15[1] = a1;
  v16 = a3;
  v17 = a3;
  v18 = 4;
  v19 = 22;
  LOBYTE(v20) = v20 & 0xE0;
  v20 = (32 * ASTR_NOT_REF_COUNTED) | v20 & 0x1F;
  v5 = astr_canonicalize(*(_QWORD *)(*((_QWORD *)cur_as_partition + 2) + 8LL), 5LL, v15, a4, 0LL, 0LL);
  v6 = v5;
  if ( v5 )
  {
    ns_bcopy_(*(_QWORD *)(v5 + 8), a2, *(unsigned int *)(v5 + 16));
    v12 = *(_DWORD *)(v6 + 16);
    astr_destroy(*(_QWORD *)(*((_QWORD *)cur_as_partition + 2) + 8LL), 5LL, v6);
  }
  else
  {
    ......// 日志記錄
    return 0;
  }
  return v12;
}           

新版本12.1-65.25反彙編代碼:

__int64 __fastcall ns_aaa_entity_encode_decode(__int64 a1, __int64 a2, int a3, unsigned int a4, unsigned int a5)
{
  __int64 v7; // rax
  __int64 v8; // r12
  __int64 v9; // rbx
  int v10; // r9d
  int v11; // r9d
  unsigned __int16 v12; // ax
  unsigned int v13; // eax
  unsigned int v14; // ebx
  unsigned int v15; // eax
  __int64 v16; // rbx
  int v17; // r8d
  int v18; // r9d
  int v19; // r8d
  int v20; // r9d
  unsigned __int16 v21; // ax
  unsigned int v22; // eax
  char v24; // [rsp+0h] [rbp-80h]
  char v25; // [rsp+0h] [rbp-80h]
  char v26; // [rsp+0h] [rbp-80h]
  char v27; // [rsp+0h] [rbp-80h]
  __int64 v28; // [rsp+18h] [rbp-68h] BYREF
  __int64 v29[2]; // [rsp+20h] [rbp-60h] BYREF
  int v30; // [rsp+30h] [rbp-50h]
  int v31; // [rsp+34h] [rbp-4Ch]
  int v32; // [rsp+38h] [rbp-48h]
  int v33; // [rsp+3Ch] [rbp-44h]
  int v34; // [rsp+40h] [rbp-40h]

  v29[0] = 0LL;
  v29[1] = a1;
  v30 = a3;
  v31 = a3;
  v32 = 4;
  v33 = 22;
  LOBYTE(v34) = v34 & 0xE0;
  v34 = (32 * ASTR_NOT_REF_COUNTED) | v34 & 0x1F;
  v7 = astr_canonicalize(*(_QWORD *)(*((_QWORD *)cur_as_partition + 2) + 8LL), 5LL, v29, a5, 0LL, 0LL);
  v8 = v7;
  if ( v7 )
  {
    v15 = *(_DWORD *)(v7 + 16);
    if ( v15 <= a4 )
    {
      ns_bcopy_(*(_QWORD *)(v8 + 8), a2, v15);
      v14 = *(_DWORD *)(v8 + 16);
      astr_destroy(*(_QWORD *)(*((_QWORD *)cur_as_partition + 2) + 8LL), 5LL, v8);
    }
    else
    {
      ...... //日志記錄
      astr_destroy(*(_QWORD *)(*((_QWORD *)cur_as_partition + 2) + 8LL), 5LL, v8);
      return 0;
    }
  }

  else
  {
    ...... //日志記錄
    return 0;
  }
  return v14;
}           

可以發現,新版本的ns_aaa_entity_encode_decode函數多了一個a4參數,隻有v15變量小于傳入的a4參數值,才進行後面的ns_bcopy_記憶體複制函數,将astr_canonicalize函數傳回的結構體偏移0x8位置指向的記憶體複制到參數a2指向的記憶體中,複制長度是上面astr_canonicalize函數傳回的結構體偏移0x10的成員。對比下兩個版本的astr_canonicalize函數并無明顯差異。是以繼續分析上層函數,檢視ns_aaa_entity_encode_decode函數的交叉引用,有不少位置調用了該函數。

Citrix CVE-2022-27518 漏洞分析

我們嘗試在該ns_aaa_entity_encode_decode函數打個斷點,通過通路https://gateway.nstest.local看能不能斷下來。

(gdb) b ns_aaa_saml_entity_encode_decode
Breakpoint 1 at 0xbebfb4
(gdb) c
Continuing.
Breakpoint 1, 0x0000000000bebfb4 in ns_aaa_saml_entity_encode_decode ()
(gdb) bt
#0  0x0000000000bebfb4 in ns_aaa_saml_entity_encode_decode ()
#1  0x0000000000bf8356 in ns_aaa_saml_verify_signature ()
#2  0x0000000000c216ca in ns_aaa_saml_process_data ()
#3  0x0000000000c25577 in ns_aaa_process_saml_req ()
#4  0x0000000000c25842 in ns_aaa_saml_auth ()
#5  0x00000000007c6a45 in ns_vpn_process_unauthenticated_request ()
#6  0x000000000080a326 in ns_aaa_cookie_valid ()
#7  0x000000000081ca31 in ns_aaa_client_handler ()
#8  0x0000000001c2e6a1 in nshttp_input ()
#9  0x0000000001c2433b in nshttp_handler ()
#10 0x00000000016e495e in ns_async_restart_http ()
#11 0x0000000000bfaf40 in ns_aaa_saml_canon_resp_handler ()
#12 0x000000000079ab48 in nsaaa_handler ()
#13 0x0000000001c6d149 in nstcp_input ()
#14 0x0000000001c59e0a in handleL4Session ()
#15 0x0000000001c5724f in dispatch_tcp ()
#16 0x00000000010cf0eb in vmpe_intf_loop_rx_proc ()
#17 0x0000000001c55472 in vc_poll ()
#18 0x00000000015b5ae3 in ns_netio ()
#19 0x00000000015baf4b in packet_engine ()
#20 0x00000000019bd9a3 in ns_enter_main ()
#21 0x00000000019c1fe9 in main ()           

成功斷了下來,通過調用棧可以看出來這裡是處理SAML響應的流程,到這裡可以基本判定這裡是漏洞點了。

我們繼續分析新版本上層ns_aaa_saml_verify_signature函數,可以發現傳入的第四個參數(即用來長度比較的參數)是0x800,第二個參數v78(即記憶體複制目的位置的參數)是一個棧變量,棧空間剛好是0x800大小。

Citrix CVE-2022-27518 漏洞分析
Citrix CVE-2022-27518 漏洞分析

而老版本直接傳入了棧變量v78,到這裡棧溢出已經呼之欲出了。

Citrix CVE-2022-27518 漏洞分析

通過逆向分析得知該函數是對SAMLResponse進行簽名驗證,我們在調用ns_bcopy_位置處打個斷點看看複制的源記憶體是什麼資料。

(gdb) b *0xBEC15A
Breakpoint 1 at 0xbec15a
(gdb) c
Continuing.

Breakpoint 1, 0x0000000000bec15a in ns_aaa_saml_entity_encode_decode ()
(gdb) x/10i $rip
0xbec15a <ns_aaa_saml_entity_encode_decode+426>:        
    callq  0x1c5e390 <ns_bcopy_>
0xbec15f <ns_aaa_saml_entity_encode_decode+431>:        mov    0x10(%rbx),%r12d
0xbec163 <ns_aaa_saml_entity_encode_decode+435>:        
    mov    27378486(%rip),%rax        # 0x26084a0 <cur_as_partition>
0xbec16a <ns_aaa_saml_entity_encode_decode+442>:        mov    0x10(%rax),%rax
0xbec16e <ns_aaa_saml_entity_encode_decode+446>:        mov    0x8(%rax),%rdi
0xbec172 <ns_aaa_saml_entity_encode_decode+450>:        mov    %rbx,%rdx
0xbec175 <ns_aaa_saml_entity_encode_decode+453>:        mov    $0x5,%esi
0xbec17a <ns_aaa_saml_entity_encode_decode+458>:        
    callq  0x1b4b2a0 <astr_destroy>
0xbec17f <ns_aaa_saml_entity_encode_decode+463>:        
    jmp    0xbec187 <ns_aaa_saml_entity_encode_decode+471>
0xbec181 <ns_aaa_saml_entity_encode_decode+465>:        mov    $0x0,%r12d
(gdb) x/10gx $rdi
0x1115fe018:    0x5057344157596753      0x356e674e66623269
0x1115fe028:    0x7155336a5a465335      0x2f5a6c7272483653
0x1115fe038:    0x544247674f624d77      0x337a446e39775850
0x1115fe048:    0x654765743451734b      0x30756d4e5a536b4c
0x1115fe058:    0x3461474947764668      0x5a38303835356d64
(gdb) set print elements 0
(gdb) x/s $rdi
0x1115fe018:     "SgYWA4WPi2bfNgn55SFZj3UqS6HrrlZ/wMbOgGBTPXw9nDz3KsQ4teGeLkSZNmu0hFvGIGa4dm55808Zuikx4s1rIbTiuyw1z5VkZGuXLl31mObPvrbowtqoBgaeTfAwImtJrw4g2kQoe35b/Z0AgSlu9/LxKRKTaG1jYk6chGNJpKTBCmEqRWKFtJsPjnB9xkAiYspO1T2AsgR9KAq9+cV93X/ZtPkfutRj4IaI3LcMnDxQ+9Pb75HYBZ9LYVqOPGowGVf/Opz40VU6xyWzRlg45ouEHTFS45xCPCe/eQe3mPjsp/kMGsM2e6611stx3Isu+GMgwDGd5hlRp4lFdQ=="           

複制的源資料是一串字元串,我們前往burp中看一下流量包剛好是<SignatureValue>字段中的資料,是以很自然地想到構造超長字元串替換<SignatureValue>标簽的内容。

Citrix CVE-2022-27518 漏洞分析

前面我們看到v78變量距離棧底部0x890位元組,是以構造如下内容:'A'*0x890+'B'*8+'C'*8放入<SignatureValue>标簽中,然後在ns_aaa_saml_verify_signature函數最後一條ret指令打個斷點

Citrix CVE-2022-27518 漏洞分析

成功驗證了棧溢出漏洞存在

Citrix CVE-2022-27518 漏洞分析

漏洞利用

檢視下nsppe程序的保護機制,沒有canary,棧可執行,程式沒有aslr無需洩露基址,可控棧空間很大,似乎是很容易利用。

但很快就發現事情似乎沒那麼簡單,Citrix接收到html中的SAMLResponse響應後,将響應base64解碼後轉換為xml文本,而根據W3C的标準,以下\x00-\x08?\x0b-\x0c?\x0e-\x1f16進制的字元是不被允許出現在XML檔案中的,即使放在<![CDATA[]]> 中,也不能幸免。

也就是說,我們隻能控制棧變量到傳回位址之間的棧空間,且可控的棧内容不能包含以上字元,是以隻能放入經過編碼的shellcode。而我們的程式高位址都是\x00,也無法在棧中構造ROP鍊,隻有一次覆寫傳回位址低位3位元組的機會。

可以尋找到合适的gadget将控制流轉移到可控棧空間内實作RCE,也可以控制傳回位址到大部分任意函數進行惡意操作。

參考文章

進宮 SAML 2.0 安全

How to Hunt Bugs in SAML; a Methodology - Part I

CitrixADC 四種常見的拓撲模式以及MIP,SNIP的差別

How to Hunt Bugs in SAML; a Methodology - Part II

APT5: Citrix ADC Threat Hunting Guidance

from https://paper.seebug.org/2049/

繼續閱讀