
CVE-2021-3560 का गहन विश्लेषण, जो Linux PolKit में एक स्थानीय विशेषाधिकार वृद्धि भेद्यता है। इसमें मूल कारण विश्लेषण, शोषण तंत्र, और रूट एक्सेस की ओर ले जाने वाली रेस कंडीशन का प्रदर्शन शामिल है।
[toc]
भेद्यता आईडी: CVE-2021-3560
भेद्यता स्कोर:
भेद्यता उत्पाद: linux PolKit (polkitd)
प्रभावित सीमा: स्रोत कोड 0.113 में पेश किया गया; https://www.venustech.com.cn/new_type/aqtg/20210611/22788.html
उपयोग की शर्तें: linux स्थानीय; dbus + polkitd मौजूद
उपयोग प्रभाव: स्थानीय विशेषाधिकार वृद्धि
स्रोत कोड प्राप्त करें:
apt source accountsserviceapt source dbusubuntu डिफ़ॉल्ट छवि ubuntu-20.04.2 पुन: प्रस्तुत करने के लिए पर्याप्त है: http://old-releases.ubuntu.com/releases/20.04.2/ubuntu-20.04.2-desktop-amd64.iso
पहले समस्या कोड देखें
/polkit-0.113/src/polkit/polkitsystembusname.c : 388 : polkit_system_bus_name_get_creds_sync
static void
on_retrieved_unix_uid_pid (GObject *src,
GAsyncResult *res,
gpointer user_data)
{
AsyncGetBusNameCredsData *data = user_data;
GVariant *v;
v = g_dbus_connection_call_finish ((GDBusConnection*)src, res,
data->caught_error ? NULL : data->error);
if (!v)
{
data->caught_error = TRUE; //某些原因失败之后设置error位
}
else
{
··· ···//执行成功的数据处理
}
··· ···
}
static gboolean
polkit_system_bus_name_get_creds_sync (PolkitSystemBusName *system_bus_name,
guint32 *out_uid,
guint32 *out_pid,
GCancellable *cancellable,
GError **error)
{
gboolean ret = FALSE;
AsyncGetBusNameCredsData data = { 0, }; //data 被初始化为0
··· ···
g_dbus_connection_call (connection,
"org.freedesktop.DBus", /* name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetConnectionUnixUser", /* method */
g_variant_new ("(s)", system_bus_name->name),
G_VARIANT_TYPE ("(u)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
on_retrieved_unix_uid_pid, //回调函数
&data);
g_dbus_connection_call (connection,
"org.freedesktop.DBus", /* name */
"/org/freedesktop/DBus", /* object path */
"org.freedesktop.DBus", /* interface name */
"GetConnectionUnixProcessID", /* method */
g_variant_new ("(s)", system_bus_name->name),
G_VARIANT_TYPE ("(u)"),
G_DBUS_CALL_FLAGS_NONE,
-1,
cancellable,
on_retrieved_unix_uid_pid, //回调函数
&data);
while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error))
g_main_context_iteration (tmp_context, TRUE); //等待总线那边执行完毕,uid、pid都处理完毕或异常
if (out_uid)
*out_uid = data.uid;
if (out_pid)
*out_pid = data.pid;
ret = TRUE; //无论如何都是 TRUE?
out:
if (tmp_context)
{
g_main_context_pop_thread_default (tmp_context);
g_main_context_unref (tmp_context);
}
if (connection != NULL)
g_object_unref (connection);
return ret;
}
polkit_system_bus_name_get_creds_sync फ़ंक्शन में, यह फ़ंक्शन कुछ अनुरोधों के लिए प्रमाणीकरण करता है (विशिष्ट तर्क परिदृश्य बाद में विस्तार से बताया जाएगा)। यह क्रमशः org.freedesktop.DBus बस के माध्यम से GetConnectionUnixUser विधि और GetConnectionUnixProcessID विधि को कॉल करता है। ये दो विधियाँ org.freedesktop.DBus बस द्वारा प्रदान की जाती हैं, जो अनुरोध प्रक्रिया के PID और उपयोगकर्ता UID को प्राप्त करती हैं, और फिर उन्हें कॉलबैक फ़ंक्शन on_retrieved_unix_uid_pid में भेजती हैं जो लौटाए गए डेटा को संसाधित करता है। फिर यह बस की ओर से प्रक्रिया पूरी होने तक ब्लॉक करता है।
on_retrieved_unix_uid_pid फ़ंक्शन में देखा जा सकता है कि यह बस से लौटाए गए परिणाम के आधार पर तय करता है कि कोई अपवाद हुआ है या नहीं (error?), और फिर अपवाद बिट सेट करता है। यदि सफल होता है, तो यह उपयोगकर्ता UID या प्रक्रिया PID लौटाता है। लेकिन polkit_system_bus_name_get_creds_sync फ़ंक्शन में, बस से परिणाम लौटने की प्रतीक्षा करने के बाद (प्रसंस्करण पूर्ण होने का संकेत यह है कि UID और PID दोनों संसाधित हो गए हैं या कोई अपवाद हुआ है), दूसरे शब्दों में, तार्किक रूप से बस से लौटने के तीन संभावित परिणाम हैं: उपयोगकर्ता UID 0 प्राप्त करना (विशेषाधिकार प्राप्त उपयोगकर्ता); उपयोगकर्ता UID गैर-0 प्राप्त करना (सामान्य उपयोगकर्ता); अपवाद। लेकिन बाद के प्रसंस्करण में, अपवाद को संभाला नहीं जाता है, और सीधे out_uid को बस से लौटाए गए data.uid पर सेट कर दिया जाता है, फिर ret को सीधे TRUE पर सेट कर दिया जाता है:
while (!((data.retrieved_uid && data.retrieved_pid) || data.caught_error))
g_main_context_iteration (tmp_context, TRUE); //等待总线那边执行完毕,uid、pid都处理完毕或异常
if (out_uid)
*out_uid = data.uid;
if (out_pid)
*out_pid = data.pid;
ret = TRUE; //无论如何都是 TRUE?
लेकिन एक स्थिति को नजरअंदाज किया गया है: जब DBUS बस की ओर से निष्पादन में अपवाद होता है, जिसके कारण कॉलबैक फ़ंक्शन अपवाद बिट सेट करके वापस आ जाता है, और data डेटा को बिल्कुल संसाधित नहीं करता है। चूंकि data को 0 से प्रारंभ किया गया था, इसके परिणामस्वरूप बाद में out_uid को सीधे 0 पर सेट कर दिया जाता है, जो विशेषाधिकार प्राप्त उपयोगकर्ता है।
तो यह समस्याग्रस्त polkit_system_bus_name_get_creds_sync फ़ंक्शन कहाँ उपयोग किया जाता है?
भेद्यता बिंदु polkitd प्रक्रिया में है:
polkit एक एप्लिकेशन-स्तरीय टूलसेट है जो विभिन्न प्राथमिकता वाली प्रक्रियाओं के बीच संचार को सक्षम करने के लिए अनुमति नियमों को परिभाषित और ऑडिट करता है: नियंत्रण निर्णय एक समान ढांचे में केंद्रित होते हैं, यह तय करते हुए कि क्या कम प्राथमिकता वाली प्रक्रिया को उच्च प्राथमिकता वाली प्रक्रिया तक पहुंचने का अधिकार है।
संक्षेप में, यह एक पृष्ठभूमि में चलने वाली रूट प्रक्रिया है जो अन्य कम विशेषाधिकार वाली प्रक्रियाओं द्वारा उच्च विशेषाधिकार वाली प्रक्रियाओं द्वारा प्रदान की जाने वाली सुविधाओं तक पहुंचने के लिए अनुमति निर्णय लेने के लिए इंटरप्रोसेस संचार के माध्यम से काम करती है। चूंकि यह प्रक्रिया gdb के साथ जुड़ने पर पुनरारंभ हो जाती है, हमें केवल स्रोत कोड स्तर पर विश्लेषण करना होगा और भेद्यता फ़ंक्शन के ट्रिगर पथ का पता लगाना होगा:
polkitd में बड़े पैमाने पर gio DBUS इंटरप्रोसेस संचार ढांचे का उपयोग किया गया है, आप पहले से मैनुअल देख सकते हैं, कुछ महत्वपूर्ण फ़ंक्शन आदि पर ध्यान दें।
आगे हम polkitd के भेद्यता ट्रिगर पथ का विश्लेषण करते हैं,
सबसे पहले main:
polkit-0.113\src\polkitbackend\polkitd.c : 155 : main
int
main (int argc,
char **argv)
{
··· ···
··· ···
loop = g_main_loop_new (NULL, FALSE);
sigint_id = g_unix_signal_add (SIGINT,
on_sigint,
NULL);
name_owner_id = g_bus_own_name (G_BUS_TYPE_SYSTEM,
"org.freedesktop.PolicyKit1", //一个总线名称注册
G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT |
(opt_replace ? G_BUS_NAME_OWNER_FLAGS_REPLACE : 0),
on_bus_acquired, //该总线被访问时的回调函数
on_name_acquired,
on_name_lost,
NULL,
NULL);
g_print ("Entering main event loop\n");
g_main_loop_run (loop); //循环启动
··· ···
··· ···
}
पहले org.freedesktop.PolicyKit1 नामक एक बस पंजीकृत करता है, फिर तीन कॉलबैक फ़ंक्शन हैं, मुख्य रूप से on_bus_acquired पर ध्यान दें, जो बस तक पहुंचने पर कॉल किया जाता है।
on_bus_acquired फ़ंक्शन में सीधे एक पंजीकरण प्रकार का फ़ंक्शन polkit_backend_authority_register कॉल किया जाता है।