作者:天融信阿尔法实验室
公众号:https://mp.weixin.qq.com/s/rg8xRmhb0ArRQTW0KajSrQ

环境搭建

虚拟机下载地址:https://www.citrix.com/downloads/citrix-gateway/

然后选择下载NSVPX-ESX-13.0-47.22_nc_64.zip这个文件,该文件是ovf,可以直接用vmware来倒入并打开

选择导入

先后配置 IP地址,子网掩码,的网关地址,然后选择4确认

默认用户名密码都是 nsroot

然后访问之前设置的IP地址,出现以下界面则环境搭建成功

漏洞复现

网上下载exp
下载地址 https://codeload.github.com/jas502n/CVE-2019-19781/zip/master

使用方式

输入要执行的命令

漏洞复现成功

漏洞分析

首先查看我们的请求路径,由于利用了目录遍历漏洞,所以访问的真实uri路径是

/vpns/portal/scripts/newbm.pl

我们去Apache 的配置文件httpd.conf 中看一下路径的配置,这里简单解释下这几项配置

首先介绍一个MOD_PERL技术

MOD_PERL技术将PERL解析器编译到APACHE服务器中一起等待客户端请求。 MOD_PERL技术在APACHE中一共有三种代码运行方式:

  1. PerlRun模式:这个模式主要兼容旧式CGI程序,仅仅使用APACHE中的PERL解析器对代码进行解析不进行缓冲。
  2. Registry模式:这个模式的Perl代码在被访问过以后会被编译成为APACHE API模块并且存储在缓冲之中,为了保证运行APACHE会选择性的产生、不只一个副本在内存中。
  3. APACHE API模式:这个模式在APACHE启动的时候就直接将代码编译驻留在缓冲之中。

配置完Apache和Perl模块后,可以用perlinfo函数查看系统环境相关变量。下图是在虚拟机中跑出来的效果,用的是XAMPP套件的老版本。

实现MOD_PERL技术,就需要我们先编写一个APACHE模块代码

例子

然后再在http.conf里做如下的配置

PerlModule example;
<Location "(service)$">
        SetHandler perl-script
        PerlHandler example
</Location> 

这样当用户访问的时候会被这个APACHE模块处理。

正常情况下我们是无法访问/vpns/portal/这个路径下的任何东西的,因为路径限制我们访问不到,但是恰好这次该系统还有一个目录遍历漏洞,这样我们可以访问的范围就扩大了不少

首先我们攻击的第一步会请求一个uri “/vpn/../vpns/portal/scripts/newbm.pl”

我们首先看一下

这里的Handler模块 我们访问 Citrix ADC VPX 虚拟机的以下路径

可以看到该路径下有以下文件

后缀为.pm的文件 即为Perl Module,也就是 Perl 模块。在这里我们看到了处理请求/vpns/portal的默认木块 Handler.pm

我们打开看一下源码

该模块只有两个函数 error函数没什么好看的,我们重点观察handler函数.

不难发现handler函数中调用了另一个模块UserPrefs模块,调用了UserPrefs的一个new方法

new( ) 方法是对象的构造函数。我们去观察一下UserPrefs的源码

构造函数是类的子程序,它返回与类名相关的一个引用。将类名与引用相结合称为“祝福”一个对象,因为建立该结合的函数名为 bless ( ),其语法为:

bless YeReference [,classname]

YeReference 是对被“祝福”的对象的引用,classname 是可选项,指定对象获取方法的包名,其缺省值为当前包名。既在当前代码中返回一个名为UserPrefs的对象。然后我们调用UserPrefs对象的csd()方法。

我们看一下csd ()方法的实现细节

结合网上的exp我们发现了一个关键的变量

以下是EXP的源码

这个username参数存储的是我们客户端传递来的 请求头中的“NSC_USER”的值。

我们看看exp中是怎么定义这个值的

“NSC_USER”的值中的%cdl值是一个随机值,主要用于写入文件的文件名

至于对"NSC_NONCE"这个请求头的处理,只是做了简单的字符串校验,并不是说校验了用户名和密码,所以第二次请求访问后台生成的XML时,"NSC_NONCE"“NSC_USER”两个请求头的值可以是任意不含特殊字符的字符串,所以该漏洞利用时并不需要提前知道后台登陆密码。

根据上面的源码截图我们看到了username的值,接下来我们看程序是如何处理username这个变量的。

可以看到在第61行代码调用了 fileread函数并将username变量作为参数传入

我们看到这这么一行注释

如果文件不存在或者已删除,则根据username创建一个新的文档。看到这里我们大致明白了这段代码的作用,就是以username变量为依据判断某路径下是否有同名文件的存在,如果文件不存在或者已删除已损毁则以username作为文件名创建一个文件。

那么这个创建的文件存放在哪里我们在源码中查找一下

可以看到 默认生成文件的路径是“/var/vpn/bookmark”, 也就是说正常情况下我们访问,生成的和username同名的文件时在该路径下的。

但是由于生成文件时并未对传入的“NSC_USER”这个头部有任何过滤。其实应该是有的,只不过默认情况下是给注释掉了。由下图的注释可以看出

所以在没有过滤的情况下,程序就以“../../../netscaler/portal/templates/filename” 这样生成的文件就保存在了,我们可以通过目录遍历漏洞访问到的目录下了。

此时我们可以控制,服务端程序在指定目录下创建文件了,但是仅仅这样是不够的,我们还需要将我们的payload一起写入我们的文件中。

我们已经知道了,如果要在指定位置创建文件,就需要在执行时调用UserPrefs对象的csd()方法。以此为依据,再能访问的范围内寻找可以利用的类。我翻看了一下,有不少类中都调用了UserPrefs对象的csd()方法,但是并有可以用来写入payload的点,例如themes.pl,可以调用UserPrefs对象的csd()方法生成文件,但是却无法向文件中写入payload,我们测试一下,先修改一下exp,将第一次请求的文件改成themes.pl

虽然会提示上传失败,但是我们直接从后台来看确实生成了一个同名的xml文档

看一下这个xml文档的内容

很显然这样生成的xml文档里没有任何有价值的或者可以利用的东西

我们经过一段时间查看源码发现了一个有利用价值的perl程序,即请求路径为“/vpns/portal/scripts/newbm.pl”实际物理路径为"/netscaler/portal/scripts/newbm.pl"的newbm模块。通过下图我们可以看到在http.conf中配置 Alias的作用是别名配置

我们看一下newbm.pl的源码

我们从源码中可以看到 newbm.pl 满足了我们所需要的条件

首先调用了UserPrefs对象的csd()方法生成文件

然后用四个变量接受我们从前台POST传入的payload,然后存储到一个哈希中,经测试"title","UI_inuse","descr"均可用来写入payload

最中我们通过POST传入的四个参数,被写入到我们通过UserPrefs对象的csd()方法生成xml文档中了。以下截图就是该文档的最终形态。

我们可以看到,我们要执行的payload语句已经写入 bookmark标签的“title”标签中了。

至此exp的第一次请求结束,所做的事情就是指定在服务端的“/netscaler/portal/templates/”路径下生成一个xml文档,然后向该文档内写入payload。

接下来就是exp执行的第二步

也就是我们要想办法访问到这个xml文档,然后还能够让程序解析并执行我们的payload也就是 [% template.new('BLOCK' = 'print "+ cmd + "') %]这串代码

按照顺序来,我们先考虑如何可以访问这个xml文档。首先第一反应就是去看http.conf文件,生成的xml文档的物理路径是“/netscaler/portal/templates/” 看一看有没有该路径的一个映射地址,这样我们就可以直接访问了,可惜并没有,也就是说像exp第一步直接访问newbm.pl的方式是行不通了。但是此时我们回到一开始的原点,即访问路径“/vpns/portal/”的默认处理模块 Handler.pm

我们观察上面两个截图的代码,我在后台搜过半天并没有发现这个\$r-path_info()方法属于那个模块,不过根据这个if判断 用的是eq来对比切用来对比的是一个具体的文件名称,紧接着就将\$r-path_info()返回的结果赋值给了“tmplfile”变量,接下来很关键,也是该漏洞最中可以访问我们生成的xml文档并解析执行其中payload的根本原因,

“template”变量指向的是Template对象的一个引用,我们可以看到Template->new{}是创建一个Template对象。

这里Template是perl的一个模块,Template Toolkit。

简单介绍一下Template Toolkit,在许多使用Perl进行“模板化”的方法中,Template Toolkit被广泛认为是功能最丰富的工具之一。与其他模板系统一样,模板工具包允许程序员将Perl代码和自定义宏嵌入HTML文档中,以便即时创建自定义文档。但是与其他工具不同,Template Toolkit在生成HTML方面与在生成XML,PDF或任何其他输出格式时一样容易。

看到这里我们应该明白了,Template Toolkit就是perl下的一个功能非常强大的模板引擎,那这么一来,一切就都解释的通了

接下来就在第32行我们调用Template->process()方法,process()调用 该方法来处理指定为第一个参数的模板$input。这可以是文件名,文件句柄(例如GLOB或IO::Handle)或对包含模板文本的文本字符串的引用。可以传递包含模板变量定义的其他哈希引用。

我们的xml文档里写入的payload之所以可以被解析并执行就是因为调用了Template对象的process方法,具体该模板引擎是如何解析xml文档的,牵扯到语法生成树和语义分析限于篇幅原因就不细讲了,感兴趣的朋友可以自己深入去了解学习

这里我们演示一下这个模板引擎解析的效果

更多的关于Template Toolkit这个模板引擎的功能非常强大而且教程网上也有很多,大家可以自行去学习和使用。

至此CVE-2019-19781 Citrix ADC远程代码执行漏洞,分析完毕。


Paper 本文由 Seebug Paper 发布,如需转载请注明来源。本文地址:https://paper.seebug.org/1117/