背景介紹:

Citrix Gateway是一種“一體化”網絡設備,它結合了負載均衡器、防火墻、VPN等。國外白帽團隊針對VPN組件進行研究,快速搜索得到了大約50,000個Citrix Gateway實例可公開訪問,因此,即使是像跨站腳本這樣的小問題也會產生潛在的巨大影響。

在研究過程中,他們發現了一個開放的重定向漏洞,該漏洞無需身份驗證即可被利用,如果Citrix Gateway部署在這樣的配置中,甚至還能將其轉移到CRLF注入中,從而導致XSS或潛在的緩存中毒。在這篇文章之前,他們對一百個Citrix Gateway實例進行了快速掃描,發現仍有超過一半的實例未修補。

了解Citrix Gateway:

Citrix Gateway是FreeBSD的衍生產品,包含幾個擴展,包括一個自定義網絡堆棧,應用程序的重要部分(就包括這個網絡堆棧)被打包到一個叫做Netscaler Packet Processing Engine(nsppe)的東西中。

提前不知道這一點使得逆向最初有點令人困惑,因為常用技術無法按預期工作。例如,由于目標是 Web 服務,因此一個簡單的起點是找到正在偵聽端口的進程,然后開始逆向,但是如果我們運行 sockstat (一個類似于FreeBSD的實用程序 netstat ),我們會得到以下結果:

root@ns# sockstat -4 -lUSER     COMMAND    PID   FD PROTO  LOCAL ADDRESS         FOREIGN ADDRESS      nsmonitor nsumond   831   6  tcp4   127.0.0.1:3013        *:*root     snmpd      615   10 tcp4   127.0.0.1:3335        *:*root     snmpd      615   20 udp4   192.168.1.90:161      *:*root     snmpd      615   21 udp4   127.0.0.1:161         *:*root     nscertforg 613   3  tcp4   127.0.0.1:15555       *:*root     aslearn    611   11 tcp4   127.0.0.1:3020        *:*root     iked       607   6  udp4   192.168.1.90:500      *:*root     iked       607   7  udp4   127.0.0.1:500         *:*root     iked       607   8  udp4   192.168.1.90:4500     *:*root     iked       607   9  udp4   127.0.0.1:4500        *:*root     iked       607   10 tcp4   127.0.0.1:8888        *:*root     nsaaad     602   3  tcp4   127.0.0.1:8766        *:*root     nskrb      598   3  tcp4   127.0.0.1:8788        *:*root     php        543   3  tcp4   127.0.0.1:9999        *:*root     imi        529   5  tcp4   *:4001                *:*root     imi        529   6  tcp4   127.0.0.1:3001        *:*root     nsconfigd  516   10 tcp4   *:3010                *:*root     nsclusterd 480   5  tcp4   127.0.0.1:7001        *:*root     nsclusterd 480   6  tcp4   127.0.0.1:7002        *:*root     nsclusterd 480   7  tcp4   127.0.0.1:7003        *:*root     nsclusterd 480   8  udp4   *:*                   *:*root     nsaggregat 478   3  tcp4   127.0.0.1:5555        *:*root     nsmap      476   5  tcp4   127.0.0.1:3014        *:*nobody   httpd      284   4  tcp4   *:80                  *:*nobody   httpd      284   5  tcp4   127.0.0.1:81          *:*nobody   httpd      283   4  tcp4   *:80                  *:*nobody   httpd      283   5  tcp4   127.0.0.1:81          *:*nobody   httpd      282   4  tcp4   *:80                  *:*nobody   httpd      282   5  tcp4   127.0.0.1:81          *:*nobody   httpd      281   4  tcp4   *:80                  *:*nobody   httpd      281   5  tcp4   127.0.0.1:81          *:*nobody   httpd      280   4  tcp4   *:80                  *:*nobody   httpd      280   5  tcp4   127.0.0.1:81          *:*root     sshd       213   4  tcp4   *:22                  *:*root     httpd      207   4  tcp4   *:80                  *:*root     httpd      207   5  tcp4   127.0.0.1:81          *:*root     syslogd    197   6  udp4   127.0.0.1:514         *:*

請注意,我們正在尋找的 Citrix Gateway 服務是在端口 443 上訪問的,該端口根本沒有出現在上面的列表中,由于 Citrix Gateway 使用了自己的網絡堆棧,因此它不一定會像普通 FreeBSD 安裝那樣填充 sockstat 等工具使用的結構。

通過查看文檔和在線搜索,確實發現如果退出 bash shell并進入提供的Citrix Gateway命令shell,可以運行一些命令來獲得其它信息。

> show ns connectiontable -ListenNAME       IP                                       PORT    SVCTYPE      Traffic Domain INTERNAL   127.0.0.1                                0       ROUTE        0              INTERNAL   192.168.1.90                             0       TCP          0              INTERNAL   192.168.1.90                             0       ANY          0              INTERNAL   fe80::20c:29ff:feae:24c2                 0       TCP          0              INTERNAL   fe80::20c:29ff:feae:24c2                 0       ANY          0              INTERNAL   ::1                                      0       ROUTE        0              INTERNAL   0.0.0.0                                  520     RIP          0              INTERNAL   127.0.0.1                                5000    RPCSVR       0              INTERNAL   192.168.1.90                             520     RIP          0              INTERNAL   192.168.1.90                             21      FTP          0              INTERNAL   fe80::20c:29ff:feae:24c2                 21      FTP          0              INTERNAL   192.168.1.90                             161     SNMP         0              INTERNAL   fe80::20c:29ff:feae:24c2                 4001    TCP          0              INTERNAL   fe80::20c:29ff:feae:24c2                 161     SNMP         0              INTERNAL   192.168.1.90                             179     ANY          0              ns_int_ulf 127.0.0.2                                5557                 0              ns_int_tcp 127.0.0.2                                53      DNS_TCP      0              ns_int_nam 127.0.0.2                                53      DNS          0              INTERNAL   192.168.1.91                             443     UNKNOWN      0              Gateway    192.168.1.91                             443     SSL          0              nshttps-12 192.168.1.90                             443     SSL          0

然而,這仍然沒有獲得太多線索,在該階段,團隊感到有點失落,由于沒有太多其它事情可做,他們便開始搜索有關Citrix Gateway和NetScaler操作系統體系結構的手冊或文檔,以嘗試了解該設備的工作原理,在瀏覽了許多泄露的幻燈片和非官方的博客文章后,他們對Citrix Gateway的工作原理有了大致的了解,至少明確知道審計 nsppe 是入手的位置。

在 nsppe 二進制文件中與網絡堆棧捆綁在一起的網關組件的一個有趣的怪癖是,任何調試都必須通過控制臺完成,你可以想想為什么會出現這種情況?在SSH會話中設置斷點會切斷連接,因為在進程暫停時,不會再有支持SSH會話的數據包處理發生,因此,大部分的分析是通過閱讀代碼而不是通過交互式調試進行的。

Ghidra 的枚舉冒險:

團隊在 netscaler/nsppe 上找到了該二進制文件,它大得令人沮喪,有 42MB,他們使用 Ghidra 對其進行反編譯,但不幸的是,許多關鍵函數的反編譯失敗了,在將“編輯”->“工具選項”->“反編譯器”下的反編譯器資源增加以下內容后,他們取得了更大的成功。

  • Cache Size (Functions): 2048
  • Decompiler Max-Payload (Mbytes): 512
  • Decompiler Timeout (seconds): 900
  • Max Instructions per Function: 3000000

一小時左右后,一切被成功反編譯,將每個函數保存到一個.c文件后,獲得了約300MB的代碼可以審核!確實有點多,但至少現在可以開始進一步審核了。

他們繼續對代碼進行 grep 查找任何類似于 URL 的字符串(ASCII 文本,用斜杠分隔),他們將其與瀏覽自己的 Citrix Gateway 實例和在線實例結合起來,因為不同的配置和版本會產生略有不同的登錄頁面,最后,他們還閱讀了 Citrix 的配置文檔,結合這三種手段,他們獲得了一個可供枚舉且大小適中的端點列表。

他們瀏覽了列表中的每一個端點,并嘗試用Burp探測它,看看Citrix Gateway是否真的會響應,如果有響應,那么又是如何響應的,有些端點起作用了,有些卻沒有,他們在代碼中搜索了所訪問的端點引用,或者在響應中看到的字符串。例如,如果一個端點重定向到/vpn/tmlogout.html,那么就搜索代碼中的/vpn/tmlogout.html

他們最終發現的一個功能是 ns_vpn_process_unauthenticated_request ,正如從名稱中猜到的那樣,許多端點都將帶回到這個函數,事實上,它被命名為“unauthenticated”是令人興奮的,因為團隊正是要尋找此類預身份驗證漏洞。

到目前為止,一直面臨的一個大問題是,他們并不清楚路徑路由是如何執行的,可以在二進制文件中看到很多 URL,但大多數都在日志消息或響應中,例如包含 URL 的 XML 響應的硬編碼字符串,在深入研究 ns_vpn_process_unauthenticated_request 時,他們意識到 Ghidra 未能識別許多編譯器優化的字符串比較,在 ns_vpn_process_unauthenticated_request 中,他們發現了以下模式的許多實例。

if ((((ulong)*(undefined8 *******)pBVar3 | 0x2020202020202020) == 0x687475612f666e2f) &&   (((ulong)(pBVar3->RR).d | 0x2020202020202020) == 0x657774726174732f)) {  bVar48 = (*(ulong *)&(pBVar3->RR).top | 0x2020202020202020) != 0x6f642e7765697662;}if (!bVar48) {  uVar37 = ns_aaa_start_webview_for_authv3((long)local_50,(long)local_48);  pBVar39 = (BN_MONT_CTX *)(uVar37 & 0xffffffff);  goto LAB_0073f539;}

一個帶有幾個比較的if語句,其中每個字節都與0x20進行異或,然后是某種響應處理程序,在上面的例子中,ns_aaa_start_webview_for_authv3停在十六進制比較值上的Ghidra會幫助顯示char[]表示,對于上述情況,有以下三個值。

0x687475612f666e2f -> htua/fn/0x657774726174732f -> ewtrats/0x6f642e7765697662 -> od.weivb

如果進行字節反轉,就會得到:

0x687475612f666e2f -> /nf/auth0x657774726174732f -> /startwe0x6f642e7765697662 -> bview.do

終于弄清楚了路徑路由是如何執行的,以及為什么它很難被搜索到,編譯器將這些短字符串比較內聯到幾個相等性檢查中,而不是調用單獨的函數, | 0x20 模式是一種將輸入小寫的小技巧,小寫ASCII字母比大寫字母提前32字節(0x20),與 0x20 進行“或”運算是將任何大寫字母轉換為小寫的簡單方法,同時可以保持現有的小寫字母相同。

查找跨站點腳本:

接下來就能夠枚舉發現更多未經身份驗證的端點,不用做太多的搜索來找出處理它們的代碼在哪里,比如發現一個端點是 /oauth/idp/logout ,那么查看路由代碼,在ns_vpn_process_unauthenticated_request 中就有以下內容 :

if (((((ulong)*(undefined8 *******)pBVar3 | 0x2020202020202020) == 0x692f687475616f2f)    && (((ulong)(pBVar3->RR).d | 0x2020202020202020) == 0x756f676f6c2f7064)) &&   ((*(byte *)&(pBVar3->RR).top | 0x20) == 0x74)) {  uVar37 = ns_aaa_oauth_fetch_logout_url(0,(long)pBVar3,(uint)uVar8);  vpn_location_url_len = (int)uVar37;  if (vpn_location_url_len < 1) {    vpn_location_url = "/vpn/tmlogout.html";    vpn_location_url_len = 0x12;    uVar14 = 0x880002;  }  else {    vpn_location_url = (char *)tmpbuf512;    uVar14 = 0x880002;  }  goto LAB_0073da38;}

/vpn/tmlogout.html 就是一直看到 Location 的響應頭,在上面的代碼中,它看起來像是默認值,被設置為 tmpbuf512 ,深入研究反編譯輸出, ns_aaa_oauth_fetch_logout_url 中發現了以下有用的日志消息,missing post_logout_redirect_uri 的部分代碼引起了團隊的注意。

...if (0x484 < uVar2) {    __format = "%s : OauthIDP logout request failed to extract redirect URI: missing post_logout_redirect_uri  %.*s for %.*s";LAB_0061f124:    uVar2 = snprintf(large_auditlog_message,0x3fff,__format,"ns_aaa_oauth_fetch_logout_url",                 (ulong)param_3,param_2,uVar3,lVar5);    goto LAB_0061f13e;}...

在Burp中使用這個端點時,確定了查詢參數 post_logout_redirect_uri ,考慮到參數的名稱以及對該函數的了解,它似乎是一個很好的開放重定向漏洞的候選者。

正如分析中看到的那樣,仔細檢查CRLF注入的參數是明智的,而且也能夠將該轉換為反射型XSS攻擊,通過在開始處插入兩個換行符( %0d%0a%0d%0a )來提前結束HTTP頭就能夠插入HTML內容。

GET /oauth/idp/logout?post_logout_redirect_uri=%0d%0a%0d%0aalert(document.cookie) HTTP/1.1Host: 192.168.1.91
HTTP/1.1 302 Object MovedLocation: 
alert(1)X-Content-Type-Options: nosniffX-XSS-Protection: 1; mode=blockConnection: closeContent-Length: 0Cache-control: no-cache, no-store, must-revalidatePragma: no-cacheContent-Type: text/html; charset=utf-8

在Chrome中,會直接彈窗,但在Firefox處理空白Location 頭的方式略有不同。Firefox的Payload是:

 ws://localhost/x%0D%0A%0D%0Aalert(1) 

漏洞影響:

利用該漏洞,可以將用戶輕松重定向到 Citrix Gateway 的釣魚頁面,以竊取用戶憑據,或者可以在受害者的瀏覽器中執行任意JavaScript,Citrix Gateway似乎對會話Cookie相當寬松, HttpOnly 很少設置,因此,竊取會話Cookie 也非常簡單,如下所示。