提供给脚本语言解释器的有趣环境变量

2020-07-14 10:09:02

在最近的一个项目中,我们获得了指定环境变量但不能指定执行的进程的能力。我们也无法控制磁盘上文件的内容,粗暴地强迫进程标识符(PID)和文件描述符找不到任何有趣的结果,从而消除了远程LD_PRELOAD攻击。幸运的是,我们执行了一个脚本语言解释器,它使我们能够通过指定特定的环境变量来执行任意命令。这篇博客文章讨论了当提供恶意环境变量时,如何由一系列脚本语言解释器执行任意命令。

快速阅读perlrun(1)手册页的环境部分会发现许多值得研究的环境变量。PERL5OPT环境变量允许指定命令行选项,但仅限于接受选项CDIMTUWdmtw.不幸的是,这意味着允许提供Perl代码以供运行的-e已用完。

不过,正如Hacker Fantastic对CVE-2016-1531的利用漏洞攻击中所示,也不会完全失败。利用此漏洞攻击会将恶意Perl模块写入/tmp/root.pm,并提供环境变量PERL5OPT=-Mroot和PERL5LIB=/tmp来实现任意代码执行。但是,这是对本地权限提升漏洞的攻击,一般技术理想情况下不需要访问文件系统。看一下BLATY对同一CVE的攻击,该攻击不需要创建文件,而是使用了环境变量PERL5OPT=-d和PERL5DB=System(";sh";);exit;。2013年还使用了相同的环境变量来解决CTF挑战。

通用技术的最后一个精妙之处是使用单个环境变量,而不是两个。@ust insteven发现这可以通过利用PERL5OPT=-M来实现。虽然可以使用-m或-M来加载Perl模块,但是-M选项允许在模块名称之后添加额外的代码。

阅读python(1)手册页的环境变量部分,PYTHONSTARTUP最初看起来可能是一个简单的解决方案。它允许指定将在交互模式下显示提示之前执行的Python脚本的路径。交互模式要求似乎不是问题,因为可以使用PYTHONINSPECT环境变量进入交互模式,这与在命令行上指定-i相同。但是,-i选项的文档解释说,当使用要执行的脚本启动Python时,不会使用PYTHONSTARTUP。这意味着PYTHONSTARTUP和PYTHONINSPECT不能组合,PYTHONSTARTUP只有在立即启动Python REPL时才有效。这最终意味着PYTHONSTARTUP不可行,因为它在执行常规Python脚本时没有效果。

其他看起来有希望的环境变量是PYTHONHOME和PYTHONPATH。这两种方法都允许您执行任意代码,但也要求您能够在文件系统上创建目录和文件。可以通过使用proc文件系统和/或ZIP文件来放松这些要求。

如果其余的大多数环境变量包含非空字符串,则简单地检查它们,如果包含,则切换通常为良性的设置。其中一个罕见的例外是PYTHONWARNINGS。

PYTHONWARNINGS的文档指出,它等效于指定-W选项。-W选项用于警告控制,以指定打印哪些警告及其频率。参数的完整形式为action:Message:Category:Module:Line。虽然警告控制似乎不是一个有前途的线索,但在检查实现后,情况很快就改变了。

[.]。def_getategory(CATEGORY):如果不是CATEGORY:如果';.';不在CATEGORY中,则返回警告:将内置导入为m Klass=CATEGORY否则:MODULE,_,KLASS=CATEGORY。rPartition(';.';)尝试:m=__import__(MODULE,NONE,NONE,[Klass]),除ImportError:RAISE_OptionError(";无效模块名称:%r";%(MODULE,))从无[.]。

上面的代码显示,只要我们指定的类别包含一个点,我们就可以触发导入任意的Python模块。

下一个问题是,Python标准库中的绝大多数模块在导入时运行的代码非常少。它们倾向于只定义稍后要使用的类,即使它们提供要运行的代码,通常也会通过检查__main__变量来保护代码(以检测文件是直接导入还是直接运行)。

一个意想不到的例外是反重力舱。Python开发人员在2008年加入了一个复活节彩蛋,运行导入反重力可以触发该彩蛋。此导入将立即打开您的浏览器,打开xkcd漫画,该漫画开玩笑说,在Python中导入反重力将赋予您飞行的能力。

至于反重力模块如何打开您的浏览器,它使用标准库中的另一个模块,称为WebBrowser。这个模块检查你的路径中是否有各种各样的浏览器,包括马赛克、Opera、Skipstone、Konquor、Chrome、Chrome、Firefox、Links、elink和Lynx。它还接受允许您指定应该执行哪个进程的环境变量浏览器。不可能在环境变量中向进程提供参数,而xkcd comic URL是该命令的一个硬编码参数。

将其转换为任意代码执行的能力取决于系统上有哪些其他可执行文件。

一种方法是利用Perl,它通常安装在系统上,甚至可以在标准Python docker映像中使用。但是,Perl二进制文件本身不能使用。这是因为第一个也是唯一一个参数是xkcd漫画URL。comic URL参数将导致错误,并且进程在不使用PERL5OPT环境变量的情况下退出。

$docker run-e&e;PERL5OPT=-mbase;print(`id`);exit';perl:5.30.2 perl https://xkcd.com/353/无法打开perl script";https://xkcd.com/353/";:没有这样的文件或目录

幸运的是,当Perl可用时,提供默认的Perl脚本也很常见,比如perldoc和perlThanks。这些脚本也会出错并以无效参数退出,但本例中的错误发生在处理PERL5OPT环境变量之后。这意味着您可以利用本文前面详细介绍的Perl环境变量有效负载。

$docker Run-e';PERL5OPT=-mbase;Print(`id`);退出';perl:5.30.2 perldoc https://xkcd.com/353/uid=0(根)GID=0(根)组=0(根)$RUN-e';PERL5OPT=-mbase;Print(`id`);退出';perl:5.30.2 perl感谢https://xkcd.com/353/uid=0(根)GID=0(根)组=。

$docker run-e';PYTHONWARNINGS=all:0:antigravity.x:0:0';-e&e&39;Browser=perlThanks';-e';PERL5OPT=-mbase;print(`id`);exit;';python:2.7.18 python/dev/null uid=0(根)gid=0(根)组=0(根)忽略无效的-W选项:未知警告类别:';反重力。x';$docker run-e';PYTHONWARNINGS=ALL:0:Antigravity.x:0:0';-e';Browser=perlThanks';-e';PERL5OPT=-mbase;print(`id`);exit;';python:3.8.2 python/dev/null uid=0(根)gid=0(根)组=0(根)无效-W选项忽略:未知警告类别:';antit。

MichałBentkowski的一篇博客文章为利用Kibana提供了有效载荷。原型污染漏洞用于设置任意环境变量,从而导致任意命令执行。Michał的有效负载使用了NODE_OPTIONS环境变量和proc文件系统,特别是/proc/self/environ。

尽管Michał的技术很有创意,并且完美地克服了它们的弱点,但这项技术并不总是能保证有效,而且有一些限制因素可以很好地消除。

第一个限制是,只有当内容在语法上是有效的JavaScript时,使用/proc/self/environ才是可行的。这需要能够创建环境变量并使其首先出现在/proc/self/environ的内容中,或者知道/强制使用将首先出现的环境变量的名称并覆盖它的值。

另一个约束,因为第一个环境变量的值以一行注释(//)结束。因此,其他环境变量中的任何换行符都可能导致语法错误并阻止有效负载执行。使用多行注释(/*)不会解决此问题,因为它们必须关闭才能在语法上有效。因此,在环境变量包含换行符的极少数情况下,需要知道/强制环境变量的名称,并将其值覆盖为不包含换行符的新值。

如果您运行ltrace-e getenv php/dev/null,您会发现php使用phPRC环境变量。该环境变量在试图查找和加载配置文件php.ini时使用。neex对CVE-2019-11043的攻击使用了一系列php设置来实现任意代码执行。Orange Tsai也有一篇很棒的博客文章,介绍了如何为相同的CVE创建他们自己的利用漏洞,该CVE使用的设置列表略有不同。利用这些知识,再加上从以前的NodeJS技术中获得的知识,再加上Brendan Scarvell的一些帮助,为PHP找到了两个环境变量的解决方案。

尚未找到Ruby的通用解决方案。Ruby确实接受环境变量RUBYOPT来指定命令行选项。手册页说明RUBYOPT只能包含-d、-E、-I、-K、-r、-T、-U、-v、-w、-W、--debug、--Disable-Feature和--Enable-FEATURE。最有希望的选项是-r,它会导致Ruby使用REQUIRED加载库。但是,这是有限的。

在Fedora系统上安装Ruby之后,json gem中的tools/server.rb文件就是一个有点有用的.rb文件的示例。当需要此文件时,Web服务器将启动,如下所示:

$docker run-it--env';RUBYOPT=-r/usr/share/gems/gems/json-2.3.0/tools/server.rb';fedora:33/bin/bash-c&39;dnf install-y ruby 1&>/dev/null;ruby/dev/null&39;浏览到:http://27dfc3850fbe:6666[2020-06-17 05:43:47]info WEBrick 1.6.0[2020-06-17 05:43:47]info ruby 2.7.1(2020-03-31)[x86_64-linux][2020-06-17 05:43:47]info weberick::HTTPServer#Start:PID=28 port=6666。

坚持使用Fedora,另一种方法是利用这样一个事实,即/usr/bin/ruby实际上是一个启动/usr/bin/ruby-mri的Bash脚本。该脚本调用可以被环境变量覆盖的Bash函数。

$docker run--env';BASH_FUNC_DECLAR%%=(){id;exit;}';fedora:33/bin/bash-c';DNF install ruby-y 1&>;/dev/null;ruby/dev/null';uid=0(Root)gid=0(Root)group=0(Root)。

这篇文章探索了环境变量的有趣用例,这些用例可以帮助您在各种脚本语言解释器上实现任意代码执行,而无需将文件写入磁盘。希望您已经喜欢阅读,并受到启发,希望找到并分享这些和其他脚本语言的改进后的有效负载。如果您找到了一种针对Ruby的通用技术,我们将非常有兴趣了解您是如何实现这一点的。