深入分析Citrix ADC远程代码执行(RCE)漏洞CVE-2019-19781
2020-03-18 14:00:36 Author: www.freebuf.com(查看原文) 阅读量:652 收藏

2019年年底,Citrix ADC和Citrix Gateway被曝出存在远程代码执行的高危漏洞CVE-2019-19781,该漏洞最吸引人的地方在于,未授权的攻击者可以利用它入侵控制Citrix设备,并实现进一步的内网资源访问获取。漏洞由安全团队Positive Technologies 和 Paddy Power Betfair发现上报,但在漏洞公告中却未给出过多的漏洞利用方式,因此,这是值得来深入研究的地方。

漏洞分析

虽然在Citrix官方的漏洞公告中未透露出漏洞利用细节,但从其中的漏洞缓解措施中,却侧面揭露了该漏洞的相关类型:

从上述缓解措施信息中,我们判断漏洞可能存在的路径为/vpns/,且有可能是一个目录遍历漏洞。有了这些线索,我们开始在httpd.conf文件中去寻找路径/vpns的定义方法,之后发现NetScaler::Portal::Handler Perl Module (Handler.pm)模块在/vpn/portal/scripts/目录下负责生成了一些方法定义。

/vpn/portal/scripts/下包含了一些脚本文件,由于前期我们判断漏洞起因是目录遍历,因此我们把分析重点放到了那些具备文件写操作的代码路径上。之后,我们在perl模块中的UsersPrefs脚本中发现了以下代码:

sub csd {

my $self = shift;

my $skip_read = shift || "";

# Santity Check

my $cgi = new CGI;

print "Content-type: text/html\n\n";

// Username variable initialized by the NSC_USER HTTP Header

my $username = Encode::decode('utf8', $ENV{'HTTP_NSC_USER'}) || errorpage("Missing NSC_USER header.”); <- MARK THIS

$self->{username} = $username;

...

$self->{session} = %session;

// Constructing the path from the username.

$self->{filename} = NetScaler::Portal::Config::c->{bookmark_dir} . Encode::encode('utf8', $username) . '.xml’;

if($skip_read eq 1) {

return;

}

简言之,上述代码用于从用户相关的NSC_USER HTTP头中构建一条路径,但是在过程中却缺乏任何安全校验措施,结果导致可用任意调用csd方法的脚本去触发目录遍历漏洞。经分析发现,/vpn/portal/scripts/下的所有脚本都会调用csd方法函数,但其中一个脚本文件newbm.pl比较特别:

my $cgi = new CGI;

print "Content-type: text/html\n\n";

my $user = NetScaler::Portal::UserPrefs->new();

my $doc = $user->csd();

...

my $newurl = Encode::decode('utf8', $cgi->param('url'));

my $newtitle = Encode::decode('utf8', $cgi->param('title'));

my $newdesc = Encode::decode('utf8', $cgi->param('desc'));

my $UI_inuse = Encode::decode('utf8', $cgi->param('UI_inuse'));

...

my $newBM = { url => $newurl,

title => $newtitle,

descr => $newdesc,

UI_inuse => $UI_inuse,

};

...

newbm.pl会首先创建一个包含各种参数信息的数组,之后,又会调用filewrite方法把数组中的信息写入到一个XML文件中:

if ($newBM->{url} =~ /^\/){

push @{$doc->{filesystems}->{filesystem}}, $newBM;

} else { # bookmark

push @{$doc->{bookmarks}->{bookmark}}, $newBM;

}

// Writing XML file to disk

$user->filewrite($doc);

按理来说,我们可以通过构造一些写入命令去控制文件路径或XML中内容,但是,在这里一切都不管用。之后,我们看了安全研究者Craig Yong写的《关于CVE-2019-19781你需要知道的》,其中特别提到了可用Perl Template Toolkit模板工具实现漏洞利用。

深入研究后,我们发现可以在XML文件中插入一些特定的指令,如果这些指令被模板引擎解析,就能触发命令执行。以下就是模板引擎解析test.xml后发生命令执行的一个例子:

综上所述,现在我们有了Perl Template Toolkit这种插入指令的文件写操作方法,但还需要一种方法让某个脚本去强制模板发生解析操作。之后,我们在模板的引用代码中发现Handler.pm可以被用来实现模板解析:

其中的变量 $tmplfile是从HTTP Request Path中构建的,而且之后为了处理这个$tmplfile变量文件,还会生成一个新的模板。现在,将我们之前创建的test.xml测试文件放到模板目录下,触发模板解析行为看看:

总结来看,为了成功漏洞利用需要进行以下步骤:

1、通过模板机制发现执行Perl代码的方式(需要绕过);

2、利用目录遍历方式,在模板目录下放入一个经过构造的XML文件;

3、浏览放入的XML文件,触发模板解析,实现XML中的代码执行。

重点在于最后一步的任意代码执行,在原本默认的Citrix配置中,是不能实现代码执行的,只有经过一个特性配置才能实现Perl代码执行。先前考虑到受漏洞影响的Citrix设备数量,且已有多个团队已经把该漏洞武器化,因此我们没有提供针对该漏洞的利用代码,但后续随着漏洞的披露以及多个安全研究者的exploit公开,我们也选择了公布具体的漏洞利用方法。

第一种方法:模板注入可导致的远程代码执行

这里有两种方法可以实现代码执行,第一种也就是目前大多数公开exploit中使用的BLOCK模板注入方法。我们通过在Perl Template Toolkit的GitHub中,发现了这个被其他用户提交的bug,而正是这个bug可被在此进行利用:

bug问题中介绍执行任意perl代码的方法,目前看似所有公开的exploit中都使用了这种方法,实现了上述漏洞中的任意命令执行,我们再来深入看看这种利用方法在漏洞中的具体实现。

根据Perl Template Toolkit的文档描述可知,模板变量是一个特殊变量,且在调用处理过程中会包含一个指向主模板的对象引用,这里的对象类型可以通过print方法打印显示,为Template::Document:

有了这个对象引用之后,就可以用受控参数去调用方法。通过研究其中的类class代码,我们发现可以综合上述特性成功去调用new方法:

# new(\%document)

#

# Creates a new self-contained Template::Document object which

# encapsulates a compiled Perl sub-routine, $block, any additional

# BLOCKs defined within the document ($defblocks, also Perl sub-routines)

# and additional $metadata about the document.

#------------------------------------------------------------------------

sub new {

my ($class, $doc) = @_;

my ($block, $defblocks, $variables, $metadata) = @$doc{ qw( BLOCK DEFBLOCKS VARIABLES METADATA ) };

$defblocks ||= { };

$metadata ||= { };

# evaluate Perl code in $block to create sub-routine reference if necessary

unless (ref $block) {

local $SIG{__WARN__} = \&catch_warnings;

$COMPERR = '';

# DON'T LOOK NOW! - blindly untainting can make you go blind!

$block = each %{ { $block => undef } } if ${^TAINT}; #untaint

$block = eval $block;

return $class->error($@)

unless defined $block;

}

该new方法可以获取BLOCK模板中的参数并调用eval方法,由此实现任意的perl代码执行。此外,我们还发现用另一个方法component ,也可以实现同样的攻击效果。

第二种方法:DATAFILE插件中的命令注入

在对源代码的方法调用安全分析中,我们发现了DATAFILE插件中的以下代码:

sub new {

my ($class, $context, $filename, $params) = @_;

my ($delim, $line, @fields, @data, @results);

my $self = [ ];

local *FD;

local $/ = "\n";

$params ||= { };

$delim = $params->{'delim'} || ':';

$delim = quotemeta($delim);

return $class->fail("No filename specified")

unless $filename;

open(FD, $filename)

|| return $class->fail("$filename: $!");

# first line of file should contain field definitions

while (! $line || $line =~ /^#/) {

$line = <FD>;

chomp $line;

$line =~ s/\r$//;

}sub new {

my ($class, $context, $filename, $params) = @_;

my ($delim, $line, @fields, @data, @results);

my $self = [ ];

local *FD;

local $/ = "\n";

$params ||= { };

$delim = $params->{'delim'} || ':';

$delim = quotemeta($delim);

return $class->fail("No filename specified")

unless $filename;

open(FD, $filename)

|| return $class->fail("$filename: $!");

# first line of file should contain field definitions

while (! $line || $line =~ /^#/) {

$line = <FD>;

chomp $line;

$line =~ s/\r$//;

}

如果你具备perl代码审计经验,就会了解使用上述只有两个参数的open函数来打开文件进行操作是不安全的。如果加上管道命令 | 后,open函数将会把它的其余部分解释为命令调用,加之由于我们可以控制文件名变量$filename,所以可以用这种方法来实现命令执行。

通过构造一个模板文件,在调用了 DATAFILE 插件后,就可导致任意命令执行,如下:

可以看到构造文件中的命令成功创建了预想的/tmp/COMMAND_INJECTION/目录:

该漏洞被相继发现披露后出现了各种exploit,但如Craig Yong在文章中说到的“需要注意的是,某些Payload会导致Citrix NetScaler过度记录错误,直到它填满/var分区”。大家可点此获取Exploit漏洞利用代码。

漏洞修复

目前,Citrix公布了详细的漏洞缓解措施,其中包含了具体的执行步骤,我们建议Citrix ADC用户及时参照该项措施进行修复。

此外,在针对设备发起的外界POST请求中,如果其中涉及到“/vpns/” and “/../”样式的字符串,且之后跟着一个对xml文件的GET请求,那么就可以直接把它判断为漏洞利用的攻击行为了。

*参考来源:mdsec,clouds 编译整理,转载请注明来自 FreeBuf.COM


文章来源: https://www.freebuf.com/vuls/227319.html
如有侵权请联系:admin#unsafe.sh